fltk 1.3.0rc3
About: FLTK (Fast Light Tool Kit) is a cross-platform C++ GUI toolkit for UNIX/Linux (X11), Microsoft Windows, and MacOS X. Release candidate.
  SfR Fresh Dox: fltk-1.3.0rc3-source.tar.gz ("inofficial" and yet experimental doxygen-generated source code documentation)  

Fl_x.cxx

Go to the documentation of this file.
00001 //
00002 // "$Id: Fl_x.cxx 8198 2011-01-06 10:24:58Z manolo $"
00003 //
00004 // X specific code for the Fast Light Tool Kit (FLTK).
00005 //
00006 // Copyright 1998-2010 by Bill Spitzak and others.
00007 //
00008 // This library is free software; you can redistribute it and/or
00009 // modify it under the terms of the GNU Library General Public
00010 // License as published by the Free Software Foundation; either
00011 // version 2 of the License, or (at your option) any later version.
00012 //
00013 // This library is distributed in the hope that it will be useful,
00014 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00016 // Library General Public License for more details.
00017 //
00018 // You should have received a copy of the GNU Library General Public
00019 // License along with this library; if not, write to the Free Software
00020 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00021 // USA.
00022 //
00023 // Please report all bugs and problems on the following page:
00024 //
00025 //     http://www.fltk.org/str.php
00026 //
00027 
00028 #ifdef WIN32
00029 //#  include "Fl_win32.cxx"
00030 #elif defined(__APPLE__)
00031 //#  include "Fl_mac.cxx"
00032 #elif !defined(FL_DOXYGEN)
00033 
00034 #  define CONSOLIDATE_MOTION 1
00035 /**** Define this if your keyboard lacks a backspace key... ****/
00036 /* #define BACKSPACE_HACK 1 */
00037 
00038 #  include <config.h>
00039 #  include <FL/Fl.H>
00040 #  include <FL/x.H>
00041 #  include <FL/Fl_Window.H>
00042 #  include <FL/fl_utf8.h>
00043 #  include <FL/Fl_Tooltip.H>
00044 #  include <FL/fl_draw.H>
00045 #  include <stdio.h>
00046 #  include <stdlib.h>
00047 #  include "flstring.h"
00048 #  include <unistd.h>
00049 #  include <sys/time.h>
00050 #  include <X11/Xmd.h>
00051 #  include <X11/Xlocale.h>
00052 #  include <X11/Xlib.h>
00053 
00054 static Fl_Xlib_Graphics_Driver fl_xlib_driver;
00055 static Fl_Display_Device fl_xlib_display(&fl_xlib_driver);
00056 FL_EXPORT Fl_Display_Device *fl_display_device = (Fl_Display_Device*)&fl_xlib_display; // does not change
00057 FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_xlib_driver; // the current target device of graphics operations
00058 FL_EXPORT Fl_Surface_Device *fl_surface = (Fl_Surface_Device*)fl_display_device; // the current target surface of graphics operations
00059 
00061 // interface to poll/select call:
00062 
00063 #  if USE_POLL
00064 
00065 #    include <poll.h>
00066 static pollfd *pollfds = 0;
00067 
00068 #  else
00069 #    if HAVE_SYS_SELECT_H
00070 #      include <sys/select.h>
00071 #    endif /* HAVE_SYS_SELECT_H */
00072 
00073 // The following #define is only needed for HP-UX 9.x and earlier:
00074 //#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
00075 
00076 static fd_set fdsets[3];
00077 static int maxfd;
00078 #    define POLLIN 1
00079 #    define POLLOUT 4
00080 #    define POLLERR 8
00081 
00082 #  endif /* USE_POLL */
00083 
00084 static int nfds = 0;
00085 static int fd_array_size = 0;
00086 struct FD {
00087 #  if !USE_POLL
00088   int fd;
00089   short events;
00090 #  endif
00091   void (*cb)(int, void*);
00092   void* arg;
00093 };
00094 
00095 static FD *fd = 0;
00096 
00097 void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
00098   remove_fd(n,events);
00099   int i = nfds++;
00100   if (i >= fd_array_size) {
00101     FD *temp;
00102     fd_array_size = 2*fd_array_size+1;
00103 
00104     if (!fd) temp = (FD*)malloc(fd_array_size*sizeof(FD));
00105     else temp = (FD*)realloc(fd, fd_array_size*sizeof(FD));
00106 
00107     if (!temp) return;
00108     fd = temp;
00109 
00110 #  if USE_POLL
00111     pollfd *tpoll;
00112 
00113     if (!pollfds) tpoll = (pollfd*)malloc(fd_array_size*sizeof(pollfd));
00114     else tpoll = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
00115 
00116     if (!tpoll) return;
00117     pollfds = tpoll;
00118 #  endif
00119   }
00120   fd[i].cb = cb;
00121   fd[i].arg = v;
00122 #  if USE_POLL
00123   pollfds[i].fd = n;
00124   pollfds[i].events = events;
00125 #  else
00126   fd[i].fd = n;
00127   fd[i].events = events;
00128   if (events & POLLIN) FD_SET(n, &fdsets[0]);
00129   if (events & POLLOUT) FD_SET(n, &fdsets[1]);
00130   if (events & POLLERR) FD_SET(n, &fdsets[2]);
00131   if (n > maxfd) maxfd = n;
00132 #  endif
00133 }
00134 
00135 void Fl::add_fd(int n, void (*cb)(int, void*), void* v) {
00136   Fl::add_fd(n, POLLIN, cb, v);
00137 }
00138 
00139 void Fl::remove_fd(int n, int events) {
00140   int i,j;
00141 # if !USE_POLL
00142   maxfd = -1; // recalculate maxfd on the fly
00143 # endif
00144   for (i=j=0; i<nfds; i++) {
00145 #  if USE_POLL
00146     if (pollfds[i].fd == n) {
00147       int e = pollfds[i].events & ~events;
00148       if (!e) continue; // if no events left, delete this fd
00149       pollfds[j].events = e;
00150     }
00151 #  else
00152     if (fd[i].fd == n) {
00153       int e = fd[i].events & ~events;
00154       if (!e) continue; // if no events left, delete this fd
00155       fd[i].events = e;
00156     }
00157     if (fd[i].fd > maxfd) maxfd = fd[i].fd;
00158 #  endif
00159     // move it down in the array if necessary:
00160     if (j<i) {
00161       fd[j] = fd[i];
00162 #  if USE_POLL
00163       pollfds[j] = pollfds[i];
00164 #  endif
00165     }
00166     j++;
00167   }
00168   nfds = j;
00169 #  if !USE_POLL
00170   if (events & POLLIN) FD_CLR(n, &fdsets[0]);
00171   if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
00172   if (events & POLLERR) FD_CLR(n, &fdsets[2]);
00173 #  endif
00174 }
00175 
00176 void Fl::remove_fd(int n) {
00177   remove_fd(n, -1);
00178 }
00179 
00180 #if CONSOLIDATE_MOTION
00181 static Fl_Window* send_motion;
00182 extern Fl_Window* fl_xmousewin;
00183 #endif
00184 static bool in_a_window; // true if in any of our windows, even destroyed ones
00185 static void do_queued_events() {
00186   in_a_window = true;
00187   while (XEventsQueued(fl_display,QueuedAfterReading)) {
00188     XEvent xevent;
00189     XNextEvent(fl_display, &xevent);
00190     fl_handle(xevent);
00191   }
00192   // we send FL_LEAVE only if the mouse did not enter some other window:
00193   if (!in_a_window) Fl::handle(FL_LEAVE, 0);
00194 #if CONSOLIDATE_MOTION
00195   else if (send_motion == fl_xmousewin) {
00196     send_motion = 0;
00197     Fl::handle(FL_MOVE, fl_xmousewin);
00198   }
00199 #endif
00200 }
00201 
00202 // these pointers are set by the Fl::lock() function:
00203 static void nothing() {}
00204 void (*fl_lock_function)() = nothing;
00205 void (*fl_unlock_function)() = nothing;
00206 
00207 // This is never called with time_to_wait < 0.0:
00208 // It should return negative on error, 0 if nothing happens before
00209 // timeout, and >0 if any callbacks were done.
00210 int fl_wait(double time_to_wait) {
00211 
00212   // OpenGL and other broken libraries call XEventsQueued
00213   // unnecessarily and thus cause the file descriptor to not be ready,
00214   // so we must check for already-read events:
00215   if (fl_display && XQLength(fl_display)) {do_queued_events(); return 1;}
00216 
00217 #  if !USE_POLL
00218   fd_set fdt[3];
00219   fdt[0] = fdsets[0];
00220   fdt[1] = fdsets[1];
00221   fdt[2] = fdsets[2];
00222 #  endif
00223   int n;
00224 
00225   fl_unlock_function();
00226 
00227   if (time_to_wait < 2147483.648) {
00228 #  if USE_POLL
00229     n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
00230 #  else
00231     timeval t;
00232     t.tv_sec = int(time_to_wait);
00233     t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
00234     n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
00235 #  endif
00236   } else {
00237 #  if USE_POLL
00238     n = ::poll(pollfds, nfds, -1);
00239 #  else
00240     n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
00241 #  endif
00242   }
00243 
00244   fl_lock_function();
00245 
00246   if (n > 0) {
00247     for (int i=0; i<nfds; i++) {
00248 #  if USE_POLL
00249       if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
00250 #  else
00251       int f = fd[i].fd;
00252       short revents = 0;
00253       if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
00254       if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
00255       if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
00256       if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
00257 #  endif
00258     }
00259   }
00260   return n;
00261 }
00262 
00263 // fl_ready() is just like fl_wait(0.0) except no callbacks are done:
00264 int fl_ready() {
00265   if (XQLength(fl_display)) return 1;
00266   if (!nfds) return 0; // nothing to select or poll
00267 #  if USE_POLL
00268   return ::poll(pollfds, nfds, 0);
00269 #  else
00270   timeval t;
00271   t.tv_sec = 0;
00272   t.tv_usec = 0;
00273   fd_set fdt[3];
00274   fdt[0] = fdsets[0];
00275   fdt[1] = fdsets[1];
00276   fdt[2] = fdsets[2];
00277   return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
00278 #  endif
00279 }
00280 
00281 // replace \r\n by \n
00282 static void convert_crlf(unsigned char *string, long& len) {
00283   unsigned char *a, *b;
00284   a = b = string;
00285   while (*a) { 
00286     if (*a == '\r' && a[1] == '\n') { a++; len--; }
00287     else *b++ = *a++;
00288   }
00289   *b = 0;
00290 }
00291 
00293 
00294 Display *fl_display;
00295 Window fl_message_window = 0;
00296 int fl_screen;
00297 XVisualInfo *fl_visual;
00298 Colormap fl_colormap;
00299 XIM fl_xim_im = 0;
00300 XIC fl_xim_ic = 0;
00301 char fl_is_over_the_spot = 0;
00302 static XRectangle status_area;
00303 
00304 static Atom WM_DELETE_WINDOW;
00305 static Atom WM_PROTOCOLS;
00306 static Atom fl_MOTIF_WM_HINTS;
00307 static Atom TARGETS;
00308 static Atom CLIPBOARD;
00309 Atom fl_XdndAware;
00310 Atom fl_XdndSelection;
00311 Atom fl_XdndEnter;
00312 Atom fl_XdndTypeList;
00313 Atom fl_XdndPosition;
00314 Atom fl_XdndLeave;
00315 Atom fl_XdndDrop;
00316 Atom fl_XdndStatus;
00317 Atom fl_XdndActionCopy;
00318 Atom fl_XdndFinished;
00319 //Atom fl_XdndProxy;
00320 Atom fl_XdndURIList;
00321 Atom fl_Xatextplainutf;
00322 Atom fl_Xatextplain;
00323 static Atom fl_XaText;
00324 Atom fl_XaCompoundText;
00325 Atom fl_XaUtf8String;
00326 Atom fl_XaTextUriList;
00327 Atom fl_NET_WM_NAME;                    // utf8 aware window label
00328 Atom fl_NET_WM_ICON_NAME;               // utf8 aware window icon name
00329 
00330 /*
00331   X defines 32-bit-entities to have a format value of max. 32,
00332   although sizeof(atom) can be 8 (64 bits) on a 64-bit OS.
00333   See also fl_open_display() for sizeof(atom) < 4.
00334   Used for XChangeProperty (see STR #2419).
00335 */
00336 static int atom_bits = 32;
00337 
00338 static void fd_callback(int,void *) {
00339   do_queued_events();
00340 }
00341 
00342 extern "C" {
00343   static int io_error_handler(Display*) {
00344     Fl::fatal("X I/O error");
00345     return 0;
00346   }
00347 
00348   static int xerror_handler(Display* d, XErrorEvent* e) {
00349     char buf1[128], buf2[128];
00350     sprintf(buf1, "XRequest.%d", e->request_code);
00351     XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
00352     XGetErrorText(d, e->error_code, buf1, 128);
00353     Fl::warning("%s: %s 0x%lx", buf2, buf1, e->resourceid);
00354     return 0;
00355   }
00356 }
00357 
00358 extern char *fl_get_font_xfld(int fnum, int size);
00359 
00360 void fl_new_ic()
00361 {
00362   XVaNestedList preedit_attr = NULL;
00363   XVaNestedList status_attr = NULL;
00364   static XFontSet fs = NULL;
00365   char *fnt;
00366   bool must_free_fnt = true;
00367   char **missing_list;
00368   int missing_count;
00369   char *def_string;
00370   static XRectangle spot;
00371   int predit = 0;
00372   int sarea = 0;
00373   XIMStyles* xim_styles = NULL;
00374 
00375 #if USE_XFT
00376 
00377 #if defined(__GNUC__)
00378 // FIXME: warning XFT support here
00379 #endif /*__GNUC__*/
00380 
00381   if (!fs) {
00382     fnt = NULL;//fl_get_font_xfld(0, 14);
00383     if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
00384     fs = XCreateFontSet(fl_display, fnt, &missing_list,
00385                         &missing_count, &def_string);
00386   }
00387 #else
00388   if (!fs) {
00389     fnt = fl_get_font_xfld(0, 14);
00390     if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
00391     fs = XCreateFontSet(fl_display, fnt, &missing_list,
00392                         &missing_count, &def_string);
00393   }
00394 #endif
00395   preedit_attr = XVaCreateNestedList(0,
00396                                      XNSpotLocation, &spot,
00397                                      XNFontSet, fs, NULL);
00398   status_attr = XVaCreateNestedList(0,
00399                                     XNAreaNeeded, &status_area,
00400                                     XNFontSet, fs, NULL);
00401 
00402   if (!XGetIMValues(fl_xim_im, XNQueryInputStyle,
00403                     &xim_styles, NULL, NULL)) {
00404     int i;
00405     XIMStyle *style;
00406     for (i = 0, style = xim_styles->supported_styles;
00407          i < xim_styles->count_styles; i++, style++) {
00408       if (*style == (XIMPreeditPosition | XIMStatusArea)) {
00409         sarea = 1;
00410         predit = 1;
00411       } else if (*style == (XIMPreeditPosition | XIMStatusNothing)) {
00412         predit = 1;
00413       }
00414     }
00415   }
00416   XFree(xim_styles);
00417 
00418   if (sarea) {
00419     fl_xim_ic = XCreateIC(fl_xim_im,
00420                           XNInputStyle, (XIMPreeditPosition | XIMStatusArea),
00421                           XNPreeditAttributes, preedit_attr,
00422                           XNStatusAttributes, status_attr,
00423                           NULL);
00424   }
00425 
00426   if (!fl_xim_ic && predit) {
00427     fl_xim_ic = XCreateIC(fl_xim_im,
00428                           XNInputStyle, (XIMPreeditPosition | XIMStatusNothing),
00429                           XNPreeditAttributes, preedit_attr,
00430                           NULL);
00431   }
00432   XFree(preedit_attr);
00433   XFree(status_attr);
00434   if (!fl_xim_ic) {
00435     fl_is_over_the_spot = 0;
00436     fl_xim_ic = XCreateIC(fl_xim_im,
00437                           XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
00438                           NULL);
00439   } else {
00440     fl_is_over_the_spot = 1;
00441     XVaNestedList status_attr = NULL;
00442     status_attr = XVaCreateNestedList(0, XNAreaNeeded, &status_area, NULL);
00443 
00444     XGetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
00445     XFree(status_attr);
00446   }
00447 }
00448 
00449 
00450 static XRectangle    spot;
00451 static int spotf = -1;
00452 static int spots = -1;
00453 
00454 void fl_reset_spot(void)
00455 {
00456   spot.x = -1;
00457   spot.y = -1;
00458   //if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
00459 }
00460 
00461 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
00462 {
00463   int change = 0;
00464   XVaNestedList preedit_attr;
00465   static XFontSet fs = NULL;
00466   char **missing_list;
00467   int missing_count;
00468   char *def_string;
00469   char *fnt = NULL;
00470   bool must_free_fnt =true;
00471 
00472   static XIC ic = NULL;
00473 
00474   if (!fl_xim_ic || !fl_is_over_the_spot) return;
00475   //XSetICFocus(fl_xim_ic);
00476   if (X != spot.x || Y != spot.y) {
00477     spot.x = X;
00478     spot.y = Y;
00479     spot.height = H;
00480     spot.width = W;
00481     change = 1;
00482   }
00483   if (font != spotf || size != spots) {
00484     spotf = font;
00485     spots = size;
00486     change = 1;
00487     if (fs) {
00488       XFreeFontSet(fl_display, fs);
00489     }
00490 #if USE_XFT
00491 
00492 #if defined(__GNUC__)
00493 // FIXME: warning XFT support here
00494 #endif /*__GNUC__*/
00495 
00496     fnt = NULL; // fl_get_font_xfld(font, size);
00497     if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
00498     fs = XCreateFontSet(fl_display, fnt, &missing_list,
00499                         &missing_count, &def_string);
00500 #else
00501     fnt = fl_get_font_xfld(font, size);
00502     if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
00503     fs = XCreateFontSet(fl_display, fnt, &missing_list,
00504                         &missing_count, &def_string);
00505 #endif
00506   }
00507   if (fl_xim_ic != ic) {
00508     ic = fl_xim_ic;
00509     change = 1;
00510   }
00511 
00512   if (fnt && must_free_fnt) free(fnt);
00513   if (!change) return;
00514 
00515 
00516   preedit_attr = XVaCreateNestedList(0,
00517                                      XNSpotLocation, &spot,
00518                                      XNFontSet, fs, NULL);
00519   XSetICValues(fl_xim_ic, XNPreeditAttributes, preedit_attr, NULL);
00520   XFree(preedit_attr);
00521 }
00522 
00523 void fl_set_status(int x, int y, int w, int h)
00524 {
00525   XVaNestedList status_attr;
00526   status_area.x = x;
00527   status_area.y = y;
00528   status_area.width = w;
00529   status_area.height = h;
00530   if (!fl_xim_ic) return;
00531   status_attr = XVaCreateNestedList(0, XNArea, &status_area, NULL);
00532 
00533   XSetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
00534   XFree(status_attr);
00535 }
00536 
00537 void fl_init_xim()
00538 {
00539   //XIMStyle *style;
00540   XIMStyles *xim_styles;
00541   if (!fl_display) return;
00542   if (fl_xim_im) return;
00543 
00544   fl_xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
00545   xim_styles = NULL;
00546   fl_xim_ic = NULL;
00547 
00548   if (fl_xim_im) {
00549     XGetIMValues (fl_xim_im, XNQueryInputStyle,
00550                   &xim_styles, NULL, NULL);
00551   } else {
00552     Fl::warning("XOpenIM() failed\n");
00553     return;
00554   }
00555 
00556   if (xim_styles && xim_styles->count_styles) {
00557     fl_new_ic();
00558    } else {
00559      Fl::warning("No XIM style found\n");
00560      XCloseIM(fl_xim_im);
00561      fl_xim_im = NULL;
00562      return;
00563   }
00564   if (!fl_xim_ic) {
00565     Fl::warning("XCreateIC() failed\n");
00566     XCloseIM(fl_xim_im);
00567     XFree(xim_styles);
00568     fl_xim_im = NULL;
00569   }
00570 }
00571 
00572 void fl_open_display() {
00573   if (fl_display) return;
00574 
00575   setlocale(LC_CTYPE, "");
00576   XSetLocaleModifiers("");
00577 
00578   XSetIOErrorHandler(io_error_handler);
00579   XSetErrorHandler(xerror_handler);
00580 
00581   Display *d = XOpenDisplay(0);
00582   if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0));
00583 
00584   fl_open_display(d);
00585 }
00586 
00587 void fl_open_display(Display* d) {
00588   fl_display = d;
00589 
00590   WM_DELETE_WINDOW      = XInternAtom(d, "WM_DELETE_WINDOW",    0);
00591   WM_PROTOCOLS          = XInternAtom(d, "WM_PROTOCOLS",        0);
00592   fl_MOTIF_WM_HINTS     = XInternAtom(d, "_MOTIF_WM_HINTS",     0);
00593   TARGETS               = XInternAtom(d, "TARGETS",             0);
00594   CLIPBOARD             = XInternAtom(d, "CLIPBOARD",           0);
00595   fl_XdndAware          = XInternAtom(d, "XdndAware",           0);
00596   fl_XdndSelection      = XInternAtom(d, "XdndSelection",       0);
00597   fl_XdndEnter          = XInternAtom(d, "XdndEnter",           0);
00598   fl_XdndTypeList       = XInternAtom(d, "XdndTypeList",        0);
00599   fl_XdndPosition       = XInternAtom(d, "XdndPosition",        0);
00600   fl_XdndLeave          = XInternAtom(d, "XdndLeave",           0);
00601   fl_XdndDrop           = XInternAtom(d, "XdndDrop",            0);
00602   fl_XdndStatus         = XInternAtom(d, "XdndStatus",          0);
00603   fl_XdndActionCopy     = XInternAtom(d, "XdndActionCopy",      0);
00604   fl_XdndFinished       = XInternAtom(d, "XdndFinished",        0);
00605   //fl_XdndProxy        = XInternAtom(d, "XdndProxy",           0);
00606   fl_XdndEnter          = XInternAtom(d, "XdndEnter",           0);
00607   fl_XdndURIList        = XInternAtom(d, "text/uri-list",       0);
00608   fl_Xatextplainutf     = XInternAtom(d, "text/plain;charset=UTF-8",0);
00609   fl_Xatextplain        = XInternAtom(d, "text/plain",          0);
00610   fl_XaText             = XInternAtom(d, "TEXT",                0);     
00611   fl_XaCompoundText     = XInternAtom(d, "COMPOUND_TEXT",       0);
00612   fl_XaUtf8String       = XInternAtom(d, "UTF8_STRING",         0);
00613   fl_XaTextUriList      = XInternAtom(d, "text/uri-list",       0);
00614   fl_NET_WM_NAME        = XInternAtom(d, "_NET_WM_NAME",        0);
00615   fl_NET_WM_ICON_NAME   = XInternAtom(d, "_NET_WM_ICON_NAME",   0);
00616   
00617   if (sizeof(Atom) < 4)
00618     atom_bits = sizeof(Atom) * 8;
00619 
00620   Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback);
00621 
00622   fl_screen = DefaultScreen(d);
00623 
00624   fl_message_window =
00625     XCreateSimpleWindow(d, RootWindow(d,fl_screen), 0,0,1,1,0, 0, 0);
00626 
00627 // construct an XVisualInfo that matches the default Visual:
00628   XVisualInfo templt; int num;
00629   templt.visualid = XVisualIDFromVisual(DefaultVisual(d, fl_screen));
00630   fl_visual = XGetVisualInfo(d, VisualIDMask, &templt, &num);
00631   fl_colormap = DefaultColormap(d, fl_screen);
00632   fl_init_xim();
00633 
00634 #if !USE_COLORMAP
00635   Fl::visual(FL_RGB);
00636 #endif
00637 }
00638 
00639 void fl_close_display() {
00640   Fl::remove_fd(ConnectionNumber(fl_display));
00641   XCloseDisplay(fl_display);
00642 }
00643 
00644 static int fl_workarea_xywh[4] = { -1, -1, -1, -1 };
00645 
00646 static void fl_init_workarea() {
00647   fl_open_display();
00648 
00649   Atom _NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", 0);
00650   Atom actual;
00651   unsigned long count, remaining;
00652   int format;
00653   unsigned *xywh;
00654 
00655   if (XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
00656                          _NET_WORKAREA, 0, 4 * sizeof(unsigned), False,
00657                          XA_CARDINAL, &actual, &format, &count, &remaining,
00658                          (unsigned char **)&xywh) || !xywh || !xywh[2] ||
00659                          !xywh[3])
00660   {
00661     fl_workarea_xywh[0] = 0;
00662     fl_workarea_xywh[1] = 0;
00663     fl_workarea_xywh[2] = DisplayWidth(fl_display, fl_screen);
00664     fl_workarea_xywh[3] = DisplayHeight(fl_display, fl_screen);
00665   }
00666   else
00667   {
00668     fl_workarea_xywh[0] = (int)xywh[0];
00669     fl_workarea_xywh[1] = (int)xywh[1];
00670     fl_workarea_xywh[2] = (int)xywh[2];
00671     fl_workarea_xywh[3] = (int)xywh[3];
00672     XFree(xywh);
00673   }
00674 }
00675 
00676 int Fl::x() {
00677   if (fl_workarea_xywh[0] < 0) fl_init_workarea();
00678   return fl_workarea_xywh[0];
00679 }
00680 
00681 int Fl::y() {
00682   if (fl_workarea_xywh[0] < 0) fl_init_workarea();
00683   return fl_workarea_xywh[1];
00684 }
00685 
00686 int Fl::w() {
00687   if (fl_workarea_xywh[0] < 0) fl_init_workarea();
00688   return fl_workarea_xywh[2];
00689 }
00690 
00691 int Fl::h() {
00692   if (fl_workarea_xywh[0] < 0) fl_init_workarea();
00693   return fl_workarea_xywh[3];
00694 }
00695 
00696 void Fl::get_mouse(int &xx, int &yy) {
00697   fl_open_display();
00698   Window root = RootWindow(fl_display, fl_screen);
00699   Window c; int mx,my,cx,cy; unsigned int mask;
00700   XQueryPointer(fl_display,root,&root,&c,&mx,&my,&cx,&cy,&mask);
00701   xx = mx;
00702   yy = my;
00703 }
00704 
00706 // Code used for paste and DnD into the program:
00707 
00708 Fl_Widget *fl_selection_requestor;
00709 char *fl_selection_buffer[2];
00710 int fl_selection_length[2];
00711 int fl_selection_buffer_length[2];
00712 char fl_i_own_selection[2] = {0,0};
00713 
00714 // Call this when a "paste" operation happens:
00715 void Fl::paste(Fl_Widget &receiver, int clipboard) {
00716   if (fl_i_own_selection[clipboard]) {
00717     // We already have it, do it quickly without window server.
00718     // Notice that the text is clobbered if set_selection is
00719     // called in response to FL_PASTE!
00720     Fl::e_text = fl_selection_buffer[clipboard];
00721     Fl::e_length = fl_selection_length[clipboard];
00722     if (!Fl::e_text) Fl::e_text = (char *)"";
00723     receiver.handle(FL_PASTE);
00724     return;
00725   }
00726   // otherwise get the window server to return it:
00727   fl_selection_requestor = &receiver;
00728   Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
00729   XConvertSelection(fl_display, property, TARGETS, property,
00730                     fl_xid(Fl::first_window()), fl_event_time);
00731 }
00732 
00733 Window fl_dnd_source_window;
00734 Atom *fl_dnd_source_types; // null-terminated list of data types being supplied
00735 Atom fl_dnd_type;
00736 Atom fl_dnd_source_action;
00737 Atom fl_dnd_action;
00738 
00739 void fl_sendClientMessage(Window window, Atom message,
00740                                  unsigned long d0,
00741                                  unsigned long d1=0,
00742                                  unsigned long d2=0,
00743                                  unsigned long d3=0,
00744                                  unsigned long d4=0)
00745 {
00746   XEvent e;
00747   e.xany.type = ClientMessage;
00748   e.xany.window = window;
00749   e.xclient.message_type = message;
00750   e.xclient.format = 32;
00751   e.xclient.data.l[0] = (long)d0;
00752   e.xclient.data.l[1] = (long)d1;
00753   e.xclient.data.l[2] = (long)d2;
00754   e.xclient.data.l[3] = (long)d3;
00755   e.xclient.data.l[4] = (long)d4;
00756   XSendEvent(fl_display, window, 0, 0, &e);
00757 }
00758 
00760 // Code for copying to clipboard and DnD out of the program:
00761 
00762 void Fl::copy(const char *stuff, int len, int clipboard) {
00763   if (!stuff || len<0) return;
00764   if (len+1 > fl_selection_buffer_length[clipboard]) {
00765     delete[] fl_selection_buffer[clipboard];
00766     fl_selection_buffer[clipboard] = new char[len+100];
00767     fl_selection_buffer_length[clipboard] = len+100;
00768   }
00769   memcpy(fl_selection_buffer[clipboard], stuff, len);
00770   fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
00771   fl_selection_length[clipboard] = len;
00772   fl_i_own_selection[clipboard] = 1;
00773   Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
00774   XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
00775 }
00776 
00778 
00779 const XEvent* fl_xevent; // the current x event
00780 ulong fl_event_time; // the last timestamp from an x event
00781 
00782 char fl_key_vector[32]; // used by Fl::get_key()
00783 
00784 // Record event mouse position and state from an XEvent:
00785 
00786 static int px, py;
00787 static ulong ptime;
00788 
00789 static void set_event_xy() {
00790 #  if CONSOLIDATE_MOTION
00791   send_motion = 0;
00792 #  endif
00793   Fl::e_x_root  = fl_xevent->xbutton.x_root;
00794   Fl::e_x       = fl_xevent->xbutton.x;
00795   Fl::e_y_root  = fl_xevent->xbutton.y_root;
00796   Fl::e_y       = fl_xevent->xbutton.y;
00797   Fl::e_state   = fl_xevent->xbutton.state << 16;
00798   fl_event_time = fl_xevent->xbutton.time;
00799 #  ifdef __sgi
00800   // get the meta key off PC keyboards:
00801   if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META;
00802 #  endif
00803   // turn off is_click if enough time or mouse movement has passed:
00804   if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 ||
00805       fl_event_time >= ptime+1000)
00806     Fl::e_is_click = 0;
00807 }
00808 
00809 // if this is same event as last && is_click, increment click count:
00810 static inline void checkdouble() {
00811   if (Fl::e_is_click == Fl::e_keysym)
00812     Fl::e_clicks++;
00813   else {
00814     Fl::e_clicks = 0;
00815     Fl::e_is_click = Fl::e_keysym;
00816   }
00817   px = Fl::e_x_root;
00818   py = Fl::e_y_root;
00819   ptime = fl_event_time;
00820 }
00821 
00822 static Fl_Window* resize_bug_fix;
00823 
00825 
00826 static char unknown[] = "<unknown>";
00827 const int unknown_len = 10;
00828 
00829 extern "C" {
00830 
00831 static int xerror = 0;
00832 
00833 static int ignoreXEvents(Display *display, XErrorEvent *event) {
00834   xerror = 1;
00835   return 0;
00836 }
00837 
00838 static XErrorHandler catchXExceptions() {
00839   xerror = 0;
00840   return ignoreXEvents;
00841 }
00842 
00843 static int wasXExceptionRaised() {
00844   return xerror;
00845 }
00846 
00847 }
00848 
00849 
00850 int fl_handle(const XEvent& thisevent)
00851 {
00852   XEvent xevent = thisevent;
00853   fl_xevent = &thisevent;
00854   Window xid = xevent.xany.window;
00855   static Window xim_win = 0;
00856 
00857   if (fl_xim_ic && xevent.type == DestroyNotify &&
00858         xid != xim_win && !fl_find(xid))
00859   {
00860     XIM xim_im;
00861     xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
00862     if (!xim_im) {
00863       /*  XIM server has crashed */
00864       XSetLocaleModifiers("@im=");
00865       fl_xim_im = NULL;
00866       fl_init_xim();
00867     } else {
00868       XCloseIM(xim_im); // see STR 2185 for comment
00869     }
00870     return 0;
00871   }
00872 
00873   if (fl_xim_ic && (xevent.type == FocusIn))
00874   {
00875 #define POOR_XIM
00876 #ifdef POOR_XIM
00877         if (xim_win != xid)
00878         {
00879                 xim_win  = xid;
00880                 XDestroyIC(fl_xim_ic);
00881                 fl_xim_ic = NULL;
00882                 fl_new_ic();
00883                 XSetICValues(fl_xim_ic,
00884                                 XNFocusWindow, xevent.xclient.window,
00885                                 XNClientWindow, xid,
00886                                 NULL);
00887         }
00888         fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
00889 #else
00890     if (Fl::first_window() && Fl::first_window()->modal()) {
00891       Window x  = fl_xid(Fl::first_window());
00892       if (x != xim_win) {
00893         xim_win  = x;
00894         XSetICValues(fl_xim_ic,
00895                         XNFocusWindow, xim_win,
00896                         XNClientWindow, xim_win,
00897                         NULL);
00898         fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
00899       }
00900     } else if (xim_win != xid && xid) {
00901       xim_win = xid;
00902       XSetICValues(fl_xim_ic,
00903                         XNFocusWindow, xevent.xclient.window,
00904                         XNClientWindow, xid,
00905                         //XNFocusWindow, xim_win,
00906                         //XNClientWindow, xim_win,
00907                         NULL);
00908       fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
00909     }
00910 #endif
00911   }
00912 
00913   if ( XFilterEvent((XEvent *)&xevent, 0) )
00914       return(1);
00915 
00916   switch (xevent.type) {
00917 
00918   case KeymapNotify:
00919     memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32);
00920     return 0;
00921 
00922   case MappingNotify:
00923     XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
00924     return 0;
00925 
00926   case SelectionNotify: {
00927     if (!fl_selection_requestor) return 0;
00928     static unsigned char* buffer = 0;
00929     if (buffer) {XFree(buffer); buffer = 0;}
00930     long bytesread = 0;
00931     if (fl_xevent->xselection.property) for (;;) {
00932       // The Xdnd code pastes 64K chunks together, possibly to avoid
00933       // bugs in X servers, or maybe to avoid an extra round-trip to
00934       // get the property length.  I copy this here:
00935       Atom actual; int format; unsigned long count, remaining;
00936       unsigned char* portion;
00937       if (XGetWindowProperty(fl_display,
00938                              fl_xevent->xselection.requestor,
00939                              fl_xevent->xselection.property,
00940                              bytesread/4, 65536, 1, 0,
00941                              &actual, &format, &count, &remaining,
00942                              &portion)) break; // quit on error
00943       if (actual == TARGETS || actual == XA_ATOM) {
00944         Atom type = XA_STRING;
00945         for (unsigned i = 0; i<count; i++) {
00946           Atom t = ((Atom*)portion)[i];
00947             if (t == fl_Xatextplainutf ||
00948                   t == fl_Xatextplain ||
00949                   t == fl_XaUtf8String) {type = t; break;}
00950             // rest are only used if no utf-8 available:
00951             if (t == fl_XaText || 
00952                   t == fl_XaTextUriList || 
00953                   t == fl_XaCompoundText) type = t;
00954         }       
00955         XFree(portion);
00956         Atom property = xevent.xselection.property;
00957         XConvertSelection(fl_display, property, type, property,
00958               fl_xid(Fl::first_window()),
00959               fl_event_time);
00960         return true;
00961       }
00962       XTextProperty text_prop; 
00963       text_prop.value=portion;
00964       text_prop.format=format;
00965       text_prop.encoding=actual;
00966       text_prop.nitems=count;
00967       char **text_list;
00968       text_list = (char**)&portion;
00969       int bytesnew = strlen(*text_list)+1; 
00970       buffer = (unsigned char*)realloc(buffer, bytesread+bytesnew+remaining);
00971       memcpy(buffer+bytesread, *text_list, bytesnew);
00972       XFree(portion); 
00973       bytesread += bytesnew - 1;
00974       if (!remaining) break;
00975     }
00976     if (buffer) {
00977       buffer[bytesread] = 0;
00978       convert_crlf(buffer, bytesread);
00979     }
00980     Fl::e_text = buffer ? (char*)buffer : (char *)"";
00981     Fl::e_length = bytesread;
00982     int old_event = Fl::e_number;
00983     fl_selection_requestor->handle(Fl::e_number = FL_PASTE);
00984     Fl::e_number = old_event;
00985     // Detect if this paste is due to Xdnd by the property name (I use
00986     // XA_SECONDARY for that) and send an XdndFinished message. It is not
00987     // clear if this has to be delayed until now or if it can be done
00988     // immediatly after calling XConvertSelection.
00989     if (fl_xevent->xselection.property == XA_SECONDARY &&
00990         fl_dnd_source_window) {
00991       fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished,
00992                            fl_xevent->xselection.requestor);
00993       fl_dnd_source_window = 0; // don't send a second time
00994     }
00995     return 1;}
00996 
00997   case SelectionClear: {
00998     int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD;
00999     fl_i_own_selection[clipboard] = 0;
01000     return 1;}
01001 
01002   case SelectionRequest: {
01003     XSelectionEvent e;
01004     e.type = SelectionNotify;
01005     e.requestor = fl_xevent->xselectionrequest.requestor;
01006     e.selection = fl_xevent->xselectionrequest.selection;
01007     int clipboard = e.selection == CLIPBOARD;
01008     e.target = fl_xevent->xselectionrequest.target;
01009     e.time = fl_xevent->xselectionrequest.time;
01010     e.property = fl_xevent->xselectionrequest.property;
01011     if (e.target == TARGETS) {
01012       Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
01013       XChangeProperty(fl_display, e.requestor, e.property,
01014                       XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
01015     } else if (/*e.target == XA_STRING &&*/ fl_selection_length[clipboard]) {
01016     if (e.target == fl_XaUtf8String ||
01017              e.target == XA_STRING ||
01018              e.target == fl_XaCompoundText ||
01019              e.target == fl_XaText ||
01020              e.target == fl_Xatextplain ||
01021              e.target == fl_Xatextplainutf) {
01022         // clobber the target type, this seems to make some applications
01023         // behave that insist on asking for XA_TEXT instead of UTF8_STRING
01024         // Does not change XA_STRING as that breaks xclipboard.
01025         if (e.target != XA_STRING) e.target = fl_XaUtf8String;
01026         XChangeProperty(fl_display, e.requestor, e.property,
01027                          e.target, 8, 0,
01028                          (unsigned char *)fl_selection_buffer[clipboard],
01029                          fl_selection_length[clipboard]);
01030       }
01031     } else {
01032 //    char* x = XGetAtomName(fl_display,e.target);
01033 //    fprintf(stderr,"selection request of %s\n",x);
01034 //    XFree(x);
01035       e.property = 0;
01036     }
01037     XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);}
01038     return 1;
01039 
01040   // events where interesting window id is in a different place:
01041   case CirculateNotify:
01042   case CirculateRequest:
01043   case ConfigureNotify:
01044   case ConfigureRequest:
01045   case CreateNotify:
01046   case DestroyNotify:
01047   case GravityNotify:
01048   case MapNotify:
01049   case MapRequest:
01050   case ReparentNotify:
01051   case UnmapNotify:
01052     xid = xevent.xmaprequest.window;
01053     break;
01054   }
01055 
01056   int event = 0;
01057   Fl_Window* window = fl_find(xid);
01058 
01059   if (window) switch (xevent.type) {
01060 
01061   case ClientMessage: {
01062     Atom message = fl_xevent->xclient.message_type;
01063     const long* data = fl_xevent->xclient.data.l;
01064     if ((Atom)(data[0]) == WM_DELETE_WINDOW) {
01065       event = FL_CLOSE;
01066     } else if (message == fl_XdndEnter) {
01067       fl_xmousewin = window;
01068       in_a_window = true;
01069       fl_dnd_source_window = data[0];
01070       // version number is data[1]>>24
01071 //      printf("XdndEnter, version %ld\n", data[1] >> 24);
01072       if (data[1]&1) {
01073         // get list of data types:
01074         Atom actual; int format; unsigned long count, remaining;
01075         unsigned char *buffer = 0;
01076         XGetWindowProperty(fl_display, fl_dnd_source_window, fl_XdndTypeList,
01077                            0, 0x8000000L, False, XA_ATOM, &actual, &format,
01078                            &count, &remaining, &buffer);
01079         if (actual != XA_ATOM || format != 32 || count<4 || !buffer)
01080           goto FAILED;
01081         delete [] fl_dnd_source_types;
01082         fl_dnd_source_types = new Atom[count+1];
01083         for (unsigned i = 0; i < count; i++) {
01084           fl_dnd_source_types[i] = ((Atom*)buffer)[i];
01085         }
01086         fl_dnd_source_types[count] = 0;
01087       } else {
01088       FAILED:
01089         // less than four data types, or if the above messes up:
01090         if (!fl_dnd_source_types) fl_dnd_source_types = new Atom[4];
01091         fl_dnd_source_types[0] = data[2];
01092         fl_dnd_source_types[1] = data[3];
01093         fl_dnd_source_types[2] = data[4];
01094         fl_dnd_source_types[3] = 0;
01095       }
01096 
01097       // Loop through the source types and pick the first text type...
01098       int i;
01099 
01100       for (i = 0; fl_dnd_source_types[i]; i ++)
01101       {
01102 //        printf("fl_dnd_source_types[%d] = %ld (%s)\n", i,
01103 //             fl_dnd_source_types[i],
01104 //             XGetAtomName(fl_display, fl_dnd_source_types[i]));
01105 
01106         if (!strncmp(XGetAtomName(fl_display, fl_dnd_source_types[i]),
01107                      "text/", 5))
01108           break;
01109       }
01110 
01111       if (fl_dnd_source_types[i])
01112         fl_dnd_type = fl_dnd_source_types[i];
01113       else
01114         fl_dnd_type = fl_dnd_source_types[0];
01115 
01116       event = FL_DND_ENTER;
01117       Fl::e_text = unknown;
01118       Fl::e_length = unknown_len;
01119       break;
01120 
01121     } else if (message == fl_XdndPosition) {
01122       fl_xmousewin = window;
01123       in_a_window = true;
01124       fl_dnd_source_window = data[0];
01125       Fl::e_x_root = data[2]>>16;
01126       Fl::e_y_root = data[2]&0xFFFF;
01127       if (window) {
01128         Fl::e_x = Fl::e_x_root-window->x();
01129         Fl::e_y = Fl::e_y_root-window->y();
01130       }
01131       fl_event_time = data[3];
01132       fl_dnd_source_action = data[4];
01133       fl_dnd_action = fl_XdndActionCopy;
01134       Fl::e_text = unknown;
01135       Fl::e_length = unknown_len;
01136       int accept = Fl::handle(FL_DND_DRAG, window);
01137       fl_sendClientMessage(data[0], fl_XdndStatus,
01138                            fl_xevent->xclient.window,
01139                            accept ? 1 : 0,
01140                            0, // used for xy rectangle to not send position inside
01141                            0, // used for width+height of rectangle
01142                            accept ? fl_dnd_action : None);
01143       return 1;
01144 
01145     } else if (message == fl_XdndLeave) {
01146       fl_dnd_source_window = 0; // don't send a finished message to it
01147       event = FL_DND_LEAVE;
01148       Fl::e_text = unknown;
01149       Fl::e_length = unknown_len;
01150       break;
01151 
01152     } else if (message == fl_XdndDrop) {
01153       fl_xmousewin = window;
01154       in_a_window = true;
01155       fl_dnd_source_window = data[0];
01156       fl_event_time = data[2];
01157       Window to_window = fl_xevent->xclient.window;
01158       Fl::e_text = unknown;
01159       Fl::e_length = unknown_len;
01160       if (Fl::handle(FL_DND_RELEASE, window)) {
01161         fl_selection_requestor = Fl::belowmouse();
01162         XConvertSelection(fl_display, fl_XdndSelection,
01163                           fl_dnd_type, XA_SECONDARY,
01164                           to_window, fl_event_time);
01165       } else {
01166         // Send the finished message if I refuse the drop.
01167         // It is not clear whether I can just send finished always,
01168         // or if I have to wait for the SelectionNotify event as the
01169         // code is currently doing.
01170         fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished, to_window);
01171         fl_dnd_source_window = 0;
01172       }
01173       return 1;
01174 
01175     }
01176     break;}
01177 
01178   case UnmapNotify:
01179     event = FL_HIDE;
01180     break;
01181 
01182   case Expose:
01183     Fl_X::i(window)->wait_for_expose = 0;
01184 #  if 0
01185     // try to keep windows on top even if WM_TRANSIENT_FOR does not work:
01186     // opaque move/resize window managers do not like this, so I disabled it.
01187     if (Fl::first_window()->non_modal() && window != Fl::first_window())
01188       Fl::first_window()->show();
01189 #  endif
01190 
01191   case GraphicsExpose:
01192     window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x, xevent.xexpose.y,
01193                    xevent.xexpose.width, xevent.xexpose.height);
01194     return 1;
01195 
01196   case FocusIn:
01197     if (fl_xim_ic) XSetICFocus(fl_xim_ic);
01198     event = FL_FOCUS;
01199     break;
01200 
01201   case FocusOut:
01202     if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
01203     event = FL_UNFOCUS;
01204     break;
01205 
01206   case KeyPress:
01207   case KeyRelease: {
01208   KEYPRESS:
01209     int keycode = xevent.xkey.keycode;
01210     fl_key_vector[keycode/8] |= (1 << (keycode%8));
01211     static char *buffer = NULL;
01212     static int buffer_len = 0;
01213     int len;
01214     KeySym keysym;
01215     if (buffer_len == 0) {
01216       buffer_len = 4096;
01217       buffer = (char*) malloc(buffer_len);
01218     }
01219     if (xevent.type == KeyPress) {
01220       event = FL_KEYDOWN;
01221       int len = 0;
01222 
01223       if (fl_xim_ic) {
01224         Status status;
01225         len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
01226                              buffer, buffer_len, &keysym, &status);
01227 
01228         while (status == XBufferOverflow && buffer_len < 50000) {
01229           buffer_len = buffer_len * 5 + 1;
01230           buffer = (char*)realloc(buffer, buffer_len);
01231           len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
01232                              buffer, buffer_len, &keysym, &status);
01233         }
01234         keysym = XKeycodeToKeysym(fl_display, keycode, 0);
01235       } else {
01236         //static XComposeStatus compose;
01237         len = XLookupString((XKeyEvent*)&(xevent.xkey),
01238                              buffer, buffer_len, &keysym, 0/*&compose*/);
01239         if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets
01240           // force it to type a character (not sure if this ever is needed):
01241           // if (!len) {buffer[0] = char(keysym); len = 1;}
01242           len = fl_utf8encode(XKeysymToUcs(keysym), buffer);
01243           if (len < 1) len = 1;
01244           // ignore all effects of shift on the keysyms, which makes it a lot
01245           // easier to program shortcuts and is Windoze-compatable:
01246           keysym = XKeycodeToKeysym(fl_display, keycode, 0);
01247         }
01248       }
01249       // MRS: Can't use Fl::event_state(FL_CTRL) since the state is not
01250       //      set until set_event_xy() is called later...
01251       if ((xevent.xkey.state & ControlMask) && keysym == '-') buffer[0] = 0x1f; // ^_
01252       buffer[len] = 0;
01253       Fl::e_text = buffer;
01254       Fl::e_length = len;
01255     } else {
01256       // Stupid X sends fake key-up events when a repeating key is held
01257       // down, probably due to some back compatibility problem. Fortunately
01258       // we can detect this because the repeating KeyPress event is in
01259       // the queue, get it and execute it instead:
01260       
01261       // Bool XkbSetDetectableAutorepeat ( display, detectable, supported_rtrn )
01262       // Display * display ;
01263       // Bool detectable ;
01264       // Bool * supported_rtrn ;
01265       // ...would be the easy way to corrct this isuue. Unfortunatly, this call is also 
01266       // broken on many Unix distros including Ubuntu and Solaris (as of Dec 2009)
01267 
01268       // Bogus KeyUp events are generated by repeated KeyDown events. One 
01269       // neccessary condition is an identical key event pending right after
01270       // the bogus KeyUp.
01271       // The new code introduced Dec 2009 differs in that it only check the very
01272       // next event in the queue, not the entire queue of events.
01273       // This function wrongly detects a repeat key if a software keyboard
01274       // sends a burst of events containing two consecutive equal keys. However, 
01275       // in every non-gaming situation, this is no problem because both KeyPress
01276       // events will cause the expected behavior.
01277       XEvent peekevent;
01278       if (XPending(fl_display)) {
01279         XPeekEvent(fl_display, &peekevent);
01280         if (   (peekevent.type == KeyPress) // must be a KeyPress event
01281             && (peekevent.xkey.keycode == xevent.xkey.keycode) // must be the same key
01282             && (peekevent.xkey.time == xevent.xkey.time) // must be sent at the exact same time
01283             ) {
01284           XNextEvent(fl_display, &xevent);
01285           goto KEYPRESS;
01286         }
01287       }
01288       
01289       event = FL_KEYUP;
01290       fl_key_vector[keycode/8] &= ~(1 << (keycode%8));
01291       // keyup events just get the unshifted keysym:
01292       keysym = XKeycodeToKeysym(fl_display, keycode, 0);
01293     }
01294 #  ifdef __sgi
01295     // You can plug a microsoft keyboard into an sgi but the extra shift
01296     // keys are not translated.  Make them translate like XFree86 does:
01297     if (!keysym) switch(keycode) {
01298     case 147: keysym = FL_Meta_L; break;
01299     case 148: keysym = FL_Meta_R; break;
01300     case 149: keysym = FL_Menu; break;
01301     }
01302 #  endif
01303 #  if BACKSPACE_HACK
01304     // Attempt to fix keyboards that send "delete" for the key in the
01305     // upper-right corner of the main keyboard.  But it appears that
01306     // very few of these remain?
01307     static int got_backspace = 0;
01308     if (!got_backspace) {
01309       if (keysym == FL_Delete) keysym = FL_BackSpace;
01310       else if (keysym == FL_BackSpace) got_backspace = 1;
01311     }
01312 #  endif
01313     // We have to get rid of the XK_KP_function keys, because they are
01314     // not produced on Windoze and thus case statements tend not to check
01315     // for them.  There are 15 of these in the range 0xff91 ... 0xff9f
01316     if (keysym >= 0xff91 && keysym <= 0xff9f) {
01317       // Map keypad keysym to character or keysym depending on
01318       // numlock state...
01319       unsigned long keysym1 = XKeycodeToKeysym(fl_display, keycode, 1);
01320       if (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))
01321         Fl::e_original_keysym = (int)(keysym1 | FL_KP);
01322       if ((xevent.xkey.state & Mod2Mask) &&
01323           (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))) {
01324         // Store ASCII numeric keypad value...
01325         keysym = keysym1 | FL_KP;
01326         buffer[0] = char(keysym1) & 0x7F;
01327         len = 1;
01328       } else {
01329         // Map keypad to special key...
01330         static const unsigned short table[15] = {
01331           FL_F+1, FL_F+2, FL_F+3, FL_F+4,
01332           FL_Home, FL_Left, FL_Up, FL_Right,
01333           FL_Down, FL_Page_Up, FL_Page_Down, FL_End,
01334           0xff0b/*XK_Clear*/, FL_Insert, FL_Delete};
01335         keysym = table[keysym-0xff91];
01336       }
01337     } else {
01338       // Store this so we can later know if the KP was used
01339       Fl::e_original_keysym = (int)keysym;
01340     }
01341     Fl::e_keysym = int(keysym);
01342   
01343     // replace XK_ISO_Left_Tab (Shift-TAB) with FL_Tab (modifier flags are set correctly by X11)
01344     if (Fl::e_keysym == 0xfe20) Fl::e_keysym = FL_Tab;
01345     
01346     set_event_xy();
01347     Fl::e_is_click = 0;
01348     break;}
01349 
01350   case ButtonPress:
01351     Fl::e_keysym = FL_Button + xevent.xbutton.button;
01352     set_event_xy();
01353     if (xevent.xbutton.button == Button4) {
01354       Fl::e_dy = -1; // Up
01355       event = FL_MOUSEWHEEL;
01356     } else if (xevent.xbutton.button == Button5) {
01357       Fl::e_dy = +1; // Down
01358       event = FL_MOUSEWHEEL;
01359     } else {
01360       Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
01361       event = FL_PUSH;
01362       checkdouble();
01363     }
01364 
01365     fl_xmousewin = window;
01366     in_a_window = true;
01367     break;
01368 
01369   case MotionNotify:
01370     set_event_xy();
01371 #  if CONSOLIDATE_MOTION
01372     send_motion = fl_xmousewin = window;
01373     in_a_window = true;
01374     return 0;
01375 #  else
01376     event = FL_MOVE;
01377     fl_xmousewin = window;
01378     in_a_window = true;
01379     break;
01380 #  endif
01381 
01382   case ButtonRelease:
01383     Fl::e_keysym = FL_Button + xevent.xbutton.button;
01384     set_event_xy();
01385     Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
01386     if (xevent.xbutton.button == Button4 ||
01387         xevent.xbutton.button == Button5) return 0;
01388     event = FL_RELEASE;
01389 
01390     fl_xmousewin = window;
01391     in_a_window = true;
01392     break;
01393 
01394   case EnterNotify:
01395     if (xevent.xcrossing.detail == NotifyInferior) break;
01396     // XInstallColormap(fl_display, Fl_X::i(window)->colormap);
01397     set_event_xy();
01398     Fl::e_state = xevent.xcrossing.state << 16;
01399     event = FL_ENTER;
01400 
01401     fl_xmousewin = window;
01402     in_a_window = true;
01403     { XIMStyles *xim_styles = NULL;
01404       if(!fl_xim_im || XGetIMValues(fl_xim_im, XNQueryInputStyle, &xim_styles, NULL, NULL)) {
01405         fl_init_xim();
01406       }
01407       if (xim_styles) XFree(xim_styles);
01408     }
01409     break;
01410 
01411   case LeaveNotify:
01412     if (xevent.xcrossing.detail == NotifyInferior) break;
01413     set_event_xy();
01414     Fl::e_state = xevent.xcrossing.state << 16;
01415     fl_xmousewin = 0;
01416     in_a_window = false; // make do_queued_events produce FL_LEAVE event
01417     return 0;
01418 
01419   // We cannot rely on the x,y position in the configure notify event.
01420   // I now think this is an unavoidable problem with X: it is impossible
01421   // for a window manager to prevent the "real" notify event from being
01422   // sent when it resizes the contents, even though it can send an
01423   // artificial event with the correct position afterwards (and some
01424   // window managers do not send this fake event anyway)
01425   // So anyway, do a round trip to find the correct x,y:
01426   case MapNotify:
01427     event = FL_SHOW;
01428 
01429   case ConfigureNotify: {
01430     if (window->parent()) break; // ignore child windows
01431 
01432     // figure out where OS really put window
01433     XWindowAttributes actual;
01434     XGetWindowAttributes(fl_display, fl_xid(window), &actual);
01435     Window cr; int X, Y, W = actual.width, H = actual.height;
01436     XTranslateCoordinates(fl_display, fl_xid(window), actual.root,
01437                           0, 0, &X, &Y, &cr);
01438 
01439     // tell Fl_Window about it and set flag to prevent echoing:
01440     resize_bug_fix = window;
01441     window->resize(X, Y, W, H);
01442     break; // allow add_handler to do something too
01443     }
01444 
01445   case ReparentNotify: {
01446     int xpos, ypos;
01447     Window junk;
01448     
01449     // on some systems, the ReparentNotify event is not handled as we would expect.
01450     XErrorHandler oldHandler = XSetErrorHandler(catchXExceptions());
01451 
01452     //ReparentNotify gives the new position of the window relative to
01453     //the new parent. FLTK cares about the position on the root window.
01454     XTranslateCoordinates(fl_display, xevent.xreparent.parent,
01455                           XRootWindow(fl_display, fl_screen),
01456                           xevent.xreparent.x, xevent.xreparent.y,
01457                           &xpos, &ypos, &junk);
01458     XSetErrorHandler(oldHandler);
01459 
01460     // tell Fl_Window about it and set flag to prevent echoing:
01461     if ( !wasXExceptionRaised() ) {
01462       resize_bug_fix = window;
01463       window->position(xpos, ypos);
01464     }
01465     break;
01466     }
01467   }
01468 
01469   return Fl::handle(event, window);
01470 }
01471 
01473 
01474 void Fl_Window::resize(int X,int Y,int W,int H) {
01475   int is_a_move = (X != x() || Y != y());
01476   int is_a_resize = (W != w() || H != h());
01477   int is_a_enlarge = (W > w() || H > h());
01478   int resize_from_program = (this != resize_bug_fix);
01479   if (!resize_from_program) resize_bug_fix = 0;
01480   if (is_a_move && resize_from_program) set_flag(FORCE_POSITION);
01481   else if (!is_a_resize && !is_a_move) return;
01482   if (is_a_resize) {
01483     Fl_Group::resize(X,Y,W,H);
01484     if (shown()) {redraw(); if(is_a_enlarge) i->wait_for_expose = 1;}
01485   } else {
01486     x(X); y(Y);
01487   }
01488 
01489   if (resize_from_program && is_a_resize && !resizable()) {
01490     size_range(w(), h(), w(), h());
01491   }
01492 
01493   if (resize_from_program && shown()) {
01494     if (is_a_resize) {
01495       if (!resizable()) size_range(w(),h(),w(),h());
01496       if (is_a_move) {
01497         XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1);
01498       } else {
01499         XResizeWindow(fl_display, i->xid, W>0 ? W : 1, H>0 ? H : 1);
01500       }
01501     } else
01502       XMoveWindow(fl_display, i->xid, X, Y);
01503   }
01504 }
01505 
01507 
01508 // A subclass of Fl_Window may call this to associate an X window it
01509 // creates with the Fl_Window:
01510 
01511 void fl_fix_focus(); // in Fl.cxx
01512 
01513 Fl_X* Fl_X::set_xid(Fl_Window* win, Window winxid) {
01514   Fl_X* xp = new Fl_X;
01515   xp->xid = winxid;
01516   xp->other_xid = 0;
01517   xp->setwindow(win);
01518   xp->next = Fl_X::first;
01519   xp->region = 0;
01520   xp->wait_for_expose = 1;
01521   xp->backbuffer_bad = 1;
01522   Fl_X::first = xp;
01523   if (win->modal()) {Fl::modal_ = win; fl_fix_focus();}
01524   return xp;
01525 }
01526 
01527 // More commonly a subclass calls this, because it hides the really
01528 // ugly parts of X and sets all the stuff for a window that is set
01529 // normally.  The global variables like fl_show_iconic are so that
01530 // subclasses of *that* class may change the behavior...
01531 
01532 char fl_show_iconic;    // hack for iconize()
01533 int fl_background_pixel = -1; // hack to speed up bg box drawing
01534 int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
01535 
01536 static const int childEventMask = ExposureMask;
01537 
01538 static const int XEventMask =
01539 ExposureMask|StructureNotifyMask
01540 |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
01541 |ButtonPressMask|ButtonReleaseMask
01542 |EnterWindowMask|LeaveWindowMask
01543 |PointerMotionMask;
01544 
01545 void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
01546 {
01547   Fl_Group::current(0); // get rid of very common user bug: forgot end()
01548 
01549   int X = win->x();
01550   int Y = win->y();
01551   int W = win->w();
01552   if (W <= 0) W = 1; // X don't like zero...
01553   int H = win->h();
01554   if (H <= 0) H = 1; // X don't like zero...
01555   if (!win->parent() && !Fl::grab()) {
01556     // center windows in case window manager does not do anything:
01557 #ifdef FL_CENTER_WINDOWS
01558     if (!(win->flags() & Fl_Widget::FORCE_POSITION)) {
01559       win->x(X = scr_x+(scr_w-W)/2);
01560       win->y(Y = scr_y+(scr_h-H)/2);
01561     }
01562 #endif // FL_CENTER_WINDOWS
01563 
01564     // force the window to be on-screen.  Usually the X window manager
01565     // does this, but a few don't, so we do it here for consistency:
01566     int scr_x, scr_y, scr_w, scr_h;
01567     Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
01568 
01569     if (win->border()) {
01570       // ensure border is on screen:
01571       // (assume extremely minimal dimensions for this border)
01572       const int top = 20;
01573       const int left = 1;
01574       const int right = 1;
01575       const int bottom = 1;
01576       if (X+W+right > scr_x+scr_w) X = scr_x+scr_w-right-W;
01577       if (X-left < scr_x) X = scr_x+left;
01578       if (Y+H+bottom > scr_y+scr_h) Y = scr_y+scr_h-bottom-H;
01579       if (Y-top < scr_y) Y = scr_y+top;
01580     }
01581     // now insure contents are on-screen (more important than border):
01582     if (X+W > scr_x+scr_w) X = scr_x+scr_w-W;
01583     if (X < scr_x) X = scr_x;
01584     if (Y+H > scr_y+scr_h) Y = scr_y+scr_h-H;
01585     if (Y < scr_y) Y = scr_y;
01586   }
01587 
01588   // if the window is a subwindow and our parent is not mapped yet, we
01589   // mark this window visible, so that mapping the parent at a later
01590   // point in time will call this function again to finally map the subwindow.
01591   if (win->parent() && !Fl_X::i(win->window())) {
01592     win->set_visible();
01593     return;
01594   }
01595 
01596   ulong root = win->parent() ?
01597     fl_xid(win->window()) : RootWindow(fl_display, fl_screen);
01598 
01599   XSetWindowAttributes attr;
01600   int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
01601   attr.event_mask = win->parent() ? childEventMask : XEventMask;
01602   attr.colormap = colormap;
01603   attr.border_pixel = 0;
01604   attr.bit_gravity = 0; // StaticGravity;
01605   if (win->override()) {
01606     attr.override_redirect = 1;
01607     attr.save_under = 1;
01608     mask |= CWOverrideRedirect | CWSaveUnder;
01609   } else attr.override_redirect = 0;
01610   if (Fl::grab()) {
01611     attr.save_under = 1; mask |= CWSaveUnder;
01612     if (!win->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
01613   }
01614   if (fl_background_pixel >= 0) {
01615     attr.background_pixel = fl_background_pixel;
01616     fl_background_pixel = -1;
01617     mask |= CWBackPixel;
01618   }
01619 
01620   Fl_X* xp =
01621     set_xid(win, XCreateWindow(fl_display,
01622                                root,
01623                                X, Y, W, H,
01624                                0, // borderwidth
01625                                visual->depth,
01626                                InputOutput,
01627                                visual->visual,
01628                                mask, &attr));
01629   int showit = 1;
01630 
01631   if (!win->parent() && !attr.override_redirect) {
01632     // Communicate all kinds 'o junk to the X Window Manager:
01633 
01634     win->label(win->label(), win->iconlabel());
01635 
01636     XChangeProperty(fl_display, xp->xid, WM_PROTOCOLS,
01637                     XA_ATOM, 32, 0, (uchar*)&WM_DELETE_WINDOW, 1);
01638 
01639     // send size limits and border:
01640     xp->sendxjunk();
01641 
01642     // set the class property, which controls the icon used:
01643     if (win->xclass()) {
01644       char buffer[1024];
01645       char *p; const char *q;
01646       // truncate on any punctuation, because they break XResource lookup:
01647       for (p = buffer, q = win->xclass(); isalnum(*q)||(*q&128);) *p++ = *q++;
01648       *p++ = 0;
01649       // create the capitalized version:
01650       q = buffer;
01651       *p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++);
01652       while ((*p++ = *q++));
01653       XChangeProperty(fl_display, xp->xid, XA_WM_CLASS, XA_STRING, 8, 0,
01654                       (unsigned char *)buffer, p-buffer-1);
01655     }
01656 
01657     if (win->non_modal() && xp->next && !fl_disable_transient_for) {
01658       // find some other window to be "transient for":
01659       Fl_Window* wp = xp->next->w;
01660       while (wp->parent()) wp = wp->window();
01661       XSetTransientForHint(fl_display, xp->xid, fl_xid(wp));
01662       if (!wp->visible()) showit = 0; // guess that wm will not show it
01663     }
01664    
01665     // Make sure that borderless windows do not show in the task bar
01666     if (!win->border()) {
01667       Atom net_wm_state = XInternAtom (fl_display, "_NET_WM_STATE", 0);
01668       Atom net_wm_state_skip_taskbar = XInternAtom (fl_display, "_NET_WM_STATE_SKIP_TASKBAR", 0);
01669       XChangeProperty (fl_display, xp->xid, net_wm_state, XA_ATOM, 32,
01670           PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1);
01671     }
01672 
01673     // Make it receptive to DnD:
01674     long version = 4;
01675     XChangeProperty(fl_display, xp->xid, fl_XdndAware,
01676                     XA_ATOM, sizeof(int)*8, 0, (unsigned char*)&version, 1);
01677 
01678     XWMHints *hints = XAllocWMHints();
01679     hints->input = True;
01680     hints->flags = InputHint;
01681     if (fl_show_iconic) {
01682       hints->flags |= StateHint;
01683       hints->initial_state = IconicState;
01684       fl_show_iconic = 0;
01685       showit = 0;
01686     }
01687     if (win->icon()) {
01688       hints->icon_pixmap = (Pixmap)win->icon();
01689       hints->flags       |= IconPixmapHint;
01690     }
01691     XSetWMHints(fl_display, xp->xid, hints);
01692     XFree(hints);
01693   }
01694 
01695   // set the window type for menu and tooltip windows to avoid animations (compiz)
01696   if (win->menu_window() || win->tooltip_window()) {
01697     Atom net_wm_type = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
01698     Atom net_wm_type_kind = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_MENU", False);
01699     XChangeProperty(fl_display, xp->xid, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_type_kind, 1);
01700   }
01701 
01702   XMapWindow(fl_display, xp->xid);
01703   if (showit) {
01704     win->set_visible();
01705     int old_event = Fl::e_number;
01706     win->handle(Fl::e_number = FL_SHOW); // get child windows to appear
01707     Fl::e_number = old_event;
01708     win->redraw();
01709   }
01710 }
01711 
01713 // Send X window stuff that can be changed over time:
01714 
01715 void Fl_X::sendxjunk() {
01716   if (w->parent() || w->override()) return; // it's not a window manager window!
01717 
01718   if (!w->size_range_set) { // default size_range based on resizable():
01719     if (w->resizable()) {
01720       Fl_Widget *o = w->resizable();
01721       int minw = o->w(); if (minw > 100) minw = 100;
01722       int minh = o->h(); if (minh > 100) minh = 100;
01723       w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
01724     } else {
01725       w->size_range(w->w(), w->h(), w->w(), w->h());
01726     }
01727     return; // because this recursively called here
01728   }
01729 
01730   XSizeHints *hints = XAllocSizeHints();
01731   // memset(&hints, 0, sizeof(hints)); jreiser suggestion to fix purify?
01732   hints->min_width = w->minw;
01733   hints->min_height = w->minh;
01734   hints->max_width = w->maxw;
01735   hints->max_height = w->maxh;
01736   hints->width_inc = w->dw;
01737   hints->height_inc = w->dh;
01738   hints->win_gravity = StaticGravity;
01739 
01740   // see the file /usr/include/X11/Xm/MwmUtil.h:
01741   // fill all fields to avoid bugs in kwm and perhaps other window managers:
01742   // 0, MWM_FUNC_ALL, MWM_DECOR_ALL
01743   long prop[5] = {0, 1, 1, 0, 0};
01744 
01745   if (hints->min_width != hints->max_width ||
01746       hints->min_height != hints->max_height) { // resizable
01747     hints->flags = PMinSize|PWinGravity;
01748     if (hints->max_width >= hints->min_width ||
01749         hints->max_height >= hints->min_height) {
01750       hints->flags = PMinSize|PMaxSize|PWinGravity;
01751       // unfortunately we can't set just one maximum size.  Guess a
01752       // value for the other one.  Some window managers will make the
01753       // window fit on screen when maximized, others will put it off screen:
01754       if (hints->max_width < hints->min_width) hints->max_width = Fl::w();
01755       if (hints->max_height < hints->min_height) hints->max_height = Fl::h();
01756     }
01757     if (hints->width_inc && hints->height_inc) hints->flags |= PResizeInc;
01758     if (w->aspect) {
01759       // stupid X!  It could insist that the corner go on the
01760       // straight line between min and max...
01761       hints->min_aspect.x = hints->max_aspect.x = hints->min_width;
01762       hints->min_aspect.y = hints->max_aspect.y = hints->min_height;
01763       hints->flags |= PAspect;
01764     }
01765   } else { // not resizable:
01766     hints->flags = PMinSize|PMaxSize|PWinGravity;
01767     prop[0] = 1; // MWM_HINTS_FUNCTIONS
01768     prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE
01769   }
01770 
01771   if (w->flags() & Fl_Widget::FORCE_POSITION) {
01772     hints->flags |= USPosition;
01773     hints->x = w->x();
01774     hints->y = w->y();
01775   }
01776 
01777   if (!w->border()) {
01778     prop[0] |= 2; // MWM_HINTS_DECORATIONS
01779     prop[2] = 0; // no decorations
01780   }
01781 
01782   XSetWMNormalHints(fl_display, xid, hints);
01783   XChangeProperty(fl_display, xid,
01784                   fl_MOTIF_WM_HINTS, fl_MOTIF_WM_HINTS,
01785                   32, 0, (unsigned char *)prop, 5);
01786   XFree(hints);
01787 }
01788 
01789 void Fl_Window::size_range_() {
01790   size_range_set = 1;
01791   if (shown()) i->sendxjunk();
01792 }
01793 
01795 
01796 // returns pointer to the filename, or null if name ends with '/'
01797 const char *fl_filename_name(const char *name) {
01798   const char *p,*q;
01799   if (!name) return (0);
01800   for (p=q=name; *p;) if (*p++ == '/') q = p;
01801   return q;
01802 }
01803 
01804 void Fl_Window::label(const char *name,const char *iname) {
01805   Fl_Widget::label(name);
01806   iconlabel_ = iname;
01807   if (shown() && !parent()) {
01808     if (!name) name = "";
01809     int namelen = strlen(name);
01810     if (!iname) iname = fl_filename_name(name);
01811     int inamelen = strlen(iname);
01812     XChangeProperty(fl_display, i->xid, fl_NET_WM_NAME,      fl_XaUtf8String, 8, 0, (uchar*)name,  namelen);    // utf8
01813     XChangeProperty(fl_display, i->xid, XA_WM_NAME,          XA_STRING,       8, 0, (uchar*)name,  namelen);    // non-utf8
01814     XChangeProperty(fl_display, i->xid, fl_NET_WM_ICON_NAME, fl_XaUtf8String, 8, 0, (uchar*)iname, inamelen);   // utf8
01815     XChangeProperty(fl_display, i->xid, XA_WM_ICON_NAME,     XA_STRING,       8, 0, (uchar*)iname, inamelen);   // non-utf8
01816   }
01817 }
01818 
01820 // Implement the virtual functions for the base Fl_Window class:
01821 
01822 // If the box is a filled rectangle, we can make the redisplay *look*
01823 // faster by using X's background pixel erasing.  We can make it
01824 // actually *be* faster by drawing the frame only, this is done by
01825 // setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
01826 //
01827 // On XFree86 (and prehaps all X's) this has a problem if the window
01828 // is resized while a save-behind window is atop it.  The previous
01829 // contents are restored to the area, but this assumes the area
01830 // is cleared to background color.  So this is disabled in this version.
01831 // Fl_Window *fl_boxcheat;
01832 static inline int can_boxcheat(uchar b) {return (b==1 || ((b&2) && b<=15));}
01833 
01834 void Fl_Window::show() {
01835   image(Fl::scheme_bg_);
01836   if (Fl::scheme_bg_) {
01837     labeltype(FL_NORMAL_LABEL);
01838     align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
01839   } else {
01840     labeltype(FL_NO_LABEL);
01841   }
01842   Fl_Tooltip::exit(this);
01843   if (!shown()) {
01844     fl_open_display();
01845     // Don't set background pixel for double-buffered windows...
01846     if (type() == FL_WINDOW && can_boxcheat(box())) {
01847       fl_background_pixel = int(fl_xpixel(color()));
01848     }
01849     Fl_X::make_xid(this);
01850   } else {
01851     XMapRaised(fl_display, i->xid);
01852   }
01853 #ifdef USE_PRINT_BUTTON
01854 void preparePrintFront(void);
01855 preparePrintFront();
01856 #endif
01857 }
01858 
01859 Window fl_window;
01860 Fl_Window *Fl_Window::current_;
01861 GC fl_gc;
01862 
01863 // make X drawing go into this window (called by subclass flush() impl.)
01864 void Fl_Window::make_current() {
01865   static GC gc; // the GC used by all X windows
01866   if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0);
01867   fl_window = i->xid;
01868   fl_gc = gc;
01869   current_ = this;
01870   fl_clip_region(0);
01871 
01872 #ifdef FLTK_USE_CAIRO
01873   // update the cairo_t context
01874   if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this);
01875 #endif
01876 
01877 }
01878 
01879 #ifdef USE_PRINT_BUTTON
01880 // to test the Fl_Printer class creating a "Print front window" button in a separate window
01881 // contains also preparePrintFront call above
01882 #include <FL/Fl_Printer.H>
01883 #include <FL/Fl_Button.H>
01884 void printFront(Fl_Widget *o, void *data)
01885 {
01886   Fl_Printer printer;
01887   o->window()->hide();
01888   Fl_Window *win = Fl::first_window();
01889   if(!win) return;
01890   int w, h;
01891   if( printer.start_job(1) ) { o->window()->show(); return; }
01892   if( printer.start_page() ) { o->window()->show(); return; }
01893   printer.printable_rect(&w,&h);
01894   // scale the printer device so that the window fits on the page
01895   float scale = 1;
01896   if (win->w() > w || win->h() > h) {
01897     scale = (float)w/win->w();
01898     if ((float)h/win->h() < scale) scale = (float)h/win->h();
01899     printer.scale(scale, scale);
01900   }
01901 
01902 // #define ROTATE 20.0
01903 #ifdef ROTATE
01904   printer.scale(scale * 0.8, scale * 0.8);
01905   printer.printable_rect(&w, &h);
01906   printer.origin(w/2, h/2 );
01907   printer.rotate(ROTATE);
01908   printer.print_widget( win, - win->w()/2, - win->h()/2 );
01909   //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
01910 #else
01911   printer.print_widget( win );
01912   //printer.print_window_part( win, 0,0,win->w(), win->h() );
01913 #endif
01914 
01915   printer.end_page();
01916   printer.end_job();
01917   o->window()->show();
01918 }
01919 
01920 void preparePrintFront(void)
01921 {
01922   static int first=1;
01923   if(!first) return;
01924   first=0;
01925   static Fl_Window w(0,0,150,30);
01926   static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
01927   b.callback(printFront);
01928   w.end();
01929   w.show();
01930 }
01931 #endif // USE_PRINT_BUTTON
01932 
01933 #endif
01934 
01935 //
01936 // End of "$Id: Fl_x.cxx 8198 2011-01-06 10:24:58Z manolo $".
01937 //