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