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