themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_x.cxx
blob3f91a0c005b96e86c61d6f607dba07637c9f241a
1 //
2 // "$Id: Fl_x.cxx 8764 2011-05-30 16:47:48Z manolo $"
3 //
4 // X specific code for the Fast Light Tool Kit (FLTK).
5 //
6 // Copyright 1998-2011 by Bill Spitzak and others.
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Library General Public
10 // License as published by the Free Software Foundation; either
11 // version 2 of the License, or (at your option) any later version.
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Library General Public License for more details.
18 // You should have received a copy of the GNU Library General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21 // USA.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
28 #ifdef WIN32
29 //# include "Fl_win32.cxx"
30 #elif defined(__APPLE__)
31 //# include "Fl_mac.cxx"
32 #elif !defined(FL_DOXYGEN)
34 # define CONSOLIDATE_MOTION 1
35 /**** Define this if your keyboard lacks a backspace key... ****/
36 /* #define BACKSPACE_HACK 1 */
38 # include <config.h>
39 # include <FL/Fl.H>
40 # include <FL/x.H>
41 # include <FL/Fl_Window.H>
42 # include <FL/fl_utf8.h>
43 # include <FL/Fl_Tooltip.H>
44 # include <FL/fl_draw.H>
45 # include <FL/Fl_Paged_Device.H>
46 # include <stdio.h>
47 # include <stdlib.h>
48 # include "flstring.h"
49 # include <unistd.h>
50 # include <sys/time.h>
51 # include <X11/Xmd.h>
52 # include <X11/Xlocale.h>
53 # include <X11/Xlib.h>
54 # include <X11/keysym.h>
55 #include <FL/Fl_Socket_Window.H>
57 #include <FL/themes.H>
59 static Fl_Cairo_Graphics_Driver fl_cairo_driver;
60 static Fl_Display_Device fl_cairo_display(&fl_cairo_driver);
62 static Fl_Xlib_Graphics_Driver fl_xlib_driver;
63 static Fl_Display_Device fl_xlib_display(&fl_xlib_driver);
65 FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_cairo_driver; // the current target device of graphics operations
66 Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_cairo_display; // the current target surface of graphics operations
67 Fl_Display_Device *Fl_Display_Device::_display = &fl_cairo_display;// the platform display
69 void
70 fl_offscreen_get_dimensions ( Fl_Offscreen p, unsigned int *w, unsigned int *h )
72 Window rr;
73 int x, y;
74 unsigned int br, dr;
75 XGetGeometry( fl_display, p, &rr, &x, &y, w, h, &br, &dr );
78 ////////////////////////////////////////////////////////////////
79 // interface to poll/select call:
81 # if USE_POLL
83 # include <poll.h>
84 static pollfd *pollfds = 0;
86 # else
87 # if HAVE_SYS_SELECT_H
88 # include <sys/select.h>
89 # endif /* HAVE_SYS_SELECT_H */
91 // The following #define is only needed for HP-UX 9.x and earlier:
92 //#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
94 static fd_set fdsets[3];
95 static int maxfd;
96 # define POLLIN 1
97 # define POLLOUT 4
98 # define POLLERR 8
100 # endif /* USE_POLL */
102 static int nfds = 0;
103 static int fd_array_size = 0;
104 struct FD {
105 # if !USE_POLL
106 int fd;
107 short events;
108 # endif
109 void (*cb)(int, void*);
110 void* arg;
113 static FD *fd = 0;
115 /* XEMBED messages */
116 #define XEMBED_EMBEDDED_NOTIFY 0
117 #define XEMBED_WINDOW_ACTIVATE 1
118 #define XEMBED_WINDOW_DEACTIVATE 2
119 #define XEMBED_REQUEST_FOCUS 3
120 #define XEMBED_FOCUS_IN 4
121 #define XEMBED_FOCUS_OUT 5
122 #define XEMBED_FOCUS_NEXT 6
123 #define XEMBED_FOCUS_PREV 7
124 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
125 #define XEMBED_MODALITY_ON 10
126 #define XEMBED_MODALITY_OFF 11
127 #define XEMBED_REGISTER_ACCELERATOR 12
128 #define XEMBED_UNREGISTER_ACCELERATOR 13
129 #define XEMBED_ACTIVATE_ACCELERATOR 14
131 /* Details for XEMBED_FOCUS_IN: */
132 #define XEMBED_FOCUS_CURRENT 0
133 #define XEMBED_FOCUS_FIRST 1
134 #define XEMBED_FOCUS_LAST 2
136 #define XEMBED_MAPPED (1<<0)
138 Window fl_parent_window = 0; /* hack into Fl_X::make_xid() */
140 void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
141 remove_fd(n,events);
142 int i = nfds++;
143 if (i >= fd_array_size) {
144 FD *temp;
145 fd_array_size = 2*fd_array_size+1;
147 if (!fd) temp = (FD*)malloc(fd_array_size*sizeof(FD));
148 else temp = (FD*)realloc(fd, fd_array_size*sizeof(FD));
150 if (!temp) return;
151 fd = temp;
153 # if USE_POLL
154 pollfd *tpoll;
156 if (!pollfds) tpoll = (pollfd*)malloc(fd_array_size*sizeof(pollfd));
157 else tpoll = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
159 if (!tpoll) return;
160 pollfds = tpoll;
161 # endif
163 fd[i].cb = cb;
164 fd[i].arg = v;
165 # if USE_POLL
166 pollfds[i].fd = n;
167 pollfds[i].events = events;
168 # else
169 fd[i].fd = n;
170 fd[i].events = events;
171 if (events & POLLIN) FD_SET(n, &fdsets[0]);
172 if (events & POLLOUT) FD_SET(n, &fdsets[1]);
173 if (events & POLLERR) FD_SET(n, &fdsets[2]);
174 if (n > maxfd) maxfd = n;
175 # endif
178 void Fl::add_fd(int n, void (*cb)(int, void*), void* v) {
179 Fl::add_fd(n, POLLIN, cb, v);
182 void Fl::remove_fd(int n, int events) {
183 int i,j;
184 # if !USE_POLL
185 maxfd = -1; // recalculate maxfd on the fly
186 # endif
187 for (i=j=0; i<nfds; i++) {
188 # if USE_POLL
189 if (pollfds[i].fd == n) {
190 int e = pollfds[i].events & ~events;
191 if (!e) continue; // if no events left, delete this fd
192 pollfds[j].events = e;
194 # else
195 if (fd[i].fd == n) {
196 int e = fd[i].events & ~events;
197 if (!e) continue; // if no events left, delete this fd
198 fd[i].events = e;
200 if (fd[i].fd > maxfd) maxfd = fd[i].fd;
201 # endif
202 // move it down in the array if necessary:
203 if (j<i) {
204 fd[j] = fd[i];
205 # if USE_POLL
206 pollfds[j] = pollfds[i];
207 # endif
209 j++;
211 nfds = j;
212 # if !USE_POLL
213 if (events & POLLIN) FD_CLR(n, &fdsets[0]);
214 if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
215 if (events & POLLERR) FD_CLR(n, &fdsets[2]);
216 # endif
219 void Fl::remove_fd(int n) {
220 remove_fd(n, -1);
223 #if CONSOLIDATE_MOTION
224 static Fl_Window* send_motion;
225 extern Fl_Window* fl_xmousewin;
226 #endif
227 static bool in_a_window; // true if in any of our windows, even destroyed ones
228 static void do_queued_events() {
229 in_a_window = true;
230 while (XEventsQueued(fl_display,QueuedAfterReading)) {
231 XEvent xevent;
232 XNextEvent(fl_display, &xevent);
233 fl_handle(xevent);
235 // we send FL_LEAVE only if the mouse did not enter some other window:
236 if (!in_a_window) Fl::handle(FL_LEAVE, 0);
237 #if CONSOLIDATE_MOTION
238 else if (send_motion == fl_xmousewin) {
239 send_motion = 0;
240 Fl::handle(FL_MOVE, fl_xmousewin);
242 #endif
245 // these pointers are set by the Fl::lock() function:
246 static void nothing() {}
247 void (*fl_lock_function)() = nothing;
248 void (*fl_unlock_function)() = nothing;
250 // This is never called with time_to_wait < 0.0:
251 // It should return negative on error, 0 if nothing happens before
252 // timeout, and >0 if any callbacks were done.
253 int fl_wait(double time_to_wait) {
255 // OpenGL and other broken libraries call XEventsQueued
256 // unnecessarily and thus cause the file descriptor to not be ready,
257 // so we must check for already-read events:
258 if (fl_display && XQLength(fl_display)) {do_queued_events(); return 1;}
260 # if !USE_POLL
261 fd_set fdt[3];
262 fdt[0] = fdsets[0];
263 fdt[1] = fdsets[1];
264 fdt[2] = fdsets[2];
265 # endif
266 int n;
268 fl_unlock_function();
270 if (time_to_wait < 2147483.648) {
271 # if USE_POLL
272 n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
273 # else
274 timeval t;
275 t.tv_sec = int(time_to_wait);
276 t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
277 n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
278 # endif
279 } else {
280 # if USE_POLL
281 n = ::poll(pollfds, nfds, -1);
282 # else
283 n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
284 # endif
287 fl_lock_function();
289 if (n > 0) {
290 for (int i=0; i<nfds; i++) {
291 # if USE_POLL
292 if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
293 # else
294 int f = fd[i].fd;
295 short revents = 0;
296 if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
297 if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
298 if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
299 if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
300 # endif
303 return n;
306 // fl_ready() is just like fl_wait(0.0) except no callbacks are done:
307 int fl_ready() {
308 if (XQLength(fl_display)) return 1;
309 if (!nfds) return 0; // nothing to select or poll
310 # if USE_POLL
311 return ::poll(pollfds, nfds, 0);
312 # else
313 timeval t;
314 t.tv_sec = 0;
315 t.tv_usec = 0;
316 fd_set fdt[3];
317 fdt[0] = fdsets[0];
318 fdt[1] = fdsets[1];
319 fdt[2] = fdsets[2];
320 return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
321 # endif
324 // replace \r\n by \n
325 static void convert_crlf(unsigned char *string, long& len) {
326 unsigned char *a, *b;
327 a = b = string;
328 while (*a) {
329 if (*a == '\r' && a[1] == '\n') { a++; len--; }
330 else *b++ = *a++;
332 *b = 0;
335 ////////////////////////////////////////////////////////////////
337 Display *fl_display;
338 Window fl_message_window = 0;
339 int fl_screen;
340 XVisualInfo *fl_visual;
341 Colormap fl_colormap;
342 XIM fl_xim_im = 0;
343 XIC fl_xim_ic = 0;
344 char fl_is_over_the_spot = 0;
345 static XRectangle status_area;
347 static Atom WM_DELETE_WINDOW;
348 static Atom WM_PROTOCOLS;
349 static Atom fl_MOTIF_WM_HINTS;
350 static Atom TARGETS;
351 static Atom CLIPBOARD;
352 Atom fl_XdndAware;
353 Atom fl_XdndSelection;
354 Atom fl_XdndEnter;
355 Atom fl_XdndTypeList;
356 Atom fl_XdndPosition;
357 Atom fl_XdndLeave;
358 Atom fl_XdndDrop;
359 Atom fl_XdndStatus;
360 Atom fl_XdndActionCopy;
361 Atom fl_XdndFinished;
362 //Atom fl_XdndProxy;
363 Atom fl_XdndURIList;
364 Atom fl_Xatextplainutf;
365 Atom fl_Xatextplain;
366 static Atom fl_XaText;
367 Atom fl_XaCompoundText;
368 Atom fl_XaUtf8String;
369 Atom fl_XaTextUriList;
370 Atom fl_NET_WM_NAME; // utf8 aware window label
371 Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name
372 Atom fl_XEMBED;
373 Atom fl_XEMBED_INFO;
376 X defines 32-bit-entities to have a format value of max. 32,
377 although sizeof(atom) can be 8 (64 bits) on a 64-bit OS.
378 See also fl_open_display() for sizeof(atom) < 4.
379 Used for XChangeProperty (see STR #2419).
381 static int atom_bits = 32;
383 static void fd_callback(int,void *) {
384 do_queued_events();
387 extern "C" {
388 static int io_error_handler(Display*) {
389 Fl::fatal("X I/O error");
390 return 0;
393 static int xerror_handler(Display* d, XErrorEvent* e) {
394 char buf1[128], buf2[128];
395 sprintf(buf1, "XRequest.%d", e->request_code);
396 XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
397 XGetErrorText(d, e->error_code, buf1, 128);
398 Fl::warning("%s: %s 0x%lx", buf2, buf1, e->resourceid);
399 return 0;
403 extern char *fl_get_font_xfld(int fnum, int size);
405 void fl_new_ic()
407 XVaNestedList preedit_attr = NULL;
408 XVaNestedList status_attr = NULL;
409 static XFontSet fs = NULL;
410 char *fnt;
411 char **missing_list;
412 int missing_count;
413 char *def_string;
414 static XRectangle spot;
415 int predit = 0;
416 int sarea = 0;
417 XIMStyles* xim_styles = NULL;
419 #if USE_XFT
421 #if defined(__GNUC__)
422 // FIXME: warning XFT support here
423 #endif /*__GNUC__*/
425 if (!fs) {
426 fnt = (char*)"-misc-fixed-*";
427 fs = XCreateFontSet(fl_display, fnt, &missing_list,
428 &missing_count, &def_string);
430 #else
431 if (!fs) {
432 bool must_free_fnt = true;
433 fnt = fl_get_font_xfld(0, 14);
434 if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
435 fs = XCreateFontSet(fl_display, fnt, &missing_list,
436 &missing_count, &def_string);
437 if (must_free_fnt) free(fnt);
439 #endif
440 preedit_attr = XVaCreateNestedList(0,
441 XNSpotLocation, &spot,
442 XNFontSet, fs, NULL);
443 status_attr = XVaCreateNestedList(0,
444 XNAreaNeeded, &status_area,
445 XNFontSet, fs, NULL);
447 if (!XGetIMValues(fl_xim_im, XNQueryInputStyle,
448 &xim_styles, NULL, NULL)) {
449 int i;
450 XIMStyle *style;
451 for (i = 0, style = xim_styles->supported_styles;
452 i < xim_styles->count_styles; i++, style++) {
453 if (*style == (XIMPreeditPosition | XIMStatusArea)) {
454 sarea = 1;
455 predit = 1;
456 } else if (*style == (XIMPreeditPosition | XIMStatusNothing)) {
457 predit = 1;
461 XFree(xim_styles);
463 if (sarea) {
464 fl_xim_ic = XCreateIC(fl_xim_im,
465 XNInputStyle, (XIMPreeditPosition | XIMStatusArea),
466 XNPreeditAttributes, preedit_attr,
467 XNStatusAttributes, status_attr,
468 NULL);
471 if (!fl_xim_ic && predit) {
472 fl_xim_ic = XCreateIC(fl_xim_im,
473 XNInputStyle, (XIMPreeditPosition | XIMStatusNothing),
474 XNPreeditAttributes, preedit_attr,
475 NULL);
477 XFree(preedit_attr);
478 XFree(status_attr);
479 if (!fl_xim_ic) {
480 fl_is_over_the_spot = 0;
481 fl_xim_ic = XCreateIC(fl_xim_im,
482 XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
483 NULL);
484 } else {
485 fl_is_over_the_spot = 1;
486 XVaNestedList status_attr = NULL;
487 status_attr = XVaCreateNestedList(0, XNAreaNeeded, &status_area, NULL);
489 XGetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
490 XFree(status_attr);
495 static XRectangle spot;
496 static int spotf = -1;
497 static int spots = -1;
499 void fl_reset_spot(void)
501 spot.x = -1;
502 spot.y = -1;
503 //if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
506 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
508 int change = 0;
509 XVaNestedList preedit_attr;
510 static XFontSet fs = NULL;
511 char **missing_list;
512 int missing_count;
513 char *def_string;
514 char *fnt = NULL;
515 bool must_free_fnt =true;
517 static XIC ic = NULL;
519 if (!fl_xim_ic || !fl_is_over_the_spot) return;
520 //XSetICFocus(fl_xim_ic);
521 if (X != spot.x || Y != spot.y) {
522 spot.x = X;
523 spot.y = Y;
524 spot.height = H;
525 spot.width = W;
526 change = 1;
528 if (font != spotf || size != spots) {
529 spotf = font;
530 spots = size;
531 change = 1;
532 if (fs) {
533 XFreeFontSet(fl_display, fs);
535 #if USE_XFT
537 #if defined(__GNUC__)
538 // FIXME: warning XFT support here
539 #endif /*__GNUC__*/
541 fnt = NULL; // fl_get_font_xfld(font, size);
542 if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
543 fs = XCreateFontSet(fl_display, fnt, &missing_list,
544 &missing_count, &def_string);
545 #else
546 fnt = fl_get_font_xfld(font, size);
547 if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
548 fs = XCreateFontSet(fl_display, fnt, &missing_list,
549 &missing_count, &def_string);
550 #endif
552 if (fl_xim_ic != ic) {
553 ic = fl_xim_ic;
554 change = 1;
557 if (fnt && must_free_fnt) free(fnt);
558 if (!change) return;
561 preedit_attr = XVaCreateNestedList(0,
562 XNSpotLocation, &spot,
563 XNFontSet, fs, NULL);
564 XSetICValues(fl_xim_ic, XNPreeditAttributes, preedit_attr, NULL);
565 XFree(preedit_attr);
568 void fl_set_status(int x, int y, int w, int h)
570 XVaNestedList status_attr;
571 status_area.x = x;
572 status_area.y = y;
573 status_area.width = w;
574 status_area.height = h;
575 if (!fl_xim_ic) return;
576 status_attr = XVaCreateNestedList(0, XNArea, &status_area, NULL);
578 XSetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
579 XFree(status_attr);
582 void fl_init_xim() {
583 static int xim_warning = 2;
584 if (xim_warning > 0) xim_warning--;
586 //XIMStyle *style;
587 XIMStyles *xim_styles;
588 if (!fl_display) return;
589 if (fl_xim_im) return;
591 fl_xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
592 xim_styles = NULL;
593 fl_xim_ic = NULL;
595 if (fl_xim_im) {
596 XGetIMValues (fl_xim_im, XNQueryInputStyle,
597 &xim_styles, NULL, NULL);
598 } else {
599 if (xim_warning)
600 Fl::warning("XOpenIM() failed");
601 // if xim_styles is allocated, free it now
602 if (xim_styles) XFree(xim_styles);
603 return;
606 if (xim_styles && xim_styles->count_styles) {
607 fl_new_ic();
608 } else {
609 if (xim_warning)
610 Fl::warning("No XIM style found");
611 XCloseIM(fl_xim_im);
612 fl_xim_im = NULL;
613 // if xim_styles is allocated, free it now
614 if (xim_styles) XFree(xim_styles);
615 return;
617 if (!fl_xim_ic) {
618 if (xim_warning)
619 Fl::warning("XCreateIC() failed");
620 XCloseIM(fl_xim_im);
621 fl_xim_im = NULL;
623 // if xim_styles is still allocated, free it now
624 if(xim_styles) XFree(xim_styles);
627 void fl_open_display() {
628 if (fl_display) return;
630 setlocale(LC_CTYPE, "");
631 XSetLocaleModifiers("");
633 XSetIOErrorHandler(io_error_handler);
634 XSetErrorHandler(xerror_handler);
636 Display *d = XOpenDisplay(0);
637 if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0));
639 fl_open_display(d);
642 void fl_open_display(Display* d) {
643 fl_display = d;
645 WM_DELETE_WINDOW = XInternAtom(d, "WM_DELETE_WINDOW", 0);
646 WM_PROTOCOLS = XInternAtom(d, "WM_PROTOCOLS", 0);
647 fl_MOTIF_WM_HINTS = XInternAtom(d, "_MOTIF_WM_HINTS", 0);
648 TARGETS = XInternAtom(d, "TARGETS", 0);
649 CLIPBOARD = XInternAtom(d, "CLIPBOARD", 0);
650 fl_XdndAware = XInternAtom(d, "XdndAware", 0);
651 fl_XdndSelection = XInternAtom(d, "XdndSelection", 0);
652 fl_XdndEnter = XInternAtom(d, "XdndEnter", 0);
653 fl_XdndTypeList = XInternAtom(d, "XdndTypeList", 0);
654 fl_XdndPosition = XInternAtom(d, "XdndPosition", 0);
655 fl_XdndLeave = XInternAtom(d, "XdndLeave", 0);
656 fl_XdndDrop = XInternAtom(d, "XdndDrop", 0);
657 fl_XdndStatus = XInternAtom(d, "XdndStatus", 0);
658 fl_XdndActionCopy = XInternAtom(d, "XdndActionCopy", 0);
659 fl_XdndFinished = XInternAtom(d, "XdndFinished", 0);
660 //fl_XdndProxy = XInternAtom(d, "XdndProxy", 0);
661 fl_XdndEnter = XInternAtom(d, "XdndEnter", 0);
662 fl_XdndURIList = XInternAtom(d, "text/uri-list", 0);
663 fl_Xatextplainutf = XInternAtom(d, "text/plain;charset=UTF-8",0);
664 fl_Xatextplain = XInternAtom(d, "text/plain", 0);
665 fl_XaText = XInternAtom(d, "TEXT", 0);
666 fl_XaCompoundText = XInternAtom(d, "COMPOUND_TEXT", 0);
667 fl_XaUtf8String = XInternAtom(d, "UTF8_STRING", 0);
668 fl_XaTextUriList = XInternAtom(d, "text/uri-list", 0);
669 fl_NET_WM_NAME = XInternAtom(d, "_NET_WM_NAME", 0);
670 fl_NET_WM_ICON_NAME = XInternAtom(d, "_NET_WM_ICON_NAME", 0);
671 fl_XEMBED = XInternAtom(d, "_XEMBED", 0);
672 fl_XEMBED_INFO = XInternAtom(d, "_XEMBED_INFO", 0);
674 if (sizeof(Atom) < 4)
675 atom_bits = sizeof(Atom) * 8;
677 Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback);
679 fl_screen = DefaultScreen(d);
681 fl_message_window =
682 XCreateSimpleWindow(d, RootWindow(d,fl_screen), 0,0,1,1,0, 0, 0);
684 // construct an XVisualInfo that matches the default Visual:
685 XVisualInfo templt; int num;
686 templt.visualid = XVisualIDFromVisual(DefaultVisual(d, fl_screen));
687 fl_visual = XGetVisualInfo(d, VisualIDMask, &templt, &num);
688 fl_colormap = DefaultColormap(d, fl_screen);
689 fl_init_xim();
691 #if !USE_COLORMAP
692 Fl::visual(FL_RGB);
693 #endif
695 Fl::get_system_colors();
697 fl_register_themes();
699 Fl_Theme::load_default();
702 void fl_close_display() {
703 Fl::remove_fd(ConnectionNumber(fl_display));
704 XCloseDisplay(fl_display);
707 static int fl_workarea_xywh[4] = { -1, -1, -1, -1 };
709 static void fl_init_workarea() {
710 fl_open_display();
712 Atom _NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", 0);
713 Atom actual;
714 unsigned long count, remaining;
715 int format;
716 unsigned *xywh;
718 if (XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
719 _NET_WORKAREA, 0, 4 * sizeof(unsigned), False,
720 XA_CARDINAL, &actual, &format, &count, &remaining,
721 (unsigned char **)&xywh) || !xywh || !xywh[2] ||
722 !xywh[3])
724 fl_workarea_xywh[0] = 0;
725 fl_workarea_xywh[1] = 0;
726 fl_workarea_xywh[2] = DisplayWidth(fl_display, fl_screen);
727 fl_workarea_xywh[3] = DisplayHeight(fl_display, fl_screen);
729 else
731 fl_workarea_xywh[0] = (int)xywh[0];
732 fl_workarea_xywh[1] = (int)xywh[1];
733 fl_workarea_xywh[2] = (int)xywh[2];
734 fl_workarea_xywh[3] = (int)xywh[3];
735 XFree(xywh);
739 int Fl::x() {
740 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
741 return fl_workarea_xywh[0];
744 int Fl::y() {
745 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
746 return fl_workarea_xywh[1];
749 int Fl::w() {
750 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
751 return fl_workarea_xywh[2];
754 int Fl::h() {
755 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
756 return fl_workarea_xywh[3];
759 void Fl::get_mouse(int &xx, int &yy) {
760 fl_open_display();
761 Window root = RootWindow(fl_display, fl_screen);
762 Window c; int mx,my,cx,cy; unsigned int mask;
763 XQueryPointer(fl_display,root,&root,&c,&mx,&my,&cx,&cy,&mask);
764 xx = mx;
765 yy = my;
768 void Fl::set_mouse(int x, int y) {
769 fl_open_display();
770 Window root = RootWindow(fl_display, fl_screen);
771 XWarpPointer(fl_display,None,root,0,0,0,0,x,y);
775 ////////////////////////////////////////////////////////////////
776 // Code used for paste and DnD into the program:
778 Fl_Widget *fl_selection_requestor;
779 char *fl_selection_buffer[2];
780 int fl_selection_length[2];
781 int fl_selection_buffer_length[2];
782 char fl_i_own_selection[2] = {0,0};
784 // Call this when a "paste" operation happens:
785 void Fl::paste(Fl_Widget &receiver, int clipboard) {
786 if (fl_i_own_selection[clipboard]) {
787 // We already have it, do it quickly without window server.
788 // Notice that the text is clobbered if set_selection is
789 // called in response to FL_PASTE!
790 Fl::e_text = fl_selection_buffer[clipboard];
791 Fl::e_length = fl_selection_length[clipboard];
792 if (!Fl::e_text) Fl::e_text = (char *)"";
793 receiver.handle(FL_PASTE);
794 return;
796 // otherwise get the window server to return it:
797 fl_selection_requestor = &receiver;
798 Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
799 XConvertSelection(fl_display, property, TARGETS, property,
800 fl_xid(Fl::first_window()), fl_event_time);
803 Window fl_dnd_source_window;
804 Atom *fl_dnd_source_types; // null-terminated list of data types being supplied
805 Atom fl_dnd_type;
806 Atom fl_dnd_source_action;
807 Atom fl_dnd_action;
809 void fl_sendClientMessage(Window window, Atom message,
810 unsigned long d0,
811 unsigned long d1=0,
812 unsigned long d2=0,
813 unsigned long d3=0,
814 unsigned long d4=0)
816 XEvent e;
817 e.xany.type = ClientMessage;
818 e.xany.window = window;
819 e.xclient.message_type = message;
820 e.xclient.format = 32;
821 e.xclient.data.l[0] = (long)d0;
822 e.xclient.data.l[1] = (long)d1;
823 e.xclient.data.l[2] = (long)d2;
824 e.xclient.data.l[3] = (long)d3;
825 e.xclient.data.l[4] = (long)d4;
826 XSendEvent(fl_display, window, 0, 0, &e);
829 ////////////////////////////////////////////////////////////////
830 // Code for copying to clipboard and DnD out of the program:
832 void Fl::copy(const char *stuff, int len, int clipboard) {
833 if (!stuff || len<0) return;
834 if (len+1 > fl_selection_buffer_length[clipboard]) {
835 delete[] fl_selection_buffer[clipboard];
836 fl_selection_buffer[clipboard] = new char[len+100];
837 fl_selection_buffer_length[clipboard] = len+100;
839 memcpy(fl_selection_buffer[clipboard], stuff, len);
840 fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
841 fl_selection_length[clipboard] = len;
842 fl_i_own_selection[clipboard] = 1;
843 Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
844 XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
847 ////////////////////////////////////////////////////////////////
849 const XEvent* fl_xevent; // the current x event
850 ulong fl_event_time; // the last timestamp from an x event
852 char fl_key_vector[32]; // used by Fl::get_key()
854 // Record event mouse position and state from an XEvent:
856 static int px, py;
857 static ulong ptime;
859 static void set_event_xy() {
860 # if CONSOLIDATE_MOTION
861 send_motion = 0;
862 # endif
863 Fl::e_x_root = fl_xevent->xbutton.x_root;
864 Fl::e_x = fl_xevent->xbutton.x;
865 Fl::e_y_root = fl_xevent->xbutton.y_root;
866 Fl::e_y = fl_xevent->xbutton.y;
867 Fl::e_state = fl_xevent->xbutton.state << 16;
868 fl_event_time = fl_xevent->xbutton.time;
869 # ifdef __sgi
870 // get the meta key off PC keyboards:
871 if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META;
872 # endif
873 // turn off is_click if enough time or mouse movement has passed:
874 if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 ||
875 fl_event_time >= ptime+1000)
876 Fl::e_is_click = 0;
879 // if this is same event as last && is_click, increment click count:
880 static inline void checkdouble() {
881 if (Fl::e_is_click == Fl::e_keysym)
882 Fl::e_clicks++;
883 else {
884 Fl::e_clicks = 0;
885 Fl::e_is_click = Fl::e_keysym;
887 px = Fl::e_x_root;
888 py = Fl::e_y_root;
889 ptime = fl_event_time;
892 static Fl_Window* resize_bug_fix;
894 ////////////////////////////////////////////////////////////////
896 static char unknown[] = "<unknown>";
897 const int unknown_len = 10;
899 extern "C" {
901 static int xerror = 0;
903 static int ignoreXEvents(Display *display, XErrorEvent *event) {
904 xerror = 1;
905 return 0;
908 static XErrorHandler catchXExceptions() {
909 xerror = 0;
910 return ignoreXEvents;
913 static int wasXExceptionRaised() {
914 return xerror;
919 bool fl_embed_called = false;
921 void fl_embed ( Fl_Window *w, Window parent )
923 fl_embed_called = true;
924 /* this destroys the existing window */
925 w->hide();
926 /* embedded windows don't need borders */
927 w->border(0);
929 fl_parent_window = parent;
930 Fl_X::make_xid( w, fl_visual, fl_colormap );
931 fl_parent_window = 0;
933 /* mark this window as wanting to be embedded */
934 unsigned long buffer[2];
936 buffer[0] = 1; /* protocol version */
937 buffer[1] = 0;
938 /* XEMBED_MAPPED; */
940 XChangeProperty (fl_display,
941 fl_xid( w ),
942 fl_XEMBED_INFO, fl_XEMBED_INFO, 32,
943 PropModeReplace,
944 (unsigned char *)buffer, 2);
946 XSync(fl_display, False );
950 static void
951 xembed_send_configure_event ( Fl_Socket_Window *w )
953 /* printf( "NTK: Sending synthetic configure event to embedded window\n" ); */
955 XConfigureEvent xc;
957 memset( &xc, 0, sizeof( xc ) );
959 xc.type = ConfigureNotify;
960 xc.event = w->plug_xid();
961 xc.window = w->plug_xid();
963 /* xc.x = w->x(); */
964 /* xc.y = w->y(); */
965 xc.x = 0;
966 xc.y = 0;
967 xc.width = w->w();
968 xc.height = w->h();
970 xc.border_width = 0;
971 xc.above = None;
972 xc.override_redirect = False;
974 XSendEvent( fl_display, w->plug_xid(), False, NoEventMask, (XEvent*)&xc );
977 static int
978 xembed_get_info ( Window window, unsigned long *version, unsigned long *flags )
980 int r;
982 Atom type;
983 int format;
984 unsigned long nitems, bytes_after;
985 unsigned char *data;
986 unsigned long *data_long;
988 r = XGetWindowProperty( fl_display, window, fl_XEMBED_INFO, 0, 2, False, fl_XEMBED_INFO, &type, &format, &nitems, &bytes_after, &data );
990 if ( r != Success || type != fl_XEMBED_INFO )
991 return 0;
993 if ( nitems < 2 )
995 XFree(data);
996 return 0;
999 data_long = (unsigned long*)data;
1001 if ( version )
1002 *version = data_long[0];
1003 if ( flags )
1004 *flags = data_long[1] & XEMBED_MAPPED;
1006 XFree(data);
1008 return 1;
1011 static int
1012 xembed_maybe_embed_window ( Window parent, Window window )
1014 if ( parent != window )
1016 Fl_Socket_Window *p = (Fl_Socket_Window*)fl_find( parent );
1018 if ( p )
1020 if ( Fl_Socket_Window::is_socket( p ) )
1022 if ( p->plug_xid() )
1024 // error, already embedded.
1026 else
1028 /* printf( "NTK: Got embed request... Replying with XEMBED_EMBEDDED_NOTIFY\n" ); */
1030 fl_sendClientMessage(window, fl_XEMBED, CurrentTime, XEMBED_EMBEDDED_NOTIFY, 0, window, 0 );
1031 fl_sendClientMessage(window, fl_XEMBED, CurrentTime, XEMBED_WINDOW_ACTIVATE );
1032 p->plug_xid( window );
1035 XMapWindow( fl_display, window );
1037 xembed_send_configure_event( p );
1038 return 1;
1044 return 0;
1047 static int fl_handle_xembed ( const XEvent &xevent )
1049 switch (xevent.type) {
1050 case DestroyNotify:
1052 /* printf( "Got destroy notify\n" ); */
1054 Fl_Socket_Window *p = (Fl_Socket_Window*)fl_find( xevent.xdestroywindow.event );
1056 if ( p && Fl_Socket_Window::is_socket( p ) )
1058 /* printf( "Ending embedding\n" ); */
1059 p->plug_xid( 0 );
1060 return 1;
1063 return 0;
1064 break;
1066 case MapRequest:
1068 /* printf( "Got map request\n" ); */
1070 return xembed_maybe_embed_window( xevent.xmaprequest.parent, xevent.xmaprequest.window );
1071 break;
1073 case CreateNotify:
1075 /* printf( "Got create notify\n" ); */
1076 const XCreateWindowEvent *cw = &fl_xevent->xcreatewindow;
1078 return xembed_maybe_embed_window( cw->parent, cw->window );
1079 break;
1081 case ReparentNotify:
1082 /* printf( "Got reparent notify\n" ); */
1083 return xembed_maybe_embed_window( xevent.xreparent.parent, xevent.xreparent.window );
1084 break;
1085 case ConfigureRequest:
1087 /* printf( "Got configure request\n" ); */
1088 XConfigureRequestEvent *xe = (XConfigureRequestEvent*)&fl_xevent->xconfigurerequest;
1090 xembed_maybe_embed_window( xe->parent, xe->window );
1092 printf( "Got configure request for %ix%i... Not gonna happen.\n", xe->width, xe->height );
1094 Fl_Socket_Window *p = (Fl_Socket_Window*)fl_find( xe->parent );
1095 if ( p && Fl_Socket_Window::is_socket( p ) )
1097 xembed_send_configure_event( p );
1098 return 1;
1100 return 0;
1101 break;
1103 case PropertyNotify:
1105 /* printf( "Got property notify.\n" ); */
1106 if ( xevent.xproperty.atom == fl_XEMBED_INFO )
1108 unsigned long version = 0;
1109 unsigned long flags = 0;
1111 xembed_get_info( xevent.xproperty.window, &version, &flags );
1113 if ( (flags & XEMBED_MAPPED) )
1115 XMapWindow( fl_display, xevent.xproperty.window );
1118 return 1;
1120 return 0;
1121 break;
1123 case ClientMessage: {
1124 Fl_Window* window = fl_find(fl_xevent->xclient.window);
1125 Atom message = fl_xevent->xclient.message_type;
1126 const long* data = fl_xevent->xclient.data.l;
1128 if ( message == fl_XEMBED )
1130 switch ( data[1] )
1132 case XEMBED_EMBEDDED_NOTIFY:
1133 /* printf( "NTK: Got XEMBED_EMBEDDED_NOTIFY\n" ); */
1134 window->resize( 0, 0, window->w(), window->h() );
1135 break;
1136 case XEMBED_WINDOW_ACTIVATE:
1137 /* printf( "NTK: Got XEMBED_WINDOW_ACTIVATE\n" ); */
1138 /* window->resize( 0, 0, window->w(), window->h() ); */
1139 break;
1140 case XEMBED_WINDOW_DEACTIVATE:
1141 /* printf( "NTK: Got XEMBED_WINDOW_DEACTIVATE\n" ); */
1142 break;
1143 default:
1144 /* unsupported message */
1145 break;
1147 return 1;
1150 return 0;
1151 break;
1153 default:
1154 return 0;
1157 return 0;
1160 int fl_handle(const XEvent& thisevent)
1162 XEvent xevent = thisevent;
1163 fl_xevent = &thisevent;
1164 Window xid = xevent.xany.window;
1165 static Window xim_win = 0;
1167 if (fl_xim_ic && xevent.type == DestroyNotify &&
1168 xid != xim_win && !fl_find(xid))
1170 XIM xim_im;
1171 xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
1172 if (!xim_im) {
1173 /* XIM server has crashed */
1174 XSetLocaleModifiers("@im=");
1175 fl_xim_im = NULL;
1176 fl_init_xim();
1177 } else {
1178 XCloseIM(xim_im); // see STR 2185 for comment
1180 return 0;
1183 if (fl_xim_ic && (xevent.type == FocusIn))
1185 #define POOR_XIM
1186 #ifdef POOR_XIM
1187 if (xim_win != xid)
1189 xim_win = xid;
1190 XDestroyIC(fl_xim_ic);
1191 fl_xim_ic = NULL;
1192 fl_new_ic();
1193 XSetICValues(fl_xim_ic,
1194 XNFocusWindow, xevent.xclient.window,
1195 XNClientWindow, xid,
1196 NULL);
1198 fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
1199 #else
1200 if (Fl::first_window() && Fl::first_window()->modal()) {
1201 Window x = fl_xid(Fl::first_window());
1202 if (x != xim_win) {
1203 xim_win = x;
1204 XSetICValues(fl_xim_ic,
1205 XNFocusWindow, xim_win,
1206 XNClientWindow, xim_win,
1207 NULL);
1208 fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
1210 } else if (xim_win != xid && xid) {
1211 xim_win = xid;
1212 XSetICValues(fl_xim_ic,
1213 XNFocusWindow, xevent.xclient.window,
1214 XNClientWindow, xid,
1215 //XNFocusWindow, xim_win,
1216 //XNClientWindow, xim_win,
1217 NULL);
1218 fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
1220 #endif
1223 if ( XFilterEvent((XEvent *)&xevent, 0) )
1224 return(1);
1226 if ( fl_handle_xembed( xevent ) )
1227 return 1;
1229 switch (xevent.type) {
1231 case KeymapNotify:
1232 memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32);
1233 return 0;
1235 case MappingNotify:
1236 XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
1237 return 0;
1239 case SelectionNotify: {
1240 if (!fl_selection_requestor) return 0;
1241 static unsigned char* buffer = 0;
1242 if (buffer) {XFree(buffer); buffer = 0;}
1243 long bytesread = 0;
1244 if (fl_xevent->xselection.property) for (;;) {
1245 // The Xdnd code pastes 64K chunks together, possibly to avoid
1246 // bugs in X servers, or maybe to avoid an extra round-trip to
1247 // get the property length. I copy this here:
1248 Atom actual; int format; unsigned long count, remaining;
1249 unsigned char* portion = NULL;
1250 if (XGetWindowProperty(fl_display,
1251 fl_xevent->xselection.requestor,
1252 fl_xevent->xselection.property,
1253 bytesread/4, 65536, 1, 0,
1254 &actual, &format, &count, &remaining,
1255 &portion)) break; // quit on error
1256 if (actual == TARGETS || actual == XA_ATOM) {
1257 Atom type = XA_STRING;
1258 for (unsigned i = 0; i<count; i++) {
1259 Atom t = ((Atom*)portion)[i];
1260 if (t == fl_Xatextplainutf ||
1261 t == fl_Xatextplain ||
1262 t == fl_XaUtf8String) {type = t; break;}
1263 // rest are only used if no utf-8 available:
1264 if (t == fl_XaText ||
1265 t == fl_XaTextUriList ||
1266 t == fl_XaCompoundText) type = t;
1268 XFree(portion);
1269 Atom property = xevent.xselection.property;
1270 XConvertSelection(fl_display, property, type, property,
1271 fl_xid(Fl::first_window()),
1272 fl_event_time);
1273 return true;
1275 // Make sure we got something sane...
1276 if ((portion == NULL) || (format != 8) || (count == 0)) {
1277 if (portion) XFree(portion);
1278 return true;
1280 buffer = (unsigned char*)realloc(buffer, bytesread+count+remaining+1);
1281 memcpy(buffer+bytesread, portion, count);
1282 XFree(portion);
1283 bytesread += count;
1284 // Cannot trust data to be null terminated
1285 buffer[bytesread] = '\0';
1286 if (!remaining) break;
1288 if (buffer) {
1289 buffer[bytesread] = 0;
1290 convert_crlf(buffer, bytesread);
1292 Fl::e_text = buffer ? (char*)buffer : (char *)"";
1293 Fl::e_length = bytesread;
1294 int old_event = Fl::e_number;
1295 fl_selection_requestor->handle(Fl::e_number = FL_PASTE);
1296 Fl::e_number = old_event;
1297 // Detect if this paste is due to Xdnd by the property name (I use
1298 // XA_SECONDARY for that) and send an XdndFinished message. It is not
1299 // clear if this has to be delayed until now or if it can be done
1300 // immediatly after calling XConvertSelection.
1301 if (fl_xevent->xselection.property == XA_SECONDARY &&
1302 fl_dnd_source_window) {
1303 fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished,
1304 fl_xevent->xselection.requestor);
1305 fl_dnd_source_window = 0; // don't send a second time
1307 return 1;}
1309 case SelectionClear: {
1310 int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD;
1311 fl_i_own_selection[clipboard] = 0;
1312 return 1;}
1314 case SelectionRequest: {
1315 XSelectionEvent e;
1316 e.type = SelectionNotify;
1317 e.requestor = fl_xevent->xselectionrequest.requestor;
1318 e.selection = fl_xevent->xselectionrequest.selection;
1319 int clipboard = e.selection == CLIPBOARD;
1320 e.target = fl_xevent->xselectionrequest.target;
1321 e.time = fl_xevent->xselectionrequest.time;
1322 e.property = fl_xevent->xselectionrequest.property;
1323 if (e.target == TARGETS) {
1324 Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
1325 XChangeProperty(fl_display, e.requestor, e.property,
1326 XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
1327 } else if (/*e.target == XA_STRING &&*/ fl_selection_length[clipboard]) {
1328 if (e.target == fl_XaUtf8String ||
1329 e.target == XA_STRING ||
1330 e.target == fl_XaCompoundText ||
1331 e.target == fl_XaText ||
1332 e.target == fl_Xatextplain ||
1333 e.target == fl_Xatextplainutf) {
1334 // clobber the target type, this seems to make some applications
1335 // behave that insist on asking for XA_TEXT instead of UTF8_STRING
1336 // Does not change XA_STRING as that breaks xclipboard.
1337 if (e.target != XA_STRING) e.target = fl_XaUtf8String;
1338 XChangeProperty(fl_display, e.requestor, e.property,
1339 e.target, 8, 0,
1340 (unsigned char *)fl_selection_buffer[clipboard],
1341 fl_selection_length[clipboard]);
1343 } else {
1344 // char* x = XGetAtomName(fl_display,e.target);
1345 // fprintf(stderr,"selection request of %s\n",x);
1346 // XFree(x);
1347 e.property = 0;
1349 XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);}
1350 return 1;
1352 // events where interesting window id is in a different place:
1353 case CirculateNotify:
1354 case CirculateRequest:
1355 case ConfigureNotify:
1356 case ConfigureRequest:
1357 case CreateNotify:
1358 case DestroyNotify:
1359 case GravityNotify:
1360 case MapNotify:
1361 case MapRequest:
1362 case ReparentNotify:
1363 case UnmapNotify:
1364 xid = xevent.xmaprequest.window;
1365 break;
1368 int event = 0;
1369 Fl_Window* window = fl_find(xid);
1371 if (window) switch (xevent.type) {
1373 case DestroyNotify: { // an X11 window was closed externally from the program
1374 Fl::handle(FL_CLOSE, window);
1375 Fl_X* X = Fl_X::i(window);
1376 if (X) { // indicates the FLTK window was not closed
1377 X->xid = (Window)0; // indicates the X11 window was already destroyed
1378 window->hide();
1379 int oldx = window->x(), oldy = window->y();
1380 window->position(0, 0);
1381 window->position(oldx, oldy);
1382 window->show(); // recreate the X11 window in support of the FLTK window
1384 return 1;
1386 case ClientMessage: {
1387 Atom message = fl_xevent->xclient.message_type;
1388 const long* data = fl_xevent->xclient.data.l;
1389 if ((Atom)(data[0]) == WM_DELETE_WINDOW) {
1390 event = FL_CLOSE;
1391 } else if (message == fl_XdndEnter) {
1392 fl_xmousewin = window;
1393 in_a_window = true;
1394 fl_dnd_source_window = data[0];
1395 // version number is data[1]>>24
1396 // printf("XdndEnter, version %ld\n", data[1] >> 24);
1397 if (data[1]&1) {
1398 // get list of data types:
1399 Atom actual; int format; unsigned long count, remaining;
1400 unsigned char *buffer = 0;
1401 XGetWindowProperty(fl_display, fl_dnd_source_window, fl_XdndTypeList,
1402 0, 0x8000000L, False, XA_ATOM, &actual, &format,
1403 &count, &remaining, &buffer);
1404 if (actual != XA_ATOM || format != 32 || count<4 || !buffer)
1405 goto FAILED;
1406 delete [] fl_dnd_source_types;
1407 fl_dnd_source_types = new Atom[count+1];
1408 for (unsigned i = 0; i < count; i++) {
1409 fl_dnd_source_types[i] = ((Atom*)buffer)[i];
1411 fl_dnd_source_types[count] = 0;
1412 } else {
1413 FAILED:
1414 // less than four data types, or if the above messes up:
1415 if (!fl_dnd_source_types) fl_dnd_source_types = new Atom[4];
1416 fl_dnd_source_types[0] = data[2];
1417 fl_dnd_source_types[1] = data[3];
1418 fl_dnd_source_types[2] = data[4];
1419 fl_dnd_source_types[3] = 0;
1422 // Loop through the source types and pick the first text type...
1423 int i;
1425 for (i = 0; fl_dnd_source_types[i]; i ++)
1427 // printf("fl_dnd_source_types[%d] = %ld (%s)\n", i,
1428 // fl_dnd_source_types[i],
1429 // XGetAtomName(fl_display, fl_dnd_source_types[i]));
1431 if (!strncmp(XGetAtomName(fl_display, fl_dnd_source_types[i]),
1432 "text/", 5))
1433 break;
1436 if (fl_dnd_source_types[i])
1437 fl_dnd_type = fl_dnd_source_types[i];
1438 else
1439 fl_dnd_type = fl_dnd_source_types[0];
1441 event = FL_DND_ENTER;
1442 Fl::e_text = unknown;
1443 Fl::e_length = unknown_len;
1444 break;
1446 } else if (message == fl_XdndPosition) {
1447 fl_xmousewin = window;
1448 in_a_window = true;
1449 fl_dnd_source_window = data[0];
1450 Fl::e_x_root = data[2]>>16;
1451 Fl::e_y_root = data[2]&0xFFFF;
1452 if (window) {
1453 Fl::e_x = Fl::e_x_root-window->x();
1454 Fl::e_y = Fl::e_y_root-window->y();
1456 fl_event_time = data[3];
1457 fl_dnd_source_action = data[4];
1458 fl_dnd_action = fl_XdndActionCopy;
1459 Fl::e_text = unknown;
1460 Fl::e_length = unknown_len;
1461 int accept = Fl::handle(FL_DND_DRAG, window);
1462 fl_sendClientMessage(data[0], fl_XdndStatus,
1463 fl_xevent->xclient.window,
1464 accept ? 1 : 0,
1465 0, // used for xy rectangle to not send position inside
1466 0, // used for width+height of rectangle
1467 accept ? fl_dnd_action : None);
1468 return 1;
1470 } else if (message == fl_XdndLeave) {
1471 fl_dnd_source_window = 0; // don't send a finished message to it
1472 event = FL_DND_LEAVE;
1473 Fl::e_text = unknown;
1474 Fl::e_length = unknown_len;
1475 break;
1477 } else if (message == fl_XdndDrop) {
1478 fl_xmousewin = window;
1479 in_a_window = true;
1480 fl_dnd_source_window = data[0];
1481 fl_event_time = data[2];
1482 Window to_window = fl_xevent->xclient.window;
1483 Fl::e_text = unknown;
1484 Fl::e_length = unknown_len;
1485 if (Fl::handle(FL_DND_RELEASE, window)) {
1486 fl_selection_requestor = Fl::belowmouse();
1487 XConvertSelection(fl_display, fl_XdndSelection,
1488 fl_dnd_type, XA_SECONDARY,
1489 to_window, fl_event_time);
1490 } else {
1491 // Send the finished message if I refuse the drop.
1492 // It is not clear whether I can just send finished always,
1493 // or if I have to wait for the SelectionNotify event as the
1494 // code is currently doing.
1495 fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished, to_window);
1496 fl_dnd_source_window = 0;
1498 return 1;
1501 break;}
1503 case UnmapNotify:
1504 event = FL_HIDE;
1505 break;
1507 case Expose:
1508 Fl_X::i(window)->wait_for_expose = 0;
1509 # if 0
1510 // try to keep windows on top even if WM_TRANSIENT_FOR does not work:
1511 // opaque move/resize window managers do not like this, so I disabled it.
1512 if (Fl::first_window()->non_modal() && window != Fl::first_window())
1513 Fl::first_window()->show();
1514 # endif
1516 case GraphicsExpose:
1517 window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x, xevent.xexpose.y,
1518 xevent.xexpose.width, xevent.xexpose.height);
1519 return 1;
1521 case FocusIn:
1522 if (fl_xim_ic) XSetICFocus(fl_xim_ic);
1523 event = FL_FOCUS;
1524 break;
1526 case FocusOut:
1527 if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
1528 event = FL_UNFOCUS;
1529 break;
1531 case KeyPress:
1532 case KeyRelease: {
1533 KEYPRESS:
1534 int keycode = xevent.xkey.keycode;
1535 fl_key_vector[keycode/8] |= (1 << (keycode%8));
1536 static char *buffer = NULL;
1537 static int buffer_len = 0;
1538 int len;
1539 KeySym keysym;
1540 if (buffer_len == 0) {
1541 buffer_len = 4096;
1542 buffer = (char*) malloc(buffer_len);
1544 if (xevent.type == KeyPress) {
1545 event = FL_KEYDOWN;
1546 int len = 0;
1548 if (fl_xim_ic) {
1549 Status status;
1550 len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
1551 buffer, buffer_len, &keysym, &status);
1553 while (status == XBufferOverflow && buffer_len < 50000) {
1554 buffer_len = buffer_len * 5 + 1;
1555 buffer = (char*)realloc(buffer, buffer_len);
1556 len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
1557 buffer, buffer_len, &keysym, &status);
1559 keysym = XKeycodeToKeysym(fl_display, keycode, 0);
1560 } else {
1561 //static XComposeStatus compose;
1562 len = XLookupString((XKeyEvent*)&(xevent.xkey),
1563 buffer, buffer_len, &keysym, 0/*&compose*/);
1564 if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets
1565 // force it to type a character (not sure if this ever is needed):
1566 // if (!len) {buffer[0] = char(keysym); len = 1;}
1567 len = fl_utf8encode(XKeysymToUcs(keysym), buffer);
1568 if (len < 1) len = 1;
1569 // ignore all effects of shift on the keysyms, which makes it a lot
1570 // easier to program shortcuts and is Windoze-compatible:
1571 keysym = XKeycodeToKeysym(fl_display, keycode, 0);
1574 // MRS: Can't use Fl::event_state(FL_CTRL) since the state is not
1575 // set until set_event_xy() is called later...
1576 if ((xevent.xkey.state & ControlMask) && keysym == '-') buffer[0] = 0x1f; // ^_
1577 buffer[len] = 0;
1578 Fl::e_text = buffer;
1579 Fl::e_length = len;
1580 } else {
1581 // Stupid X sends fake key-up events when a repeating key is held
1582 // down, probably due to some back compatibility problem. Fortunately
1583 // we can detect this because the repeating KeyPress event is in
1584 // the queue, get it and execute it instead:
1586 // Bool XkbSetDetectableAutorepeat ( display, detectable, supported_rtrn )
1587 // Display * display ;
1588 // Bool detectable ;
1589 // Bool * supported_rtrn ;
1590 // ...would be the easy way to corrct this isuue. Unfortunatly, this call is also
1591 // broken on many Unix distros including Ubuntu and Solaris (as of Dec 2009)
1593 // Bogus KeyUp events are generated by repeated KeyDown events. One
1594 // neccessary condition is an identical key event pending right after
1595 // the bogus KeyUp.
1596 // The new code introduced Dec 2009 differs in that it only check the very
1597 // next event in the queue, not the entire queue of events.
1598 // This function wrongly detects a repeat key if a software keyboard
1599 // sends a burst of events containing two consecutive equal keys. However,
1600 // in every non-gaming situation, this is no problem because both KeyPress
1601 // events will cause the expected behavior.
1602 XEvent peekevent;
1603 if (XPending(fl_display)) {
1604 XPeekEvent(fl_display, &peekevent);
1605 if ( (peekevent.type == KeyPress) // must be a KeyPress event
1606 && (peekevent.xkey.keycode == xevent.xkey.keycode) // must be the same key
1607 && (peekevent.xkey.time == xevent.xkey.time) // must be sent at the exact same time
1609 XNextEvent(fl_display, &xevent);
1610 goto KEYPRESS;
1614 event = FL_KEYUP;
1615 fl_key_vector[keycode/8] &= ~(1 << (keycode%8));
1616 // keyup events just get the unshifted keysym:
1617 keysym = XKeycodeToKeysym(fl_display, keycode, 0);
1619 # ifdef __sgi
1620 // You can plug a microsoft keyboard into an sgi but the extra shift
1621 // keys are not translated. Make them translate like XFree86 does:
1622 if (!keysym) switch(keycode) {
1623 case 147: keysym = FL_Meta_L; break;
1624 case 148: keysym = FL_Meta_R; break;
1625 case 149: keysym = FL_Menu; break;
1627 # endif
1628 # if BACKSPACE_HACK
1629 // Attempt to fix keyboards that send "delete" for the key in the
1630 // upper-right corner of the main keyboard. But it appears that
1631 // very few of these remain?
1632 static int got_backspace = 0;
1633 if (!got_backspace) {
1634 if (keysym == FL_Delete) keysym = FL_BackSpace;
1635 else if (keysym == FL_BackSpace) got_backspace = 1;
1637 # endif
1638 // For the first few years, there wasn't a good consensus on what the
1639 // Windows keys should be mapped to for X11. So we need to help out a
1640 // bit and map all variants to the same FLTK key...
1641 switch (keysym) {
1642 case XK_Meta_L:
1643 case XK_Hyper_L:
1644 case XK_Super_L:
1645 keysym = FL_Meta_L;
1646 break;
1647 case XK_Meta_R:
1648 case XK_Hyper_R:
1649 case XK_Super_R:
1650 keysym = FL_Meta_R;
1651 break;
1653 // Convert the multimedia keys to safer, portable values
1654 switch (keysym) { // XF names come from X11/XF86keysym.h
1655 case 0x1008FF11: // XF86XK_AudioLowerVolume:
1656 keysym = FL_Volume_Down;
1657 break;
1658 case 0x1008FF12: // XF86XK_AudioMute:
1659 keysym = FL_Volume_Mute;
1660 break;
1661 case 0x1008FF13: // XF86XK_AudioRaiseVolume:
1662 keysym = FL_Volume_Up;
1663 break;
1664 case 0x1008FF14: // XF86XK_AudioPlay:
1665 keysym = FL_Media_Play;
1666 break;
1667 case 0x1008FF15: // XF86XK_AudioStop:
1668 keysym = FL_Media_Stop;
1669 break;
1670 case 0x1008FF16: // XF86XK_AudioPrev:
1671 keysym = FL_Media_Prev;
1672 break;
1673 case 0x1008FF17: // XF86XK_AudioNext:
1674 keysym = FL_Media_Next;
1675 break;
1676 case 0x1008FF18: // XF86XK_HomePage:
1677 keysym = FL_Home_Page;
1678 break;
1679 case 0x1008FF19: // XF86XK_Mail:
1680 keysym = FL_Mail;
1681 break;
1682 case 0x1008FF1B: // XF86XK_Search:
1683 keysym = FL_Search;
1684 break;
1685 case 0x1008FF26: // XF86XK_Back:
1686 keysym = FL_Back;
1687 break;
1688 case 0x1008FF27: // XF86XK_Forward:
1689 keysym = FL_Forward;
1690 break;
1691 case 0x1008FF28: // XF86XK_Stop:
1692 keysym = FL_Stop;
1693 break;
1694 case 0x1008FF29: // XF86XK_Refresh:
1695 keysym = FL_Refresh;
1696 break;
1697 case 0x1008FF2F: // XF86XK_Sleep:
1698 keysym = FL_Sleep;
1699 break;
1700 case 0x1008FF30: // XF86XK_Favorites:
1701 keysym = FL_Favorites;
1702 break;
1704 // We have to get rid of the XK_KP_function keys, because they are
1705 // not produced on Windoze and thus case statements tend not to check
1706 // for them. There are 15 of these in the range 0xff91 ... 0xff9f
1707 if (keysym >= 0xff91 && keysym <= 0xff9f) {
1708 // Map keypad keysym to character or keysym depending on
1709 // numlock state...
1710 unsigned long keysym1 = XKeycodeToKeysym(fl_display, keycode, 1);
1711 if (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))
1712 Fl::e_original_keysym = (int)(keysym1 | FL_KP);
1713 if ((xevent.xkey.state & Mod2Mask) &&
1714 (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))) {
1715 // Store ASCII numeric keypad value...
1716 keysym = keysym1 | FL_KP;
1717 buffer[0] = char(keysym1) & 0x7F;
1718 len = 1;
1719 } else {
1720 // Map keypad to special key...
1721 static const unsigned short table[15] = {
1722 FL_F+1, FL_F+2, FL_F+3, FL_F+4,
1723 FL_Home, FL_Left, FL_Up, FL_Right,
1724 FL_Down, FL_Page_Up, FL_Page_Down, FL_End,
1725 0xff0b/*XK_Clear*/, FL_Insert, FL_Delete};
1726 keysym = table[keysym-0xff91];
1728 } else {
1729 // Store this so we can later know if the KP was used
1730 Fl::e_original_keysym = (int)keysym;
1732 Fl::e_keysym = int(keysym);
1734 // replace XK_ISO_Left_Tab (Shift-TAB) with FL_Tab (modifier flags are set correctly by X11)
1735 if (Fl::e_keysym == 0xfe20) Fl::e_keysym = FL_Tab;
1737 set_event_xy();
1738 Fl::e_is_click = 0;
1739 break;}
1741 case ButtonPress:
1742 Fl::e_keysym = FL_Button + xevent.xbutton.button;
1743 set_event_xy();
1744 if (xevent.xbutton.button == Button4) {
1745 Fl::e_dy = -1; // Up
1746 Fl::e_dx = 0;
1747 event = FL_MOUSEWHEEL;
1748 } else if (xevent.xbutton.button == Button5) {
1749 Fl::e_dy = +1; // Down
1750 Fl::e_dx = 0;
1751 event = FL_MOUSEWHEEL;
1752 } else if (xevent.xbutton.button == 6) {
1753 Fl::e_dy = 0;
1754 Fl::e_dx = -1; // Left
1755 event = FL_MOUSEWHEEL;
1756 } else if (xevent.xbutton.button == 7) {
1757 Fl::e_dy = 0;
1758 Fl::e_dx = +1; // Right
1759 event = FL_MOUSEWHEEL;
1760 } else {
1761 Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
1762 event = FL_PUSH;
1763 checkdouble();
1766 fl_xmousewin = window;
1767 in_a_window = true;
1768 break;
1770 case MotionNotify:
1771 set_event_xy();
1772 # if CONSOLIDATE_MOTION
1773 send_motion = fl_xmousewin = window;
1774 in_a_window = true;
1775 return 0;
1776 # else
1777 event = FL_MOVE;
1778 fl_xmousewin = window;
1779 in_a_window = true;
1780 break;
1781 # endif
1783 case ButtonRelease:
1784 Fl::e_keysym = FL_Button + xevent.xbutton.button;
1785 set_event_xy();
1786 Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
1787 if (xevent.xbutton.button == Button4 ||
1788 xevent.xbutton.button == Button5 ||
1789 xevent.xbutton.button == 6 ||
1790 xevent.xbutton.button == 7) return 0;
1791 event = FL_RELEASE;
1793 fl_xmousewin = window;
1794 in_a_window = true;
1795 break;
1797 case EnterNotify:
1798 if (xevent.xcrossing.detail == NotifyInferior) break;
1799 // XInstallColormap(fl_display, Fl_X::i(window)->colormap);
1800 set_event_xy();
1801 Fl::e_state = xevent.xcrossing.state << 16;
1802 event = FL_ENTER;
1804 fl_xmousewin = window;
1805 in_a_window = true;
1806 { XIMStyles *xim_styles = NULL;
1807 if(!fl_xim_im || XGetIMValues(fl_xim_im, XNQueryInputStyle, &xim_styles, NULL, NULL)) {
1808 fl_init_xim();
1810 if (xim_styles) XFree(xim_styles);
1812 break;
1814 case LeaveNotify:
1815 if (xevent.xcrossing.detail == NotifyInferior) break;
1816 set_event_xy();
1817 Fl::e_state = xevent.xcrossing.state << 16;
1818 fl_xmousewin = 0;
1819 in_a_window = false; // make do_queued_events produce FL_LEAVE event
1820 return 0;
1822 // We cannot rely on the x,y position in the configure notify event.
1823 // I now think this is an unavoidable problem with X: it is impossible
1824 // for a window manager to prevent the "real" notify event from being
1825 // sent when it resizes the contents, even though it can send an
1826 // artificial event with the correct position afterwards (and some
1827 // window managers do not send this fake event anyway)
1828 // So anyway, do a round trip to find the correct x,y:
1829 case MapNotify:
1830 event = FL_SHOW;
1831 break;
1833 case ConfigureNotify: {
1834 if (window->parent()) break; // ignore child windows
1836 // figure out where OS really put window
1837 XWindowAttributes actual;
1838 XGetWindowAttributes(fl_display, fl_xid(window), &actual);
1839 Window cr; int X, Y, W = actual.width, H = actual.height;
1840 XTranslateCoordinates(fl_display, fl_xid(window), actual.root,
1841 0, 0, &X, &Y, &cr);
1843 // tell Fl_Window about it and set flag to prevent echoing:
1844 resize_bug_fix = window;
1845 window->resize(X, Y, W, H);
1846 break; // allow add_handler to do something too
1849 case ReparentNotify: {
1850 int xpos, ypos;
1851 Window junk;
1853 // on some systems, the ReparentNotify event is not handled as we would expect.
1854 XErrorHandler oldHandler = XSetErrorHandler(catchXExceptions());
1856 //ReparentNotify gives the new position of the window relative to
1857 //the new parent. FLTK cares about the position on the root window.
1858 XTranslateCoordinates(fl_display, xevent.xreparent.parent,
1859 XRootWindow(fl_display, fl_screen),
1860 xevent.xreparent.x, xevent.xreparent.y,
1861 &xpos, &ypos, &junk);
1862 XSetErrorHandler(oldHandler);
1864 // tell Fl_Window about it and set flag to prevent echoing:
1865 if ( !wasXExceptionRaised() ) {
1866 resize_bug_fix = window;
1867 window->position(xpos, ypos);
1869 break;
1873 return Fl::handle(event, window);
1876 ////////////////////////////////////////////////////////////////
1878 void Fl_Window::resize(int X,int Y,int W,int H) {
1879 int is_a_move = (X != x() || Y != y());
1880 int is_a_resize = (W != w() || H != h());
1881 int is_a_enlarge = (W > w() || H > h());
1882 int resize_from_program = (this != resize_bug_fix);
1883 if (!resize_from_program) resize_bug_fix = 0;
1884 if (is_a_move && resize_from_program) set_flag(FORCE_POSITION);
1885 else if (!is_a_resize && !is_a_move) return;
1886 if (is_a_resize) {
1887 Fl_Group::resize(X,Y,W,H);
1888 if (shown()) {redraw(); if(is_a_enlarge) i->wait_for_expose = 1;}
1889 } else {
1890 x(X); y(Y);
1893 if (resize_from_program && is_a_resize && !resizable()) {
1894 size_range(w(), h(), w(), h());
1897 if (resize_from_program && shown()) {
1898 if (is_a_resize) {
1899 if (!resizable()) size_range(w(),h(),w(),h());
1900 if (is_a_move) {
1901 XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1);
1902 } else {
1903 XResizeWindow(fl_display, i->xid, W>0 ? W : 1, H>0 ? H : 1);
1905 } else
1906 XMoveWindow(fl_display, i->xid, X, Y);
1909 if ( is_a_resize && i )
1910 i->cairo_surface_invalid = 1;
1913 ////////////////////////////////////////////////////////////////
1915 // A subclass of Fl_Window may call this to associate an X window it
1916 // creates with the Fl_Window:
1918 void fl_fix_focus(); // in Fl.cxx
1920 Fl_X* Fl_X::set_xid(Fl_Window* win, Window winxid) {
1921 Fl_X* xp = new Fl_X;
1922 xp->xid = winxid;
1923 xp->other_xid = 0;
1924 cairo_surface_t *cs = Fl::cairo_create_surface( winxid, win->w(), win->h() );
1925 xp->cc = cairo_create( cs );
1926 cairo_surface_destroy( cs );
1927 xp->cairo_surface_invalid = 0;
1928 xp->other_cc = 0;
1929 xp->setwindow(win);
1930 xp->next = Fl_X::first;
1931 xp->region = 0;
1932 xp->wait_for_expose = 1;
1933 // xp->backbuffer_bad = 1;
1934 Fl_X::first = xp;
1935 if (win->modal()) {Fl::modal_ = win; fl_fix_focus();}
1936 return xp;
1939 // More commonly a subclass calls this, because it hides the really
1940 // ugly parts of X and sets all the stuff for a window that is set
1941 // normally. The global variables like fl_show_iconic are so that
1942 // subclasses of *that* class may change the behavior...
1944 char fl_show_iconic; // hack for iconize()
1945 int fl_background_pixel = -1; // hack to speed up bg box drawing
1946 int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
1948 static const int childEventMask = ExposureMask;
1950 static const int XEventMask =
1951 ExposureMask|StructureNotifyMask
1952 |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
1953 |ButtonPressMask|ButtonReleaseMask
1954 |EnterWindowMask|LeaveWindowMask
1955 |PointerMotionMask;
1957 static const int XEmbedEventMask = XEventMask |
1958 SubstructureRedirectMask |
1959 SubstructureNotifyMask |
1960 PropertyChangeMask;
1962 void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
1964 Fl_Group::current(0); // get rid of very common user bug: forgot end()
1966 int X = win->x();
1967 int Y = win->y();
1968 int W = win->w();
1969 if (W <= 0) W = 1; // X don't like zero...
1970 int H = win->h();
1971 if (H <= 0) H = 1; // X don't like zero...
1972 if (!win->parent() && !Fl::grab()) {
1973 // center windows in case window manager does not do anything:
1974 #ifdef FL_CENTER_WINDOWS
1975 if (!(win->flags() & Fl_Widget::FORCE_POSITION)) {
1976 win->x(X = scr_x+(scr_w-W)/2);
1977 win->y(Y = scr_y+(scr_h-H)/2);
1979 #endif // FL_CENTER_WINDOWS
1981 // force the window to be on-screen. Usually the X window manager
1982 // does this, but a few don't, so we do it here for consistency:
1983 int scr_x, scr_y, scr_w, scr_h;
1984 Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
1986 if (win->border()) {
1987 // ensure border is on screen:
1988 // (assume extremely minimal dimensions for this border)
1989 const int top = 20;
1990 const int left = 1;
1991 const int right = 1;
1992 const int bottom = 1;
1993 if (X+W+right > scr_x+scr_w) X = scr_x+scr_w-right-W;
1994 if (X-left < scr_x) X = scr_x+left;
1995 if (Y+H+bottom > scr_y+scr_h) Y = scr_y+scr_h-bottom-H;
1996 if (Y-top < scr_y) Y = scr_y+top;
1998 // now insure contents are on-screen (more important than border):
1999 if (X+W > scr_x+scr_w) X = scr_x+scr_w-W;
2000 if (X < scr_x) X = scr_x;
2001 if (Y+H > scr_y+scr_h) Y = scr_y+scr_h-H;
2002 if (Y < scr_y) Y = scr_y;
2005 // if the window is a subwindow and our parent is not mapped yet, we
2006 // mark this window visible, so that mapping the parent at a later
2007 // point in time will call this function again to finally map the subwindow.
2008 if (win->parent() && !Fl_X::i(win->window())) {
2009 win->set_visible();
2010 return;
2013 ulong root = win->parent() ?
2014 fl_xid(win->window()) : RootWindow(fl_display, fl_screen);
2016 if ( fl_parent_window )
2017 root = fl_parent_window;
2019 XSetWindowAttributes attr;
2020 int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
2021 attr.event_mask = win->parent() ? childEventMask : XEventMask;
2023 if ( Fl_Socket_Window::is_socket( win ) )
2024 attr.event_mask = XEmbedEventMask;
2026 attr.colormap = colormap;
2027 attr.border_pixel = 0;
2028 attr.bit_gravity = 0; // StaticGravity;
2029 if (win->override()) {
2030 attr.override_redirect = 1;
2031 attr.save_under = 1;
2032 mask |= CWOverrideRedirect | CWSaveUnder;
2033 } else attr.override_redirect = 0;
2034 if (Fl::grab()) {
2035 attr.save_under = 1; mask |= CWSaveUnder;
2036 if (!win->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
2038 if (fl_background_pixel >= 0) {
2039 attr.background_pixel = fl_background_pixel;
2040 fl_background_pixel = -1;
2041 mask |= CWBackPixel;
2044 Fl_X* xp =
2045 set_xid(win, XCreateWindow(fl_display,
2046 root,
2047 X, Y, W, H,
2048 0, // borderwidth
2049 visual->depth,
2050 InputOutput,
2051 visual->visual,
2052 mask, &attr));
2053 int showit = 1;
2055 if (!win->parent() && !attr.override_redirect) {
2056 // Communicate all kinds 'o junk to the X Window Manager:
2058 win->label(win->label(), win->iconlabel());
2060 XChangeProperty(fl_display, xp->xid, WM_PROTOCOLS,
2061 XA_ATOM, 32, 0, (uchar*)&WM_DELETE_WINDOW, 1);
2063 // send size limits and border:
2064 xp->sendxjunk();
2066 // set the class property, which controls the icon used:
2067 if (win->xclass()) {
2068 char buffer[1024];
2069 char *p; const char *q;
2070 // replace punctuation with underscores, because it breaks XResource lookup
2071 for (p = buffer, q = win->xclass(); *q || (*q&128); q++)
2072 *p++ = isalnum(*q) || *q == '-' || *q == ' ' ? *q : '_';
2073 *p++ = 0;
2074 // create the capitalized version:
2075 q = buffer;
2076 *p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++);
2077 while ((*p++ = *q++));
2078 XChangeProperty(fl_display, xp->xid, XA_WM_CLASS, XA_STRING, 8, 0,
2079 (unsigned char *)buffer, p-buffer-1);
2082 if (win->non_modal() && xp->next && !fl_disable_transient_for) {
2083 // find some other window to be "transient for":
2084 Fl_Window* wp = xp->next->w;
2085 while (wp->parent()) wp = wp->window();
2086 XSetTransientForHint(fl_display, xp->xid, fl_xid(wp));
2087 if (!wp->visible()) showit = 0; // guess that wm will not show it
2090 // Make sure that borderless windows do not show in the task bar
2091 if (!win->border()) {
2092 Atom net_wm_state = XInternAtom (fl_display, "_NET_WM_STATE", 0);
2093 Atom net_wm_state_skip_taskbar = XInternAtom (fl_display, "_NET_WM_STATE_SKIP_TASKBAR", 0);
2094 XChangeProperty (fl_display, xp->xid, net_wm_state, XA_ATOM, 32,
2095 PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1);
2098 // Make it receptive to DnD:
2099 long version = 4;
2100 XChangeProperty(fl_display, xp->xid, fl_XdndAware,
2101 XA_ATOM, sizeof(int)*8, 0, (unsigned char*)&version, 1);
2103 XWMHints *hints = XAllocWMHints();
2104 hints->input = True;
2105 hints->flags = InputHint;
2106 if (fl_show_iconic) {
2107 hints->flags |= StateHint;
2108 hints->initial_state = IconicState;
2109 fl_show_iconic = 0;
2110 showit = 0;
2112 if (win->icon()) {
2113 hints->icon_pixmap = (Pixmap)win->icon();
2114 hints->flags |= IconPixmapHint;
2116 XSetWMHints(fl_display, xp->xid, hints);
2117 XFree(hints);
2120 // set the window type for menu and tooltip windows to avoid animations (compiz)
2121 if (win->menu_window() || win->tooltip_window()) {
2122 Atom net_wm_type = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
2123 Atom net_wm_type_kind = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_MENU", False);
2124 XChangeProperty(fl_display, xp->xid, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_type_kind, 1);
2127 XMapWindow(fl_display, xp->xid);
2128 if (showit) {
2129 win->set_visible();
2130 int old_event = Fl::e_number;
2131 win->handle(Fl::e_number = FL_SHOW); // get child windows to appear
2132 Fl::e_number = old_event;
2133 // win->redraw();
2137 ////////////////////////////////////////////////////////////////
2138 // Send X window stuff that can be changed over time:
2140 void Fl_X::sendxjunk() {
2141 if (w->parent() || w->override()) return; // it's not a window manager window!
2143 if (!w->size_range_set) { // default size_range based on resizable():
2144 if (w->resizable()) {
2145 Fl_Widget *o = w->resizable();
2146 int minw = o->w(); if (minw > 100) minw = 100;
2147 int minh = o->h(); if (minh > 100) minh = 100;
2148 w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
2149 } else {
2150 w->size_range(w->w(), w->h(), w->w(), w->h());
2152 return; // because this recursively called here
2155 XSizeHints *hints = XAllocSizeHints();
2156 // memset(&hints, 0, sizeof(hints)); jreiser suggestion to fix purify?
2157 hints->min_width = w->minw;
2158 hints->min_height = w->minh;
2159 hints->max_width = w->maxw;
2160 hints->max_height = w->maxh;
2161 hints->width_inc = w->dw;
2162 hints->height_inc = w->dh;
2163 hints->win_gravity = StaticGravity;
2165 // see the file /usr/include/X11/Xm/MwmUtil.h:
2166 // fill all fields to avoid bugs in kwm and perhaps other window managers:
2167 // 0, MWM_FUNC_ALL, MWM_DECOR_ALL
2168 long prop[5] = {0, 1, 1, 0, 0};
2170 if (hints->min_width != hints->max_width ||
2171 hints->min_height != hints->max_height) { // resizable
2172 hints->flags = PMinSize|PWinGravity;
2173 if (hints->max_width >= hints->min_width ||
2174 hints->max_height >= hints->min_height) {
2175 hints->flags = PMinSize|PMaxSize|PWinGravity;
2176 // unfortunately we can't set just one maximum size. Guess a
2177 // value for the other one. Some window managers will make the
2178 // window fit on screen when maximized, others will put it off screen:
2179 if (hints->max_width < hints->min_width) hints->max_width = Fl::w();
2180 if (hints->max_height < hints->min_height) hints->max_height = Fl::h();
2182 if (hints->width_inc && hints->height_inc) hints->flags |= PResizeInc;
2183 if (w->aspect) {
2184 // stupid X! It could insist that the corner go on the
2185 // straight line between min and max...
2186 hints->min_aspect.x = hints->max_aspect.x = hints->min_width;
2187 hints->min_aspect.y = hints->max_aspect.y = hints->min_height;
2188 hints->flags |= PAspect;
2190 } else { // not resizable:
2191 hints->flags = PMinSize|PMaxSize|PWinGravity;
2192 prop[0] = 1; // MWM_HINTS_FUNCTIONS
2193 prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE
2196 if (w->flags() & Fl_Widget::FORCE_POSITION) {
2197 hints->flags |= USPosition;
2198 hints->x = w->x();
2199 hints->y = w->y();
2202 if (!w->border()) {
2203 prop[0] |= 2; // MWM_HINTS_DECORATIONS
2204 prop[2] = 0; // no decorations
2207 XSetWMNormalHints(fl_display, xid, hints);
2208 XChangeProperty(fl_display, xid,
2209 fl_MOTIF_WM_HINTS, fl_MOTIF_WM_HINTS,
2210 32, 0, (unsigned char *)prop, 5);
2211 XFree(hints);
2214 void Fl_Window::size_range_() {
2215 size_range_set = 1;
2216 if (shown()) i->sendxjunk();
2219 ////////////////////////////////////////////////////////////////
2221 // returns pointer to the filename, or null if name ends with '/'
2222 const char *fl_filename_name(const char *name) {
2223 const char *p,*q;
2224 if (!name) return (0);
2225 for (p=q=name; *p;) if (*p++ == '/') q = p;
2226 return q;
2229 void Fl_Window::label(const char *name,const char *iname) {
2230 Fl_Widget::label(name);
2231 iconlabel_ = iname;
2232 if (shown() && !parent()) {
2233 if (!name) name = "";
2234 int namelen = strlen(name);
2235 if (!iname) iname = fl_filename_name(name);
2236 int inamelen = strlen(iname);
2237 XChangeProperty(fl_display, i->xid, fl_NET_WM_NAME, fl_XaUtf8String, 8, 0, (uchar*)name, namelen); // utf8
2238 XChangeProperty(fl_display, i->xid, XA_WM_NAME, XA_STRING, 8, 0, (uchar*)name, namelen); // non-utf8
2239 XChangeProperty(fl_display, i->xid, fl_NET_WM_ICON_NAME, fl_XaUtf8String, 8, 0, (uchar*)iname, inamelen); // utf8
2240 XChangeProperty(fl_display, i->xid, XA_WM_ICON_NAME, XA_STRING, 8, 0, (uchar*)iname, inamelen); // non-utf8
2244 ////////////////////////////////////////////////////////////////
2245 // Implement the virtual functions for the base Fl_Window class:
2247 // If the box is a filled rectangle, we can make the redisplay *look*
2248 // faster by using X's background pixel erasing. We can make it
2249 // actually *be* faster by drawing the frame only, this is done by
2250 // setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
2252 // On XFree86 (and prehaps all X's) this has a problem if the window
2253 // is resized while a save-behind window is atop it. The previous
2254 // contents are restored to the area, but this assumes the area
2255 // is cleared to background color. So this is disabled in this version.
2256 // Fl_Window *fl_boxcheat;
2257 static inline int can_boxcheat(uchar b) {return (b==1 || ((b&2) && b<=15));}
2259 void Fl_Window::show() {
2260 image(Fl::scheme_bg_);
2261 if (Fl::scheme_bg_) {
2262 // labeltype(FL_NORMAL_LABEL);
2263 align(FL_ALIGN_IMAGE_BACKDROP);
2266 labeltype(FL_NO_LABEL);
2268 Fl_Tooltip::exit(this);
2269 if (!shown()) {
2270 fl_open_display();
2272 /* // Don't set background pixel for double-buffered windows... */
2273 /* if (type() == FL_WINDOW && can_boxcheat(box())) { */
2274 /* fl_background_pixel = int(fl_xpixel(color())); */
2275 /* } */
2277 Fl_X::make_xid(this);
2278 } else {
2279 XMapRaised(fl_display, i->xid);
2281 #ifdef USE_PRINT_BUTTON
2282 void preparePrintFront(void);
2283 preparePrintFront();
2284 #endif
2287 Window fl_window;
2288 Fl_Window *Fl_Window::current_;
2289 GC fl_gc;
2291 // make X drawing go into this window (called by subclass flush() impl.)
2292 void Fl_Window::make_current() {
2293 static GC gc; // the GC used by all X windows
2294 if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0);
2295 fl_window = i->xid;
2296 fl_gc = gc;
2298 if ( i->cairo_surface_invalid && i->cc )
2300 cairo_destroy( i->cc ); i->cc = 0;
2303 if ( ! i->cc )
2305 cairo_surface_t *cs = Fl::cairo_create_surface( i->xid, w(), h() );
2306 i->cc = cairo_create( cs );
2307 cairo_surface_destroy( cs );
2310 Fl::cairo_make_current( i->cc );
2312 current_ = this;
2314 fl_clip_region(i->region);
2317 Window fl_xid_(const Fl_Window *w) {
2318 Fl_X *temp = Fl_X::i(w);
2319 return temp ? temp->xid : 0;
2322 static void decorated_win_size(Fl_Window *win, int &w, int &h)
2324 w = win->w();
2325 h = win->h();
2326 if (!win->shown() || win->parent() || !win->border() || !win->visible()) return;
2327 Window root, parent, *children;
2328 unsigned n = 0;
2329 Status status = XQueryTree(fl_display, Fl_X::i(win)->xid, &root, &parent, &children, &n);
2330 if (status != 0 && n) XFree(children);
2331 // when compiz is used, root and parent are the same window
2332 // and I don't know where to find the window decoration
2333 if (status == 0 || root == parent) return;
2334 XWindowAttributes attributes;
2335 XGetWindowAttributes(fl_display, parent, &attributes);
2336 w = attributes.width;
2337 h = attributes.height;
2340 int Fl_Window::decorated_h()
2342 int w, h;
2343 decorated_win_size(this, w, h);
2344 return h;
2347 int Fl_Window::decorated_w()
2349 int w, h;
2350 decorated_win_size(this, w, h);
2351 return w;
2354 void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
2356 if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
2357 this->print_widget(win, x_offset, y_offset);
2358 return;
2360 Fl_Display_Device::display_device()->set_current();
2361 win->show();
2362 Fl::check();
2363 win->make_current();
2364 Window root, parent, *children, child_win, from;
2365 unsigned n = 0;
2366 int bx, bt, do_it;
2367 from = fl_window;
2368 do_it = (XQueryTree(fl_display, fl_window, &root, &parent, &children, &n) != 0 &&
2369 XTranslateCoordinates(fl_display, fl_window, parent, 0, 0, &bx, &bt, &child_win) == True);
2370 if (n) XFree(children);
2371 // hack to bypass STR #2648: when compiz is used, root and parent are the same window
2372 // and I don't know where to find the window decoration
2373 if (do_it && root == parent) do_it = 0;
2374 if (!do_it) {
2375 this->set_current();
2376 this->print_widget(win, x_offset, y_offset);
2377 return;
2379 fl_window = parent;
2380 uchar *top_image = 0, *left_image = 0, *right_image = 0, *bottom_image = 0;
2381 top_image = fl_read_image(NULL, 0, 0, - (win->w() + 2 * bx), bt);
2382 if (bx) {
2383 left_image = fl_read_image(NULL, 0, bt, -bx, win->h() + bx);
2384 right_image = fl_read_image(NULL, win->w() + bx, bt, -bx, win->h() + bx);
2385 bottom_image = fl_read_image(NULL, 0, bt + win->h(), -(win->w() + 2*bx), bx);
2387 fl_window = from;
2388 this->set_current();
2389 if (top_image) {
2390 fl_draw_image(top_image, x_offset, y_offset, win->w() + 2 * bx, bt, 3);
2391 delete[] top_image;
2393 if (bx) {
2394 if (left_image) fl_draw_image(left_image, x_offset, y_offset + bt, bx, win->h() + bx, 3);
2395 if (right_image) fl_draw_image(right_image, x_offset + win->w() + bx, y_offset + bt, bx, win->h() + bx, 3);
2396 if (bottom_image) fl_draw_image(bottom_image, x_offset, y_offset + bt + win->h(), win->w() + 2*bx, bx, 3);
2397 if (left_image) delete[] left_image;
2398 if (right_image) delete[] right_image;
2399 if (bottom_image) delete[] bottom_image;
2401 this->print_widget( win, x_offset + bx, y_offset + bt );
2404 #ifdef USE_PRINT_BUTTON
2405 // to test the Fl_Printer class creating a "Print front window" button in a separate window
2406 // contains also preparePrintFront call above
2407 #include <FL/Fl_Printer.H>
2408 #include <FL/Fl_Button.H>
2409 void printFront(Fl_Widget *o, void *data)
2411 Fl_Printer printer;
2412 o->window()->hide();
2413 Fl_Window *win = Fl::first_window();
2414 if(!win) return;
2415 int w, h;
2416 if( printer.start_job(1) ) { o->window()->show(); return; }
2417 if( printer.start_page() ) { o->window()->show(); return; }
2418 printer.printable_rect(&w,&h);
2419 // scale the printer device so that the window fits on the page
2420 float scale = 1;
2421 int ww = win->decorated_w();
2422 int wh = win->decorated_h();
2423 if (ww > w || wh > h) {
2424 scale = (float)w/ww;
2425 if ((float)h/wh < scale) scale = (float)h/wh;
2426 printer.scale(scale, scale);
2429 // #define ROTATE 20.0
2430 #ifdef ROTATE
2431 printer.scale(scale * 0.8, scale * 0.8);
2432 printer.printable_rect(&w, &h);
2433 printer.origin(w/2, h/2 );
2434 printer.rotate(ROTATE);
2435 printer.print_widget( win, - win->w()/2, - win->h()/2 );
2436 //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
2437 #else
2438 printer.print_window(win);
2439 #endif
2441 printer.end_page();
2442 printer.end_job();
2443 o->window()->show();
2446 void preparePrintFront(void)
2448 static int first=1;
2449 if(!first) return;
2450 first=0;
2451 static Fl_Window w(0,0,150,30);
2452 static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
2453 b.callback(printFront);
2454 w.end();
2455 w.show();
2457 #endif // USE_PRINT_BUTTON
2459 #endif
2462 // End of "$Id: Fl_x.cxx 8764 2011-05-30 16:47:48Z manolo $".