XWindow.C

Go to the documentation of this file.
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: */
Generated on Sun May 8 08:04:48 2011 for iLab Neuromorphic Vision Toolkit by  doxygen 1.6.3