00001 /*!@file GUI/XWindow.C Implementation for a simple window class */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/GUI/XWindow.C $ 00035 // $Id: XWindow.C 14329 2010-12-21 19:52:08Z itti $ 00036 // 00037 00038 #include "GUI/XWindow.H" 00039 00040 #include "Image/Image.H" 00041 #include "Image/Layout.H" 00042 #include "Image/MathOps.H" 00043 #include "Image/Pixels.H" 00044 #include "Util/Assert.H" 00045 #include "Util/StringConversions.H" 00046 #include "Util/log.H" 00047 #include "Util/sformat.H" 00048 #include "rutz/atomic.h" 00049 #include "rutz/mutex.h" 00050 #include "rutz/pipe.h" 00051 00052 #include <X11/Xutil.h> 00053 #include <cerrno> 00054 #include <iostream> 00055 #include <fstream> 00056 #include <stdlib.h> 00057 #include <sys/shm.h> 00058 #include <sys/stat.h> 00059 00060 /* On Mac OSX, the multi-threading approach of using a pthreads mutex 00061 to guard the global Display connection seems to not work 00062 consistently and we get intermittent errors like this: 00063 00064 Xlib: unexpected async reply (sequence 0x23d)! 00065 Xlib: sequence lost (0x10000 > 0x23d) in reply type 0x0! 00066 00067 The official X11 way to do threads is to initialize with 00068 XInitThreads(), then lock/unlock the display with XLockDisplay() 00069 and XUnlockDisplay(). The man page says that those aren't required 00070 if an external mutex is used, but in our case that doesn't seem to 00071 work. Switching to XInitThreads()/XLockDisplay()/XUnlockDisplay() 00072 does seem to eliminate the Xlib errors in practice. The errors also 00073 seem to go away if we just do XInitThreads() but then do the 00074 locking with a pthreads mutex as before. Maybe the problem is that 00075 we do use some unlocked Xlib calls (though they are calls that 00076 don't require a Display*). 00077 00078 For now, we can keep the code for both locking styles around and 00079 toggle between them at compile time with the 00080 XWINDOW_PTHREADS_LOCKING macro. 00081 */ 00082 00083 // #define XWINDOW_PTHREADS_LOCKING 00084 00085 namespace 00086 { 00087 int fourByteAlign(int v) 00088 { 00089 const int remainder = v % 4; 00090 return (remainder == 0) ? v : v + (4-remainder); 00091 } 00092 00093 int getByteOrder() 00094 { 00095 union 00096 { 00097 int i; 00098 byte b[sizeof(int)]; 00099 } u; 00100 memset(&u, 0, sizeof(u)); 00101 u.b[0] = 1; 00102 if (u.i == 1) 00103 return LSBFirst; 00104 00105 // else ... 00106 return MSBFirst; 00107 } 00108 00109 Display* g_display = 0; 00110 00111 #ifdef XWINDOW_PTHREADS_LOCKING 00112 pthread_mutex_t g_display_mutex = PTHREAD_MUTEX_INITIALIZER; 00113 #endif 00114 00115 pthread_once_t display_init_once = PTHREAD_ONCE_INIT; 00116 00117 void display_init() 00118 { 00119 XInitThreads(); 00120 #ifdef XWINDOW_PTHREADS_LOCKING 00121 pthread_mutex_init(&g_display_mutex, 0); 00122 #endif 00123 const char* displayname = getenv("DISPLAY"); 00124 if (displayname == 0 || displayname[0] == '\0') 00125 { 00126 LINFO("the DISPLAY environment variable is not set; " 00127 "assuming DISPLAY=\":0.0\""); 00128 displayname = ":0.0"; 00129 } 00130 g_display = XOpenDisplay(displayname); 00131 } 00132 00133 #ifdef XWINDOW_PTHREADS_LOCKING 00134 # define LOCK_DISPLAY GVX_MUTEX_LOCK(&g_display_mutex) 00135 #else 00136 00137 struct DisplayLock 00138 { 00139 DisplayLock(Display* d) : dd(d) { XLockDisplay(d); } 00140 00141 ~DisplayLock() { XUnlockDisplay(dd); } 00142 00143 private: 00144 DisplayLock(const DisplayLock&); 00145 DisplayLock& operator=(const DisplayLock&); 00146 00147 Display* const dd; 00148 }; 00149 00150 # define LOCK_DISPLAY DisplayLock tmplock(g_display) 00151 00152 #endif // !defined(XWINDOW_PTHREADS_LOCKING) 00153 } 00154 00155 // ###################################################################### 00156 struct XWindow::XWinImage 00157 { 00158 XWinImage(Display* dpy, bool attemptShm, const Dims& windims, 00159 const int depth); 00160 00161 ~XWinImage(); 00162 00163 void destroy(Display* dpy); 00164 00165 void destroyImage(); 00166 00167 void createImage(Display* dpy, Visual* vis, const Dims& dims); 00168 00169 Dims getDims() const { return itsImgDims; } 00170 00171 byte* getBuf() const { return reinterpret_cast<byte*>(itsBuf); } 00172 00173 void copyPixelsFrom(const Image<byte>& img, const Point2D<int>& winpos); 00174 00175 void copyPixelsFrom(const Image<PixRGB<byte> >& img, const Point2D<int>& winpos); 00176 00177 void redraw(Display* dpy, Window win, GC gc, const Point2D<int>& pos); 00178 00179 private: 00180 const int itsBytesPerPixel; 00181 00182 XImage* itsImage; //<! pointer to image data 00183 00184 XShmSegmentInfo itsShmInfo; //<! info on shared memory segment 00185 char* itsBuf; //<! shared memory buffer 00186 bool itsUsingShm; //<! true if we can use shared memory 00187 Dims itsImgDims; //<! XImage size 00188 bool itsDestroyed; 00189 }; 00190 00191 static size_t get_shmmax() 00192 { 00193 { 00194 std::ifstream f("/proc/sys/kernel/shmmax"); 00195 if (f.is_open()) 00196 { 00197 size_t shmmax = 0; 00198 f >> shmmax; 00199 return shmmax; 00200 } 00201 } 00202 00203 const char* progs[] = { "/usr/sbin/sysctl", "/sbin/sysctl" }; 00204 00205 const char* args[] = { "kern.sysv.shmmax", "kernel.shmmax" }; 00206 00207 for (size_t i = 0; i < sizeof(progs) / sizeof(progs[0]); ++i) 00208 { 00209 try 00210 { 00211 rutz::exec_pipe p("r", progs[i], args[i], (const char*) 0); 00212 std::string w; 00213 while (p.stream() >> w) 00214 { 00215 if (w.size() > 0 && isdigit(w[0])) 00216 return fromStr<size_t>(w); 00217 } 00218 } 00219 catch (...) { continue; } 00220 } 00221 00222 return 0; 00223 } 00224 00225 // ###################################################################### 00226 XWindow::XWinImage::XWinImage(Display* dpy, bool attemptShm, 00227 const Dims& windims, 00228 const int depth) 00229 : 00230 itsBytesPerPixel(depth == 16 ? 2 : 4), 00231 itsImage(0), 00232 itsShmInfo(), 00233 itsBuf(0), 00234 itsUsingShm(false), 00235 itsImgDims(), 00236 itsDestroyed(false) 00237 { 00238 // Get a padded row width that is 4-byte aligned 00239 const int padded_width = fourByteAlign(windims.w()); 00240 00241 // allocate an image buffer of the size of the window: 00242 const size_t bufsize = padded_width*windims.h()*itsBytesPerPixel; 00243 LDEBUG("bufsize=%"ZU, bufsize); 00244 00245 if (attemptShm) 00246 { 00247 itsShmInfo.shmid = shmget(IPC_PRIVATE, bufsize, IPC_CREAT | 0777); 00248 LDEBUG("shmid=%d", itsShmInfo.shmid); 00249 if (itsShmInfo.shmid == -1) 00250 { 00251 const int errno_save = errno; 00252 00253 const size_t shmmax = get_shmmax(); 00254 const std::string shmmax_info = 00255 shmmax > 0 00256 ? sformat("%"ZU, shmmax) 00257 : "unknown"; 00258 00259 errno = errno_save; 00260 00261 const std::string extra_help = 00262 (errno == ENOMEM 00263 || (shmmax != 0 && bufsize > shmmax)) 00264 ? ("; try increasing the shmmax value in " 00265 "/proc/sys/kernel/shmmax, or with " 00266 "sysctl kern.sysv.shmmax (BSD) " 00267 "or with sysctl kernel.shmmax (Linux)") 00268 : ""; 00269 00270 PLERROR("shmget() failed (requested size=%"ZU", shmmax=%s)%s", 00271 bufsize, shmmax_info.c_str(), extra_help.c_str()); 00272 00273 LINFO("switching to slower non-shared memory approach"); 00274 } 00275 else 00276 { 00277 itsBuf = (char*)shmat(itsShmInfo.shmid, NULL, 0); // attach shared memory 00278 if (itsBuf == 0) LFATAL("Cannot get shared memory"); 00279 itsShmInfo.shmaddr = itsBuf; // link buffer to shminfo structure 00280 itsShmInfo.readOnly = False; 00281 XShmAttach(dpy, &itsShmInfo); // now attach to X 00282 itsUsingShm = true; 00283 } 00284 } 00285 00286 if (!itsUsingShm) 00287 { 00288 itsBuf = (char*)malloc(bufsize); 00289 LDEBUG("buffer: %p", itsBuf); 00290 if (itsBuf == NULL) LFATAL("Cannot allocate image buffer"); 00291 } 00292 } 00293 00294 // ###################################################################### 00295 XWindow::XWinImage::~XWinImage() 00296 { 00297 // we can't cleanly run the destruction routines that we need here 00298 // in the destructor, because destroy() requires a Display* with 00299 // g_display_mutex already locked, and we have no way to pass the 00300 // Display* into the destructor; therefore, we instead require that 00301 // XWinImage::destroy() be called explicitly before the destructor 00302 // is run, and we verify that this has actually been done with an 00303 // assertion: 00304 00305 ASSERT(itsDestroyed == true); 00306 } 00307 00308 // ###################################################################### 00309 void XWindow::XWinImage::destroy(Display* dpy) 00310 { 00311 if (itsUsingShm) 00312 { 00313 XShmDetach(dpy, &itsShmInfo); 00314 this->destroyImage(); 00315 shmdt(itsShmInfo.shmaddr); 00316 shmctl(itsShmInfo.shmid, IPC_RMID, NULL); 00317 } 00318 else 00319 { 00320 this->destroyImage(); 00321 free(itsBuf); 00322 } 00323 00324 itsDestroyed = true; 00325 } 00326 00327 // ###################################################################### 00328 void XWindow::XWinImage::destroyImage() 00329 { 00330 if (itsImage) 00331 { 00332 // We have to set the XImage's data to zero before destroying 00333 // the XImage, because otherwise the XImage will want to free 00334 // the data as it is destroyed, which means that 'itsBuf' would 00335 // become a dangling pointer, and that we'd be double-free'ing 00336 // it when we try to free() it ourselves later. 00337 itsImage->data = 0; 00338 XDestroyImage(itsImage); 00339 itsImage = 0; 00340 } 00341 } 00342 00343 // ###################################################################### 00344 void XWindow::XWinImage::createImage(Display* dpy, Visual* vis, 00345 const Dims& dims) 00346 { 00347 this->destroyImage(); 00348 00349 uint depth = 0; 00350 int pad = 0; 00351 00352 // make sure we use the correct depth values 00353 if (itsBytesPerPixel == 2) 00354 { depth = 16; pad = 16;} 00355 else if (itsBytesPerPixel == 4) 00356 { depth = 24; pad = 32;} 00357 else 00358 LFATAL("bogus bytes-per-pixel value '%d'", itsBytesPerPixel); 00359 00360 if (itsUsingShm) 00361 // ok for shared memory; X will allocate the buffer: 00362 itsImage = XShmCreateImage(g_display, vis, depth, ZPixmap, 00363 itsBuf, &itsShmInfo, dims.w(), dims.h()); 00364 else 00365 // cannot alloc in shared memory... do conventional alloc 00366 itsImage = XCreateImage(g_display, vis, depth, ZPixmap, 0, 00367 itsBuf, dims.w(), dims.h(), pad, 00368 fourByteAlign(itsBytesPerPixel * dims.w())); 00369 00370 // by default, XCreateImage() chooses the byte order according to 00371 // the machine running X server; however since we are building the 00372 // image here on the X client, we want to switch the byte order to 00373 // match the byte order on the client: 00374 00375 LDEBUG("X server byte order is %s (%d)", 00376 itsImage->byte_order == LSBFirst ? "LSBFirst" : 00377 itsImage->byte_order == MSBFirst ? "MSBFirst" : "unknown", 00378 itsImage->byte_order); 00379 00380 itsImage->byte_order = getByteOrder(); 00381 00382 LDEBUG("using image byte order %s (%d)", 00383 itsImage->byte_order == LSBFirst ? "LSBFirst" : 00384 itsImage->byte_order == MSBFirst ? "MSBFirst" : "unknown", 00385 itsImage->byte_order); 00386 00387 itsImgDims = dims; 00388 } 00389 00390 // ###################################################################### 00391 void XWindow::XWinImage::copyPixelsFrom(const Image<byte>& img, 00392 const Point2D<int>& winpos) 00393 { 00394 ASSERT(winpos.i >= 0); 00395 ASSERT(winpos.i + img.getWidth() <= itsImgDims.w()); 00396 ASSERT(winpos.j >= 0); 00397 ASSERT(winpos.j + img.getHeight() <= itsImgDims.h()); 00398 00399 const int w = img.getWidth(); 00400 const int h = img.getHeight(); 00401 00402 const byte* im = img.getArrayPtr(); 00403 byte* bu = reinterpret_cast<byte*>(itsBuf) 00404 + itsBytesPerPixel * (winpos.i + winpos.j * itsImgDims.w()); 00405 00406 if (itsBytesPerPixel == 2) 00407 { 00408 const int bytes_per_row = fourByteAlign(itsBytesPerPixel * itsImgDims.w()); 00409 00410 // 16 bit format: 565, lowest byte first 00411 for (int j = 0; j < h; ++j) 00412 { 00413 byte* bu_row = bu + j*bytes_per_row; 00414 for (int i = 0; i < w; ++i) 00415 { 00416 *bu_row++ = ((*im & 0x1C)<<3) | ((*im & 0xF8)>>3); 00417 *bu_row++ = ((*im & 0xE0)>>5) | (*im & 0xF8); 00418 im++; 00419 } 00420 } 00421 } 00422 else if (itsBytesPerPixel == 4) 00423 { 00424 const int wskip = itsBytesPerPixel * (itsImgDims.w() - img.getWidth()); 00425 00426 // 24 bit format with extra byte for padding 00427 switch (getByteOrder()) 00428 { 00429 case LSBFirst: 00430 for (int j = 0; j < h; ++j) 00431 { 00432 for (int i = 0; i < w; ++i) 00433 { *bu++ = *im; *bu++ = *im; *bu++ = *im++; *bu++ = 255; } 00434 bu += wskip; 00435 } 00436 break; 00437 00438 case MSBFirst: 00439 for (int j = 0; j < h; ++j) 00440 { 00441 for (int i = 0; i < w; ++i) 00442 { *bu++ = 255; *bu++ = *im; *bu++ = *im; *bu++ = *im++; } 00443 bu += wskip; 00444 } 00445 break; 00446 00447 default: 00448 LFATAL("invalid byte order %d", getByteOrder()); 00449 } 00450 } 00451 else 00452 LFATAL("bogus bytes-per-pixel value '%d'", itsBytesPerPixel); 00453 } 00454 00455 // ###################################################################### 00456 void XWindow::XWinImage::copyPixelsFrom(const Image<PixRGB<byte> >& img, 00457 const Point2D<int>& winpos) 00458 { 00459 ASSERT(winpos.i >= 0); 00460 ASSERT(winpos.i + img.getWidth() <= itsImgDims.w()); 00461 ASSERT(winpos.j >= 0); 00462 ASSERT(winpos.j + img.getHeight() <= itsImgDims.h()); 00463 00464 const int w = img.getWidth(); 00465 const int h = img.getHeight(); 00466 00467 const byte* im = reinterpret_cast<const byte*>(img.getArrayPtr()); 00468 byte* bu = reinterpret_cast<byte*>(itsBuf) 00469 + itsBytesPerPixel * (winpos.i + winpos.j * itsImgDims.w()); 00470 00471 if (itsBytesPerPixel == 2) 00472 { 00473 const int bytes_per_row = fourByteAlign(itsBytesPerPixel * itsImgDims.w()); 00474 00475 // 16 bit format: 565, lowest byte first 00476 for (int j = 0; j < h; ++j) 00477 { 00478 byte* bu_row = bu + j*bytes_per_row; 00479 for (int i = 0; i < w; ++i) 00480 { 00481 *bu_row++ = ((im[1] & 0x1C)<<3) | ((im[2] & 0xF8)>>3); 00482 *bu_row++ = ((im[1] & 0xE0)>>5) | (im[0] & 0xF8); 00483 im += 3; 00484 } 00485 } 00486 } 00487 else if (itsBytesPerPixel == 4) 00488 { 00489 const int wskip = itsBytesPerPixel * (itsImgDims.w() - img.getWidth()); 00490 00491 // 24 bit format with extra byte for padding 00492 switch (getByteOrder()) 00493 { 00494 case LSBFirst: 00495 for (int j = 0; j < h; ++j) 00496 { 00497 for (int i = 0; i < w; ++i) 00498 { *bu++ = im[2]; *bu++ = im[1]; *bu++ = *im; *bu++ = 255; im += 3; } 00499 bu += wskip; 00500 } 00501 break; 00502 00503 case MSBFirst: 00504 for (int j = 0; j < h; ++j) 00505 { 00506 for (int i = 0; i < w; ++i) 00507 { *bu++ = 255; *bu++ = *im; *bu++ = im[1]; *bu++ = im[2]; im += 3; } 00508 bu += wskip; 00509 } 00510 break; 00511 00512 default: 00513 LFATAL("invalid byte order %d", getByteOrder()); 00514 } 00515 } 00516 else 00517 LFATAL("bogus bytes-per-pixel value '%d'", itsBytesPerPixel); 00518 } 00519 00520 // ###################################################################### 00521 void XWindow::XWinImage::redraw(Display* dpy, Window win, GC gc, 00522 const Point2D<int>& pos) 00523 { 00524 if (itsImage) 00525 { 00526 if (itsUsingShm) 00527 XShmPutImage(dpy, win, gc, itsImage, 0, 0, 00528 pos.i, pos.j, 00529 itsImgDims.w(), itsImgDims.h(), 0); 00530 else 00531 XPutImage(dpy, win, gc, itsImage, 0, 0, 00532 pos.i, pos.j, 00533 itsImgDims.w(), itsImgDims.h()); 00534 } 00535 else 00536 { 00537 XClearWindow(dpy,win); 00538 } 00539 } 00540 00541 // ###################################################################### 00542 XWindow::XWindow(const Dims& dims, const int x, const int y, 00543 const char* title) 00544 : 00545 itsMapped(false) 00546 { 00547 if (dims.isEmpty()) 00548 LFATAL("window dimensions must be non-empty"); 00549 00550 itsWinDims = dims; 00551 00552 // connect to X server: 00553 pthread_once(&display_init_once, &display_init); 00554 if (g_display == 0) 00555 LFATAL("Cannot connect to X server"); 00556 00557 LOCK_DISPLAY; 00558 00559 // get default screen: 00560 const int screen = DefaultScreen(g_display); 00561 itsVisual = DefaultVisual(g_display, screen); 00562 itsDepth = DefaultDepth(g_display, screen); 00563 LDEBUG("Your screen depth is %d bpp", itsDepth); 00564 00565 // check if we are using automatic window placement: 00566 int x0 = x; 00567 int y0 = y; 00568 if (x0 == -1 && y0 == -1) 00569 { 00570 x0 = 0; 00571 y0 = 0; 00572 } 00573 00574 // open window: 00575 itsWindow = XCreateSimpleWindow(g_display, 00576 DefaultRootWindow(g_display), 00577 x0, y0, itsWinDims.w(), itsWinDims.h(), 2, 00578 BlackPixel(g_display, screen), 00579 WhitePixel(g_display, screen)); 00580 if (itsWindow <= 0) LFATAL("Cannot open window"); 00581 00582 // set title: 00583 XStoreName(g_display, itsWindow, (char*)title); 00584 itsTitle = title; 00585 00586 // pop this window up on the screen: 00587 XMapRaised(g_display, itsWindow); 00588 itsMapped = true; 00589 00590 // set position of window if not using auto-placement: 00591 if (x != -1 || y != -1) 00592 { 00593 XMoveWindow(g_display, itsWindow, x, y); 00594 } 00595 00596 // set events that we are interested in: 00597 XSelectInput(g_display, itsWindow, NoEventMask); // ExposureMask|ButtonPressMask 00598 00599 // flush X request queue to server: 00600 XFlush(g_display); 00601 00602 // create graphics context for later: 00603 itsGc = XCreateGC(g_display, itsWindow, 0, NULL); 00604 00605 XSetWindowBackground(g_display, itsWindow, 00606 WhitePixel(g_display, DefaultScreen(g_display))); 00607 00608 // alloc XImage in shared memory if posible (for faster display): 00609 itsAttemptShm = XShmQueryExtension(g_display); 00610 00611 // check if remote display, and disable shared memory if so: 00612 char* disp = getenv("DISPLAY"); 00613 if (disp != NULL && disp[0] != ':') 00614 { itsAttemptShm = 0; LDEBUG("Not using shared memory for remote display"); } 00615 00616 this->setDimsImpl(dims); 00617 } 00618 00619 // ###################################################################### 00620 void XWindow::setVisible(const bool v) 00621 { 00622 if (itsMapped != v) 00623 { 00624 Point2D<int> p1 = this->getPosition(); 00625 LDEBUG("old position %d,%d", p1.i, p1.j); 00626 00627 { 00628 LOCK_DISPLAY; 00629 00630 if (v) 00631 { 00632 LDEBUG("mapping window %s", itsTitle.c_str()); 00633 XMapWindow(g_display, itsWindow); 00634 } 00635 else 00636 { 00637 LDEBUG("unmapping window %s", itsTitle.c_str()); 00638 XUnmapWindow(g_display, itsWindow); 00639 } 00640 00641 itsMapped = v; 00642 } 00643 00644 Point2D<int> p2 = this->getPosition(); 00645 LDEBUG("new position %d,%d", p2.i, p2.j); 00646 } 00647 } 00648 00649 // ###################################################################### 00650 const char* XWindow::getTitle() const 00651 { 00652 return itsTitle.c_str(); 00653 } 00654 00655 // ###################################################################### 00656 void XWindow::setTitle(const char* title) 00657 { 00658 LOCK_DISPLAY; 00659 00660 XStoreName(g_display, itsWindow, (char*)title); 00661 itsTitle = title; 00662 } 00663 00664 // ###################################################################### 00665 void XWindow::setPosition(const int x, const int y) 00666 { 00667 LOCK_DISPLAY; 00668 00669 XMoveWindow(g_display, itsWindow, x, y); 00670 } 00671 00672 // ###################################################################### 00673 Point2D<int> XWindow::getPosition() 00674 { 00675 LOCK_DISPLAY; 00676 00677 XWindowAttributes xwa; 00678 XGetWindowAttributes(g_display, itsWindow, &xwa); 00679 return Point2D<int>(xwa.x, xwa.y); 00680 } 00681 00682 // ###################################################################### 00683 void XWindow::drawImage(const Image< PixRGB<byte> >& img, 00684 const int x, const int y, const bool resizeWindow) 00685 { 00686 if (resizeWindow) 00687 this->setDims(img.getDims()); 00688 00689 ASSERT(x >= 0 && y >= 0 && x < itsWinDims.w() && y < itsWinDims.h()); 00690 const int w = img.getWidth(); 00691 const int h = img.getHeight(); 00692 const Dims d = img.getDims(); 00693 ASSERT(x + w <= itsWinDims.w() && y + h <= itsWinDims.h()); 00694 if (d != itsXimage->getDims()) 00695 { 00696 LOCK_DISPLAY; 00697 itsXimage->createImage(g_display, itsVisual, d); 00698 } 00699 00700 itsXimage->copyPixelsFrom(img, Point2D<int>(0,0)); 00701 00702 itsImgPos = Point2D<int>(x, y); 00703 00704 this->redrawImage(); 00705 } 00706 00707 // ###################################################################### 00708 void XWindow::drawImage(const Image<byte>& img, const int x, const int y, 00709 const bool resizeWindow) 00710 { 00711 if (resizeWindow) 00712 this->setDims(img.getDims()); 00713 00714 ASSERT(x >= 0 && y >= 0 && x < itsWinDims.w() && y < itsWinDims.h()); 00715 const int w = img.getWidth(); 00716 const int h = img.getHeight(); 00717 const Dims d = img.getDims(); 00718 ASSERT(x + w <= itsWinDims.w() && y + h <= itsWinDims.h()); 00719 if (d != itsXimage->getDims()) 00720 { 00721 LOCK_DISPLAY; 00722 itsXimage->createImage(g_display, itsVisual, d); 00723 } 00724 00725 itsXimage->copyPixelsFrom(img, Point2D<int>(0,0)); 00726 00727 itsImgPos = Point2D<int>(x, y); 00728 00729 this->redrawImage(); 00730 } 00731 00732 // ###################################################################### 00733 void XWindow::drawImage(const Image<float>& img, 00734 const int x, const int y, 00735 bool normalize, const bool resizeWindow) 00736 { 00737 if (resizeWindow) 00738 this->setDims(img.getDims()); 00739 00740 Image<float> image(img); 00741 00742 if (normalize) 00743 inplaceNormalize(image, 0.0f, 255.0f); 00744 else 00745 inplaceClamp(image, 0.0f, 255.0f); 00746 00747 this->drawImage(Image<byte>(image), x, y); 00748 } 00749 00750 // ###################################################################### 00751 void XWindow::drawRgbLayout(const Layout<PixRGB<byte> >& layout, 00752 const int x, const int y, 00753 const bool resizeWindow) 00754 { 00755 this->drawLayout<PixRGB<byte> >(layout, x, y, resizeWindow); 00756 } 00757 00758 // ###################################################################### 00759 void XWindow::drawGrayLayout(const Layout<byte>& layout, 00760 const int x, const int y, 00761 const bool resizeWindow) 00762 { 00763 this->drawLayout<byte>(layout, x, y, resizeWindow); 00764 } 00765 00766 // ###################################################################### 00767 XWindow::~XWindow() 00768 { 00769 LOCK_DISPLAY; 00770 00771 LDEBUG("Closing down..."); 00772 itsXimage->destroy(g_display); 00773 itsXimage.reset(0); 00774 00775 if (itsGc) XFreeGC(g_display, itsGc); 00776 if (g_display && itsWindow) 00777 { 00778 LDEBUG("XDestroyWindow..."); 00779 XDestroyWindow(g_display, itsWindow); 00780 XFlush(g_display); 00781 } 00782 00783 // NB: We don't call XCloseDisplay() here because other windows 00784 // (current and future) might still want to use the existing 00785 // Display*. So we just rely on the display being cleaned up 00786 // automatically when the program exits. 00787 } 00788 00789 // ###################################################################### 00790 void XWindow::redrawImage() 00791 { 00792 LOCK_DISPLAY; 00793 00794 itsXimage->redraw(g_display, itsWindow, itsGc, itsImgPos); 00795 XFlush(g_display); 00796 } 00797 00798 // ###################################################################### 00799 Dims XWindow::getDims() const 00800 { 00801 return itsWinDims; 00802 } 00803 00804 // ###################################################################### 00805 void XWindow::setDims(const Dims& dims) 00806 { 00807 if (dims == itsWinDims) 00808 return; 00809 00810 LOCK_DISPLAY; 00811 00812 this->setDimsImpl(dims); 00813 } 00814 00815 // ###################################################################### 00816 void XWindow::selectInput(long event_mask) const 00817 { 00818 LOCK_DISPLAY; 00819 00820 XSelectInput(g_display, itsWindow, event_mask); 00821 } 00822 00823 // ###################################################################### 00824 Atom XWindow::setWMProtocol(const char* atomname) const 00825 { 00826 LOCK_DISPLAY; 00827 00828 Atom a = XInternAtom(g_display, atomname, false); 00829 if (0 == XSetWMProtocols(g_display, itsWindow, &a, 1)) 00830 LFATAL("Error setting the WM protocol"); 00831 00832 return a; 00833 } 00834 00835 // ###################################################################### 00836 Bool XWindow::checkMaskEvent(long event_mask, XEvent* event) const 00837 { 00838 LOCK_DISPLAY; 00839 return XCheckWindowEvent(g_display, itsWindow, event_mask, event); 00840 } 00841 00842 // ###################################################################### 00843 Bool XWindow::checkTypedEvent(int event_type, XEvent* event) const 00844 { 00845 LOCK_DISPLAY; 00846 return XCheckTypedWindowEvent(g_display, itsWindow, event_type, event); 00847 } 00848 00849 // ###################################################################### 00850 void XWindow::setDimsImpl(const Dims& dims) 00851 { 00852 // NOTE: this function MUST be called with g_display_mutex already 00853 // locked! 00854 00855 // first release the lock that prevent the window from being resized: 00856 XSizeHints hints; 00857 hints.flags = PMinSize | PMaxSize; 00858 hints.min_width = 0; hints.max_width = 60000; 00859 hints.min_height = 0; hints.max_height = 60000; 00860 XSetWMNormalHints(g_display, itsWindow, &hints); 00861 00862 XResizeWindow(g_display, itsWindow, dims.w(), dims.h()); 00863 itsWinDims = dims; 00864 00865 if (itsXimage.is_valid()) 00866 itsXimage->destroy(g_display); 00867 00868 itsXimage.reset(new XWinImage(g_display, itsAttemptShm, itsWinDims, 00869 itsDepth)); 00870 00871 // prevent the window from being resized by the use (by fixing both 00872 // its minimum and maximum size to exactly itsWinDims): 00873 hints.flags = PMinSize | PMaxSize | PBaseSize; 00874 hints.min_width = hints.max_width = hints.base_width = itsWinDims.w(); 00875 hints.min_height = hints.max_height = hints.base_height = itsWinDims.h(); 00876 XSetWMNormalHints(g_display, itsWindow, &hints); 00877 } 00878 00879 // ###################################################################### 00880 template <class T> 00881 void XWindow::drawLayout(const Layout<T>& layout, 00882 const int x, const int y, 00883 const bool resizeWindow) 00884 { 00885 if (resizeWindow) 00886 this->setDims(layout.getDims()); 00887 00888 ASSERT(x >= 0 && y >= 0 && x < itsWinDims.w() && y < itsWinDims.h()); 00889 const int w = layout.getDims().w(); 00890 const int h = layout.getDims().h(); 00891 const Dims d = layout.getDims(); 00892 ASSERT(x + w <= itsWinDims.w() && y + h <= itsWinDims.h()); 00893 if (d != itsXimage->getDims()) 00894 { 00895 LOCK_DISPLAY; 00896 itsXimage->createImage(g_display, itsVisual, d); 00897 } 00898 00899 typedef std::pair<const Layout<T>*, Point2D<int> > tile; 00900 00901 std::vector<tile> q; 00902 00903 q.push_back(std::make_pair(&layout, Point2D<int>(0,0))); 00904 00905 while (!q.empty()) 00906 { 00907 tile t = q.back(); 00908 q.pop_back(); 00909 00910 const Layout<T>* const cur = t.first; 00911 const Point2D<int> p = t.second; 00912 00913 const size_t np = cur->numParts(); 00914 00915 if (np == 0) 00916 { 00917 if (cur->leafImage().initialized()) 00918 itsXimage->copyPixelsFrom(cur->leafImage(), p); 00919 } 00920 else if (cur->getDir() == Layout<T>::H) 00921 { 00922 Point2D<int> pp = p; 00923 for (size_t i = 0; i < np; ++i) 00924 { 00925 q.push_back(std::make_pair(&cur->part(i), pp)); 00926 pp.i += cur->part(i).getDims().w(); 00927 } 00928 } 00929 else 00930 { 00931 Point2D<int> pp = p; 00932 for (size_t i = 0; i < np; ++i) 00933 { 00934 q.push_back(std::make_pair(&cur->part(i), pp)); 00935 pp.j += cur->part(i).getDims().h(); 00936 } 00937 } 00938 } 00939 00940 itsImgPos = Point2D<int>(x, y); 00941 00942 this->redrawImage(); 00943 } 00944 00945 // ###################################################################### 00946 /* So things look consistent in everyone's emacs... */ 00947 /* Local Variables: */ 00948 /* indent-tabs-mode: nil */ 00949 /* End: */