You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2185 lines
67KB

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