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