blob: 4ef92b06439d6205eace55954cba411708b844f0 [file] [log] [blame]
DRC2ff39b82011-07-28 08:38:59 +00001//
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.
12//
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.
17//
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.
22//
23// Please report all bugs and problems on the following page:
24//
25// http://www.fltk.org/str.php
26//
27
28#ifdef WIN32
29//# include "Fl_win32.cxx"
30#elif defined(__APPLE__)
31//# include "Fl_mac.cxx"
32#elif !defined(FL_DOXYGEN)
33
34# define CONSOLIDATE_MOTION 1
35/**** Define this if your keyboard lacks a backspace key... ****/
36/* #define BACKSPACE_HACK 1 */
37
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
56static Fl_Xlib_Graphics_Driver fl_xlib_driver;
57static Fl_Display_Device fl_xlib_display(&fl_xlib_driver);
58FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_xlib_driver; // the current target device of graphics operations
59Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_xlib_display; // the current target surface of graphics operations
60Fl_Display_Device *Fl_Display_Device::_display = &fl_xlib_display;// the platform display
61
62////////////////////////////////////////////////////////////////
63// interface to poll/select call:
64
65# if USE_POLL
66
67# include <poll.h>
68static pollfd *pollfds = 0;
69
70# else
71# if HAVE_SYS_SELECT_H
72# include <sys/select.h>
73# endif /* HAVE_SYS_SELECT_H */
74
75// The following #define is only needed for HP-UX 9.x and earlier:
76//#define select(a,b,c,d,e) select((a),(int *)(b),(int *)(c),(int *)(d),(e))
77
78static fd_set fdsets[3];
79static int maxfd;
80# define POLLIN 1
81# define POLLOUT 4
82# define POLLERR 8
83
84# endif /* USE_POLL */
85
86static int nfds = 0;
87static int fd_array_size = 0;
88struct FD {
89# if !USE_POLL
90 int fd;
91 short events;
92# endif
93 void (*cb)(int, void*);
94 void* arg;
95};
96
97static FD *fd = 0;
98
99void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
100 remove_fd(n,events);
101 int i = nfds++;
102 if (i >= fd_array_size) {
103 FD *temp;
104 fd_array_size = 2*fd_array_size+1;
105
106 if (!fd) temp = (FD*)malloc(fd_array_size*sizeof(FD));
107 else temp = (FD*)realloc(fd, fd_array_size*sizeof(FD));
108
109 if (!temp) return;
110 fd = temp;
111
112# if USE_POLL
113 pollfd *tpoll;
114
115 if (!pollfds) tpoll = (pollfd*)malloc(fd_array_size*sizeof(pollfd));
116 else tpoll = (pollfd*)realloc(pollfds, fd_array_size*sizeof(pollfd));
117
118 if (!tpoll) return;
119 pollfds = tpoll;
120# endif
121 }
122 fd[i].cb = cb;
123 fd[i].arg = v;
124# if USE_POLL
125 pollfds[i].fd = n;
126 pollfds[i].events = events;
127# else
128 fd[i].fd = n;
129 fd[i].events = events;
130 if (events & POLLIN) FD_SET(n, &fdsets[0]);
131 if (events & POLLOUT) FD_SET(n, &fdsets[1]);
132 if (events & POLLERR) FD_SET(n, &fdsets[2]);
133 if (n > maxfd) maxfd = n;
134# endif
135}
136
137void Fl::add_fd(int n, void (*cb)(int, void*), void* v) {
138 Fl::add_fd(n, POLLIN, cb, v);
139}
140
141void Fl::remove_fd(int n, int events) {
142 int i,j;
143# if !USE_POLL
144 maxfd = -1; // recalculate maxfd on the fly
145# endif
146 for (i=j=0; i<nfds; i++) {
147# if USE_POLL
148 if (pollfds[i].fd == n) {
149 int e = pollfds[i].events & ~events;
150 if (!e) continue; // if no events left, delete this fd
151 pollfds[j].events = e;
152 }
153# else
154 if (fd[i].fd == n) {
155 int e = fd[i].events & ~events;
156 if (!e) continue; // if no events left, delete this fd
157 fd[i].events = e;
158 }
159 if (fd[i].fd > maxfd) maxfd = fd[i].fd;
160# endif
161 // move it down in the array if necessary:
162 if (j<i) {
163 fd[j] = fd[i];
164# if USE_POLL
165 pollfds[j] = pollfds[i];
166# endif
167 }
168 j++;
169 }
170 nfds = j;
171# if !USE_POLL
172 if (events & POLLIN) FD_CLR(n, &fdsets[0]);
173 if (events & POLLOUT) FD_CLR(n, &fdsets[1]);
174 if (events & POLLERR) FD_CLR(n, &fdsets[2]);
175# endif
176}
177
178void Fl::remove_fd(int n) {
179 remove_fd(n, -1);
180}
181
182#if CONSOLIDATE_MOTION
183static Fl_Window* send_motion;
184extern Fl_Window* fl_xmousewin;
185#endif
186static bool in_a_window; // true if in any of our windows, even destroyed ones
187static void do_queued_events() {
188 in_a_window = true;
189 while (XEventsQueued(fl_display,QueuedAfterReading)) {
190 XEvent xevent;
191 XNextEvent(fl_display, &xevent);
192 fl_handle(xevent);
193 }
194 // we send FL_LEAVE only if the mouse did not enter some other window:
195 if (!in_a_window) Fl::handle(FL_LEAVE, 0);
196#if CONSOLIDATE_MOTION
197 else if (send_motion == fl_xmousewin) {
198 send_motion = 0;
199 Fl::handle(FL_MOVE, fl_xmousewin);
200 }
201#endif
202}
203
204// these pointers are set by the Fl::lock() function:
205static void nothing() {}
206void (*fl_lock_function)() = nothing;
207void (*fl_unlock_function)() = nothing;
208
209// This is never called with time_to_wait < 0.0:
210// It should return negative on error, 0 if nothing happens before
211// timeout, and >0 if any callbacks were done.
212int fl_wait(double time_to_wait) {
213
214 // OpenGL and other broken libraries call XEventsQueued
215 // unnecessarily and thus cause the file descriptor to not be ready,
216 // so we must check for already-read events:
217 if (fl_display && XQLength(fl_display)) {do_queued_events(); return 1;}
218
219# if !USE_POLL
220 fd_set fdt[3];
221 fdt[0] = fdsets[0];
222 fdt[1] = fdsets[1];
223 fdt[2] = fdsets[2];
224# endif
225 int n;
226
227 fl_unlock_function();
228
229 if (time_to_wait < 2147483.648) {
230# if USE_POLL
231 n = ::poll(pollfds, nfds, int(time_to_wait*1000 + .5));
232# else
233 timeval t;
234 t.tv_sec = int(time_to_wait);
235 t.tv_usec = int(1000000 * (time_to_wait-t.tv_sec));
236 n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
237# endif
238 } else {
239# if USE_POLL
240 n = ::poll(pollfds, nfds, -1);
241# else
242 n = ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],0);
243# endif
244 }
245
246 fl_lock_function();
247
248 if (n > 0) {
249 for (int i=0; i<nfds; i++) {
250# if USE_POLL
251 if (pollfds[i].revents) fd[i].cb(pollfds[i].fd, fd[i].arg);
252# else
253 int f = fd[i].fd;
254 short revents = 0;
255 if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
256 if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
257 if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
258 if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
259# endif
260 }
261 }
262 return n;
263}
264
265// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
266int fl_ready() {
267 if (XQLength(fl_display)) return 1;
268 if (!nfds) return 0; // nothing to select or poll
269# if USE_POLL
270 return ::poll(pollfds, nfds, 0);
271# else
272 timeval t;
273 t.tv_sec = 0;
274 t.tv_usec = 0;
275 fd_set fdt[3];
276 fdt[0] = fdsets[0];
277 fdt[1] = fdsets[1];
278 fdt[2] = fdsets[2];
279 return ::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t);
280# endif
281}
282
283// replace \r\n by \n
284static void convert_crlf(unsigned char *string, long& len) {
285 unsigned char *a, *b;
286 a = b = string;
287 while (*a) {
288 if (*a == '\r' && a[1] == '\n') { a++; len--; }
289 else *b++ = *a++;
290 }
291 *b = 0;
292}
293
294////////////////////////////////////////////////////////////////
295
296Display *fl_display;
297Window fl_message_window = 0;
298int fl_screen;
299XVisualInfo *fl_visual;
300Colormap fl_colormap;
301XIM fl_xim_im = 0;
302XIC fl_xim_ic = 0;
303char fl_is_over_the_spot = 0;
304static XRectangle status_area;
305
306static Atom WM_DELETE_WINDOW;
307static Atom WM_PROTOCOLS;
308static Atom fl_MOTIF_WM_HINTS;
309static Atom TARGETS;
310static Atom CLIPBOARD;
311Atom fl_XdndAware;
312Atom fl_XdndSelection;
313Atom fl_XdndEnter;
314Atom fl_XdndTypeList;
315Atom fl_XdndPosition;
316Atom fl_XdndLeave;
317Atom fl_XdndDrop;
318Atom fl_XdndStatus;
319Atom fl_XdndActionCopy;
320Atom fl_XdndFinished;
321//Atom fl_XdndProxy;
322Atom fl_XdndURIList;
323Atom fl_Xatextplainutf;
324Atom fl_Xatextplain;
325static Atom fl_XaText;
326Atom fl_XaCompoundText;
327Atom fl_XaUtf8String;
328Atom fl_XaTextUriList;
329Atom fl_NET_WM_NAME; // utf8 aware window label
330Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name
331
332/*
333 X defines 32-bit-entities to have a format value of max. 32,
334 although sizeof(atom) can be 8 (64 bits) on a 64-bit OS.
335 See also fl_open_display() for sizeof(atom) < 4.
336 Used for XChangeProperty (see STR #2419).
337*/
338static int atom_bits = 32;
339
340static void fd_callback(int,void *) {
341 do_queued_events();
342}
343
344extern "C" {
345 static int io_error_handler(Display*) {
346 Fl::fatal("X I/O error");
347 return 0;
348 }
349
350 static int xerror_handler(Display* d, XErrorEvent* e) {
351 char buf1[128], buf2[128];
352 sprintf(buf1, "XRequest.%d", e->request_code);
353 XGetErrorDatabaseText(d,"",buf1,buf1,buf2,128);
354 XGetErrorText(d, e->error_code, buf1, 128);
355 Fl::warning("%s: %s 0x%lx", buf2, buf1, e->resourceid);
356 return 0;
357 }
358}
359
360extern char *fl_get_font_xfld(int fnum, int size);
361
362void fl_new_ic()
363{
364 XVaNestedList preedit_attr = NULL;
365 XVaNestedList status_attr = NULL;
366 static XFontSet fs = NULL;
367 char *fnt;
368 char **missing_list;
369 int missing_count;
370 char *def_string;
371 static XRectangle spot;
372 int predit = 0;
373 int sarea = 0;
374 XIMStyles* xim_styles = NULL;
375
376#if USE_XFT
377
378#if defined(__GNUC__)
379// FIXME: warning XFT support here
380#endif /*__GNUC__*/
381
382 if (!fs) {
383 fnt = (char*)"-misc-fixed-*";
384 fs = XCreateFontSet(fl_display, fnt, &missing_list,
385 &missing_count, &def_string);
386 }
387#else
388 if (!fs) {
389 bool must_free_fnt = true;
390 fnt = fl_get_font_xfld(0, 14);
391 if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
392 fs = XCreateFontSet(fl_display, fnt, &missing_list,
393 &missing_count, &def_string);
394 if (must_free_fnt) free(fnt);
395 }
396#endif
397 preedit_attr = XVaCreateNestedList(0,
398 XNSpotLocation, &spot,
399 XNFontSet, fs, NULL);
400 status_attr = XVaCreateNestedList(0,
401 XNAreaNeeded, &status_area,
402 XNFontSet, fs, NULL);
403
404 if (!XGetIMValues(fl_xim_im, XNQueryInputStyle,
405 &xim_styles, NULL, NULL)) {
406 int i;
407 XIMStyle *style;
408 for (i = 0, style = xim_styles->supported_styles;
409 i < xim_styles->count_styles; i++, style++) {
410 if (*style == (XIMPreeditPosition | XIMStatusArea)) {
411 sarea = 1;
412 predit = 1;
413 } else if (*style == (XIMPreeditPosition | XIMStatusNothing)) {
414 predit = 1;
415 }
416 }
417 }
418 XFree(xim_styles);
419
420 if (sarea) {
421 fl_xim_ic = XCreateIC(fl_xim_im,
422 XNInputStyle, (XIMPreeditPosition | XIMStatusArea),
423 XNPreeditAttributes, preedit_attr,
424 XNStatusAttributes, status_attr,
425 NULL);
426 }
427
428 if (!fl_xim_ic && predit) {
429 fl_xim_ic = XCreateIC(fl_xim_im,
430 XNInputStyle, (XIMPreeditPosition | XIMStatusNothing),
431 XNPreeditAttributes, preedit_attr,
432 NULL);
433 }
434 XFree(preedit_attr);
435 XFree(status_attr);
436 if (!fl_xim_ic) {
437 fl_is_over_the_spot = 0;
438 fl_xim_ic = XCreateIC(fl_xim_im,
439 XNInputStyle, (XIMPreeditNothing | XIMStatusNothing),
440 NULL);
441 } else {
442 fl_is_over_the_spot = 1;
443 XVaNestedList status_attr = NULL;
444 status_attr = XVaCreateNestedList(0, XNAreaNeeded, &status_area, NULL);
445
446 XGetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
447 XFree(status_attr);
448 }
449}
450
451
452static XRectangle spot;
453static int spotf = -1;
454static int spots = -1;
455
456void fl_reset_spot(void)
457{
458 spot.x = -1;
459 spot.y = -1;
460 //if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
461}
462
463void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
464{
465 int change = 0;
466 XVaNestedList preedit_attr;
467 static XFontSet fs = NULL;
468 char **missing_list;
469 int missing_count;
470 char *def_string;
471 char *fnt = NULL;
472 bool must_free_fnt =true;
473
474 static XIC ic = NULL;
475
476 if (!fl_xim_ic || !fl_is_over_the_spot) return;
477 //XSetICFocus(fl_xim_ic);
478 if (X != spot.x || Y != spot.y) {
479 spot.x = X;
480 spot.y = Y;
481 spot.height = H;
482 spot.width = W;
483 change = 1;
484 }
485 if (font != spotf || size != spots) {
486 spotf = font;
487 spots = size;
488 change = 1;
489 if (fs) {
490 XFreeFontSet(fl_display, fs);
491 }
492#if USE_XFT
493
494#if defined(__GNUC__)
495// FIXME: warning XFT support here
496#endif /*__GNUC__*/
497
498 fnt = NULL; // fl_get_font_xfld(font, size);
499 if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
500 fs = XCreateFontSet(fl_display, fnt, &missing_list,
501 &missing_count, &def_string);
502#else
503 fnt = fl_get_font_xfld(font, size);
504 if (!fnt) {fnt = (char*)"-misc-fixed-*";must_free_fnt=false;}
505 fs = XCreateFontSet(fl_display, fnt, &missing_list,
506 &missing_count, &def_string);
507#endif
508 }
509 if (fl_xim_ic != ic) {
510 ic = fl_xim_ic;
511 change = 1;
512 }
513
514 if (fnt && must_free_fnt) free(fnt);
515 if (!change) return;
516
517
518 preedit_attr = XVaCreateNestedList(0,
519 XNSpotLocation, &spot,
520 XNFontSet, fs, NULL);
521 XSetICValues(fl_xim_ic, XNPreeditAttributes, preedit_attr, NULL);
522 XFree(preedit_attr);
523}
524
525void fl_set_status(int x, int y, int w, int h)
526{
527 XVaNestedList status_attr;
528 status_area.x = x;
529 status_area.y = y;
530 status_area.width = w;
531 status_area.height = h;
532 if (!fl_xim_ic) return;
533 status_attr = XVaCreateNestedList(0, XNArea, &status_area, NULL);
534
535 XSetICValues(fl_xim_ic, XNStatusAttributes, status_attr, NULL);
536 XFree(status_attr);
537}
538
539void fl_init_xim() {
540 static int xim_warning = 2;
541 if (xim_warning > 0) xim_warning--;
542
543 //XIMStyle *style;
544 XIMStyles *xim_styles;
545 if (!fl_display) return;
546 if (fl_xim_im) return;
547
548 fl_xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
549 xim_styles = NULL;
550 fl_xim_ic = NULL;
551
552 if (fl_xim_im) {
553 XGetIMValues (fl_xim_im, XNQueryInputStyle,
554 &xim_styles, NULL, NULL);
555 } else {
556 if (xim_warning)
557 Fl::warning("XOpenIM() failed");
558 // if xim_styles is allocated, free it now
559 if (xim_styles) XFree(xim_styles);
560 return;
561 }
562
563 if (xim_styles && xim_styles->count_styles) {
564 fl_new_ic();
565 } else {
566 if (xim_warning)
567 Fl::warning("No XIM style found");
568 XCloseIM(fl_xim_im);
569 fl_xim_im = NULL;
570 // if xim_styles is allocated, free it now
571 if (xim_styles) XFree(xim_styles);
572 return;
573 }
574 if (!fl_xim_ic) {
575 if (xim_warning)
576 Fl::warning("XCreateIC() failed");
577 XCloseIM(fl_xim_im);
578 fl_xim_im = NULL;
579 }
580 // if xim_styles is still allocated, free it now
581 if(xim_styles) XFree(xim_styles);
582}
583
584void fl_open_display() {
585 if (fl_display) return;
586
587 setlocale(LC_CTYPE, "");
588 XSetLocaleModifiers("");
589
590 XSetIOErrorHandler(io_error_handler);
591 XSetErrorHandler(xerror_handler);
592
593 Display *d = XOpenDisplay(0);
594 if (!d) Fl::fatal("Can't open display: %s",XDisplayName(0));
595
596 fl_open_display(d);
597}
598
599void fl_open_display(Display* d) {
600 fl_display = d;
601
602 WM_DELETE_WINDOW = XInternAtom(d, "WM_DELETE_WINDOW", 0);
603 WM_PROTOCOLS = XInternAtom(d, "WM_PROTOCOLS", 0);
604 fl_MOTIF_WM_HINTS = XInternAtom(d, "_MOTIF_WM_HINTS", 0);
605 TARGETS = XInternAtom(d, "TARGETS", 0);
606 CLIPBOARD = XInternAtom(d, "CLIPBOARD", 0);
607 fl_XdndAware = XInternAtom(d, "XdndAware", 0);
608 fl_XdndSelection = XInternAtom(d, "XdndSelection", 0);
609 fl_XdndEnter = XInternAtom(d, "XdndEnter", 0);
610 fl_XdndTypeList = XInternAtom(d, "XdndTypeList", 0);
611 fl_XdndPosition = XInternAtom(d, "XdndPosition", 0);
612 fl_XdndLeave = XInternAtom(d, "XdndLeave", 0);
613 fl_XdndDrop = XInternAtom(d, "XdndDrop", 0);
614 fl_XdndStatus = XInternAtom(d, "XdndStatus", 0);
615 fl_XdndActionCopy = XInternAtom(d, "XdndActionCopy", 0);
616 fl_XdndFinished = XInternAtom(d, "XdndFinished", 0);
617 //fl_XdndProxy = XInternAtom(d, "XdndProxy", 0);
618 fl_XdndEnter = XInternAtom(d, "XdndEnter", 0);
619 fl_XdndURIList = XInternAtom(d, "text/uri-list", 0);
620 fl_Xatextplainutf = XInternAtom(d, "text/plain;charset=UTF-8",0);
621 fl_Xatextplain = XInternAtom(d, "text/plain", 0);
622 fl_XaText = XInternAtom(d, "TEXT", 0);
623 fl_XaCompoundText = XInternAtom(d, "COMPOUND_TEXT", 0);
624 fl_XaUtf8String = XInternAtom(d, "UTF8_STRING", 0);
625 fl_XaTextUriList = XInternAtom(d, "text/uri-list", 0);
626 fl_NET_WM_NAME = XInternAtom(d, "_NET_WM_NAME", 0);
627 fl_NET_WM_ICON_NAME = XInternAtom(d, "_NET_WM_ICON_NAME", 0);
628
629 if (sizeof(Atom) < 4)
630 atom_bits = sizeof(Atom) * 8;
631
632 Fl::add_fd(ConnectionNumber(d), POLLIN, fd_callback);
633
634 fl_screen = DefaultScreen(d);
635
636 fl_message_window =
637 XCreateSimpleWindow(d, RootWindow(d,fl_screen), 0,0,1,1,0, 0, 0);
638
639// construct an XVisualInfo that matches the default Visual:
640 XVisualInfo templt; int num;
641 templt.visualid = XVisualIDFromVisual(DefaultVisual(d, fl_screen));
642 fl_visual = XGetVisualInfo(d, VisualIDMask, &templt, &num);
643 fl_colormap = DefaultColormap(d, fl_screen);
644 fl_init_xim();
645
646#if !USE_COLORMAP
647 Fl::visual(FL_RGB);
648#endif
649}
650
651void fl_close_display() {
652 Fl::remove_fd(ConnectionNumber(fl_display));
653 XCloseDisplay(fl_display);
654}
655
656static int fl_workarea_xywh[4] = { -1, -1, -1, -1 };
657
658static void fl_init_workarea() {
659 fl_open_display();
660
661 Atom _NET_WORKAREA = XInternAtom(fl_display, "_NET_WORKAREA", 0);
662 Atom actual;
663 unsigned long count, remaining;
664 int format;
665 unsigned *xywh;
666
667 if (XGetWindowProperty(fl_display, RootWindow(fl_display, fl_screen),
668 _NET_WORKAREA, 0, 4 * sizeof(unsigned), False,
669 XA_CARDINAL, &actual, &format, &count, &remaining,
670 (unsigned char **)&xywh) || !xywh || !xywh[2] ||
671 !xywh[3])
672 {
673 fl_workarea_xywh[0] = 0;
674 fl_workarea_xywh[1] = 0;
675 fl_workarea_xywh[2] = DisplayWidth(fl_display, fl_screen);
676 fl_workarea_xywh[3] = DisplayHeight(fl_display, fl_screen);
677 }
678 else
679 {
680 fl_workarea_xywh[0] = (int)xywh[0];
681 fl_workarea_xywh[1] = (int)xywh[1];
682 fl_workarea_xywh[2] = (int)xywh[2];
683 fl_workarea_xywh[3] = (int)xywh[3];
684 XFree(xywh);
685 }
686}
687
688int Fl::x() {
689 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
690 return fl_workarea_xywh[0];
691}
692
693int Fl::y() {
694 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
695 return fl_workarea_xywh[1];
696}
697
698int Fl::w() {
699 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
700 return fl_workarea_xywh[2];
701}
702
703int Fl::h() {
704 if (fl_workarea_xywh[0] < 0) fl_init_workarea();
705 return fl_workarea_xywh[3];
706}
707
708void Fl::get_mouse(int &xx, int &yy) {
709 fl_open_display();
710 Window root = RootWindow(fl_display, fl_screen);
711 Window c; int mx,my,cx,cy; unsigned int mask;
712 XQueryPointer(fl_display,root,&root,&c,&mx,&my,&cx,&cy,&mask);
713 xx = mx;
714 yy = my;
715}
716
717////////////////////////////////////////////////////////////////
718// Code used for paste and DnD into the program:
719
720Fl_Widget *fl_selection_requestor;
721char *fl_selection_buffer[2];
722int fl_selection_length[2];
723int fl_selection_buffer_length[2];
724char fl_i_own_selection[2] = {0,0};
725
726// Call this when a "paste" operation happens:
727void Fl::paste(Fl_Widget &receiver, int clipboard) {
728 if (fl_i_own_selection[clipboard]) {
729 // We already have it, do it quickly without window server.
730 // Notice that the text is clobbered if set_selection is
731 // called in response to FL_PASTE!
732 Fl::e_text = fl_selection_buffer[clipboard];
733 Fl::e_length = fl_selection_length[clipboard];
734 if (!Fl::e_text) Fl::e_text = (char *)"";
735 receiver.handle(FL_PASTE);
736 return;
737 }
738 // otherwise get the window server to return it:
739 fl_selection_requestor = &receiver;
740 Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
741 XConvertSelection(fl_display, property, TARGETS, property,
742 fl_xid(Fl::first_window()), fl_event_time);
743}
744
745Window fl_dnd_source_window;
746Atom *fl_dnd_source_types; // null-terminated list of data types being supplied
747Atom fl_dnd_type;
748Atom fl_dnd_source_action;
749Atom fl_dnd_action;
750
751void fl_sendClientMessage(Window window, Atom message,
752 unsigned long d0,
753 unsigned long d1=0,
754 unsigned long d2=0,
755 unsigned long d3=0,
756 unsigned long d4=0)
757{
758 XEvent e;
759 e.xany.type = ClientMessage;
760 e.xany.window = window;
761 e.xclient.message_type = message;
762 e.xclient.format = 32;
763 e.xclient.data.l[0] = (long)d0;
764 e.xclient.data.l[1] = (long)d1;
765 e.xclient.data.l[2] = (long)d2;
766 e.xclient.data.l[3] = (long)d3;
767 e.xclient.data.l[4] = (long)d4;
768 XSendEvent(fl_display, window, 0, 0, &e);
769}
770
771////////////////////////////////////////////////////////////////
772// Code for copying to clipboard and DnD out of the program:
773
774void Fl::copy(const char *stuff, int len, int clipboard) {
775 if (!stuff || len<0) return;
776 if (len+1 > fl_selection_buffer_length[clipboard]) {
777 delete[] fl_selection_buffer[clipboard];
778 fl_selection_buffer[clipboard] = new char[len+100];
779 fl_selection_buffer_length[clipboard] = len+100;
780 }
781 memcpy(fl_selection_buffer[clipboard], stuff, len);
782 fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
783 fl_selection_length[clipboard] = len;
784 fl_i_own_selection[clipboard] = 1;
785 Atom property = clipboard ? CLIPBOARD : XA_PRIMARY;
786 XSetSelectionOwner(fl_display, property, fl_message_window, fl_event_time);
787}
788
789////////////////////////////////////////////////////////////////
790
791const XEvent* fl_xevent; // the current x event
792ulong fl_event_time; // the last timestamp from an x event
793
794char fl_key_vector[32]; // used by Fl::get_key()
795
796// Record event mouse position and state from an XEvent:
797
798static int px, py;
799static ulong ptime;
800
801static void set_event_xy() {
802# if CONSOLIDATE_MOTION
803 send_motion = 0;
804# endif
805 Fl::e_x_root = fl_xevent->xbutton.x_root;
806 Fl::e_x = fl_xevent->xbutton.x;
807 Fl::e_y_root = fl_xevent->xbutton.y_root;
808 Fl::e_y = fl_xevent->xbutton.y;
809 Fl::e_state = fl_xevent->xbutton.state << 16;
810 fl_event_time = fl_xevent->xbutton.time;
811# ifdef __sgi
812 // get the meta key off PC keyboards:
813 if (fl_key_vector[18]&0x18) Fl::e_state |= FL_META;
814# endif
815 // turn off is_click if enough time or mouse movement has passed:
816 if (abs(Fl::e_x_root-px)+abs(Fl::e_y_root-py) > 3 ||
817 fl_event_time >= ptime+1000)
818 Fl::e_is_click = 0;
819}
820
821// if this is same event as last && is_click, increment click count:
822static inline void checkdouble() {
823 if (Fl::e_is_click == Fl::e_keysym)
824 Fl::e_clicks++;
825 else {
826 Fl::e_clicks = 0;
827 Fl::e_is_click = Fl::e_keysym;
828 }
829 px = Fl::e_x_root;
830 py = Fl::e_y_root;
831 ptime = fl_event_time;
832}
833
834static Fl_Window* resize_bug_fix;
835
836////////////////////////////////////////////////////////////////
837
838static char unknown[] = "<unknown>";
839const int unknown_len = 10;
840
841extern "C" {
842
843static int xerror = 0;
844
845static int ignoreXEvents(Display *display, XErrorEvent *event) {
846 xerror = 1;
847 return 0;
848}
849
850static XErrorHandler catchXExceptions() {
851 xerror = 0;
852 return ignoreXEvents;
853}
854
855static int wasXExceptionRaised() {
856 return xerror;
857}
858
859}
860
861
862int fl_handle(const XEvent& thisevent)
863{
864 XEvent xevent = thisevent;
865 fl_xevent = &thisevent;
866 Window xid = xevent.xany.window;
867 static Window xim_win = 0;
868
869 if (fl_xim_ic && xevent.type == DestroyNotify &&
870 xid != xim_win && !fl_find(xid))
871 {
872 XIM xim_im;
873 xim_im = XOpenIM(fl_display, NULL, NULL, NULL);
874 if (!xim_im) {
875 /* XIM server has crashed */
876 XSetLocaleModifiers("@im=");
877 fl_xim_im = NULL;
878 fl_init_xim();
879 } else {
880 XCloseIM(xim_im); // see STR 2185 for comment
881 }
882 return 0;
883 }
884
885 if (fl_xim_ic && (xevent.type == FocusIn))
886 {
887#define POOR_XIM
888#ifdef POOR_XIM
889 if (xim_win != xid)
890 {
891 xim_win = xid;
892 XDestroyIC(fl_xim_ic);
893 fl_xim_ic = NULL;
894 fl_new_ic();
895 XSetICValues(fl_xim_ic,
896 XNFocusWindow, xevent.xclient.window,
897 XNClientWindow, xid,
898 NULL);
899 }
900 fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
901#else
902 if (Fl::first_window() && Fl::first_window()->modal()) {
903 Window x = fl_xid(Fl::first_window());
904 if (x != xim_win) {
905 xim_win = x;
906 XSetICValues(fl_xim_ic,
907 XNFocusWindow, xim_win,
908 XNClientWindow, xim_win,
909 NULL);
910 fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
911 }
912 } else if (xim_win != xid && xid) {
913 xim_win = xid;
914 XSetICValues(fl_xim_ic,
915 XNFocusWindow, xevent.xclient.window,
916 XNClientWindow, xid,
917 //XNFocusWindow, xim_win,
918 //XNClientWindow, xim_win,
919 NULL);
920 fl_set_spot(spotf, spots, spot.x, spot.y, spot.width, spot.height);
921 }
922#endif
923 }
924
925 if ( XFilterEvent((XEvent *)&xevent, 0) )
926 return(1);
927
928 switch (xevent.type) {
929
930 case KeymapNotify:
931 memcpy(fl_key_vector, xevent.xkeymap.key_vector, 32);
932 return 0;
933
934 case MappingNotify:
935 XRefreshKeyboardMapping((XMappingEvent*)&xevent.xmapping);
936 return 0;
937
938 case SelectionNotify: {
939 if (!fl_selection_requestor) return 0;
940 static unsigned char* buffer = 0;
941 if (buffer) {XFree(buffer); buffer = 0;}
942 long bytesread = 0;
943 if (fl_xevent->xselection.property) for (;;) {
944 // The Xdnd code pastes 64K chunks together, possibly to avoid
945 // bugs in X servers, or maybe to avoid an extra round-trip to
946 // get the property length. I copy this here:
947 Atom actual; int format; unsigned long count, remaining;
948 unsigned char* portion = NULL;
949 if (XGetWindowProperty(fl_display,
950 fl_xevent->xselection.requestor,
951 fl_xevent->xselection.property,
952 bytesread/4, 65536, 1, 0,
953 &actual, &format, &count, &remaining,
954 &portion)) break; // quit on error
955 if (actual == TARGETS || actual == XA_ATOM) {
956 Atom type = XA_STRING;
957 for (unsigned i = 0; i<count; i++) {
958 Atom t = ((Atom*)portion)[i];
959 if (t == fl_Xatextplainutf ||
960 t == fl_Xatextplain ||
961 t == fl_XaUtf8String) {type = t; break;}
962 // rest are only used if no utf-8 available:
963 if (t == fl_XaText ||
964 t == fl_XaTextUriList ||
965 t == fl_XaCompoundText) type = t;
966 }
967 XFree(portion);
968 Atom property = xevent.xselection.property;
969 XConvertSelection(fl_display, property, type, property,
970 fl_xid(Fl::first_window()),
971 fl_event_time);
972 return true;
973 }
974 // Make sure we got something sane...
975 if ((portion == NULL) || (format != 8) || (count == 0)) {
976 if (portion) XFree(portion);
977 return true;
978 }
979 buffer = (unsigned char*)realloc(buffer, bytesread+count+remaining+1);
980 memcpy(buffer+bytesread, portion, count);
981 XFree(portion);
982 bytesread += count;
983 // Cannot trust data to be null terminated
984 buffer[bytesread] = '\0';
985 if (!remaining) break;
986 }
987 if (buffer) {
988 buffer[bytesread] = 0;
989 convert_crlf(buffer, bytesread);
990 }
991 Fl::e_text = buffer ? (char*)buffer : (char *)"";
992 Fl::e_length = bytesread;
993 int old_event = Fl::e_number;
994 fl_selection_requestor->handle(Fl::e_number = FL_PASTE);
995 Fl::e_number = old_event;
996 // Detect if this paste is due to Xdnd by the property name (I use
997 // XA_SECONDARY for that) and send an XdndFinished message. It is not
998 // clear if this has to be delayed until now or if it can be done
999 // immediatly after calling XConvertSelection.
1000 if (fl_xevent->xselection.property == XA_SECONDARY &&
1001 fl_dnd_source_window) {
1002 fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished,
1003 fl_xevent->xselection.requestor);
1004 fl_dnd_source_window = 0; // don't send a second time
1005 }
1006 return 1;}
1007
1008 case SelectionClear: {
1009 int clipboard = fl_xevent->xselectionclear.selection == CLIPBOARD;
1010 fl_i_own_selection[clipboard] = 0;
1011 return 1;}
1012
1013 case SelectionRequest: {
1014 XSelectionEvent e;
1015 e.type = SelectionNotify;
1016 e.requestor = fl_xevent->xselectionrequest.requestor;
1017 e.selection = fl_xevent->xselectionrequest.selection;
1018 int clipboard = e.selection == CLIPBOARD;
1019 e.target = fl_xevent->xselectionrequest.target;
1020 e.time = fl_xevent->xselectionrequest.time;
1021 e.property = fl_xevent->xselectionrequest.property;
1022 if (e.target == TARGETS) {
1023 Atom a[3] = {fl_XaUtf8String, XA_STRING, fl_XaText};
1024 XChangeProperty(fl_display, e.requestor, e.property,
1025 XA_ATOM, atom_bits, 0, (unsigned char*)a, 3);
1026 } else if (/*e.target == XA_STRING &&*/ fl_selection_length[clipboard]) {
1027 if (e.target == fl_XaUtf8String ||
1028 e.target == XA_STRING ||
1029 e.target == fl_XaCompoundText ||
1030 e.target == fl_XaText ||
1031 e.target == fl_Xatextplain ||
1032 e.target == fl_Xatextplainutf) {
1033 // clobber the target type, this seems to make some applications
1034 // behave that insist on asking for XA_TEXT instead of UTF8_STRING
1035 // Does not change XA_STRING as that breaks xclipboard.
1036 if (e.target != XA_STRING) e.target = fl_XaUtf8String;
1037 XChangeProperty(fl_display, e.requestor, e.property,
1038 e.target, 8, 0,
1039 (unsigned char *)fl_selection_buffer[clipboard],
1040 fl_selection_length[clipboard]);
1041 }
1042 } else {
1043// char* x = XGetAtomName(fl_display,e.target);
1044// fprintf(stderr,"selection request of %s\n",x);
1045// XFree(x);
1046 e.property = 0;
1047 }
1048 XSendEvent(fl_display, e.requestor, 0, 0, (XEvent *)&e);}
1049 return 1;
1050
1051 // events where interesting window id is in a different place:
1052 case CirculateNotify:
1053 case CirculateRequest:
1054 case ConfigureNotify:
1055 case ConfigureRequest:
1056 case CreateNotify:
1057 case DestroyNotify:
1058 case GravityNotify:
1059 case MapNotify:
1060 case MapRequest:
1061 case ReparentNotify:
1062 case UnmapNotify:
1063 xid = xevent.xmaprequest.window;
1064 break;
1065 }
1066
1067 int event = 0;
1068 Fl_Window* window = fl_find(xid);
1069
1070 if (window) switch (xevent.type) {
1071
1072 case DestroyNotify: { // an X11 window was closed externally from the program
1073 Fl::handle(FL_CLOSE, window);
1074 Fl_X* X = Fl_X::i(window);
1075 if (X) { // indicates the FLTK window was not closed
1076 X->xid = (Window)0; // indicates the X11 window was already destroyed
1077 window->hide();
1078 int oldx = window->x(), oldy = window->y();
1079 window->position(0, 0);
1080 window->position(oldx, oldy);
1081 window->show(); // recreate the X11 window in support of the FLTK window
1082 }
1083 return 1;
1084 }
1085 case ClientMessage: {
1086 Atom message = fl_xevent->xclient.message_type;
1087 const long* data = fl_xevent->xclient.data.l;
1088 if ((Atom)(data[0]) == WM_DELETE_WINDOW) {
1089 event = FL_CLOSE;
1090 } else if (message == fl_XdndEnter) {
1091 fl_xmousewin = window;
1092 in_a_window = true;
1093 fl_dnd_source_window = data[0];
1094 // version number is data[1]>>24
1095// printf("XdndEnter, version %ld\n", data[1] >> 24);
1096 if (data[1]&1) {
1097 // get list of data types:
1098 Atom actual; int format; unsigned long count, remaining;
1099 unsigned char *buffer = 0;
1100 XGetWindowProperty(fl_display, fl_dnd_source_window, fl_XdndTypeList,
1101 0, 0x8000000L, False, XA_ATOM, &actual, &format,
1102 &count, &remaining, &buffer);
1103 if (actual != XA_ATOM || format != 32 || count<4 || !buffer)
1104 goto FAILED;
1105 delete [] fl_dnd_source_types;
1106 fl_dnd_source_types = new Atom[count+1];
1107 for (unsigned i = 0; i < count; i++) {
1108 fl_dnd_source_types[i] = ((Atom*)buffer)[i];
1109 }
1110 fl_dnd_source_types[count] = 0;
1111 } else {
1112 FAILED:
1113 // less than four data types, or if the above messes up:
1114 if (!fl_dnd_source_types) fl_dnd_source_types = new Atom[4];
1115 fl_dnd_source_types[0] = data[2];
1116 fl_dnd_source_types[1] = data[3];
1117 fl_dnd_source_types[2] = data[4];
1118 fl_dnd_source_types[3] = 0;
1119 }
1120
1121 // Loop through the source types and pick the first text type...
1122 int i;
1123
1124 for (i = 0; fl_dnd_source_types[i]; i ++)
1125 {
1126// printf("fl_dnd_source_types[%d] = %ld (%s)\n", i,
1127// fl_dnd_source_types[i],
1128// XGetAtomName(fl_display, fl_dnd_source_types[i]));
1129
1130 if (!strncmp(XGetAtomName(fl_display, fl_dnd_source_types[i]),
1131 "text/", 5))
1132 break;
1133 }
1134
1135 if (fl_dnd_source_types[i])
1136 fl_dnd_type = fl_dnd_source_types[i];
1137 else
1138 fl_dnd_type = fl_dnd_source_types[0];
1139
1140 event = FL_DND_ENTER;
1141 Fl::e_text = unknown;
1142 Fl::e_length = unknown_len;
1143 break;
1144
1145 } else if (message == fl_XdndPosition) {
1146 fl_xmousewin = window;
1147 in_a_window = true;
1148 fl_dnd_source_window = data[0];
1149 Fl::e_x_root = data[2]>>16;
1150 Fl::e_y_root = data[2]&0xFFFF;
1151 if (window) {
1152 Fl::e_x = Fl::e_x_root-window->x();
1153 Fl::e_y = Fl::e_y_root-window->y();
1154 }
1155 fl_event_time = data[3];
1156 fl_dnd_source_action = data[4];
1157 fl_dnd_action = fl_XdndActionCopy;
1158 Fl::e_text = unknown;
1159 Fl::e_length = unknown_len;
1160 int accept = Fl::handle(FL_DND_DRAG, window);
1161 fl_sendClientMessage(data[0], fl_XdndStatus,
1162 fl_xevent->xclient.window,
1163 accept ? 1 : 0,
1164 0, // used for xy rectangle to not send position inside
1165 0, // used for width+height of rectangle
1166 accept ? fl_dnd_action : None);
1167 return 1;
1168
1169 } else if (message == fl_XdndLeave) {
1170 fl_dnd_source_window = 0; // don't send a finished message to it
1171 event = FL_DND_LEAVE;
1172 Fl::e_text = unknown;
1173 Fl::e_length = unknown_len;
1174 break;
1175
1176 } else if (message == fl_XdndDrop) {
1177 fl_xmousewin = window;
1178 in_a_window = true;
1179 fl_dnd_source_window = data[0];
1180 fl_event_time = data[2];
1181 Window to_window = fl_xevent->xclient.window;
1182 Fl::e_text = unknown;
1183 Fl::e_length = unknown_len;
1184 if (Fl::handle(FL_DND_RELEASE, window)) {
1185 fl_selection_requestor = Fl::belowmouse();
1186 XConvertSelection(fl_display, fl_XdndSelection,
1187 fl_dnd_type, XA_SECONDARY,
1188 to_window, fl_event_time);
1189 } else {
1190 // Send the finished message if I refuse the drop.
1191 // It is not clear whether I can just send finished always,
1192 // or if I have to wait for the SelectionNotify event as the
1193 // code is currently doing.
1194 fl_sendClientMessage(fl_dnd_source_window, fl_XdndFinished, to_window);
1195 fl_dnd_source_window = 0;
1196 }
1197 return 1;
1198
1199 }
1200 break;}
1201
1202 case UnmapNotify:
1203 event = FL_HIDE;
1204 break;
1205
1206 case Expose:
1207 Fl_X::i(window)->wait_for_expose = 0;
1208# if 0
1209 // try to keep windows on top even if WM_TRANSIENT_FOR does not work:
1210 // opaque move/resize window managers do not like this, so I disabled it.
1211 if (Fl::first_window()->non_modal() && window != Fl::first_window())
1212 Fl::first_window()->show();
1213# endif
1214
1215 case GraphicsExpose:
1216 window->damage(FL_DAMAGE_EXPOSE, xevent.xexpose.x, xevent.xexpose.y,
1217 xevent.xexpose.width, xevent.xexpose.height);
1218 return 1;
1219
1220 case FocusIn:
1221 if (fl_xim_ic) XSetICFocus(fl_xim_ic);
1222 event = FL_FOCUS;
1223 break;
1224
1225 case FocusOut:
1226 if (fl_xim_ic) XUnsetICFocus(fl_xim_ic);
1227 event = FL_UNFOCUS;
1228 break;
1229
1230 case KeyPress:
1231 case KeyRelease: {
1232 KEYPRESS:
1233 int keycode = xevent.xkey.keycode;
1234 fl_key_vector[keycode/8] |= (1 << (keycode%8));
1235 static char *buffer = NULL;
1236 static int buffer_len = 0;
1237 int len;
1238 KeySym keysym;
1239 if (buffer_len == 0) {
1240 buffer_len = 4096;
1241 buffer = (char*) malloc(buffer_len);
1242 }
1243 if (xevent.type == KeyPress) {
1244 event = FL_KEYDOWN;
1245 int len = 0;
1246
1247 if (fl_xim_ic) {
1248 Status status;
1249 len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
1250 buffer, buffer_len, &keysym, &status);
1251
1252 while (status == XBufferOverflow && buffer_len < 50000) {
1253 buffer_len = buffer_len * 5 + 1;
1254 buffer = (char*)realloc(buffer, buffer_len);
1255 len = XUtf8LookupString(fl_xim_ic, (XKeyPressedEvent *)&xevent.xkey,
1256 buffer, buffer_len, &keysym, &status);
1257 }
1258 keysym = XKeycodeToKeysym(fl_display, keycode, 0);
1259 } else {
1260 //static XComposeStatus compose;
1261 len = XLookupString((XKeyEvent*)&(xevent.xkey),
1262 buffer, buffer_len, &keysym, 0/*&compose*/);
1263 if (keysym && keysym < 0x400) { // a character in latin-1,2,3,4 sets
1264 // force it to type a character (not sure if this ever is needed):
1265 // if (!len) {buffer[0] = char(keysym); len = 1;}
1266 len = fl_utf8encode(XKeysymToUcs(keysym), buffer);
1267 if (len < 1) len = 1;
1268 // ignore all effects of shift on the keysyms, which makes it a lot
1269 // easier to program shortcuts and is Windoze-compatible:
1270 keysym = XKeycodeToKeysym(fl_display, keycode, 0);
1271 }
1272 }
1273 // MRS: Can't use Fl::event_state(FL_CTRL) since the state is not
1274 // set until set_event_xy() is called later...
1275 if ((xevent.xkey.state & ControlMask) && keysym == '-') buffer[0] = 0x1f; // ^_
1276 buffer[len] = 0;
1277 Fl::e_text = buffer;
1278 Fl::e_length = len;
1279 } else {
1280 // Stupid X sends fake key-up events when a repeating key is held
1281 // down, probably due to some back compatibility problem. Fortunately
1282 // we can detect this because the repeating KeyPress event is in
1283 // the queue, get it and execute it instead:
1284
1285 // Bool XkbSetDetectableAutorepeat ( display, detectable, supported_rtrn )
1286 // Display * display ;
1287 // Bool detectable ;
1288 // Bool * supported_rtrn ;
1289 // ...would be the easy way to corrct this isuue. Unfortunatly, this call is also
1290 // broken on many Unix distros including Ubuntu and Solaris (as of Dec 2009)
1291
1292 // Bogus KeyUp events are generated by repeated KeyDown events. One
1293 // neccessary condition is an identical key event pending right after
1294 // the bogus KeyUp.
1295 // The new code introduced Dec 2009 differs in that it only check the very
1296 // next event in the queue, not the entire queue of events.
1297 // This function wrongly detects a repeat key if a software keyboard
1298 // sends a burst of events containing two consecutive equal keys. However,
1299 // in every non-gaming situation, this is no problem because both KeyPress
1300 // events will cause the expected behavior.
1301 XEvent peekevent;
1302 if (XPending(fl_display)) {
1303 XPeekEvent(fl_display, &peekevent);
1304 if ( (peekevent.type == KeyPress) // must be a KeyPress event
1305 && (peekevent.xkey.keycode == xevent.xkey.keycode) // must be the same key
1306 && (peekevent.xkey.time == xevent.xkey.time) // must be sent at the exact same time
1307 ) {
1308 XNextEvent(fl_display, &xevent);
1309 goto KEYPRESS;
1310 }
1311 }
1312
1313 event = FL_KEYUP;
1314 fl_key_vector[keycode/8] &= ~(1 << (keycode%8));
1315 // keyup events just get the unshifted keysym:
1316 keysym = XKeycodeToKeysym(fl_display, keycode, 0);
1317 }
1318# ifdef __sgi
1319 // You can plug a microsoft keyboard into an sgi but the extra shift
1320 // keys are not translated. Make them translate like XFree86 does:
1321 if (!keysym) switch(keycode) {
1322 case 147: keysym = FL_Meta_L; break;
1323 case 148: keysym = FL_Meta_R; break;
1324 case 149: keysym = FL_Menu; break;
1325 }
1326# endif
1327# if BACKSPACE_HACK
1328 // Attempt to fix keyboards that send "delete" for the key in the
1329 // upper-right corner of the main keyboard. But it appears that
1330 // very few of these remain?
1331 static int got_backspace = 0;
1332 if (!got_backspace) {
1333 if (keysym == FL_Delete) keysym = FL_BackSpace;
1334 else if (keysym == FL_BackSpace) got_backspace = 1;
1335 }
1336# endif
1337 // For the first few years, there wasn't a good consensus on what the
1338 // Windows keys should be mapped to for X11. So we need to help out a
1339 // bit and map all variants to the same FLTK key...
1340 switch (keysym) {
1341 case XK_Meta_L:
1342 case XK_Hyper_L:
1343 case XK_Super_L:
1344 keysym = FL_Meta_L;
1345 break;
1346 case XK_Meta_R:
1347 case XK_Hyper_R:
1348 case XK_Super_R:
1349 keysym = FL_Meta_R;
1350 break;
1351 }
1352 // Convert the multimedia keys to safer, portable values
1353 switch (keysym) { // XF names come from X11/XF86keysym.h
1354 case 0x1008FF11: // XF86XK_AudioLowerVolume:
1355 keysym = FL_Volume_Down;
1356 break;
1357 case 0x1008FF12: // XF86XK_AudioMute:
1358 keysym = FL_Volume_Mute;
1359 break;
1360 case 0x1008FF13: // XF86XK_AudioRaiseVolume:
1361 keysym = FL_Volume_Up;
1362 break;
1363 case 0x1008FF14: // XF86XK_AudioPlay:
1364 keysym = FL_Media_Play;
1365 break;
1366 case 0x1008FF15: // XF86XK_AudioStop:
1367 keysym = FL_Media_Stop;
1368 break;
1369 case 0x1008FF16: // XF86XK_AudioPrev:
1370 keysym = FL_Media_Prev;
1371 break;
1372 case 0x1008FF17: // XF86XK_AudioNext:
1373 keysym = FL_Media_Next;
1374 break;
1375 case 0x1008FF18: // XF86XK_HomePage:
1376 keysym = FL_Home_Page;
1377 break;
1378 case 0x1008FF19: // XF86XK_Mail:
1379 keysym = FL_Mail;
1380 break;
1381 case 0x1008FF1B: // XF86XK_Search:
1382 keysym = FL_Search;
1383 break;
1384 case 0x1008FF26: // XF86XK_Back:
1385 keysym = FL_Back;
1386 break;
1387 case 0x1008FF27: // XF86XK_Forward:
1388 keysym = FL_Forward;
1389 break;
1390 case 0x1008FF28: // XF86XK_Stop:
1391 keysym = FL_Stop;
1392 break;
1393 case 0x1008FF29: // XF86XK_Refresh:
1394 keysym = FL_Refresh;
1395 break;
1396 case 0x1008FF2F: // XF86XK_Sleep:
1397 keysym = FL_Sleep;
1398 break;
1399 case 0x1008FF30: // XF86XK_Favorites:
1400 keysym = FL_Favorites;
1401 break;
1402 }
1403 // We have to get rid of the XK_KP_function keys, because they are
1404 // not produced on Windoze and thus case statements tend not to check
1405 // for them. There are 15 of these in the range 0xff91 ... 0xff9f
1406 if (keysym >= 0xff91 && keysym <= 0xff9f) {
1407 // Map keypad keysym to character or keysym depending on
1408 // numlock state...
1409 unsigned long keysym1 = XKeycodeToKeysym(fl_display, keycode, 1);
1410 if (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))
1411 Fl::e_original_keysym = (int)(keysym1 | FL_KP);
1412 if ((xevent.xkey.state & Mod2Mask) &&
1413 (keysym1 <= 0x7f || (keysym1 > 0xff9f && keysym1 <= FL_KP_Last))) {
1414 // Store ASCII numeric keypad value...
1415 keysym = keysym1 | FL_KP;
1416 buffer[0] = char(keysym1) & 0x7F;
1417 len = 1;
1418 } else {
1419 // Map keypad to special key...
1420 static const unsigned short table[15] = {
1421 FL_F+1, FL_F+2, FL_F+3, FL_F+4,
1422 FL_Home, FL_Left, FL_Up, FL_Right,
1423 FL_Down, FL_Page_Up, FL_Page_Down, FL_End,
1424 0xff0b/*XK_Clear*/, FL_Insert, FL_Delete};
1425 keysym = table[keysym-0xff91];
1426 }
1427 } else {
1428 // Store this so we can later know if the KP was used
1429 Fl::e_original_keysym = (int)keysym;
1430 }
1431 Fl::e_keysym = int(keysym);
1432
1433 // replace XK_ISO_Left_Tab (Shift-TAB) with FL_Tab (modifier flags are set correctly by X11)
1434 if (Fl::e_keysym == 0xfe20) Fl::e_keysym = FL_Tab;
1435
1436 set_event_xy();
1437 Fl::e_is_click = 0;
1438 break;}
1439
1440 case ButtonPress:
1441 Fl::e_keysym = FL_Button + xevent.xbutton.button;
1442 set_event_xy();
1443 if (xevent.xbutton.button == Button4) {
1444 Fl::e_dy = -1; // Up
1445 event = FL_MOUSEWHEEL;
1446 } else if (xevent.xbutton.button == Button5) {
1447 Fl::e_dy = +1; // Down
1448 event = FL_MOUSEWHEEL;
1449 } else {
1450 Fl::e_state |= (FL_BUTTON1 << (xevent.xbutton.button-1));
1451 event = FL_PUSH;
1452 checkdouble();
1453 }
1454
1455 fl_xmousewin = window;
1456 in_a_window = true;
1457 break;
1458
1459 case MotionNotify:
1460 set_event_xy();
1461# if CONSOLIDATE_MOTION
1462 send_motion = fl_xmousewin = window;
1463 in_a_window = true;
1464 return 0;
1465# else
1466 event = FL_MOVE;
1467 fl_xmousewin = window;
1468 in_a_window = true;
1469 break;
1470# endif
1471
1472 case ButtonRelease:
1473 Fl::e_keysym = FL_Button + xevent.xbutton.button;
1474 set_event_xy();
1475 Fl::e_state &= ~(FL_BUTTON1 << (xevent.xbutton.button-1));
1476 if (xevent.xbutton.button == Button4 ||
1477 xevent.xbutton.button == Button5) return 0;
1478 event = FL_RELEASE;
1479
1480 fl_xmousewin = window;
1481 in_a_window = true;
1482 break;
1483
1484 case EnterNotify:
1485 if (xevent.xcrossing.detail == NotifyInferior) break;
1486 // XInstallColormap(fl_display, Fl_X::i(window)->colormap);
1487 set_event_xy();
1488 Fl::e_state = xevent.xcrossing.state << 16;
1489 event = FL_ENTER;
1490
1491 fl_xmousewin = window;
1492 in_a_window = true;
1493 { XIMStyles *xim_styles = NULL;
1494 if(!fl_xim_im || XGetIMValues(fl_xim_im, XNQueryInputStyle, &xim_styles, NULL, NULL)) {
1495 fl_init_xim();
1496 }
1497 if (xim_styles) XFree(xim_styles);
1498 }
1499 break;
1500
1501 case LeaveNotify:
1502 if (xevent.xcrossing.detail == NotifyInferior) break;
1503 set_event_xy();
1504 Fl::e_state = xevent.xcrossing.state << 16;
1505 fl_xmousewin = 0;
1506 in_a_window = false; // make do_queued_events produce FL_LEAVE event
1507 return 0;
1508
1509 // We cannot rely on the x,y position in the configure notify event.
1510 // I now think this is an unavoidable problem with X: it is impossible
1511 // for a window manager to prevent the "real" notify event from being
1512 // sent when it resizes the contents, even though it can send an
1513 // artificial event with the correct position afterwards (and some
1514 // window managers do not send this fake event anyway)
1515 // So anyway, do a round trip to find the correct x,y:
1516 case MapNotify:
1517 event = FL_SHOW;
1518
1519 case ConfigureNotify: {
1520 if (window->parent()) break; // ignore child windows
1521
1522 // figure out where OS really put window
1523 XWindowAttributes actual;
1524 XGetWindowAttributes(fl_display, fl_xid(window), &actual);
1525 Window cr; int X, Y, W = actual.width, H = actual.height;
1526 XTranslateCoordinates(fl_display, fl_xid(window), actual.root,
1527 0, 0, &X, &Y, &cr);
1528
1529 // tell Fl_Window about it and set flag to prevent echoing:
1530 resize_bug_fix = window;
1531 window->resize(X, Y, W, H);
1532 break; // allow add_handler to do something too
1533 }
1534
1535 case ReparentNotify: {
1536 int xpos, ypos;
1537 Window junk;
1538
1539 // on some systems, the ReparentNotify event is not handled as we would expect.
1540 XErrorHandler oldHandler = XSetErrorHandler(catchXExceptions());
1541
1542 //ReparentNotify gives the new position of the window relative to
1543 //the new parent. FLTK cares about the position on the root window.
1544 XTranslateCoordinates(fl_display, xevent.xreparent.parent,
1545 XRootWindow(fl_display, fl_screen),
1546 xevent.xreparent.x, xevent.xreparent.y,
1547 &xpos, &ypos, &junk);
1548 XSetErrorHandler(oldHandler);
1549
1550 // tell Fl_Window about it and set flag to prevent echoing:
1551 if ( !wasXExceptionRaised() ) {
1552 resize_bug_fix = window;
1553 window->position(xpos, ypos);
1554 }
1555 break;
1556 }
1557 }
1558
1559 return Fl::handle(event, window);
1560}
1561
1562////////////////////////////////////////////////////////////////
1563
1564void Fl_Window::resize(int X,int Y,int W,int H) {
1565 int is_a_move = (X != x() || Y != y());
1566 int is_a_resize = (W != w() || H != h());
1567 int is_a_enlarge = (W > w() || H > h());
1568 int resize_from_program = (this != resize_bug_fix);
1569 if (!resize_from_program) resize_bug_fix = 0;
1570 if (is_a_move && resize_from_program) set_flag(FORCE_POSITION);
1571 else if (!is_a_resize && !is_a_move) return;
1572 if (is_a_resize) {
1573 Fl_Group::resize(X,Y,W,H);
1574 if (shown()) {redraw(); if(is_a_enlarge) i->wait_for_expose = 1;}
1575 } else {
1576 x(X); y(Y);
1577 }
1578
1579 if (resize_from_program && is_a_resize && !resizable()) {
1580 size_range(w(), h(), w(), h());
1581 }
1582
1583 if (resize_from_program && shown()) {
1584 if (is_a_resize) {
1585 if (!resizable()) size_range(w(),h(),w(),h());
1586 if (is_a_move) {
1587 XMoveResizeWindow(fl_display, i->xid, X, Y, W>0 ? W : 1, H>0 ? H : 1);
1588 } else {
1589 XResizeWindow(fl_display, i->xid, W>0 ? W : 1, H>0 ? H : 1);
1590 }
1591 } else
1592 XMoveWindow(fl_display, i->xid, X, Y);
1593 }
1594}
1595
1596////////////////////////////////////////////////////////////////
1597
1598// A subclass of Fl_Window may call this to associate an X window it
1599// creates with the Fl_Window:
1600
1601void fl_fix_focus(); // in Fl.cxx
1602
1603Fl_X* Fl_X::set_xid(Fl_Window* win, Window winxid) {
1604 Fl_X* xp = new Fl_X;
1605 xp->xid = winxid;
1606 xp->other_xid = 0;
1607 xp->setwindow(win);
1608 xp->next = Fl_X::first;
1609 xp->region = 0;
1610 xp->wait_for_expose = 1;
1611 xp->backbuffer_bad = 1;
1612 Fl_X::first = xp;
1613 if (win->modal()) {Fl::modal_ = win; fl_fix_focus();}
1614 return xp;
1615}
1616
1617// More commonly a subclass calls this, because it hides the really
1618// ugly parts of X and sets all the stuff for a window that is set
1619// normally. The global variables like fl_show_iconic are so that
1620// subclasses of *that* class may change the behavior...
1621
1622char fl_show_iconic; // hack for iconize()
1623int fl_background_pixel = -1; // hack to speed up bg box drawing
1624int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
1625
1626static const int childEventMask = ExposureMask;
1627
1628static const int XEventMask =
1629ExposureMask|StructureNotifyMask
1630|KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask
1631|ButtonPressMask|ButtonReleaseMask
1632|EnterWindowMask|LeaveWindowMask
1633|PointerMotionMask;
1634
1635void Fl_X::make_xid(Fl_Window* win, XVisualInfo *visual, Colormap colormap)
1636{
1637 Fl_Group::current(0); // get rid of very common user bug: forgot end()
1638
1639 int X = win->x();
1640 int Y = win->y();
1641 int W = win->w();
1642 if (W <= 0) W = 1; // X don't like zero...
1643 int H = win->h();
1644 if (H <= 0) H = 1; // X don't like zero...
1645 if (!win->parent() && !Fl::grab()) {
1646 // center windows in case window manager does not do anything:
1647#ifdef FL_CENTER_WINDOWS
1648 if (!(win->flags() & Fl_Widget::FORCE_POSITION)) {
1649 win->x(X = scr_x+(scr_w-W)/2);
1650 win->y(Y = scr_y+(scr_h-H)/2);
1651 }
1652#endif // FL_CENTER_WINDOWS
1653
1654 // force the window to be on-screen. Usually the X window manager
1655 // does this, but a few don't, so we do it here for consistency:
1656 int scr_x, scr_y, scr_w, scr_h;
1657 Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y);
1658
1659 if (win->border()) {
1660 // ensure border is on screen:
1661 // (assume extremely minimal dimensions for this border)
1662 const int top = 20;
1663 const int left = 1;
1664 const int right = 1;
1665 const int bottom = 1;
1666 if (X+W+right > scr_x+scr_w) X = scr_x+scr_w-right-W;
1667 if (X-left < scr_x) X = scr_x+left;
1668 if (Y+H+bottom > scr_y+scr_h) Y = scr_y+scr_h-bottom-H;
1669 if (Y-top < scr_y) Y = scr_y+top;
1670 }
1671 // now insure contents are on-screen (more important than border):
1672 if (X+W > scr_x+scr_w) X = scr_x+scr_w-W;
1673 if (X < scr_x) X = scr_x;
1674 if (Y+H > scr_y+scr_h) Y = scr_y+scr_h-H;
1675 if (Y < scr_y) Y = scr_y;
1676 }
1677
1678 // if the window is a subwindow and our parent is not mapped yet, we
1679 // mark this window visible, so that mapping the parent at a later
1680 // point in time will call this function again to finally map the subwindow.
1681 if (win->parent() && !Fl_X::i(win->window())) {
1682 win->set_visible();
1683 return;
1684 }
1685
1686 ulong root = win->parent() ?
1687 fl_xid(win->window()) : RootWindow(fl_display, fl_screen);
1688
1689 XSetWindowAttributes attr;
1690 int mask = CWBorderPixel|CWColormap|CWEventMask|CWBitGravity;
1691 attr.event_mask = win->parent() ? childEventMask : XEventMask;
1692 attr.colormap = colormap;
1693 attr.border_pixel = 0;
1694 attr.bit_gravity = 0; // StaticGravity;
1695 if (win->override()) {
1696 attr.override_redirect = 1;
1697 attr.save_under = 1;
1698 mask |= CWOverrideRedirect | CWSaveUnder;
1699 } else attr.override_redirect = 0;
1700 if (Fl::grab()) {
1701 attr.save_under = 1; mask |= CWSaveUnder;
1702 if (!win->border()) {attr.override_redirect = 1; mask |= CWOverrideRedirect;}
1703 }
1704 if (fl_background_pixel >= 0) {
1705 attr.background_pixel = fl_background_pixel;
1706 fl_background_pixel = -1;
1707 mask |= CWBackPixel;
1708 }
1709
1710 Fl_X* xp =
1711 set_xid(win, XCreateWindow(fl_display,
1712 root,
1713 X, Y, W, H,
1714 0, // borderwidth
1715 visual->depth,
1716 InputOutput,
1717 visual->visual,
1718 mask, &attr));
1719 int showit = 1;
1720
1721 if (!win->parent() && !attr.override_redirect) {
1722 // Communicate all kinds 'o junk to the X Window Manager:
1723
1724 win->label(win->label(), win->iconlabel());
1725
1726 XChangeProperty(fl_display, xp->xid, WM_PROTOCOLS,
1727 XA_ATOM, 32, 0, (uchar*)&WM_DELETE_WINDOW, 1);
1728
1729 // send size limits and border:
1730 xp->sendxjunk();
1731
1732 // set the class property, which controls the icon used:
1733 if (win->xclass()) {
1734 char buffer[1024];
1735 char *p; const char *q;
1736 // truncate on any punctuation, because they break XResource lookup:
1737 for (p = buffer, q = win->xclass(); isalnum(*q)||(*q&128);) *p++ = *q++;
1738 *p++ = 0;
1739 // create the capitalized version:
1740 q = buffer;
1741 *p = toupper(*q++); if (*p++ == 'X') *p++ = toupper(*q++);
1742 while ((*p++ = *q++));
1743 XChangeProperty(fl_display, xp->xid, XA_WM_CLASS, XA_STRING, 8, 0,
1744 (unsigned char *)buffer, p-buffer-1);
1745 }
1746
1747 if (win->non_modal() && xp->next && !fl_disable_transient_for) {
1748 // find some other window to be "transient for":
1749 Fl_Window* wp = xp->next->w;
1750 while (wp->parent()) wp = wp->window();
1751 XSetTransientForHint(fl_display, xp->xid, fl_xid(wp));
1752 if (!wp->visible()) showit = 0; // guess that wm will not show it
1753 }
1754
1755 // Make sure that borderless windows do not show in the task bar
1756 if (!win->border()) {
1757 Atom net_wm_state = XInternAtom (fl_display, "_NET_WM_STATE", 0);
1758 Atom net_wm_state_skip_taskbar = XInternAtom (fl_display, "_NET_WM_STATE_SKIP_TASKBAR", 0);
1759 XChangeProperty (fl_display, xp->xid, net_wm_state, XA_ATOM, 32,
1760 PropModeAppend, (unsigned char*) &net_wm_state_skip_taskbar, 1);
1761 }
1762
1763 // Make it receptive to DnD:
1764 long version = 4;
1765 XChangeProperty(fl_display, xp->xid, fl_XdndAware,
1766 XA_ATOM, sizeof(int)*8, 0, (unsigned char*)&version, 1);
1767
1768 XWMHints *hints = XAllocWMHints();
1769 hints->input = True;
1770 hints->flags = InputHint;
1771 if (fl_show_iconic) {
1772 hints->flags |= StateHint;
1773 hints->initial_state = IconicState;
1774 fl_show_iconic = 0;
1775 showit = 0;
1776 }
1777 if (win->icon()) {
1778 hints->icon_pixmap = (Pixmap)win->icon();
1779 hints->flags |= IconPixmapHint;
1780 }
1781 XSetWMHints(fl_display, xp->xid, hints);
1782 XFree(hints);
1783 }
1784
1785 // set the window type for menu and tooltip windows to avoid animations (compiz)
1786 if (win->menu_window() || win->tooltip_window()) {
1787 Atom net_wm_type = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE", False);
1788 Atom net_wm_type_kind = XInternAtom(fl_display, "_NET_WM_WINDOW_TYPE_MENU", False);
1789 XChangeProperty(fl_display, xp->xid, net_wm_type, XA_ATOM, 32, PropModeReplace, (unsigned char*)&net_wm_type_kind, 1);
1790 }
1791
1792 XMapWindow(fl_display, xp->xid);
1793 if (showit) {
1794 win->set_visible();
1795 int old_event = Fl::e_number;
1796 win->handle(Fl::e_number = FL_SHOW); // get child windows to appear
1797 Fl::e_number = old_event;
1798 win->redraw();
1799 }
1800}
1801
1802////////////////////////////////////////////////////////////////
1803// Send X window stuff that can be changed over time:
1804
1805void Fl_X::sendxjunk() {
1806 if (w->parent() || w->override()) return; // it's not a window manager window!
1807
1808 if (!w->size_range_set) { // default size_range based on resizable():
1809 if (w->resizable()) {
1810 Fl_Widget *o = w->resizable();
1811 int minw = o->w(); if (minw > 100) minw = 100;
1812 int minh = o->h(); if (minh > 100) minh = 100;
1813 w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
1814 } else {
1815 w->size_range(w->w(), w->h(), w->w(), w->h());
1816 }
1817 return; // because this recursively called here
1818 }
1819
1820 XSizeHints *hints = XAllocSizeHints();
1821 // memset(&hints, 0, sizeof(hints)); jreiser suggestion to fix purify?
1822 hints->min_width = w->minw;
1823 hints->min_height = w->minh;
1824 hints->max_width = w->maxw;
1825 hints->max_height = w->maxh;
1826 hints->width_inc = w->dw;
1827 hints->height_inc = w->dh;
1828 hints->win_gravity = StaticGravity;
1829
1830 // see the file /usr/include/X11/Xm/MwmUtil.h:
1831 // fill all fields to avoid bugs in kwm and perhaps other window managers:
1832 // 0, MWM_FUNC_ALL, MWM_DECOR_ALL
1833 long prop[5] = {0, 1, 1, 0, 0};
1834
1835 if (hints->min_width != hints->max_width ||
1836 hints->min_height != hints->max_height) { // resizable
1837 hints->flags = PMinSize|PWinGravity;
1838 if (hints->max_width >= hints->min_width ||
1839 hints->max_height >= hints->min_height) {
1840 hints->flags = PMinSize|PMaxSize|PWinGravity;
1841 // unfortunately we can't set just one maximum size. Guess a
1842 // value for the other one. Some window managers will make the
1843 // window fit on screen when maximized, others will put it off screen:
1844 if (hints->max_width < hints->min_width) hints->max_width = Fl::w();
1845 if (hints->max_height < hints->min_height) hints->max_height = Fl::h();
1846 }
1847 if (hints->width_inc && hints->height_inc) hints->flags |= PResizeInc;
1848 if (w->aspect) {
1849 // stupid X! It could insist that the corner go on the
1850 // straight line between min and max...
1851 hints->min_aspect.x = hints->max_aspect.x = hints->min_width;
1852 hints->min_aspect.y = hints->max_aspect.y = hints->min_height;
1853 hints->flags |= PAspect;
1854 }
1855 } else { // not resizable:
1856 hints->flags = PMinSize|PMaxSize|PWinGravity;
1857 prop[0] = 1; // MWM_HINTS_FUNCTIONS
1858 prop[1] = 1|2|16; // MWM_FUNC_ALL | MWM_FUNC_RESIZE | MWM_FUNC_MAXIMIZE
1859 }
1860
1861 if (w->flags() & Fl_Widget::FORCE_POSITION) {
1862 hints->flags |= USPosition;
1863 hints->x = w->x();
1864 hints->y = w->y();
1865 }
1866
1867 if (!w->border()) {
1868 prop[0] |= 2; // MWM_HINTS_DECORATIONS
1869 prop[2] = 0; // no decorations
1870 }
1871
1872 XSetWMNormalHints(fl_display, xid, hints);
1873 XChangeProperty(fl_display, xid,
1874 fl_MOTIF_WM_HINTS, fl_MOTIF_WM_HINTS,
1875 32, 0, (unsigned char *)prop, 5);
1876 XFree(hints);
1877}
1878
1879void Fl_Window::size_range_() {
1880 size_range_set = 1;
1881 if (shown()) i->sendxjunk();
1882}
1883
1884////////////////////////////////////////////////////////////////
1885
1886// returns pointer to the filename, or null if name ends with '/'
1887const char *fl_filename_name(const char *name) {
1888 const char *p,*q;
1889 if (!name) return (0);
1890 for (p=q=name; *p;) if (*p++ == '/') q = p;
1891 return q;
1892}
1893
1894void Fl_Window::label(const char *name,const char *iname) {
1895 Fl_Widget::label(name);
1896 iconlabel_ = iname;
1897 if (shown() && !parent()) {
1898 if (!name) name = "";
1899 int namelen = strlen(name);
1900 if (!iname) iname = fl_filename_name(name);
1901 int inamelen = strlen(iname);
1902 XChangeProperty(fl_display, i->xid, fl_NET_WM_NAME, fl_XaUtf8String, 8, 0, (uchar*)name, namelen); // utf8
1903 XChangeProperty(fl_display, i->xid, XA_WM_NAME, XA_STRING, 8, 0, (uchar*)name, namelen); // non-utf8
1904 XChangeProperty(fl_display, i->xid, fl_NET_WM_ICON_NAME, fl_XaUtf8String, 8, 0, (uchar*)iname, inamelen); // utf8
1905 XChangeProperty(fl_display, i->xid, XA_WM_ICON_NAME, XA_STRING, 8, 0, (uchar*)iname, inamelen); // non-utf8
1906 }
1907}
1908
1909////////////////////////////////////////////////////////////////
1910// Implement the virtual functions for the base Fl_Window class:
1911
1912// If the box is a filled rectangle, we can make the redisplay *look*
1913// faster by using X's background pixel erasing. We can make it
1914// actually *be* faster by drawing the frame only, this is done by
1915// setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
1916//
1917// On XFree86 (and prehaps all X's) this has a problem if the window
1918// is resized while a save-behind window is atop it. The previous
1919// contents are restored to the area, but this assumes the area
1920// is cleared to background color. So this is disabled in this version.
1921// Fl_Window *fl_boxcheat;
1922static inline int can_boxcheat(uchar b) {return (b==1 || ((b&2) && b<=15));}
1923
1924void Fl_Window::show() {
1925 image(Fl::scheme_bg_);
1926 if (Fl::scheme_bg_) {
1927 labeltype(FL_NORMAL_LABEL);
1928 align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
1929 } else {
1930 labeltype(FL_NO_LABEL);
1931 }
1932 Fl_Tooltip::exit(this);
1933 if (!shown()) {
1934 fl_open_display();
1935 // Don't set background pixel for double-buffered windows...
1936 if (type() == FL_WINDOW && can_boxcheat(box())) {
1937 fl_background_pixel = int(fl_xpixel(color()));
1938 }
1939 Fl_X::make_xid(this);
1940 } else {
1941 XMapRaised(fl_display, i->xid);
1942 }
1943#ifdef USE_PRINT_BUTTON
1944void preparePrintFront(void);
1945preparePrintFront();
1946#endif
1947}
1948
1949Window fl_window;
1950Fl_Window *Fl_Window::current_;
1951GC fl_gc;
1952
1953// make X drawing go into this window (called by subclass flush() impl.)
1954void Fl_Window::make_current() {
1955 static GC gc; // the GC used by all X windows
1956 if (!gc) gc = XCreateGC(fl_display, i->xid, 0, 0);
1957 fl_window = i->xid;
1958 fl_gc = gc;
1959 current_ = this;
1960 fl_clip_region(0);
1961
1962#ifdef FLTK_USE_CAIRO
1963 // update the cairo_t context
1964 if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this);
1965#endif
1966}
1967
1968Window fl_xid_(const Fl_Window *w) {
1969 Fl_X *temp = Fl_X::i(w);
1970 return temp ? temp->xid : 0;
1971}
1972
1973static void decorated_win_size(Fl_Window *win, int &w, int &h)
1974{
1975 w = win->w();
1976 h = win->h();
1977 if (!win->shown() || win->parent() || !win->border() || !win->visible()) return;
1978 Window root, parent, *children;
1979 unsigned n = 0;
1980 Status status = XQueryTree(fl_display, Fl_X::i(win)->xid, &root, &parent, &children, &n);
1981 if (status != 0 && n) XFree(children);
1982 // when compiz is used, root and parent are the same window
1983 // and I don't know where to find the window decoration
1984 if (status == 0 || root == parent) return;
1985 XWindowAttributes attributes;
1986 XGetWindowAttributes(fl_display, parent, &attributes);
1987 w = attributes.width;
1988 h = attributes.height;
1989}
1990
1991int Fl_Window::decorated_h()
1992{
1993 int w, h;
1994 decorated_win_size(this, w, h);
1995 return h;
1996}
1997
1998int Fl_Window::decorated_w()
1999{
2000 int w, h;
2001 decorated_win_size(this, w, h);
2002 return w;
2003}
2004
2005void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
2006{
2007 if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
2008 this->print_widget(win, x_offset, y_offset);
2009 return;
2010 }
2011 Fl_Display_Device::display_device()->set_current();
2012 win->show();
2013 Fl::check();
2014 win->make_current();
2015 Window root, parent, *children, child_win, from;
2016 unsigned n = 0;
2017 int bx, bt, do_it;
2018 from = fl_window;
2019 do_it = (XQueryTree(fl_display, fl_window, &root, &parent, &children, &n) != 0 &&
2020 XTranslateCoordinates(fl_display, fl_window, parent, 0, 0, &bx, &bt, &child_win) == True);
2021 if (n) XFree(children);
2022 // hack to bypass STR #2648: when compiz is used, root and parent are the same window
2023 // and I don't know where to find the window decoration
2024 if (do_it && root == parent) do_it = 0;
2025 if (!do_it) {
2026 this->set_current();
2027 this->print_widget(win, x_offset, y_offset);
2028 return;
2029 }
2030 fl_window = parent;
2031 uchar *top_image = 0, *left_image = 0, *right_image = 0, *bottom_image = 0;
2032 top_image = fl_read_image(NULL, 0, 0, - (win->w() + 2 * bx), bt);
2033 if (bx) {
2034 left_image = fl_read_image(NULL, 0, bt, -bx, win->h() + bx);
2035 right_image = fl_read_image(NULL, win->w() + bx, bt, -bx, win->h() + bx);
2036 bottom_image = fl_read_image(NULL, 0, bt + win->h(), -(win->w() + 2*bx), bx);
2037 }
2038 fl_window = from;
2039 this->set_current();
2040 if (top_image) {
2041 fl_draw_image(top_image, x_offset, y_offset, win->w() + 2 * bx, bt, 3);
2042 delete[] top_image;
2043 }
2044 if (bx) {
2045 if (left_image) fl_draw_image(left_image, x_offset, y_offset + bt, bx, win->h() + bx, 3);
2046 if (right_image) fl_draw_image(right_image, x_offset + win->w() + bx, y_offset + bt, bx, win->h() + bx, 3);
2047 if (bottom_image) fl_draw_image(bottom_image, x_offset, y_offset + bt + win->h(), win->w() + 2*bx, bx, 3);
2048 if (left_image) delete[] left_image;
2049 if (right_image) delete[] right_image;
2050 if (bottom_image) delete[] bottom_image;
2051 }
2052 this->print_widget( win, x_offset + bx, y_offset + bt );
2053}
2054
2055#ifdef USE_PRINT_BUTTON
2056// to test the Fl_Printer class creating a "Print front window" button in a separate window
2057// contains also preparePrintFront call above
2058#include <FL/Fl_Printer.H>
2059#include <FL/Fl_Button.H>
2060void printFront(Fl_Widget *o, void *data)
2061{
2062 Fl_Printer printer;
2063 o->window()->hide();
2064 Fl_Window *win = Fl::first_window();
2065 if(!win) return;
2066 int w, h;
2067 if( printer.start_job(1) ) { o->window()->show(); return; }
2068 if( printer.start_page() ) { o->window()->show(); return; }
2069 printer.printable_rect(&w,&h);
2070 // scale the printer device so that the window fits on the page
2071 float scale = 1;
2072 int ww = win->decorated_w();
2073 int wh = win->decorated_h();
2074 if (ww > w || wh > h) {
2075 scale = (float)w/ww;
2076 if ((float)h/wh < scale) scale = (float)h/wh;
2077 printer.scale(scale, scale);
2078 }
2079
2080// #define ROTATE 20.0
2081#ifdef ROTATE
2082 printer.scale(scale * 0.8, scale * 0.8);
2083 printer.printable_rect(&w, &h);
2084 printer.origin(w/2, h/2 );
2085 printer.rotate(ROTATE);
2086 printer.print_widget( win, - win->w()/2, - win->h()/2 );
2087 //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
2088#else
2089 printer.print_window(win);
2090#endif
2091
2092 printer.end_page();
2093 printer.end_job();
2094 o->window()->show();
2095}
2096
2097void preparePrintFront(void)
2098{
2099 static int first=1;
2100 if(!first) return;
2101 first=0;
2102 static Fl_Window w(0,0,150,30);
2103 static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
2104 b.callback(printFront);
2105 w.end();
2106 w.show();
2107}
2108#endif // USE_PRINT_BUTTON
2109
2110#endif
2111
2112//
2113// End of "$Id: Fl_x.cxx 8764 2011-05-30 16:47:48Z manolo $".
2114//