DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 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 | |
| 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> |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 55 | # 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 |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 64 | |
| 65 | static Fl_Xlib_Graphics_Driver fl_xlib_driver; |
| 66 | static Fl_Display_Device fl_xlib_display(&fl_xlib_driver); |
| 67 | FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_xlib_driver; // the current target device of graphics operations |
| 68 | Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_xlib_display; // the current target surface of graphics operations |
| 69 | Fl_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> |
| 77 | static 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 | |
| 87 | static fd_set fdsets[3]; |
| 88 | static int maxfd; |
| 89 | # define POLLIN 1 |
| 90 | # define POLLOUT 4 |
| 91 | # define POLLERR 8 |
| 92 | |
| 93 | # endif /* USE_POLL */ |
| 94 | |
| 95 | static int nfds = 0; |
| 96 | static int fd_array_size = 0; |
| 97 | struct FD { |
| 98 | # if !USE_POLL |
| 99 | int fd; |
| 100 | short events; |
| 101 | # endif |
| 102 | void (*cb)(int, void*); |
| 103 | void* arg; |
| 104 | }; |
| 105 | |
| 106 | static FD *fd = 0; |
| 107 | |
| 108 | void 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 | |
| 146 | void Fl::add_fd(int n, void (*cb)(int, void*), void* v) { |
| 147 | Fl::add_fd(n, POLLIN, cb, v); |
| 148 | } |
| 149 | |
| 150 | void 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 | |
| 187 | void Fl::remove_fd(int n) { |
| 188 | remove_fd(n, -1); |
| 189 | } |
| 190 | |
| 191 | #if CONSOLIDATE_MOTION |
| 192 | static Fl_Window* send_motion; |
| 193 | extern Fl_Window* fl_xmousewin; |
| 194 | #endif |
| 195 | static bool in_a_window; // true if in any of our windows, even destroyed ones |
| 196 | static 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: |
| 214 | static void nothing() {} |
| 215 | void (*fl_lock_function)() = nothing; |
| 216 | void (*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. |
| 221 | int 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: |
| 275 | int 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 |
| 293 | static 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 | |
| 305 | Display *fl_display; |
| 306 | Window fl_message_window = 0; |
| 307 | int fl_screen; |
| 308 | XVisualInfo *fl_visual; |
| 309 | Colormap fl_colormap; |
| 310 | XIM fl_xim_im = 0; |
| 311 | XIC fl_xim_ic = 0; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 312 | Window fl_xim_win = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 313 | char fl_is_over_the_spot = 0; |
| 314 | static XRectangle status_area; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 315 | static bool have_xfixes = false; |
| 316 | static int xfixes_event_base = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 317 | |
| 318 | static Atom WM_DELETE_WINDOW; |
| 319 | static Atom WM_PROTOCOLS; |
| 320 | static Atom fl_MOTIF_WM_HINTS; |
| 321 | static Atom TARGETS; |
| 322 | static Atom CLIPBOARD; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 323 | static Atom TIMESTAMP; |
| 324 | static Atom PRIMARY_TIMESTAMP; |
| 325 | static Atom CLIPBOARD_TIMESTAMP; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 326 | Atom fl_XdndAware; |
| 327 | Atom fl_XdndSelection; |
| 328 | Atom fl_XdndEnter; |
| 329 | Atom fl_XdndTypeList; |
| 330 | Atom fl_XdndPosition; |
| 331 | Atom fl_XdndLeave; |
| 332 | Atom fl_XdndDrop; |
| 333 | Atom fl_XdndStatus; |
| 334 | Atom fl_XdndActionCopy; |
| 335 | Atom fl_XdndFinished; |
| 336 | //Atom fl_XdndProxy; |
| 337 | Atom fl_XdndURIList; |
| 338 | Atom fl_Xatextplainutf; |
| 339 | Atom fl_Xatextplain; |
| 340 | static Atom fl_XaText; |
| 341 | Atom fl_XaCompoundText; |
| 342 | Atom fl_XaUtf8String; |
| 343 | Atom fl_XaTextUriList; |
| 344 | Atom fl_NET_WM_NAME; // utf8 aware window label |
| 345 | Atom fl_NET_WM_ICON_NAME; // utf8 aware window icon name |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 346 | Atom fl_NET_SUPPORTING_WM_CHECK; |
| 347 | Atom fl_NET_WM_STATE; |
| 348 | Atom fl_NET_WM_STATE_FULLSCREEN; |
Pierre Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 349 | Atom fl_NET_WM_ICON; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 350 | |
| 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 | */ |
| 357 | static int atom_bits = 32; |
| 358 | |
| 359 | static void fd_callback(int,void *) { |
| 360 | do_queued_events(); |
| 361 | } |
| 362 | |
| 363 | extern "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 | |
| 379 | extern char *fl_get_font_xfld(int fnum, int size); |
| 380 | |
| 381 | void 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 | |
| 471 | static XRectangle spot; |
| 472 | static int spotf = -1; |
| 473 | static int spots = -1; |
| 474 | |
| 475 | void fl_reset_spot(void) |
| 476 | { |
| 477 | spot.x = -1; |
| 478 | spot.y = -1; |
| 479 | //if (fl_xim_ic) XUnsetICFocus(fl_xim_ic); |
| 480 | } |
| 481 | |
| 482 | void 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 | |
| 544 | void 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 | |
| 558 | void 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 603 | void fl_xim_deactivate(void); |
| 604 | |
| 605 | void 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 | |
| 627 | void 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 | |
| 638 | extern Fl_Window *fl_xfocus; |
| 639 | |
| 640 | void 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 662 | void 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 | |
| 677 | void 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); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 685 | TIMESTAMP = XInternAtom(d, "TIMESTAMP", 0); |
| 686 | PRIMARY_TIMESTAMP = XInternAtom(d, "PRIMARY_TIMESTAMP", 0); |
| 687 | CLIPBOARD_TIMESTAMP = XInternAtom(d, "CLIPBOARD_TIMESTAMP", 0); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 688 | 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); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 709 | 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 Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 712 | fl_NET_WM_ICON = XInternAtom(d, "_NET_WM_ICON", 0); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 713 | |
| 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 |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 734 | |
| 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 |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 742 | } |
| 743 | |
| 744 | void fl_close_display() { |
| 745 | Fl::remove_fd(ConnectionNumber(fl_display)); |
| 746 | XCloseDisplay(fl_display); |
| 747 | } |
| 748 | |
| 749 | static int fl_workarea_xywh[4] = { -1, -1, -1, -1 }; |
| 750 | |
| 751 | static 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 | |
| 781 | int Fl::x() { |
| 782 | if (fl_workarea_xywh[0] < 0) fl_init_workarea(); |
| 783 | return fl_workarea_xywh[0]; |
| 784 | } |
| 785 | |
| 786 | int Fl::y() { |
| 787 | if (fl_workarea_xywh[0] < 0) fl_init_workarea(); |
| 788 | return fl_workarea_xywh[1]; |
| 789 | } |
| 790 | |
| 791 | int Fl::w() { |
| 792 | if (fl_workarea_xywh[0] < 0) fl_init_workarea(); |
| 793 | return fl_workarea_xywh[2]; |
| 794 | } |
| 795 | |
| 796 | int Fl::h() { |
| 797 | if (fl_workarea_xywh[0] < 0) fl_init_workarea(); |
| 798 | return fl_workarea_xywh[3]; |
| 799 | } |
| 800 | |
| 801 | void 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 | |
| 813 | Fl_Widget *fl_selection_requestor; |
| 814 | char *fl_selection_buffer[2]; |
| 815 | int fl_selection_length[2]; |
| 816 | int fl_selection_buffer_length[2]; |
| 817 | char fl_i_own_selection[2] = {0,0}; |
| 818 | |
| 819 | // Call this when a "paste" operation happens: |
| 820 | void 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 | |
| 838 | Window fl_dnd_source_window; |
| 839 | Atom *fl_dnd_source_types; // null-terminated list of data types being supplied |
| 840 | Atom fl_dnd_type; |
| 841 | Atom fl_dnd_source_action; |
| 842 | Atom fl_dnd_action; |
| 843 | |
| 844 | void 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 864 | |
| 865 | /* |
| 866 | Get window property value (32 bit format) |
| 867 | Returns zero on success, -1 on error |
| 868 | */ |
| 869 | static 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 889 | //////////////////////////////////////////////////////////////// |
| 890 | // Code for copying to clipboard and DnD out of the program: |
| 891 | |
| 892 | void 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 | //////////////////////////////////////////////////////////////// |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 908 | // Code for tracking clipboard changes: |
| 909 | |
| 910 | static Time primary_timestamp = -1; |
| 911 | static Time clipboard_timestamp = -1; |
| 912 | |
| 913 | extern bool fl_clipboard_notify_empty(void); |
| 914 | extern void fl_trigger_clipboard_notify(int source); |
| 915 | |
| 916 | static 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 | |
| 944 | static 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 | |
| 955 | static 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 | |
| 979 | void 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 | //////////////////////////////////////////////////////////////// |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 996 | |
| 997 | const XEvent* fl_xevent; // the current x event |
| 998 | ulong fl_event_time; // the last timestamp from an x event |
| 999 | |
| 1000 | char fl_key_vector[32]; // used by Fl::get_key() |
| 1001 | |
| 1002 | // Record event mouse position and state from an XEvent: |
| 1003 | |
| 1004 | static int px, py; |
| 1005 | static ulong ptime; |
| 1006 | |
| 1007 | static 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: |
| 1028 | static 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 | |
| 1040 | static Fl_Window* resize_bug_fix; |
| 1041 | |
| 1042 | //////////////////////////////////////////////////////////////// |
| 1043 | |
| 1044 | static char unknown[] = "<unknown>"; |
| 1045 | const int unknown_len = 10; |
| 1046 | |
| 1047 | extern "C" { |
| 1048 | |
| 1049 | static int xerror = 0; |
| 1050 | |
| 1051 | static int ignoreXEvents(Display *display, XErrorEvent *event) { |
| 1052 | xerror = 1; |
| 1053 | return 0; |
| 1054 | } |
| 1055 | |
| 1056 | static XErrorHandler catchXExceptions() { |
| 1057 | xerror = 0; |
| 1058 | return ignoreXEvents; |
| 1059 | } |
| 1060 | |
| 1061 | static int wasXExceptionRaised() { |
| 1062 | return xerror; |
| 1063 | } |
| 1064 | |
| 1065 | } |
| 1066 | |
| 1067 | |
| 1068 | int fl_handle(const XEvent& thisevent) |
| 1069 | { |
| 1070 | XEvent xevent = thisevent; |
| 1071 | fl_xevent = &thisevent; |
| 1072 | Window xid = xevent.xany.window; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1073 | |
| 1074 | if (fl_xim_ic && xevent.type == DestroyNotify && |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1075 | xid != fl_xim_win && !fl_find(xid)) |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1076 | { |
| 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1090 | if (fl_xim_ic) { |
| 1091 | if (XFilterEvent((XEvent *)&xevent, 0)) |
| 1092 | return 1; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1093 | } |
| 1094 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1095 | 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: { |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1106 | 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 |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1121 | |
| 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1134 | 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 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1170 | |
| 1171 | if (!fl_selection_requestor) return 0; |
| 1172 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1173 | 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; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1193 | poll_clipboard_owner(); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1194 | 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; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1406 | // 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(); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1409 | 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*/); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1449 | // 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); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1458 | } |
| 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(); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1629 | Fl::e_dx = Fl::e_dy = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1630 | 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; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1636 | } 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; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1642 | } 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1652 | 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1677 | 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1777 | #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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1796 | return Fl::handle(event, window); |
| 1797 | } |
| 1798 | |
| 1799 | //////////////////////////////////////////////////////////////// |
| 1800 | |
| 1801 | void 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1835 | #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 | |
| 1839 | static 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 | |
| 1855 | int 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 */ |
| 1876 | void 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 | |
| 1889 | void 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1904 | // A subclass of Fl_Window may call this to associate an X window it |
| 1905 | // creates with the Fl_Window: |
| 1906 | |
| 1907 | void fl_fix_focus(); // in Fl.cxx |
| 1908 | |
| 1909 | Fl_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 | |
| 1928 | char fl_show_iconic; // hack for iconize() |
| 1929 | int fl_background_pixel = -1; // hack to speed up bg box drawing |
| 1930 | int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR |
| 1931 | |
| 1932 | static const int childEventMask = ExposureMask; |
| 1933 | |
| 1934 | static const int XEventMask = |
| 1935 | ExposureMask|StructureNotifyMask |
| 1936 | |KeyPressMask|KeyReleaseMask|KeymapStateMask|FocusChangeMask |
| 1937 | |ButtonPressMask|ButtonReleaseMask |
| 1938 | |EnterWindowMask|LeaveWindowMask |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1939 | |PropertyChangeMask |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1940 | |PointerMotionMask; |
| 1941 | |
| 1942 | void 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 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2011 | // 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2021 | 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2080 | // 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2086 | // 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 Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 2100 | if (win->icon_->legacy_icon) { |
| 2101 | hints->icon_pixmap = (Pixmap)win->icon_->legacy_icon; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2102 | hints->flags |= IconPixmapHint; |
| 2103 | } |
| 2104 | XSetWMHints(fl_display, xp->xid, hints); |
| 2105 | XFree(hints); |
Pierre Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 2106 | |
| 2107 | xp->set_icons(); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2108 | } |
| 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2117 | #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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2127 | XMapWindow(fl_display, xp->xid); |
| 2128 | if (showit) { |
| 2129 | win->set_visible(); |
| 2130 | int old_event = Fl::e_number; |
| 2131 | win->handle(Fl::e_number = FL_SHOW); // get child windows to appear |
| 2132 | Fl::e_number = old_event; |
| 2133 | win->redraw(); |
| 2134 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2135 | |
| 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2141 | } |
| 2142 | |
| 2143 | //////////////////////////////////////////////////////////////// |
| 2144 | // Send X window stuff that can be changed over time: |
| 2145 | |
| 2146 | void 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 | |
| 2220 | void Fl_Window::size_range_() { |
| 2221 | size_range_set = 1; |
| 2222 | if (shown()) i->sendxjunk(); |
| 2223 | } |
| 2224 | |
| 2225 | //////////////////////////////////////////////////////////////// |
| 2226 | |
Pierre Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 2227 | static unsigned long *default_net_wm_icons = 0L; |
| 2228 | static size_t default_net_wm_icons_size = 0; |
| 2229 | |
| 2230 | void 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 | |
| 2278 | void 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 | |
| 2290 | void 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 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2314 | int 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 | |
| 2347 | int 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 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2402 | // returns pointer to the filename, or null if name ends with '/' |
| 2403 | const 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 | |
| 2410 | void 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; |
| 2438 | static inline int can_boxcheat(uchar b) {return (b==1 || ((b&2) && b<=15));} |
| 2439 | |
| 2440 | void 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 |
| 2460 | void preparePrintFront(void); |
| 2461 | preparePrintFront(); |
| 2462 | #endif |
| 2463 | } |
| 2464 | |
| 2465 | Window fl_window; |
| 2466 | Fl_Window *Fl_Window::current_; |
| 2467 | GC fl_gc; |
| 2468 | |
| 2469 | // make X drawing go into this window (called by subclass flush() impl.) |
| 2470 | void 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 | |
| 2484 | Window fl_xid_(const Fl_Window *w) { |
| 2485 | Fl_X *temp = Fl_X::i(w); |
| 2486 | return temp ? temp->xid : 0; |
| 2487 | } |
| 2488 | |
| 2489 | static 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 | |
| 2507 | int Fl_Window::decorated_h() |
| 2508 | { |
| 2509 | int w, h; |
| 2510 | decorated_win_size(this, w, h); |
| 2511 | return h; |
| 2512 | } |
| 2513 | |
| 2514 | int Fl_Window::decorated_w() |
| 2515 | { |
| 2516 | int w, h; |
| 2517 | decorated_win_size(this, w, h); |
| 2518 | return w; |
| 2519 | } |
| 2520 | |
| 2521 | void 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> |
| 2576 | void 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 | |
| 2613 | void 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 | // |