DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1 | // |
| 2 | // "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $" |
| 3 | // |
| 4 | // WIN32-specific code for the Fast Light Tool Kit (FLTK). |
| 5 | // |
| 6 | // Copyright 1998-2010 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 | // This file contains win32-specific code for fltk which is always linked |
| 29 | // in. Search other files for "WIN32" or filenames ending in _win32.cxx |
| 30 | // for other system-specific code. |
| 31 | |
| 32 | // This file must be #include'd in Fl.cxx and not compiled separately. |
| 33 | |
| 34 | #ifndef FL_DOXYGEN |
| 35 | #include <FL/Fl.H> |
| 36 | #include <FL/fl_utf8.h> |
| 37 | #include <FL/Fl_Window.H> |
| 38 | #include <FL/fl_draw.H> |
| 39 | #include <FL/Enumerations.H> |
| 40 | #include <FL/Fl_Tooltip.H> |
| 41 | #include <FL/Fl_Paged_Device.H> |
| 42 | #include "flstring.h" |
| 43 | #include "Fl_Font.H" |
| 44 | #include <stdio.h> |
| 45 | #include <stdlib.h> |
| 46 | #include <sys/types.h> |
| 47 | #include <time.h> |
| 48 | #ifdef __CYGWIN__ |
| 49 | # include <sys/time.h> |
| 50 | # include <unistd.h> |
| 51 | #endif |
| 52 | |
| 53 | #if !defined(NO_TRACK_MOUSE) |
| 54 | # include <commctrl.h> // TrackMouseEvent |
| 55 | // fabien: Ms Visual Studio >= 2003 permit embedded lib reference |
| 56 | // that makes fltk use easier as only fltk libs are now requested |
| 57 | // This idea could be extended to fltk libs themselves, |
| 58 | // implementer should then care about DLL linkage flags ... |
| 59 | # if (_MSC_VER>=1310) |
| 60 | # pragma comment (lib, "comctl32.lib") |
| 61 | # endif |
| 62 | #endif |
| 63 | |
| 64 | #if defined(__GNUC__) |
| 65 | # include <wchar.h> |
| 66 | #endif |
| 67 | |
| 68 | #include <ole2.h> |
| 69 | #include <shellapi.h> |
| 70 | |
| 71 | #include "aimm.h" |
| 72 | |
| 73 | // |
| 74 | // USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()... |
| 75 | // USE_ASYNC_SELECT is OBSOLETED in 1.3 for the following reasons: |
| 76 | /* |
| 77 | This feature was supposed to provide an efficient alternative to the current |
| 78 | polling method, but as it has been discussed (Thanks Albrecht!) : |
| 79 | - the async mode would imply to change the socket to non blocking mode. |
| 80 | This can have unexpected side effects for 3rd party apps, especially |
| 81 | if it is set on-the-fly when socket service is really needed, as it is |
| 82 | done today and on purpose, but still the 3rd party developer wouldn't easily |
| 83 | control the sequencing of socket operations. |
| 84 | - Finer granularity of events furthered by the async select is a plus only |
| 85 | for socket 3rd party impl., it is simply not needed for the 'light' fltk |
| 86 | use we make of wsock, so here it would also be a bad point, because of all |
| 87 | the logic add-ons necessary for using this functionality, without a clear |
| 88 | benefit. |
| 89 | |
| 90 | So async mode select would not add benefits to fltk, worse, it can slowdown |
| 91 | fltk because of this finer granularity and instrumentation code to be added |
| 92 | for async mode proper operation, not mentioning the side effects... |
| 93 | */ |
| 94 | |
| 95 | static Fl_GDI_Graphics_Driver fl_gdi_driver; |
| 96 | static Fl_Display_Device fl_gdi_display(&fl_gdi_driver); |
| 97 | FL_EXPORT Fl_Graphics_Driver *fl_graphics_driver = (Fl_Graphics_Driver*)&fl_gdi_driver; // the current target driver of graphics operations |
| 98 | Fl_Surface_Device* Fl_Surface_Device::_surface = (Fl_Surface_Device*)&fl_gdi_display; // the current target surface of graphics operations |
| 99 | Fl_Display_Device *Fl_Display_Device::_display = &fl_gdi_display; // the platform display |
| 100 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 101 | bool use_simple_keyboard = false; |
| 102 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 103 | // dynamic wsock dll handling api: |
| 104 | #if defined(__CYGWIN__) && !defined(SOCKET) |
| 105 | # define SOCKET int |
| 106 | #endif |
| 107 | |
| 108 | // note: winsock2.h has been #include'd in Fl.cxx |
| 109 | #define WSCK_DLL_NAME "WS2_32.DLL" |
| 110 | |
| 111 | typedef int (WINAPI* fl_wsk_select_f)(int, fd_set*, fd_set*, fd_set*, const struct timeval*); |
| 112 | typedef int (WINAPI* fl_wsk_fd_is_set_f)(SOCKET, fd_set *); |
| 113 | |
| 114 | static HMODULE s_wsock_mod = 0; |
| 115 | static fl_wsk_select_f s_wsock_select = 0; |
| 116 | static fl_wsk_fd_is_set_f fl_wsk_fd_is_set = 0; |
| 117 | |
| 118 | static HMODULE get_wsock_mod() { |
| 119 | if (!s_wsock_mod) { |
| 120 | s_wsock_mod = LoadLibrary(WSCK_DLL_NAME); |
| 121 | if (s_wsock_mod==NULL) |
| 122 | Fl::fatal("FLTK Lib Error: %s file not found! Please check your winsock dll accessibility.\n",WSCK_DLL_NAME); |
| 123 | s_wsock_select = (fl_wsk_select_f) GetProcAddress(s_wsock_mod, "select"); |
| 124 | fl_wsk_fd_is_set = (fl_wsk_fd_is_set_f) GetProcAddress(s_wsock_mod, "__WSAFDIsSet"); |
| 125 | } |
| 126 | return s_wsock_mod; |
| 127 | } |
| 128 | |
| 129 | /* |
| 130 | * Dynamic linking of imm32.dll |
| 131 | * This library is only needed for a hand full (four ATM) functions relating to |
| 132 | * international text rendering and locales. Dynamically loading reduces initial |
| 133 | * size and link dependencies. |
| 134 | */ |
| 135 | static HMODULE s_imm_module = 0; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 136 | typedef BOOL (WINAPI* flTypeImmAssociateContextEx)(HWND, HIMC, DWORD); |
| 137 | static flTypeImmAssociateContextEx flImmAssociateContextEx = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 138 | typedef HIMC (WINAPI* flTypeImmGetContext)(HWND); |
| 139 | static flTypeImmGetContext flImmGetContext = 0; |
| 140 | typedef BOOL (WINAPI* flTypeImmSetCompositionWindow)(HIMC, LPCOMPOSITIONFORM); |
| 141 | static flTypeImmSetCompositionWindow flImmSetCompositionWindow = 0; |
| 142 | typedef BOOL (WINAPI* flTypeImmReleaseContext)(HWND, HIMC); |
| 143 | static flTypeImmReleaseContext flImmReleaseContext = 0; |
| 144 | typedef BOOL (WINAPI* flTypeImmIsIME)(HKL); |
| 145 | static flTypeImmIsIME flImmIsIME = 0; |
| 146 | |
| 147 | static HMODULE get_imm_module() { |
| 148 | if (!s_imm_module) { |
| 149 | s_imm_module = LoadLibrary("IMM32.DLL"); |
| 150 | if (!s_imm_module) |
| 151 | Fl::fatal("FLTK Lib Error: IMM32.DLL file not found!\n\n" |
| 152 | "Please check your input method manager library accessibility."); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 153 | flImmAssociateContextEx = (flTypeImmAssociateContextEx)GetProcAddress(s_imm_module, "ImmAssociateContextEx"); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 154 | flImmGetContext = (flTypeImmGetContext)GetProcAddress(s_imm_module, "ImmGetContext"); |
| 155 | flImmSetCompositionWindow = (flTypeImmSetCompositionWindow)GetProcAddress(s_imm_module, "ImmSetCompositionWindow"); |
| 156 | flImmReleaseContext = (flTypeImmReleaseContext)GetProcAddress(s_imm_module, "ImmReleaseContext"); |
| 157 | flImmIsIME = (flTypeImmIsIME)GetProcAddress(s_imm_module, "ImmIsIME"); |
| 158 | } |
| 159 | return s_imm_module; |
| 160 | } |
| 161 | |
| 162 | // USE_TRACK_MOUSE - define NO_TRACK_MOUSE if you don't have |
| 163 | // TrackMouseEvent()... |
| 164 | // |
| 165 | // Now (Dec. 2008) we can assume that current Cygwin/MinGW versions |
| 166 | // support the TrackMouseEvent() function, but WinCE obviously doesn't |
| 167 | // support it (STR 2095). Therefore, USE_TRACK_MOUSE is enabled by |
| 168 | // default, but you can disable it by defining NO_TRACK_MOUSE. |
| 169 | // |
| 170 | // TrackMouseEvent is only used to support window leave notifications |
| 171 | // under Windows. It can be used to get FL_LEAVE events, when the |
| 172 | // mouse leaves the _main_ application window (FLTK detects subwindow |
| 173 | // leave events by using normal move events). |
| 174 | // |
| 175 | // Implementation note: If the mouse cursor leaves one subwindow and |
| 176 | // enters another window, then Windows sends a WM_MOUSEMOVE message to |
| 177 | // the new window before it sends a WM_MOUSELEAVE message to the old |
| 178 | // (just left) window. We save the current mouse window in a static variable, |
| 179 | // and if we get a WM_MOUSELEAVE event for the current mouse window, this |
| 180 | // means that the top level window has been left (otherwise we would have |
| 181 | // got another WM_MOUSEMOVE message before). |
| 182 | |
| 183 | // #define NO_TRACK_MOUSE |
| 184 | |
| 185 | #if !defined(NO_TRACK_MOUSE) |
| 186 | # define USE_TRACK_MOUSE |
| 187 | #endif // NO_TRACK_MOUSE |
| 188 | |
| 189 | static Fl_Window *track_mouse_win=0; // current TrackMouseEvent() window |
| 190 | |
| 191 | // USE_CAPTURE_MOUSE_WIN - this must be defined for TrackMouseEvent to work |
| 192 | // correctly with subwindows - otherwise a single mouse click and release |
| 193 | // (without a move) would generate phantom leave events. |
| 194 | // This defines, if the current mouse window (maybe a subwindow) or the |
| 195 | // main window should get mouse events after pushing (and holding) a mouse |
| 196 | // button, i.e. when dragging the mouse. This is done by calling SetCapture |
| 197 | // (see below). |
| 198 | |
| 199 | #ifdef USE_TRACK_MOUSE |
| 200 | #define USE_CAPTURE_MOUSE_WIN |
| 201 | #endif // USE_TRACK_MOUSE |
| 202 | |
| 203 | // |
| 204 | // WM_SYNCPAINT is an "undocumented" message, which is finally defined in |
| 205 | // VC++ 6.0. |
| 206 | // |
| 207 | |
| 208 | #ifndef WM_SYNCPAINT |
| 209 | # define WM_SYNCPAINT 0x0088 |
| 210 | #endif |
| 211 | |
| 212 | #ifndef WM_MOUSELEAVE |
| 213 | # define WM_MOUSELEAVE 0x02a3 |
| 214 | #endif |
| 215 | |
| 216 | #ifndef WM_MOUSEWHEEL |
| 217 | # define WM_MOUSEWHEEL 0x020a |
| 218 | #endif |
| 219 | |
| 220 | #ifndef WHEEL_DELTA |
| 221 | # define WHEEL_DELTA 120 // according to MSDN. |
| 222 | #endif |
| 223 | |
| 224 | |
| 225 | // |
| 226 | // WM_FLSELECT is the user-defined message that we get when one of |
| 227 | // the sockets has pending data, etc. |
| 228 | // |
| 229 | |
| 230 | #define WM_FLSELECT (WM_APP+1) // WM_APP is used for hide-window |
| 231 | |
| 232 | |
| 233 | //////////////////////////////////////////////////////////////// |
| 234 | // interface to poll/select call: |
| 235 | |
| 236 | // fd's are only implemented for sockets. Microsoft Windows does not |
| 237 | // have a unified IO system, so it doesn't support select() on files, |
| 238 | // devices, or pipes... |
| 239 | // |
| 240 | // Microsoft provides the Berkeley select() call and an asynchronous |
| 241 | // select function that sends a WIN32 message when the select condition |
| 242 | // exists. However, we don't try to use the asynchronous WSAAsyncSelect() |
| 243 | // any more for good reasons (see above). |
| 244 | // |
| 245 | // A.S. Dec 2009: We got reports that current winsock2.h files define |
| 246 | // POLLIN, POLLOUT, and POLLERR with conflicting values WRT what we |
| 247 | // used before (STR #2301). Therefore we must not use these values |
| 248 | // for our internal purposes, but use FL_READ, FL_WRITE, and |
| 249 | // FL_EXCEPT, as defined for use in Fl::add_fd(). |
| 250 | // |
| 251 | static int maxfd = 0; |
| 252 | static fd_set fdsets[3]; |
| 253 | |
| 254 | extern IDropTarget *flIDropTarget; |
| 255 | |
| 256 | static int nfds = 0; |
| 257 | static int fd_array_size = 0; |
| 258 | static struct FD { |
| 259 | int fd; |
| 260 | short events; |
| 261 | void (*cb)(int, void*); |
| 262 | void* arg; |
| 263 | } *fd = 0; |
| 264 | |
| 265 | extern unsigned int fl_codepage; |
| 266 | |
| 267 | void fl_reset_spot() |
| 268 | { |
| 269 | } |
| 270 | |
| 271 | void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) |
| 272 | { |
| 273 | if (!win) return; |
| 274 | Fl_Window* tw = win; |
| 275 | while (tw->parent()) tw = tw->window(); // find top level window |
| 276 | |
| 277 | get_imm_module(); |
| 278 | HIMC himc = flImmGetContext(fl_xid(tw)); |
| 279 | |
| 280 | if (himc) { |
| 281 | COMPOSITIONFORM cfs; |
| 282 | cfs.dwStyle = CFS_POINT; |
| 283 | cfs.ptCurrentPos.x = X; |
| 284 | cfs.ptCurrentPos.y = Y - tw->labelsize(); |
| 285 | MapWindowPoints(fl_xid(win), fl_xid(tw), &cfs.ptCurrentPos, 1); |
| 286 | flImmSetCompositionWindow(himc, &cfs); |
| 287 | flImmReleaseContext(fl_xid(tw), himc); |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | void fl_set_status(int x, int y, int w, int h) |
| 292 | { |
| 293 | } |
| 294 | |
| 295 | void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) { |
| 296 | remove_fd(n,events); |
| 297 | int i = nfds++; |
| 298 | if (i >= fd_array_size) { |
| 299 | fd_array_size = 2*fd_array_size+1; |
| 300 | fd = (FD*)realloc(fd, fd_array_size*sizeof(FD)); |
| 301 | } |
| 302 | fd[i].fd = n; |
| 303 | fd[i].events = (short)events; |
| 304 | fd[i].cb = cb; |
| 305 | fd[i].arg = v; |
| 306 | |
| 307 | if (events & FL_READ) FD_SET((unsigned)n, &fdsets[0]); |
| 308 | if (events & FL_WRITE) FD_SET((unsigned)n, &fdsets[1]); |
| 309 | if (events & FL_EXCEPT) FD_SET((unsigned)n, &fdsets[2]); |
| 310 | if (n > maxfd) maxfd = n; |
| 311 | } |
| 312 | |
| 313 | void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) { |
| 314 | Fl::add_fd(fd, FL_READ, cb, v); |
| 315 | } |
| 316 | |
| 317 | void Fl::remove_fd(int n, int events) { |
| 318 | int i,j; |
| 319 | for (i=j=0; i<nfds; i++) { |
| 320 | if (fd[i].fd == n) { |
| 321 | short e = fd[i].events & ~events; |
| 322 | if (!e) continue; // if no events left, delete this fd |
| 323 | fd[i].events = e; |
| 324 | } |
| 325 | // move it down in the array if necessary: |
| 326 | if (j<i) { |
| 327 | fd[j]=fd[i]; |
| 328 | } |
| 329 | j++; |
| 330 | } |
| 331 | nfds = j; |
| 332 | |
| 333 | if (events & FL_READ) FD_CLR(unsigned(n), &fdsets[0]); |
| 334 | if (events & FL_WRITE) FD_CLR(unsigned(n), &fdsets[1]); |
| 335 | if (events & FL_EXCEPT) FD_CLR(unsigned(n), &fdsets[2]); |
| 336 | } |
| 337 | |
| 338 | void Fl::remove_fd(int n) { |
| 339 | remove_fd(n, -1); |
| 340 | } |
| 341 | |
| 342 | // these pointers are set by the Fl::lock() function: |
| 343 | static void nothing() {} |
| 344 | void (*fl_lock_function)() = nothing; |
| 345 | void (*fl_unlock_function)() = nothing; |
| 346 | |
| 347 | static void* thread_message_; |
| 348 | void* Fl::thread_message() { |
| 349 | void* r = thread_message_; |
| 350 | thread_message_ = 0; |
| 351 | return r; |
| 352 | } |
| 353 | |
| 354 | IActiveIMMApp *fl_aimm = NULL; |
| 355 | MSG fl_msg; |
| 356 | |
| 357 | // This is never called with time_to_wait < 0.0. |
| 358 | // It *should* return negative on error, 0 if nothing happens before |
| 359 | // timeout, and >0 if any callbacks were done. This version only |
| 360 | // returns zero if nothing happens during a 0.0 timeout, otherwise |
| 361 | // it returns 1. |
| 362 | int fl_wait(double time_to_wait) { |
| 363 | int have_message = 0; |
| 364 | |
| 365 | run_checks(); |
| 366 | |
| 367 | // idle processing |
| 368 | static char in_idle; |
| 369 | if (Fl::idle && !in_idle) { |
| 370 | in_idle = 1; |
| 371 | Fl::idle(); |
| 372 | in_idle = 0; |
| 373 | } |
| 374 | |
| 375 | if (nfds) { |
| 376 | // For WIN32 we need to poll for socket input FIRST, since |
| 377 | // the event queue is not something we can select() on... |
| 378 | timeval t; |
| 379 | t.tv_sec = 0; |
| 380 | t.tv_usec = 0; |
| 381 | |
| 382 | fd_set fdt[3]; |
| 383 | memcpy(fdt, fdsets, sizeof fdt); // one shot faster fdt init |
| 384 | if (get_wsock_mod()&& s_wsock_select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t)) { |
| 385 | // We got something - do the callback! |
| 386 | for (int i = 0; i < nfds; i ++) { |
| 387 | SOCKET f = fd[i].fd; |
| 388 | short revents = 0; |
| 389 | if (fl_wsk_fd_is_set(f, &fdt[0])) revents |= FL_READ; |
| 390 | if (fl_wsk_fd_is_set(f, &fdt[1])) revents |= FL_WRITE; |
| 391 | if (fl_wsk_fd_is_set(f, &fdt[2])) revents |= FL_EXCEPT; |
| 392 | if (fd[i].events & revents) fd[i].cb(f, fd[i].arg); |
| 393 | } |
| 394 | time_to_wait = 0.0; // just peek for any messages |
| 395 | } else { |
| 396 | // we need to check them periodically, so set a short timeout: |
| 397 | if (time_to_wait > .001) time_to_wait = .001; |
| 398 | } |
| 399 | } |
| 400 | |
| 401 | if (Fl::idle || Fl::damage()) |
| 402 | time_to_wait = 0.0; |
| 403 | |
| 404 | // if there are no more windows and this timer is set |
| 405 | // to FOREVER, continue through or look up indefinitely |
| 406 | if (!Fl::first_window() && time_to_wait==1e20) |
| 407 | time_to_wait = 0.0; |
| 408 | |
| 409 | fl_unlock_function(); |
| 410 | |
| 411 | time_to_wait = (time_to_wait > 10000 ? 10000 : time_to_wait); |
| 412 | int t_msec = (int) (time_to_wait * 1000.0 + 0.5); |
| 413 | MsgWaitForMultipleObjects(0, NULL, FALSE, t_msec, QS_ALLINPUT); |
| 414 | |
| 415 | fl_lock_function(); |
| 416 | |
| 417 | // Execute the message we got, and all other pending messages: |
| 418 | // have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE); |
| 419 | have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE); |
| 420 | if (have_message > 0) { |
| 421 | while (have_message != 0 && have_message != -1) { |
| 422 | if (fl_msg.message == fl_wake_msg) { |
| 423 | // Used for awaking wait() from another thread |
| 424 | thread_message_ = (void*)fl_msg.wParam; |
| 425 | Fl_Awake_Handler func; |
| 426 | void *data; |
| 427 | while (Fl::get_awake_handler_(func, data)==0) { |
| 428 | func(data); |
| 429 | } |
| 430 | } |
| 431 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 432 | // Don't bother with key to character translation as we do |
| 433 | // it manually for simpley keyboard widgets. In fact, calling |
| 434 | // TranslateMessage() just makes it more difficult as it sets |
| 435 | // a bunch of internal state. |
| 436 | if (!use_simple_keyboard) |
| 437 | TranslateMessage(&fl_msg); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 438 | DispatchMessageW(&fl_msg); |
| 439 | have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE); |
| 440 | } |
| 441 | } |
| 442 | Fl::flush(); |
| 443 | |
| 444 | // This should return 0 if only timer events were handled: |
| 445 | return 1; |
| 446 | } |
| 447 | |
| 448 | // fl_ready() is just like fl_wait(0.0) except no callbacks are done: |
| 449 | int fl_ready() { |
| 450 | if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1; |
| 451 | if (!nfds) return 0; |
| 452 | timeval t; |
| 453 | t.tv_sec = 0; |
| 454 | t.tv_usec = 0; |
| 455 | fd_set fdt[3]; |
| 456 | memcpy(fdt, fdsets, sizeof fdt); |
| 457 | return get_wsock_mod() ? s_wsock_select(0,&fdt[0],&fdt[1],&fdt[2],&t) : 0; |
| 458 | } |
| 459 | |
| 460 | //////////////////////////////////////////////////////////////// |
| 461 | |
| 462 | int Fl::x() |
| 463 | { |
| 464 | RECT r; |
| 465 | |
| 466 | SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); |
| 467 | return r.left; |
| 468 | } |
| 469 | |
| 470 | int Fl::y() |
| 471 | { |
| 472 | RECT r; |
| 473 | |
| 474 | SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); |
| 475 | return r.top; |
| 476 | } |
| 477 | |
| 478 | int Fl::h() |
| 479 | { |
| 480 | RECT r; |
| 481 | |
| 482 | SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); |
| 483 | return r.bottom - r.top; |
| 484 | } |
| 485 | |
| 486 | int Fl::w() |
| 487 | { |
| 488 | RECT r; |
| 489 | |
| 490 | SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0); |
| 491 | return r.right - r.left; |
| 492 | } |
| 493 | |
| 494 | void Fl::get_mouse(int &x, int &y) { |
| 495 | POINT p; |
| 496 | GetCursorPos(&p); |
| 497 | x = p.x; |
| 498 | y = p.y; |
| 499 | } |
| 500 | |
| 501 | //////////////////////////////////////////////////////////////// |
| 502 | // code used for selections: |
| 503 | |
| 504 | char *fl_selection_buffer[2]; |
| 505 | int fl_selection_length[2]; |
| 506 | int fl_selection_buffer_length[2]; |
| 507 | char fl_i_own_selection[2]; |
| 508 | |
| 509 | UINT fl_get_lcid_codepage(LCID id) |
| 510 | { |
| 511 | char buf[8]; |
| 512 | buf[GetLocaleInfo(id, LOCALE_IDEFAULTANSICODEPAGE, buf, 8)] = 0; |
| 513 | return atol(buf); |
| 514 | } |
| 515 | |
| 516 | // Convert \n -> \r\n |
| 517 | class Lf2CrlfConvert { |
| 518 | char *out; |
| 519 | int outlen; |
| 520 | public: |
| 521 | Lf2CrlfConvert(const char *in, int inlen) { |
| 522 | outlen = 0; |
| 523 | const char *i; |
| 524 | char *o; |
| 525 | int lencount; |
| 526 | // Predict size of \r\n conversion buffer |
| 527 | for ( i=in, lencount = inlen; lencount--; ) { |
| 528 | if ( *i == '\r' && *(i+1) == '\n' ) // leave \r\n untranslated |
| 529 | { i+=2; outlen+=2; } |
| 530 | else if ( *i == '\n' ) // \n by itself? leave room to insert \r |
| 531 | { i++; outlen+=2; } |
| 532 | else |
| 533 | { ++i; ++outlen; } |
| 534 | } |
| 535 | // Alloc conversion buffer + NULL |
| 536 | out = new char[outlen+1]; |
| 537 | // Handle \n -> \r\n conversion |
| 538 | for ( i=in, o=out, lencount = inlen; lencount--; ) { |
| 539 | if ( *i == '\r' && *(i+1) == '\n' ) // leave \r\n untranslated |
| 540 | { *o++ = *i++; *o++ = *i++; } |
| 541 | else if ( *i == '\n' ) // \n by itself? insert \r |
| 542 | { *o++ = '\r'; *o++ = *i++; } |
| 543 | else |
| 544 | { *o++ = *i++; } |
| 545 | } |
| 546 | *o++ = 0; |
| 547 | } |
| 548 | ~Lf2CrlfConvert() { |
| 549 | delete[] out; |
| 550 | } |
| 551 | int GetLength() const { return(outlen); } |
| 552 | const char* GetValue() const { return(out); } |
| 553 | }; |
| 554 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 555 | void fl_update_clipboard(void) { |
Peter Ã…strand | 0aae4f4 | 2011-08-11 09:14:54 +0000 | [diff] [blame] | 556 | Fl_Window *w1 = Fl::first_window(); |
| 557 | if (!w1) |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 558 | return; |
| 559 | |
Peter Ã…strand | 0aae4f4 | 2011-08-11 09:14:54 +0000 | [diff] [blame] | 560 | HWND hwnd = fl_xid(w1); |
| 561 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 562 | if (!OpenClipboard(hwnd)) |
| 563 | return; |
| 564 | |
| 565 | EmptyClipboard(); |
| 566 | |
| 567 | int utf16_len = fl_utf8toUtf16(fl_selection_buffer[1], |
| 568 | fl_selection_length[1], 0, 0); |
| 569 | |
| 570 | HGLOBAL hMem = GlobalAlloc(GHND, utf16_len * 2 + 2); // moveable and zero'ed mem alloc. |
| 571 | LPVOID memLock = GlobalLock(hMem); |
| 572 | |
| 573 | fl_utf8toUtf16(fl_selection_buffer[1], fl_selection_length[1], |
| 574 | (unsigned short*) memLock, utf16_len + 1); |
| 575 | |
| 576 | GlobalUnlock(hMem); |
| 577 | SetClipboardData(CF_UNICODETEXT, hMem); |
| 578 | |
| 579 | CloseClipboard(); |
| 580 | |
| 581 | // In case Windows managed to lob of a WM_DESTROYCLIPBOARD during |
| 582 | // the above. |
| 583 | fl_i_own_selection[1] = 1; |
| 584 | } |
| 585 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 586 | // call this when you create a selection: |
| 587 | void Fl::copy(const char *stuff, int len, int clipboard) { |
| 588 | if (!stuff || len<0) return; |
| 589 | |
| 590 | // Convert \n -> \r\n (for old apps like Notepad, DOS) |
| 591 | Lf2CrlfConvert buf(stuff, len); |
| 592 | len = buf.GetLength(); |
| 593 | stuff = buf.GetValue(); |
| 594 | |
| 595 | if (len+1 > fl_selection_buffer_length[clipboard]) { |
| 596 | delete[] fl_selection_buffer[clipboard]; |
| 597 | fl_selection_buffer[clipboard] = new char[len+100]; |
| 598 | fl_selection_buffer_length[clipboard] = len+100; |
| 599 | } |
| 600 | memcpy(fl_selection_buffer[clipboard], stuff, len); |
| 601 | fl_selection_buffer[clipboard][len] = 0; // needed for direct paste |
| 602 | fl_selection_length[clipboard] = len; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 603 | fl_i_own_selection[clipboard] = 1; |
| 604 | if (clipboard) |
| 605 | fl_update_clipboard(); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 606 | } |
| 607 | |
| 608 | // Call this when a "paste" operation happens: |
| 609 | void Fl::paste(Fl_Widget &receiver, int clipboard) { |
| 610 | if (!clipboard || fl_i_own_selection[clipboard]) { |
| 611 | // We already have it, do it quickly without window server. |
| 612 | // Notice that the text is clobbered if set_selection is |
| 613 | // called in response to FL_PASTE! |
| 614 | |
| 615 | // Convert \r\n -> \n |
| 616 | char *i = fl_selection_buffer[clipboard]; |
| 617 | if (i==0L) { |
| 618 | Fl::e_text = 0; |
| 619 | return; |
| 620 | } |
| 621 | Fl::e_text = new char[fl_selection_length[clipboard]+1]; |
| 622 | char *o = Fl::e_text; |
| 623 | while (*i) { |
| 624 | if ( *i == '\r' && *(i+1) == '\n') i++; |
| 625 | else *o++ = *i++; |
| 626 | } |
| 627 | *o = 0; |
| 628 | Fl::e_length = o - Fl::e_text; |
| 629 | receiver.handle(FL_PASTE); |
| 630 | delete [] Fl::e_text; |
| 631 | Fl::e_text = 0; |
| 632 | } else { |
| 633 | if (!OpenClipboard(NULL)) return; |
| 634 | HANDLE h = GetClipboardData(CF_UNICODETEXT); |
| 635 | if (h) { |
| 636 | wchar_t *memLock = (wchar_t*) GlobalLock(h); |
| 637 | int utf16_len = wcslen(memLock); |
| 638 | Fl::e_text = (char*) malloc (utf16_len * 4 + 1); |
| 639 | int utf8_len = fl_utf8fromwc(Fl::e_text, utf16_len * 4, memLock, utf16_len); |
| 640 | *(Fl::e_text + utf8_len) = 0; |
| 641 | LPSTR a,b; |
| 642 | a = b = Fl::e_text; |
| 643 | while (*a) { // strip the CRLF pairs ($%$#@^) |
| 644 | if (*a == '\r' && a[1] == '\n') a++; |
| 645 | else *b++ = *a++; |
| 646 | } |
| 647 | *b = 0; |
| 648 | Fl::e_length = b - Fl::e_text; |
| 649 | receiver.handle(FL_PASTE); |
| 650 | GlobalUnlock(h); |
| 651 | free(Fl::e_text); |
| 652 | Fl::e_text = 0; |
| 653 | } |
| 654 | CloseClipboard(); |
| 655 | } |
| 656 | } |
| 657 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 658 | static HWND clipboard_wnd = 0; |
| 659 | static HWND next_clipboard_wnd = 0; |
| 660 | |
| 661 | static bool initial_clipboard = true; |
| 662 | |
| 663 | void fl_clipboard_notify_change() { |
| 664 | // No need to do anything here... |
| 665 | } |
| 666 | |
| 667 | void fl_clipboard_notify_target(HWND wnd) { |
| 668 | if (clipboard_wnd) |
| 669 | return; |
| 670 | |
| 671 | // We get one fake WM_DRAWCLIPBOARD immediately, which we therefore |
| 672 | // need to ignore. |
| 673 | initial_clipboard = true; |
| 674 | |
| 675 | clipboard_wnd = wnd; |
| 676 | next_clipboard_wnd = SetClipboardViewer(wnd); |
| 677 | } |
| 678 | |
| 679 | void fl_clipboard_notify_untarget(HWND wnd) { |
| 680 | if (wnd != clipboard_wnd) |
| 681 | return; |
| 682 | |
| 683 | ChangeClipboardChain(wnd, next_clipboard_wnd); |
| 684 | clipboard_wnd = next_clipboard_wnd = 0; |
| 685 | |
| 686 | if (Fl::first_window()) |
| 687 | fl_clipboard_notify_target(fl_xid(Fl::first_window())); |
| 688 | } |
| 689 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 690 | //////////////////////////////////////////////////////////////// |
| 691 | char fl_is_ime = 0; |
| 692 | void fl_get_codepage() |
| 693 | { |
| 694 | HKL hkl = GetKeyboardLayout(0); |
| 695 | TCHAR ld[8]; |
| 696 | |
| 697 | GetLocaleInfo (LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE, ld, 6); |
| 698 | DWORD ccp = atol(ld); |
| 699 | fl_is_ime = 0; |
| 700 | |
| 701 | fl_codepage = ccp; |
| 702 | if (fl_aimm) { |
| 703 | fl_aimm->GetCodePageA(GetKeyboardLayout(0), &fl_codepage); |
| 704 | } else if (get_imm_module() && flImmIsIME(hkl)) { |
| 705 | fl_is_ime = 1; |
| 706 | } |
| 707 | } |
| 708 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 709 | void fl_update_focus(void) |
| 710 | { |
| 711 | Fl_Widget *focus; |
| 712 | Fl_Window *win; |
| 713 | |
| 714 | get_imm_module(); |
| 715 | |
| 716 | focus = Fl::grab(); |
| 717 | if (!focus) |
| 718 | focus = Fl::focus(); |
| 719 | if (!focus) |
| 720 | return; |
| 721 | |
| 722 | // Grabs are special in that events are sent to the first |
| 723 | // available window |
| 724 | if (focus == Fl::grab()) |
| 725 | win = Fl::first_window(); |
| 726 | else { |
| 727 | win = focus->as_window(); |
| 728 | if (!win) |
| 729 | win = focus->window(); |
| 730 | } |
| 731 | |
| 732 | if (!win) { |
| 733 | Fl::warning("Cannot find window for widget receiving focus"); |
| 734 | return; |
| 735 | } |
| 736 | |
| 737 | // No Win32 window created yet |
| 738 | if (!Fl_X::i(win) || !fl_xid(win)) |
| 739 | return; |
| 740 | |
| 741 | if (focus->simple_keyboard()) { |
| 742 | use_simple_keyboard = true; |
| 743 | if (flImmGetContext(fl_xid(win)) != 0) |
| 744 | flImmAssociateContextEx(fl_xid(win), 0, 0); |
| 745 | } else { |
| 746 | use_simple_keyboard = false; |
| 747 | if (flImmGetContext(fl_xid(win)) == 0) |
| 748 | flImmAssociateContextEx(fl_xid(win), 0, IACE_DEFAULT); |
| 749 | } |
| 750 | } |
| 751 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 752 | HWND fl_capture; |
| 753 | |
| 754 | static int mouse_event(Fl_Window *window, int what, int button, |
| 755 | WPARAM wParam, LPARAM lParam) |
| 756 | { |
| 757 | static int px, py, pmx, pmy; |
| 758 | POINT pt; |
| 759 | Fl::e_x = pt.x = (signed short)LOWORD(lParam); |
| 760 | Fl::e_y = pt.y = (signed short)HIWORD(lParam); |
| 761 | ClientToScreen(fl_xid(window), &pt); |
| 762 | Fl::e_x_root = pt.x; |
| 763 | Fl::e_y_root = pt.y; |
| 764 | #ifdef USE_CAPTURE_MOUSE_WIN |
| 765 | Fl_Window *mouse_window = window; // save "mouse window" |
| 766 | #endif |
| 767 | while (window->parent()) { |
| 768 | Fl::e_x += window->x(); |
| 769 | Fl::e_y += window->y(); |
| 770 | window = window->window(); |
| 771 | } |
| 772 | |
| 773 | ulong state = Fl::e_state & 0xff0000; // keep shift key states |
| 774 | #if 0 |
| 775 | // mouse event reports some shift flags, perhaps save them? |
| 776 | if (wParam & MK_SHIFT) state |= FL_SHIFT; |
| 777 | if (wParam & MK_CONTROL) state |= FL_CTRL; |
| 778 | #endif |
| 779 | if (wParam & MK_LBUTTON) state |= FL_BUTTON1; |
| 780 | if (wParam & MK_MBUTTON) state |= FL_BUTTON2; |
| 781 | if (wParam & MK_RBUTTON) state |= FL_BUTTON3; |
| 782 | Fl::e_state = state; |
| 783 | |
| 784 | switch (what) { |
| 785 | case 1: // double-click |
| 786 | if (Fl::e_is_click) {Fl::e_clicks++; goto J1;} |
| 787 | case 0: // single-click |
| 788 | Fl::e_clicks = 0; |
| 789 | J1: |
| 790 | #ifdef USE_CAPTURE_MOUSE_WIN |
| 791 | if (!fl_capture) SetCapture(fl_xid(mouse_window)); // use mouse window |
| 792 | #else |
| 793 | if (!fl_capture) SetCapture(fl_xid(window)); // use main window |
| 794 | #endif |
| 795 | Fl::e_keysym = FL_Button + button; |
| 796 | Fl::e_is_click = 1; |
| 797 | px = pmx = Fl::e_x_root; py = pmy = Fl::e_y_root; |
| 798 | return Fl::handle(FL_PUSH,window); |
| 799 | |
| 800 | case 2: // release: |
| 801 | if (!fl_capture) ReleaseCapture(); |
| 802 | Fl::e_keysym = FL_Button + button; |
| 803 | return Fl::handle(FL_RELEASE,window); |
| 804 | |
| 805 | case 3: // move: |
| 806 | default: // avoid compiler warning |
| 807 | // MSWindows produces extra events even if mouse does not move, ignore em: |
| 808 | if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) return 1; |
| 809 | pmx = Fl::e_x_root; pmy = Fl::e_y_root; |
| 810 | if (abs(Fl::e_x_root-px)>5 || abs(Fl::e_y_root-py)>5) Fl::e_is_click = 0; |
| 811 | return Fl::handle(FL_MOVE,window); |
| 812 | |
| 813 | } |
| 814 | } |
| 815 | |
| 816 | // convert a MSWindows VK_x to an Fltk (X) Keysym: |
| 817 | // See also the inverse converter in Fl_get_key_win32.cxx |
| 818 | // This table is in numeric order by VK: |
| 819 | static const struct {unsigned short vk, fltk, extended;} vktab[] = { |
| 820 | {VK_BACK, FL_BackSpace}, |
| 821 | {VK_TAB, FL_Tab}, |
| 822 | {VK_CLEAR, FL_KP+'5', 0xff0b/*XK_Clear*/}, |
| 823 | {VK_RETURN, FL_Enter, FL_KP_Enter}, |
| 824 | {VK_SHIFT, FL_Shift_L, FL_Shift_R}, |
| 825 | {VK_CONTROL, FL_Control_L, FL_Control_R}, |
| 826 | {VK_MENU, FL_Alt_L, FL_Alt_R}, |
| 827 | {VK_PAUSE, FL_Pause}, |
| 828 | {VK_CAPITAL, FL_Caps_Lock}, |
| 829 | {VK_ESCAPE, FL_Escape}, |
| 830 | {VK_SPACE, ' '}, |
| 831 | {VK_PRIOR, FL_KP+'9', FL_Page_Up}, |
| 832 | {VK_NEXT, FL_KP+'3', FL_Page_Down}, |
| 833 | {VK_END, FL_KP+'1', FL_End}, |
| 834 | {VK_HOME, FL_KP+'7', FL_Home}, |
| 835 | {VK_LEFT, FL_KP+'4', FL_Left}, |
| 836 | {VK_UP, FL_KP+'8', FL_Up}, |
| 837 | {VK_RIGHT, FL_KP+'6', FL_Right}, |
| 838 | {VK_DOWN, FL_KP+'2', FL_Down}, |
| 839 | {VK_SNAPSHOT, FL_Print}, // does not work on NT |
| 840 | {VK_INSERT, FL_KP+'0', FL_Insert}, |
| 841 | {VK_DELETE, FL_KP+'.', FL_Delete}, |
| 842 | {VK_LWIN, FL_Meta_L}, |
| 843 | {VK_RWIN, FL_Meta_R}, |
| 844 | {VK_APPS, FL_Menu}, |
| 845 | {VK_SLEEP, FL_Sleep}, |
| 846 | {VK_MULTIPLY, FL_KP+'*'}, |
| 847 | {VK_ADD, FL_KP+'+'}, |
| 848 | {VK_SUBTRACT, FL_KP+'-'}, |
| 849 | {VK_DECIMAL, FL_KP+'.'}, |
| 850 | {VK_DIVIDE, FL_KP+'/'}, |
| 851 | {VK_NUMLOCK, FL_Num_Lock}, |
| 852 | {VK_SCROLL, FL_Scroll_Lock}, |
| 853 | # if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500) |
| 854 | {VK_BROWSER_BACK, FL_Back}, |
| 855 | {VK_BROWSER_FORWARD, FL_Forward}, |
| 856 | {VK_BROWSER_REFRESH, FL_Refresh}, |
| 857 | {VK_BROWSER_STOP, FL_Stop}, |
| 858 | {VK_BROWSER_SEARCH, FL_Search}, |
| 859 | {VK_BROWSER_FAVORITES, FL_Favorites}, |
| 860 | {VK_BROWSER_HOME, FL_Home_Page}, |
| 861 | {VK_VOLUME_MUTE, FL_Volume_Mute}, |
| 862 | {VK_VOLUME_DOWN, FL_Volume_Down}, |
| 863 | {VK_VOLUME_UP, FL_Volume_Up}, |
| 864 | {VK_MEDIA_NEXT_TRACK, FL_Media_Next}, |
| 865 | {VK_MEDIA_PREV_TRACK, FL_Media_Prev}, |
| 866 | {VK_MEDIA_STOP, FL_Media_Stop}, |
| 867 | {VK_MEDIA_PLAY_PAUSE, FL_Media_Play}, |
| 868 | {VK_LAUNCH_MAIL, FL_Mail}, |
| 869 | #endif |
| 870 | {0xba, ';'}, |
| 871 | {0xbb, '='}, |
| 872 | {0xbc, ','}, |
| 873 | {0xbd, '-'}, |
| 874 | {0xbe, '.'}, |
| 875 | {0xbf, '/'}, |
| 876 | {0xc0, '`'}, |
| 877 | {0xdb, '['}, |
| 878 | {0xdc, '\\'}, |
| 879 | {0xdd, ']'}, |
| 880 | {0xde, '\''} |
| 881 | }; |
| 882 | static int ms2fltk(int vk, int extended) { |
| 883 | static unsigned short vklut[256]; |
| 884 | static unsigned short extendedlut[256]; |
| 885 | if (!vklut[1]) { // init the table |
| 886 | unsigned int i; |
| 887 | for (i = 0; i < 256; i++) vklut[i] = tolower(i); |
| 888 | for (i=VK_F1; i<=VK_F16; i++) vklut[i] = i+(FL_F-(VK_F1-1)); |
| 889 | for (i=VK_NUMPAD0; i<=VK_NUMPAD9; i++) vklut[i] = i+(FL_KP+'0'-VK_NUMPAD0); |
| 890 | for (i = 0; i < sizeof(vktab)/sizeof(*vktab); i++) { |
| 891 | vklut[vktab[i].vk] = vktab[i].fltk; |
| 892 | extendedlut[vktab[i].vk] = vktab[i].extended; |
| 893 | } |
| 894 | for (i = 0; i < 256; i++) if (!extendedlut[i]) extendedlut[i] = vklut[i]; |
| 895 | } |
| 896 | return extended ? extendedlut[vk] : vklut[vk]; |
| 897 | } |
| 898 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 899 | static xchar msdead2fltk(xchar in) |
| 900 | { |
| 901 | switch (in) { |
| 902 | case 0x0060: // GRAVE ACCENT |
| 903 | return 0x0300; // COMBINING GRAVE ACCENT |
| 904 | case 0x00b4: // ACUTE ACCENT |
| 905 | return 0x0301; // COMBINING ACUTE ACCENT |
| 906 | case 0x005e: // CIRCUMFLEX ACCENT |
| 907 | return 0x0302; // COMBINING CIRCUMFLEX ACCENT |
| 908 | case 0x007e: // TILDE |
| 909 | return 0x0303; // COMBINING TILDE |
| 910 | case 0x00a8: // DIAERESIS |
| 911 | return 0x0308; // COMBINING DIAERESIS |
| 912 | // FIXME: Windows dead key behaviour isn't documented and I don't have |
| 913 | // any more keyboards to test with... |
| 914 | } |
| 915 | |
| 916 | // hope that Windows gave us something proper to begin with |
| 917 | return in; |
| 918 | } |
| 919 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 920 | #if USE_COLORMAP |
| 921 | extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx |
| 922 | #endif |
| 923 | |
| 924 | |
| 925 | ///////////////////////////////////////////////////////////////////////////// |
| 926 | /// Win32 timers |
| 927 | /// |
| 928 | |
| 929 | struct Win32Timer |
| 930 | { |
| 931 | UINT_PTR handle; |
| 932 | Fl_Timeout_Handler callback; |
| 933 | void *data; |
| 934 | }; |
| 935 | static Win32Timer* win32_timers; |
| 936 | static int win32_timer_alloc; |
| 937 | static int win32_timer_used; |
| 938 | static HWND s_TimerWnd; |
| 939 | |
| 940 | static void realloc_timers() |
| 941 | { |
| 942 | if (win32_timer_alloc == 0) { |
| 943 | win32_timer_alloc = 8; |
| 944 | } |
| 945 | win32_timer_alloc *= 2; |
| 946 | Win32Timer* new_timers = new Win32Timer[win32_timer_alloc]; |
| 947 | memset(new_timers, 0, sizeof(Win32Timer) * win32_timer_used); |
| 948 | memcpy(new_timers, win32_timers, sizeof(Win32Timer) * win32_timer_used); |
| 949 | Win32Timer* delete_me = win32_timers; |
| 950 | win32_timers = new_timers; |
| 951 | delete [] delete_me; |
| 952 | } |
| 953 | |
| 954 | static void delete_timer(Win32Timer& t) |
| 955 | { |
| 956 | KillTimer(s_TimerWnd, t.handle); |
| 957 | memset(&t, 0, sizeof(Win32Timer)); |
| 958 | } |
| 959 | |
| 960 | /// END TIMERS |
| 961 | ///////////////////////////////////////////////////////////////////////////// |
| 962 | |
| 963 | static Fl_Window* resize_bug_fix; |
| 964 | |
| 965 | extern void fl_save_pen(void); |
| 966 | extern void fl_restore_pen(void); |
| 967 | |
| 968 | static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
| 969 | { |
| 970 | // Copy the message to fl_msg so add_handler code can see it, it is |
| 971 | // already there if this is called by DispatchMessage, but not if |
| 972 | // Windows calls this directly. |
| 973 | fl_msg.hwnd = hWnd; |
| 974 | fl_msg.message = uMsg; |
| 975 | fl_msg.wParam = wParam; |
| 976 | fl_msg.lParam = lParam; |
| 977 | //fl_msg.time = ??? |
| 978 | //fl_msg.pt = ??? |
| 979 | //fl_msg.lPrivate = ??? |
| 980 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 981 | MSG fl_orig_msg = fl_msg; |
| 982 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 983 | Fl_Window *window = fl_find(hWnd); |
| 984 | |
| 985 | if (window) switch (uMsg) { |
| 986 | |
| 987 | case WM_QUIT: // this should not happen? |
| 988 | Fl::fatal("WM_QUIT message"); |
| 989 | |
| 990 | case WM_CLOSE: // user clicked close box |
| 991 | Fl::handle(FL_CLOSE, window); |
| 992 | PostQuitMessage(0); |
| 993 | return 0; |
| 994 | |
| 995 | case WM_SYNCPAINT : |
| 996 | case WM_NCPAINT : |
| 997 | case WM_ERASEBKGND : |
| 998 | // Andreas Weitl - WM_SYNCPAINT needs to be passed to DefWindowProc |
| 999 | // so that Windows can generate the proper paint messages... |
| 1000 | // Similarly, WM_NCPAINT and WM_ERASEBKGND need this, too... |
| 1001 | break; |
| 1002 | |
| 1003 | case WM_PAINT: { |
| 1004 | Fl_Region R; |
| 1005 | Fl_X *i = Fl_X::i(window); |
| 1006 | i->wait_for_expose = 0; |
| 1007 | char redraw_whole_window = false; |
| 1008 | if (!i->region && window->damage()) { |
| 1009 | // Redraw the whole window... |
| 1010 | i->region = CreateRectRgn(0, 0, window->w(), window->h()); |
| 1011 | redraw_whole_window = true; |
| 1012 | } |
| 1013 | |
| 1014 | // We need to merge WIN32's damage into FLTK's damage. |
| 1015 | R = CreateRectRgn(0,0,0,0); |
| 1016 | int r = GetUpdateRgn(hWnd,R,0); |
| 1017 | if (r==NULLREGION && !redraw_whole_window) { |
| 1018 | break; |
| 1019 | } |
| 1020 | |
| 1021 | if (i->region) { |
| 1022 | // Also tell WIN32 that we are drawing someplace else as well... |
| 1023 | CombineRgn(i->region, i->region, R, RGN_OR); |
| 1024 | XDestroyRegion(R); |
| 1025 | } else { |
| 1026 | i->region = R; |
| 1027 | } |
| 1028 | if (window->type() == FL_DOUBLE_WINDOW) ValidateRgn(hWnd,0); |
| 1029 | else ValidateRgn(hWnd,i->region); |
| 1030 | |
| 1031 | window->clear_damage((uchar)(window->damage()|FL_DAMAGE_EXPOSE)); |
| 1032 | // These next two statements should not be here, so that all update |
| 1033 | // is deferred until Fl::flush() is called during idle. However WIN32 |
| 1034 | // apparently is very unhappy if we don't obey it and draw right now. |
| 1035 | // Very annoying! |
| 1036 | fl_GetDC(hWnd); // Make sure we have a DC for this window... |
| 1037 | fl_save_pen(); |
| 1038 | i->flush(); |
| 1039 | fl_restore_pen(); |
| 1040 | window->clear_damage(); |
| 1041 | } return 0; |
| 1042 | |
| 1043 | case WM_LBUTTONDOWN: mouse_event(window, 0, 1, wParam, lParam); return 0; |
| 1044 | case WM_LBUTTONDBLCLK:mouse_event(window, 1, 1, wParam, lParam); return 0; |
| 1045 | case WM_LBUTTONUP: mouse_event(window, 2, 1, wParam, lParam); return 0; |
| 1046 | case WM_MBUTTONDOWN: mouse_event(window, 0, 2, wParam, lParam); return 0; |
| 1047 | case WM_MBUTTONDBLCLK:mouse_event(window, 1, 2, wParam, lParam); return 0; |
| 1048 | case WM_MBUTTONUP: mouse_event(window, 2, 2, wParam, lParam); return 0; |
| 1049 | case WM_RBUTTONDOWN: mouse_event(window, 0, 3, wParam, lParam); return 0; |
| 1050 | case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0; |
| 1051 | case WM_RBUTTONUP: mouse_event(window, 2, 3, wParam, lParam); return 0; |
| 1052 | |
| 1053 | case WM_MOUSEMOVE: |
| 1054 | #ifdef USE_TRACK_MOUSE |
| 1055 | if (track_mouse_win != window) { |
| 1056 | TRACKMOUSEEVENT tme; |
| 1057 | tme.cbSize = sizeof(TRACKMOUSEEVENT); |
| 1058 | tme.dwFlags = TME_LEAVE; |
| 1059 | tme.hwndTrack = hWnd; |
| 1060 | _TrackMouseEvent(&tme); |
| 1061 | track_mouse_win = window; |
| 1062 | } |
| 1063 | #endif // USE_TRACK_MOUSE |
| 1064 | mouse_event(window, 3, 0, wParam, lParam); |
| 1065 | return 0; |
| 1066 | |
| 1067 | case WM_MOUSELEAVE: |
| 1068 | if (track_mouse_win == window) { // we left the top level window ! |
| 1069 | Fl_Window *tw = window; |
| 1070 | while (tw->parent()) tw = tw->window(); // find top level window |
| 1071 | Fl::belowmouse(0); |
| 1072 | Fl::handle(FL_LEAVE, tw); |
| 1073 | } |
| 1074 | track_mouse_win = 0; // force TrackMouseEvent() restart |
| 1075 | break; |
| 1076 | |
| 1077 | case WM_SETFOCUS: |
| 1078 | Fl::handle(FL_FOCUS, window); |
| 1079 | break; |
| 1080 | |
| 1081 | case WM_KILLFOCUS: |
| 1082 | Fl::handle(FL_UNFOCUS, window); |
| 1083 | Fl::flush(); // it never returns to main loop when deactivated... |
| 1084 | break; |
| 1085 | |
| 1086 | case WM_SHOWWINDOW: |
| 1087 | if (!window->parent()) { |
| 1088 | Fl::handle(wParam ? FL_SHOW : FL_HIDE, window); |
| 1089 | } |
| 1090 | break; |
| 1091 | |
| 1092 | case WM_ACTIVATEAPP: |
| 1093 | // From eric@vfx.sel.sony.com, we should process WM_ACTIVATEAPP |
| 1094 | // messages to restore the correct state of the shift/ctrl/alt/lock |
| 1095 | // keys... Added control, shift, alt, and meta keys, and changed |
| 1096 | // to use GetAsyncKeyState and do it when wParam is 1 |
| 1097 | // (that means we have focus...) |
| 1098 | if (wParam) |
| 1099 | { |
| 1100 | ulong state = 0; |
| 1101 | if (GetAsyncKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK; |
| 1102 | if (GetAsyncKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK; |
| 1103 | if (GetAsyncKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK; |
| 1104 | if (GetAsyncKeyState(VK_CONTROL)&~1) state |= FL_CTRL; |
| 1105 | if (GetAsyncKeyState(VK_SHIFT)&~1) state |= FL_SHIFT; |
| 1106 | if (GetAsyncKeyState(VK_MENU)) state |= FL_ALT; |
| 1107 | if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1) state |= FL_META; |
| 1108 | Fl::e_state = state; |
| 1109 | return 0; |
| 1110 | } |
| 1111 | break; |
| 1112 | |
| 1113 | case WM_INPUTLANGCHANGE: |
| 1114 | fl_get_codepage(); |
| 1115 | break; |
| 1116 | case WM_IME_COMPOSITION: |
| 1117 | // if (!fl_is_nt4() && lParam & GCS_RESULTCLAUSE) { |
| 1118 | // HIMC himc = ImmGetContext(hWnd); |
| 1119 | // wlen = ImmGetCompositionStringW(himc, GCS_RESULTSTR, |
| 1120 | // wbuf, sizeof(wbuf)) / sizeof(short); |
| 1121 | // if (wlen < 0) wlen = 0; |
| 1122 | // wbuf[wlen] = 0; |
| 1123 | // ImmReleaseContext(hWnd, himc); |
| 1124 | // } |
| 1125 | break; |
| 1126 | case WM_KEYDOWN: |
| 1127 | case WM_SYSKEYDOWN: |
| 1128 | case WM_KEYUP: |
| 1129 | case WM_SYSKEYUP: |
| 1130 | // save the keysym until we figure out the characters: |
| 1131 | Fl::e_keysym = Fl::e_original_keysym = ms2fltk(wParam,lParam&(1<<24)); |
| 1132 | // See if TranslateMessage turned it into a WM_*CHAR message: |
| 1133 | if (PeekMessageW(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) |
| 1134 | { |
| 1135 | uMsg = fl_msg.message; |
| 1136 | wParam = fl_msg.wParam; |
| 1137 | lParam = fl_msg.lParam; |
| 1138 | } |
| 1139 | case WM_DEADCHAR: |
| 1140 | case WM_SYSDEADCHAR: |
| 1141 | case WM_CHAR: |
| 1142 | case WM_SYSCHAR: { |
| 1143 | ulong state = Fl::e_state & 0xff000000; // keep the mouse button state |
| 1144 | // if GetKeyState is expensive we might want to comment some of these out: |
| 1145 | if (GetKeyState(VK_SHIFT)&~1) state |= FL_SHIFT; |
| 1146 | if (GetKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK; |
| 1147 | if (GetKeyState(VK_CONTROL)&~1) state |= FL_CTRL; |
| 1148 | // Alt gets reported for the Alt-GR switch on foreign keyboards. |
| 1149 | // so we need to check the event as well to get it right: |
| 1150 | if ((lParam&(1<<29)) //same as GetKeyState(VK_MENU) |
| 1151 | && uMsg != WM_CHAR) state |= FL_ALT; |
| 1152 | if (GetKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK; |
| 1153 | if ((GetKeyState(VK_LWIN)|GetKeyState(VK_RWIN))&~1) { |
| 1154 | // WIN32 bug? GetKeyState returns garbage if the user hit the |
| 1155 | // meta key to pop up start menu. Sigh. |
| 1156 | if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1) |
| 1157 | state |= FL_META; |
| 1158 | } |
| 1159 | if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK; |
| 1160 | Fl::e_state = state; |
| 1161 | static char buffer[1024]; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1162 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1163 | if (use_simple_keyboard) { |
| 1164 | BYTE keystate[256]; |
| 1165 | WCHAR wbuf[8]; |
| 1166 | int ret; |
| 1167 | |
| 1168 | // I'm not sure if we ever get WM_CHAR (& friends) without an initial |
| 1169 | // WM_KEYDOWN (& friends), but if we do then we should not send such |
| 1170 | // side band events to simple keyboard widgets. |
| 1171 | if ((fl_orig_msg.message != WM_KEYDOWN) && |
| 1172 | (fl_orig_msg.message != WM_SYSKEYDOWN) && |
| 1173 | (fl_orig_msg.message != WM_KEYUP) && |
| 1174 | (fl_orig_msg.message != WM_SYSKEYUP)) |
| 1175 | break; |
| 1176 | |
| 1177 | GetKeyboardState(keystate); |
| 1178 | |
| 1179 | // Pressing Ctrl wreaks havoc with the symbol lookup, so turn that off. |
| 1180 | // But AltGr shows up as Ctrl+Alt in Windows, so keep Ctrl if Alt is |
| 1181 | // active. |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1182 | if (!(keystate[VK_MENU] & 0x80)) |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1183 | keystate[VK_CONTROL] = keystate[VK_LCONTROL] = keystate[VK_RCONTROL] = 0; |
| 1184 | |
| 1185 | // We cannot inspect or modify Windows' internal state of the keyboard |
| 1186 | // so we have to try to infer information from ToUnicode() and wedge |
| 1187 | // things into a known state. |
| 1188 | for (int i = 0;i < 2;i++) { |
| 1189 | ret = ToUnicode(fl_orig_msg.wParam, 0, keystate, wbuf, |
| 1190 | sizeof(wbuf)/sizeof(wbuf[0]), 0); |
| 1191 | |
| 1192 | // No symbol for this key (or unexpected length) |
| 1193 | if ((ret == 0) || (ret < -1)) { |
| 1194 | buffer[0] = 0; |
| 1195 | Fl::e_length = 0; |
| 1196 | break; |
| 1197 | } |
| 1198 | |
| 1199 | // A dead key. Convert this to a Unicode combining character so |
| 1200 | // that the application can tell the difference between dead and |
| 1201 | // normal keys. |
| 1202 | if (ret == -1) { |
| 1203 | xchar u = (xchar) msdead2fltk(wbuf[0]); |
| 1204 | Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); |
| 1205 | buffer[Fl::e_length] = 0; |
| 1206 | break; |
| 1207 | } |
| 1208 | |
| 1209 | // If we have two characters (or more) from ToUnicode(), that's |
| 1210 | // an invalid sequence. One character chould mean a proper symbol, |
| 1211 | // or it could mean a composed one. In both cases we need to call |
| 1212 | // ToUnicode() again to get something sane. |
| 1213 | if (i == 0) |
| 1214 | continue; |
| 1215 | |
| 1216 | // We should now have something sane. Give whatever we have to the |
| 1217 | // application. |
| 1218 | Fl::e_length = fl_utf8fromwc(buffer, 1024, wbuf, ret); |
| 1219 | buffer[Fl::e_length] = 0; |
| 1220 | } |
| 1221 | } else if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) { |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1222 | xchar u = (xchar) wParam; |
| 1223 | // Fl::e_length = fl_unicode2utf(&u, 1, buffer); |
| 1224 | Fl::e_length = fl_utf8fromwc(buffer, 1024, &u, 1); |
| 1225 | buffer[Fl::e_length] = 0; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1226 | } else { |
| 1227 | buffer[0] = 0; |
| 1228 | Fl::e_length = 0; |
| 1229 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1230 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1231 | // The keypad area is a bit odd in that we need to change the keysym |
| 1232 | // to properly indicate what the user meant, unlike other keys where |
| 1233 | // we normally change the text and keep keysym stable. |
| 1234 | if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) { |
| 1235 | // The initial mapping tables give us a keysym that corresponds to |
| 1236 | // numlock being on, so we only do something when it is off. |
| 1237 | if (!(state & FL_NUM_LOCK)) { |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1238 | switch (Fl::e_keysym) { |
| 1239 | case FL_KP + '0' : |
| 1240 | Fl::e_keysym = FL_Insert; |
| 1241 | break; |
| 1242 | case FL_KP + '1' : |
| 1243 | Fl::e_keysym = FL_End; |
| 1244 | break; |
| 1245 | case FL_KP + '2' : |
| 1246 | Fl::e_keysym = FL_Down; |
| 1247 | break; |
| 1248 | case FL_KP + '3' : |
| 1249 | Fl::e_keysym = FL_Page_Down; |
| 1250 | break; |
| 1251 | case FL_KP + '4' : |
| 1252 | Fl::e_keysym = FL_Left; |
| 1253 | break; |
| 1254 | case FL_KP + '6' : |
| 1255 | Fl::e_keysym = FL_Right; |
| 1256 | break; |
| 1257 | case FL_KP + '7' : |
| 1258 | Fl::e_keysym = FL_Home; |
| 1259 | break; |
| 1260 | case FL_KP + '8' : |
| 1261 | Fl::e_keysym = FL_Up; |
| 1262 | break; |
| 1263 | case FL_KP + '9' : |
| 1264 | Fl::e_keysym = FL_Page_Up; |
| 1265 | break; |
| 1266 | case FL_KP + '.' : |
| 1267 | Fl::e_keysym = FL_Delete; |
| 1268 | break; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1269 | } |
| 1270 | } |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1271 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1272 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1273 | Fl::e_text = buffer; |
| 1274 | if (lParam & (1<<31)) { // key up events. |
| 1275 | if (Fl::handle(FL_KEYUP, window)) return 0; |
| 1276 | break; |
| 1277 | } |
| 1278 | // for (int i = lParam&0xff; i--;) |
| 1279 | while (window->parent()) window = window->window(); |
| 1280 | if (Fl::handle(FL_KEYBOARD,window)) { |
| 1281 | if (uMsg==WM_DEADCHAR || uMsg==WM_SYSDEADCHAR) |
| 1282 | Fl::compose_state = 1; |
| 1283 | return 0; |
| 1284 | } |
| 1285 | break;} |
| 1286 | |
| 1287 | case WM_MOUSEWHEEL: { |
| 1288 | static int delta = 0; // running total of all motion |
| 1289 | delta += (SHORT)(HIWORD(wParam)); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1290 | Fl::e_dx = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1291 | Fl::e_dy = -delta / WHEEL_DELTA; |
| 1292 | delta += Fl::e_dy * WHEEL_DELTA; |
| 1293 | if (Fl::e_dy) Fl::handle(FL_MOUSEWHEEL, window); |
| 1294 | return 0; |
| 1295 | } |
| 1296 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1297 | // This is only defined on Vista and upwards... |
| 1298 | #ifndef WM_MOUSEHWHEEL |
| 1299 | #define WM_MOUSEHWHEEL 0x020E |
| 1300 | #endif |
| 1301 | |
| 1302 | case WM_MOUSEHWHEEL: { |
| 1303 | static int delta = 0; // running total of all motion |
| 1304 | delta += (SHORT)(HIWORD(wParam)); |
| 1305 | Fl::e_dy = 0; |
| 1306 | Fl::e_dx = delta / WHEEL_DELTA; |
| 1307 | delta -= Fl::e_dx * WHEEL_DELTA; |
| 1308 | if (Fl::e_dx) Fl::handle(FL_MOUSEWHEEL, window); |
| 1309 | return 0; |
| 1310 | } |
| 1311 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1312 | case WM_GETMINMAXINFO: |
| 1313 | Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam); |
| 1314 | break; |
| 1315 | |
| 1316 | case WM_SIZE: |
| 1317 | if (!window->parent()) { |
| 1318 | if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) { |
| 1319 | Fl::handle(FL_HIDE, window); |
| 1320 | } else { |
| 1321 | Fl::handle(FL_SHOW, window); |
| 1322 | resize_bug_fix = window; |
| 1323 | window->size(LOWORD(lParam), HIWORD(lParam)); |
| 1324 | } |
| 1325 | } |
| 1326 | break; |
| 1327 | |
| 1328 | case WM_MOVE: { |
| 1329 | resize_bug_fix = window; |
| 1330 | int nx = LOWORD(lParam); |
| 1331 | int ny = HIWORD(lParam); |
| 1332 | if (nx & 0x8000) nx -= 65536; |
| 1333 | if (ny & 0x8000) ny -= 65536; |
| 1334 | window->position(nx, ny); } |
| 1335 | break; |
| 1336 | |
| 1337 | case WM_SETCURSOR: |
| 1338 | if (LOWORD(lParam) == HTCLIENT) { |
| 1339 | while (window->parent()) window = window->window(); |
| 1340 | SetCursor(Fl_X::i(window)->cursor); |
| 1341 | return 0; |
| 1342 | } |
| 1343 | break; |
| 1344 | |
| 1345 | #if USE_COLORMAP |
| 1346 | case WM_QUERYNEWPALETTE : |
| 1347 | fl_GetDC(hWnd); |
| 1348 | if (fl_select_palette()) InvalidateRect(hWnd, NULL, FALSE); |
| 1349 | break; |
| 1350 | |
| 1351 | case WM_PALETTECHANGED: |
| 1352 | fl_GetDC(hWnd); |
| 1353 | if ((HWND)wParam != hWnd && fl_select_palette()) UpdateColors(fl_gc); |
| 1354 | break; |
| 1355 | |
| 1356 | case WM_CREATE : |
| 1357 | fl_GetDC(hWnd); |
| 1358 | fl_select_palette(); |
| 1359 | break; |
| 1360 | #endif |
| 1361 | |
| 1362 | case WM_DESTROYCLIPBOARD: |
| 1363 | fl_i_own_selection[1] = 0; |
| 1364 | return 1; |
| 1365 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1366 | case WM_CHANGECBCHAIN: |
| 1367 | if ((hWnd == clipboard_wnd) && |
| 1368 | (next_clipboard_wnd == (HWND)wParam)) { |
| 1369 | next_clipboard_wnd = (HWND)lParam; |
| 1370 | return 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1371 | } |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1372 | break; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1373 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1374 | case WM_DRAWCLIPBOARD: |
| 1375 | // When the clipboard moves between two FLTK windows, |
| 1376 | // fl_i_own_selection will temporarily be false as we are |
| 1377 | // processing this message. Hence the need to use fl_find(). |
| 1378 | if (!initial_clipboard && !fl_find(GetClipboardOwner())) |
| 1379 | fl_trigger_clipboard_notify(1); |
| 1380 | initial_clipboard = false; |
| 1381 | |
| 1382 | if (next_clipboard_wnd) |
| 1383 | SendMessage(next_clipboard_wnd, WM_DRAWCLIPBOARD, wParam, lParam); |
| 1384 | |
| 1385 | return 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1386 | |
| 1387 | default: |
| 1388 | if (Fl::handle(0,0)) return 0; |
| 1389 | break; |
| 1390 | } |
| 1391 | |
| 1392 | |
| 1393 | return DefWindowProcW(hWnd, uMsg, wParam, lParam); |
| 1394 | } |
| 1395 | |
| 1396 | //////////////////////////////////////////////////////////////// |
| 1397 | // This function gets the dimensions of the top/left borders and |
| 1398 | // the title bar, if there is one, based on the FL_BORDER, FL_MODAL |
| 1399 | // and FL_NONMODAL flags, and on the window's size range. |
| 1400 | // It returns the following values: |
| 1401 | // |
| 1402 | // value | border | title bar |
| 1403 | // 0 | none | no |
| 1404 | // 1 | fix | yes |
| 1405 | // 2 | size | yes |
| 1406 | |
| 1407 | int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) { |
| 1408 | int W, H, xoff, yoff, dx, dy; |
| 1409 | int ret = bx = by = bt = 0; |
| 1410 | |
| 1411 | int fallback = 1; |
| 1412 | if (!w->parent()) { |
| 1413 | HWND hwnd = fl_xid(w); |
| 1414 | if (hwnd) { |
| 1415 | // The block below calculates the window borders by requesting the |
| 1416 | // required decorated window rectangle for a desired client rectangle. |
| 1417 | // If any part of the function above fails, we will drop to a |
| 1418 | // fallback to get the best guess which is always available. |
| 1419 | HWND hwnd = fl_xid(w); |
| 1420 | // request the style flags of this window, as WIN32 sees them |
| 1421 | LONG style = GetWindowLong(hwnd, GWL_STYLE); |
| 1422 | LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE); |
| 1423 | RECT r; |
| 1424 | r.left = w->x(); |
| 1425 | r.top = w->y(); |
| 1426 | r.right = w->x()+w->w(); |
| 1427 | r.bottom = w->y()+w->h(); |
| 1428 | // get the decoration rectangle for the desired client rectangle |
| 1429 | BOOL ok = AdjustWindowRectEx(&r, style, FALSE, exstyle); |
| 1430 | if (ok) { |
| 1431 | X = r.left; |
| 1432 | Y = r.top; |
| 1433 | W = r.right - r.left; |
| 1434 | H = r.bottom - r.top; |
| 1435 | bx = w->x() - r.left; |
| 1436 | by = r.bottom - w->y() - w->h(); // height of the bootm frame |
| 1437 | bt = w->y() - r.top - by; // height of top caption bar |
| 1438 | xoff = bx; |
| 1439 | yoff = by + bt; |
| 1440 | dx = W - w->w(); |
| 1441 | dy = H - w->h(); |
| 1442 | if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) |
| 1443 | ret = 2; |
| 1444 | else |
| 1445 | ret = 1; |
| 1446 | fallback = 0; |
| 1447 | } |
| 1448 | } |
| 1449 | } |
| 1450 | // This is the original (pre 1.1.7) routine to calculate window border sizes. |
| 1451 | if (fallback) { |
| 1452 | if (w->border() && !w->parent()) { |
| 1453 | if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) { |
| 1454 | ret = 2; |
| 1455 | bx = GetSystemMetrics(SM_CXSIZEFRAME); |
| 1456 | by = GetSystemMetrics(SM_CYSIZEFRAME); |
| 1457 | } else { |
| 1458 | ret = 1; |
| 1459 | bx = GetSystemMetrics(SM_CXFIXEDFRAME); |
| 1460 | by = GetSystemMetrics(SM_CYFIXEDFRAME); |
| 1461 | } |
| 1462 | bt = GetSystemMetrics(SM_CYCAPTION); |
| 1463 | } |
| 1464 | //The coordinates of the whole window, including non-client area |
| 1465 | xoff = bx; |
| 1466 | yoff = by + bt; |
| 1467 | dx = 2*bx; |
| 1468 | dy = 2*by + bt; |
| 1469 | X = w->x()-xoff; |
| 1470 | Y = w->y()-yoff; |
| 1471 | W = w->w()+dx; |
| 1472 | H = w->h()+dy; |
| 1473 | } |
| 1474 | |
| 1475 | //Proceed to positioning the window fully inside the screen, if possible |
| 1476 | //Make border's lower right corner visible |
| 1477 | int scr_x, scr_y, scr_w, scr_h; |
| 1478 | Fl::screen_xywh(scr_x, scr_y, scr_w, scr_h, X, Y); |
| 1479 | if (scr_x+scr_w < X+W) X = scr_x+scr_w - W; |
| 1480 | if (scr_y+scr_h < Y+H) Y = scr_y+scr_h - H; |
| 1481 | //Make border's upper left corner visible |
| 1482 | if (X<scr_x) X = scr_x; |
| 1483 | if (Y<scr_y) Y = scr_y; |
| 1484 | //Make client area's lower right corner visible |
| 1485 | if (scr_x+scr_w < X+dx+ w->w()) X = scr_x+scr_w - w->w() - dx; |
| 1486 | if (scr_y+scr_h < Y+dy+ w->h()) Y = scr_y+scr_h - w->h() - dy; |
| 1487 | //Make client area's upper left corner visible |
| 1488 | if (X+xoff < scr_x) X = scr_x-xoff; |
| 1489 | if (Y+yoff < scr_y) Y = scr_y-yoff; |
| 1490 | //Return the client area's top left corner in (X,Y) |
| 1491 | X+=xoff; |
| 1492 | Y+=yoff; |
| 1493 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1494 | if (w->flags() & Fl_Widget::FULLSCREEN) { |
| 1495 | X = Y = 0; |
| 1496 | bx = by = bt = 0; |
| 1497 | } |
| 1498 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1499 | return ret; |
| 1500 | } |
| 1501 | |
| 1502 | //////////////////////////////////////////////////////////////// |
| 1503 | |
| 1504 | void Fl_Window::resize(int X,int Y,int W,int H) { |
| 1505 | UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER |
| 1506 | | SWP_NOACTIVATE | SWP_NOOWNERZORDER; |
| 1507 | int is_a_resize = (W != w() || H != h()); |
| 1508 | int resize_from_program = (this != resize_bug_fix); |
| 1509 | if (!resize_from_program) resize_bug_fix = 0; |
| 1510 | if (X != x() || Y != y()) { |
| 1511 | force_position(1); |
| 1512 | } else { |
| 1513 | if (!is_a_resize) return; |
| 1514 | flags |= SWP_NOMOVE; |
| 1515 | } |
| 1516 | if (is_a_resize) { |
| 1517 | Fl_Group::resize(X,Y,W,H); |
| 1518 | if (visible_r()) { |
| 1519 | redraw(); |
| 1520 | // only wait for exposure if this window has a size - a window |
| 1521 | // with no width or height will never get an exposure event |
| 1522 | if (i && W>0 && H>0) |
| 1523 | i->wait_for_expose = 1; |
| 1524 | } |
| 1525 | } else { |
| 1526 | x(X); y(Y); |
| 1527 | flags |= SWP_NOSIZE; |
| 1528 | } |
| 1529 | if (!border()) flags |= SWP_NOACTIVATE; |
| 1530 | if (resize_from_program && shown()) { |
| 1531 | if (!resizable()) size_range(w(),h(),w(),h()); |
| 1532 | int dummy_x, dummy_y, bt, bx, by; |
| 1533 | //Ignore window managing when resizing, so that windows (and more |
| 1534 | //specifically menus) can be moved offscreen. |
| 1535 | if (Fl_X::fake_X_wm(this, dummy_x, dummy_y, bt, bx, by)) { |
| 1536 | X -= bx; |
| 1537 | Y -= by+bt; |
| 1538 | W += 2*bx; |
| 1539 | H += 2*by+bt; |
| 1540 | } |
| 1541 | // avoid zero size windows. A zero sized window on Win32 |
| 1542 | // will cause continouly new redraw events. |
| 1543 | if (W<=0) W = 1; |
| 1544 | if (H<=0) H = 1; |
| 1545 | SetWindowPos(i->xid, 0, X, Y, W, H, flags); |
| 1546 | } |
| 1547 | } |
| 1548 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1549 | static void make_fullscreen(Fl_Window *w, Window xid, int X, int Y, int W, int H) { |
| 1550 | int sx, sy, sw, sh; |
| 1551 | Fl::screen_xywh(sx, sy, sw, sh, X, Y, W, H); |
| 1552 | DWORD flags = GetWindowLong(xid, GWL_STYLE); |
| 1553 | flags = flags & ~(WS_THICKFRAME|WS_CAPTION); |
| 1554 | SetWindowLong(xid, GWL_STYLE, flags); |
| 1555 | // SWP_NOSENDCHANGING is so that we can override size limits |
| 1556 | SetWindowPos(xid, HWND_TOP, sx, sy, sw, sh, SWP_NOSENDCHANGING | SWP_FRAMECHANGED); |
| 1557 | } |
| 1558 | |
| 1559 | void fullscreen_x(Fl_Window *w) { |
| 1560 | w->_set_fullscreen(); |
| 1561 | make_fullscreen(w, fl_xid(w), w->x(), w->y(), w->w(), w->h()); |
| 1562 | Fl::handle(FL_FULLSCREEN, w); |
| 1563 | } |
| 1564 | |
| 1565 | void fullscreen_off_x(Fl_Window *w, int X, int Y, int W, int H) { |
| 1566 | w->_clear_fullscreen(); |
| 1567 | DWORD style = GetWindowLong(fl_xid(w), GWL_STYLE); |
| 1568 | // Remove the xid temporarily so that Fl_X::fake_X_wm() behaves like it |
| 1569 | // does in Fl_X::make(). |
| 1570 | HWND xid = fl_xid(w); |
| 1571 | Fl_X::i(w)->xid = NULL; |
| 1572 | int x, y, bt, bx, by; |
| 1573 | switch (Fl_X::fake_X_wm(w, x, y, bt, bx, by)) { |
| 1574 | case 0: |
| 1575 | break; |
| 1576 | case 1: |
| 1577 | style |= WS_CAPTION; |
| 1578 | break; |
| 1579 | case 2: |
| 1580 | if (w->border()) { |
| 1581 | style |= WS_THICKFRAME | WS_CAPTION; |
| 1582 | } |
| 1583 | break; |
| 1584 | } |
| 1585 | Fl_X::i(w)->xid = xid; |
| 1586 | // Adjust for decorations (but not if that puts the decorations |
| 1587 | // outside the screen) |
| 1588 | if ((X != w->x()) || (Y != w->y())) { |
| 1589 | X -= bx; |
| 1590 | Y -= by+bt; |
| 1591 | } |
| 1592 | W += bx*2; |
| 1593 | H += by*2+bt; |
| 1594 | SetWindowLong(fl_xid(w), GWL_STYLE, style); |
| 1595 | SetWindowPos(fl_xid(w), 0, X, Y, W, H, |
| 1596 | SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| 1597 | Fl::handle(FL_FULLSCREEN, w); |
| 1598 | } |
| 1599 | |
| 1600 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1601 | //////////////////////////////////////////////////////////////// |
| 1602 | |
| 1603 | /* |
| 1604 | * This silly little class remembers the name of all window classes |
| 1605 | * we register to avoid double registration. It has the added bonus |
| 1606 | * of freeing everything on application close as well. |
| 1607 | */ |
| 1608 | class NameList { |
| 1609 | public: |
| 1610 | NameList() { name = (char**)malloc(sizeof(char**)); NName = 1; nName = 0; } |
| 1611 | ~NameList() { |
| 1612 | int i; |
| 1613 | for (i=0; i<nName; i++) free(name[i]); |
| 1614 | if (name) free(name); |
| 1615 | } |
| 1616 | void add_name(const char *n) { |
| 1617 | if (NName==nName) { |
| 1618 | NName += 5; |
| 1619 | name = (char**)realloc(name, NName * sizeof(char*)); |
| 1620 | } |
| 1621 | name[nName++] = strdup(n); |
| 1622 | } |
| 1623 | char has_name(const char *n) { |
| 1624 | int i; |
| 1625 | for (i=0; i<nName; i++) { |
| 1626 | if (strcmp(name[i], n)==0) return 1; |
| 1627 | } |
| 1628 | return 0; |
| 1629 | } |
| 1630 | private: |
| 1631 | char **name; |
| 1632 | int nName, NName; |
| 1633 | }; |
| 1634 | |
| 1635 | void fl_fix_focus(); // in Fl.cxx |
| 1636 | |
| 1637 | char fl_show_iconic; // hack for Fl_Window::iconic() |
| 1638 | // int fl_background_pixel = -1; // color to use for background |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1639 | UINT fl_wake_msg = 0; |
| 1640 | int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR |
| 1641 | |
| 1642 | Fl_X* Fl_X::make(Fl_Window* w) { |
| 1643 | Fl_Group::current(0); // get rid of very common user bug: forgot end() |
| 1644 | |
| 1645 | // if the window is a subwindow and our parent is not mapped yet, we |
| 1646 | // mark this window visible, so that mapping the parent at a later |
| 1647 | // point in time will call this function again to finally map the subwindow. |
| 1648 | if (w->parent() && !Fl_X::i(w->window())) { |
| 1649 | w->set_visible(); |
| 1650 | return 0L; |
| 1651 | } |
| 1652 | |
| 1653 | static NameList class_name_list; |
| 1654 | static const char *first_class_name = 0L; |
| 1655 | const char *class_name = w->xclass(); |
| 1656 | if (!class_name) class_name = first_class_name; // reuse first class name used |
| 1657 | if (!class_name) class_name = "FLTK"; // default to create a "FLTK" WNDCLASS |
| 1658 | if (!first_class_name) { |
| 1659 | first_class_name = class_name; |
| 1660 | } |
| 1661 | |
| 1662 | wchar_t class_namew[100]; // (limited) buffer for Windows class name |
| 1663 | |
| 1664 | // convert UTF-8 class_name to wchar_t for RegisterClassExW and CreateWindowExW |
| 1665 | |
| 1666 | fl_utf8toUtf16(class_name,strlen(class_name), // in |
| 1667 | (unsigned short*)class_namew, // out |
| 1668 | sizeof(class_namew)/sizeof(wchar_t)); // max. size |
| 1669 | |
| 1670 | if (!class_name_list.has_name(class_name)) { |
| 1671 | WNDCLASSEXW wcw; |
| 1672 | memset(&wcw, 0, sizeof(wcw)); |
| 1673 | wcw.cbSize = sizeof(WNDCLASSEXW); |
| 1674 | |
| 1675 | // Documentation states a device context consumes about 800 bytes |
| 1676 | // of memory... so who cares? If 800 bytes per window is what it |
| 1677 | // takes to speed things up, I'm game. |
| 1678 | //wc.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC | CS_DBLCLKS; |
| 1679 | wcw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS; |
| 1680 | wcw.lpfnWndProc = (WNDPROC)WndProc; |
| 1681 | wcw.cbClsExtra = wcw.cbWndExtra = 0; |
| 1682 | wcw.hInstance = fl_display; |
| 1683 | if (!w->icon()) |
| 1684 | w->icon((void *)LoadIcon(NULL, IDI_APPLICATION)); |
| 1685 | wcw.hIcon = wcw.hIconSm = (HICON)w->icon(); |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1686 | wcw.hCursor = LoadCursor(NULL, IDC_ARROW); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1687 | //uchar r,g,b; Fl::get_color(FL_GRAY,r,g,b); |
| 1688 | //wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(r,g,b)); |
| 1689 | wcw.hbrBackground = NULL; |
| 1690 | wcw.lpszMenuName = NULL; |
| 1691 | wcw.lpszClassName = class_namew; |
| 1692 | RegisterClassExW(&wcw); |
| 1693 | class_name_list.add_name(class_name); |
| 1694 | } |
| 1695 | |
| 1696 | const wchar_t* message_namew = L"FLTK::ThreadWakeup"; |
| 1697 | if (!fl_wake_msg) fl_wake_msg = RegisterWindowMessageW(message_namew); |
| 1698 | |
| 1699 | HWND parent; |
| 1700 | DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS; |
| 1701 | DWORD styleEx = WS_EX_LEFT; |
| 1702 | |
| 1703 | int xp = w->x(); |
| 1704 | int yp = w->y(); |
| 1705 | int wp = w->w(); |
| 1706 | int hp = w->h(); |
| 1707 | |
| 1708 | int showit = 1; |
| 1709 | |
| 1710 | if (w->parent()) { |
| 1711 | style |= WS_CHILD; |
| 1712 | styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; |
| 1713 | parent = fl_xid(w->window()); |
| 1714 | } else { |
| 1715 | if (!w->size_range_set) { |
| 1716 | if (w->resizable()) { |
| 1717 | Fl_Widget *o = w->resizable(); |
| 1718 | int minw = o->w(); if (minw > 100) minw = 100; |
| 1719 | int minh = o->h(); if (minh > 100) minh = 100; |
| 1720 | w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0); |
| 1721 | } else { |
| 1722 | w->size_range(w->w(), w->h(), w->w(), w->h()); |
| 1723 | } |
| 1724 | } |
| 1725 | styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT; |
| 1726 | int xwm = xp , ywm = yp , bt, bx, by; |
| 1727 | switch (fake_X_wm(w, xwm, ywm, bt, bx, by)) { |
| 1728 | // No border (used for menus) |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1729 | case 0: |
| 1730 | style |= WS_POPUP; |
| 1731 | styleEx |= WS_EX_TOOLWINDOW; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1732 | break; |
| 1733 | |
| 1734 | // Thin border and title bar |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1735 | case 1: |
| 1736 | style |= WS_DLGFRAME | WS_CAPTION; |
| 1737 | if (!w->modal()) |
| 1738 | style |= WS_SYSMENU | WS_MINIMIZEBOX; |
| 1739 | break; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1740 | |
| 1741 | // Thick, resizable border and title bar, with maximize button |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1742 | case 2: |
| 1743 | style |= WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_CAPTION; |
| 1744 | if (!w->modal()) |
| 1745 | style |= WS_MINIMIZEBOX; |
| 1746 | break; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1747 | } |
| 1748 | if (by+bt) { |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1749 | wp += 2*bx; |
| 1750 | hp += 2*by+bt; |
| 1751 | } |
| 1752 | if (!w->force_position()) { |
| 1753 | xp = yp = CW_USEDEFAULT; |
| 1754 | } else { |
| 1755 | if (!Fl::grab()) { |
| 1756 | xp = xwm; yp = ywm; |
| 1757 | w->x(xp);w->y(yp); |
| 1758 | } |
| 1759 | xp -= bx; |
| 1760 | yp -= by+bt; |
| 1761 | } |
| 1762 | |
| 1763 | parent = 0; |
| 1764 | if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) { |
| 1765 | // find some other window to be "transient for": |
| 1766 | Fl_Window* w = Fl_X::first->w; |
| 1767 | while (w->parent()) w = w->window(); |
| 1768 | parent = fl_xid(w); |
| 1769 | if (!w->visible()) showit = 0; |
| 1770 | } else if (Fl::grab()) parent = fl_xid(Fl::grab()); |
| 1771 | } |
| 1772 | |
| 1773 | Fl_X* x = new Fl_X; |
| 1774 | x->other_xid = 0; |
| 1775 | x->setwindow(w); |
| 1776 | x->region = 0; |
| 1777 | x->private_dc = 0; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1778 | x->cursor = LoadCursor(NULL, IDC_ARROW); |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 1779 | x->custom_cursor = 0; |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1780 | if (!fl_codepage) fl_get_codepage(); |
| 1781 | |
| 1782 | WCHAR *lab = NULL; |
| 1783 | if (w->label()) { |
| 1784 | int l = strlen(w->label()); |
| 1785 | // lab = (WCHAR*) malloc((l + 1) * sizeof(short)); |
| 1786 | // l = fl_utf2unicode((unsigned char*)w->label(), l, (xchar*)lab); |
| 1787 | // lab[l] = 0; |
| 1788 | unsigned wlen = fl_utf8toUtf16(w->label(), l, NULL, 0); // Pass NULL to query length |
| 1789 | wlen++; |
| 1790 | lab = (WCHAR *) malloc(sizeof(WCHAR)*wlen); |
| 1791 | wlen = fl_utf8toUtf16(w->label(), l, (unsigned short*)lab, wlen); |
| 1792 | lab[wlen] = 0; |
| 1793 | } |
| 1794 | x->xid = CreateWindowExW( |
| 1795 | styleEx, |
| 1796 | class_namew, lab, style, |
| 1797 | xp, yp, wp, hp, |
| 1798 | parent, |
| 1799 | NULL, // menu |
| 1800 | fl_display, |
| 1801 | NULL // creation parameters |
| 1802 | ); |
| 1803 | if (lab) free(lab); |
| 1804 | |
Pierre Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 1805 | x->set_icons(); |
| 1806 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1807 | if (w->flags() & Fl_Widget::FULLSCREEN) { |
| 1808 | /* We need to make sure that the fullscreen is created on the |
| 1809 | default monitor, ie the desktop where the shortcut is located |
| 1810 | etc. This requires that CreateWindow is called with CW_USEDEFAULT |
| 1811 | for x and y. We can then use GetWindowRect to determine which |
| 1812 | monitor the window was placed on. */ |
| 1813 | RECT rect; |
| 1814 | GetWindowRect(x->xid, &rect); |
| 1815 | make_fullscreen(w, x->xid, rect.left, rect.top, |
| 1816 | rect.right - rect.left, rect.bottom - rect.top); |
| 1817 | } |
| 1818 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1819 | x->next = Fl_X::first; |
| 1820 | Fl_X::first = x; |
| 1821 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1822 | fl_clipboard_notify_target(x->xid); |
| 1823 | |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1824 | x->wait_for_expose = 1; |
| 1825 | if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;} |
| 1826 | if (showit) { |
| 1827 | w->set_visible(); |
| 1828 | int old_event = Fl::e_number; |
| 1829 | w->handle(Fl::e_number = FL_SHOW); // get child windows to appear |
| 1830 | Fl::e_number = old_event; |
| 1831 | w->redraw(); // force draw to happen |
| 1832 | } |
| 1833 | // If we've captured the mouse, we dont want to activate any |
| 1834 | // other windows from the code, or we lose the capture. |
| 1835 | ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE : |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 1836 | (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL); |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 1837 | |
| 1838 | // Register all windows for potential drag'n'drop operations |
| 1839 | fl_OleInitialize(); |
| 1840 | RegisterDragDrop(x->xid, flIDropTarget); |
| 1841 | |
| 1842 | if (!fl_aimm) { |
| 1843 | CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER, |
| 1844 | IID_IActiveIMMApp, (void**) &fl_aimm); |
| 1845 | if (fl_aimm) { |
| 1846 | fl_aimm->Activate(TRUE); |
| 1847 | } |
| 1848 | } |
| 1849 | |
| 1850 | if (w->modal()) {Fl::modal_ = w; fl_fix_focus();} |
| 1851 | return x; |
| 1852 | } |
| 1853 | |
| 1854 | |
| 1855 | |
| 1856 | |
| 1857 | ///////////////////////////////////////////////////////////////////////////// |
| 1858 | /// Win32 timers |
| 1859 | /// |
| 1860 | |
| 1861 | |
| 1862 | static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg, |
| 1863 | WPARAM wParam, LPARAM lParam) |
| 1864 | { |
| 1865 | switch (msg) { |
| 1866 | case WM_TIMER: |
| 1867 | { |
| 1868 | unsigned int id = wParam - 1; |
| 1869 | if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) { |
| 1870 | Fl_Timeout_Handler cb = win32_timers[id].callback; |
| 1871 | void* data = win32_timers[id].data; |
| 1872 | delete_timer(win32_timers[id]); |
| 1873 | if (cb) { |
| 1874 | (*cb)(data); |
| 1875 | } |
| 1876 | } |
| 1877 | } |
| 1878 | return 0; |
| 1879 | |
| 1880 | default: |
| 1881 | break; |
| 1882 | } |
| 1883 | |
| 1884 | return DefWindowProc(hwnd, msg, wParam, lParam); |
| 1885 | } |
| 1886 | |
| 1887 | void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data) |
| 1888 | { |
| 1889 | repeat_timeout(time, cb, data); |
| 1890 | } |
| 1891 | |
| 1892 | void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data) |
| 1893 | { |
| 1894 | int timer_id = -1; |
| 1895 | for (int i = 0; i < win32_timer_used; ++i) { |
| 1896 | if ( !win32_timers[i].handle ) { |
| 1897 | timer_id = i; |
| 1898 | break; |
| 1899 | } |
| 1900 | } |
| 1901 | if (timer_id == -1) { |
| 1902 | if (win32_timer_used == win32_timer_alloc) { |
| 1903 | realloc_timers(); |
| 1904 | } |
| 1905 | timer_id = win32_timer_used++; |
| 1906 | } |
| 1907 | unsigned int elapsed = (unsigned int)(time * 1000); |
| 1908 | |
| 1909 | if ( !s_TimerWnd ) { |
| 1910 | const char* timer_class = "FLTimer"; |
| 1911 | WNDCLASSEX wc; |
| 1912 | memset(&wc, 0, sizeof(wc)); |
| 1913 | wc.cbSize = sizeof (wc); |
| 1914 | wc.style = CS_CLASSDC; |
| 1915 | wc.lpfnWndProc = (WNDPROC)s_TimerProc; |
| 1916 | wc.hInstance = fl_display; |
| 1917 | wc.lpszClassName = timer_class; |
| 1918 | /*ATOM atom =*/ RegisterClassEx(&wc); |
| 1919 | // create a zero size window to handle timer events |
| 1920 | s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW, |
| 1921 | timer_class, "", |
| 1922 | WS_POPUP, |
| 1923 | 0, 0, 0, 0, |
| 1924 | NULL, NULL, fl_display, NULL); |
| 1925 | // just in case this OS won't let us create a 0x0 size window: |
| 1926 | if (!s_TimerWnd) |
| 1927 | s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW, |
| 1928 | timer_class, "", |
| 1929 | WS_POPUP, |
| 1930 | 0, 0, 1, 1, |
| 1931 | NULL, NULL, fl_display, NULL); |
| 1932 | ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE); |
| 1933 | } |
| 1934 | |
| 1935 | win32_timers[timer_id].callback = cb; |
| 1936 | win32_timers[timer_id].data = data; |
| 1937 | |
| 1938 | win32_timers[timer_id].handle = |
| 1939 | SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL); |
| 1940 | } |
| 1941 | |
| 1942 | int Fl::has_timeout(Fl_Timeout_Handler cb, void* data) |
| 1943 | { |
| 1944 | for (int i = 0; i < win32_timer_used; ++i) { |
| 1945 | Win32Timer& t = win32_timers[i]; |
| 1946 | if (t.handle && t.callback == cb && t.data == data) { |
| 1947 | return 1; |
| 1948 | } |
| 1949 | } |
| 1950 | return 0; |
| 1951 | } |
| 1952 | |
| 1953 | void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) |
| 1954 | { |
| 1955 | int i; |
| 1956 | for (i = 0; i < win32_timer_used; ++i) { |
| 1957 | Win32Timer& t = win32_timers[i]; |
| 1958 | if (t.handle && t.callback == cb && |
| 1959 | (t.data == data || data == NULL)) { |
| 1960 | delete_timer(t); |
| 1961 | } |
| 1962 | } |
| 1963 | } |
| 1964 | |
| 1965 | /// END TIMERS |
| 1966 | ///////////////////////////////////////////////////////////////////////////// |
| 1967 | |
| 1968 | |
| 1969 | |
| 1970 | //////////////////////////////////////////////////////////////// |
| 1971 | |
| 1972 | HINSTANCE fl_display = GetModuleHandle(NULL); |
| 1973 | |
| 1974 | void Fl_Window::size_range_() { |
| 1975 | size_range_set = 1; |
| 1976 | } |
| 1977 | |
| 1978 | void Fl_X::set_minmax(LPMINMAXINFO minmax) |
| 1979 | { |
| 1980 | int td, wd, hd, dummy_x, dummy_y; |
| 1981 | |
| 1982 | fake_X_wm(w, dummy_x, dummy_y, td, wd, hd); |
| 1983 | wd *= 2; |
| 1984 | hd *= 2; |
| 1985 | hd += td; |
| 1986 | |
| 1987 | minmax->ptMinTrackSize.x = w->minw + wd; |
| 1988 | minmax->ptMinTrackSize.y = w->minh + hd; |
| 1989 | if (w->maxw) { |
| 1990 | minmax->ptMaxTrackSize.x = w->maxw + wd; |
| 1991 | minmax->ptMaxSize.x = w->maxw + wd; |
| 1992 | } |
| 1993 | if (w->maxh) { |
| 1994 | minmax->ptMaxTrackSize.y = w->maxh + hd; |
| 1995 | minmax->ptMaxSize.y = w->maxh + hd; |
| 1996 | } |
| 1997 | } |
| 1998 | |
| 1999 | //////////////////////////////////////////////////////////////// |
| 2000 | |
| 2001 | #include <FL/filename.H> // need so FL_EXPORT fl_filename_name works |
| 2002 | |
| 2003 | // returns pointer to the filename, or null if name ends with '/' |
| 2004 | const char *fl_filename_name(const char *name) { |
| 2005 | const char *p,*q; |
| 2006 | if (!name) return (0); |
| 2007 | q = name; |
| 2008 | if (q[0] && q[1]==':') q += 2; // skip leading drive letter |
| 2009 | for (p = q; *p; p++) if (*p == '/' || *p == '\\') q = p+1; |
| 2010 | return q; |
| 2011 | } |
| 2012 | |
| 2013 | void Fl_Window::label(const char *name,const char *iname) { |
| 2014 | Fl_Widget::label(name); |
| 2015 | iconlabel_ = iname; |
| 2016 | if (shown() && !parent()) { |
| 2017 | if (!name) name = ""; |
| 2018 | int l = strlen(name); |
| 2019 | // WCHAR *lab = (WCHAR*) malloc((l + 1) * sizeof(short)); |
| 2020 | // l = fl_utf2unicode((unsigned char*)name, l, (xchar*)lab); |
| 2021 | unsigned wlen = fl_utf8toUtf16(name, l, NULL, 0); // Pass NULL to query length |
| 2022 | wlen++; |
| 2023 | unsigned short * lab = (unsigned short*)malloc(sizeof(unsigned short)*wlen); |
| 2024 | wlen = fl_utf8toUtf16(name, l, lab, wlen); |
| 2025 | lab[wlen] = 0; |
| 2026 | SetWindowTextW(i->xid, (WCHAR *)lab); |
| 2027 | free(lab); |
| 2028 | } |
| 2029 | } |
| 2030 | |
| 2031 | //////////////////////////////////////////////////////////////// |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2032 | |
Pierre Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 2033 | static HICON image_to_icon(const Fl_RGB_Image *image, bool is_icon=true, |
| 2034 | int hotx = 0, int hoty = 0) { |
| 2035 | BITMAPV5HEADER bi; |
| 2036 | HBITMAP bitmap, mask; |
| 2037 | DWORD *bits; |
| 2038 | HICON icon; |
| 2039 | |
| 2040 | if (!is_icon) { |
| 2041 | if ((hotx < 0) || (hotx >= image->w())) |
| 2042 | return NULL; |
| 2043 | if ((hoty < 0) || (hoty >= image->h())) |
| 2044 | return NULL; |
| 2045 | } |
| 2046 | |
| 2047 | memset(&bi, 0, sizeof(BITMAPV5HEADER)); |
| 2048 | |
| 2049 | bi.bV5Size = sizeof(BITMAPV5HEADER); |
| 2050 | bi.bV5Width = image->w(); |
| 2051 | bi.bV5Height = -image->h(); // Negative for top-down |
| 2052 | bi.bV5Planes = 1; |
| 2053 | bi.bV5BitCount = 32; |
| 2054 | bi.bV5Compression = BI_BITFIELDS; |
| 2055 | bi.bV5RedMask = 0x00FF0000; |
| 2056 | bi.bV5GreenMask = 0x0000FF00; |
| 2057 | bi.bV5BlueMask = 0x000000FF; |
| 2058 | bi.bV5AlphaMask = 0xFF000000; |
| 2059 | |
| 2060 | HDC hdc; |
| 2061 | |
| 2062 | hdc = GetDC(NULL); |
| 2063 | bitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0); |
| 2064 | ReleaseDC(NULL, hdc); |
| 2065 | |
| 2066 | if (bits == NULL) |
| 2067 | return NULL; |
| 2068 | |
| 2069 | const uchar *i = (const uchar*)*image->data(); |
| 2070 | for (int y = 0;y < image->h();y++) { |
| 2071 | for (int x = 0;x < image->w();x++) { |
| 2072 | switch (image->d()) { |
| 2073 | case 1: |
| 2074 | *bits = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0]; |
| 2075 | break; |
| 2076 | case 2: |
| 2077 | *bits = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0]; |
| 2078 | break; |
| 2079 | case 3: |
| 2080 | *bits = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2]; |
| 2081 | break; |
| 2082 | case 4: |
| 2083 | *bits = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2]; |
| 2084 | break; |
| 2085 | } |
| 2086 | i += image->d(); |
| 2087 | bits++; |
| 2088 | } |
| 2089 | i += image->ld(); |
| 2090 | } |
| 2091 | |
| 2092 | // A mask bitmap is still needed even though it isn't used |
| 2093 | mask = CreateBitmap(image->w(),image->h(),1,1,NULL); |
| 2094 | if (mask == NULL) { |
| 2095 | DeleteObject(bitmap); |
| 2096 | return NULL; |
| 2097 | } |
| 2098 | |
| 2099 | ICONINFO ii; |
| 2100 | |
| 2101 | ii.fIcon = is_icon; |
| 2102 | ii.xHotspot = hotx; |
| 2103 | ii.yHotspot = hoty; |
| 2104 | ii.hbmMask = mask; |
| 2105 | ii.hbmColor = bitmap; |
| 2106 | |
| 2107 | icon = CreateIconIndirect(&ii); |
| 2108 | |
| 2109 | DeleteObject(bitmap); |
| 2110 | DeleteObject(mask); |
| 2111 | |
| 2112 | if (icon == NULL) |
| 2113 | return NULL; |
| 2114 | |
| 2115 | return icon; |
| 2116 | } |
| 2117 | |
| 2118 | //////////////////////////////////////////////////////////////// |
| 2119 | |
| 2120 | static HICON default_big_icon = NULL; |
| 2121 | static HICON default_small_icon = NULL; |
| 2122 | |
| 2123 | const Fl_RGB_Image *find_best_icon(int ideal_width, |
| 2124 | const Fl_RGB_Image *icons[], int count) { |
| 2125 | const Fl_RGB_Image *best; |
| 2126 | |
| 2127 | best = NULL; |
| 2128 | |
| 2129 | for (int i = 0;i < count;i++) { |
| 2130 | if (best == NULL) |
| 2131 | best = icons[i]; |
| 2132 | else { |
| 2133 | if (best->w() < ideal_width) { |
| 2134 | if (icons[i]->w() > best->w()) |
| 2135 | best = icons[i]; |
| 2136 | } else { |
| 2137 | if ((icons[i]->w() >= ideal_width) && |
| 2138 | (icons[i]->w() < best->w())) |
| 2139 | best = icons[i]; |
| 2140 | } |
| 2141 | } |
| 2142 | } |
| 2143 | |
| 2144 | return best; |
| 2145 | } |
| 2146 | |
| 2147 | void Fl_X::set_default_icons(const Fl_RGB_Image *icons[], int count) { |
| 2148 | const Fl_RGB_Image *best_big, *best_small; |
| 2149 | |
| 2150 | if (default_big_icon != NULL) |
| 2151 | DestroyIcon(default_big_icon); |
| 2152 | if (default_small_icon != NULL) |
| 2153 | DestroyIcon(default_small_icon); |
| 2154 | |
| 2155 | best_big = find_best_icon(GetSystemMetrics(SM_CXICON), icons, count); |
| 2156 | best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), icons, count); |
| 2157 | |
| 2158 | if (best_big != NULL) |
| 2159 | default_big_icon = image_to_icon(best_big); |
| 2160 | else |
| 2161 | default_big_icon = NULL; |
| 2162 | |
| 2163 | if (best_small != NULL) |
| 2164 | default_small_icon = image_to_icon(best_small); |
| 2165 | else |
| 2166 | default_small_icon = NULL; |
| 2167 | } |
| 2168 | |
| 2169 | void Fl_X::set_default_icons(HICON big_icon, HICON small_icon) { |
| 2170 | if (default_big_icon != NULL) |
| 2171 | DestroyIcon(default_big_icon); |
| 2172 | if (default_small_icon != NULL) |
| 2173 | DestroyIcon(default_small_icon); |
| 2174 | |
| 2175 | if (big_icon != NULL) |
| 2176 | default_big_icon = CopyIcon(big_icon); |
| 2177 | if (small_icon != NULL) |
| 2178 | default_small_icon = CopyIcon(small_icon); |
| 2179 | } |
| 2180 | |
| 2181 | void Fl_X::set_icons() { |
| 2182 | HICON big_icon, small_icon; |
| 2183 | |
| 2184 | big_icon = NULL; |
| 2185 | small_icon = NULL; |
| 2186 | |
| 2187 | if (w->icon_->count) { |
| 2188 | const Fl_RGB_Image *best_big, *best_small; |
| 2189 | |
| 2190 | best_big = find_best_icon(GetSystemMetrics(SM_CXICON), |
| 2191 | (const Fl_RGB_Image **)w->icon_->icons, |
| 2192 | w->icon_->count); |
| 2193 | best_small = find_best_icon(GetSystemMetrics(SM_CXSMICON), |
| 2194 | (const Fl_RGB_Image **)w->icon_->icons, |
| 2195 | w->icon_->count); |
| 2196 | |
| 2197 | if (best_big != NULL) |
| 2198 | big_icon = image_to_icon(best_big); |
| 2199 | if (best_small != NULL) |
| 2200 | small_icon = image_to_icon(best_small); |
| 2201 | } else { |
| 2202 | big_icon = default_big_icon; |
| 2203 | small_icon = default_small_icon; |
| 2204 | } |
| 2205 | |
| 2206 | if (big_icon != NULL) |
| 2207 | SendMessage(xid, WM_SETICON, ICON_BIG, (LPARAM)big_icon); |
| 2208 | if (small_icon != NULL) |
| 2209 | SendMessage(xid, WM_SETICON, ICON_SMALL, (LPARAM)small_icon); |
| 2210 | |
| 2211 | if (w->icon_->count) { |
| 2212 | if (big_icon != NULL) |
| 2213 | DestroyIcon(big_icon); |
| 2214 | if (small_icon != NULL) |
| 2215 | DestroyIcon(small_icon); |
| 2216 | } |
| 2217 | } |
| 2218 | |
| 2219 | void Fl_Window::default_icons(HICON big_icon, HICON small_icon) { |
| 2220 | Fl_X::set_default_icons(big_icon, small_icon); |
| 2221 | } |
| 2222 | |
| 2223 | void Fl_Window::icons(HICON big_icon, HICON small_icon) { |
| 2224 | free_icons(); |
| 2225 | |
| 2226 | if (big_icon != NULL) |
| 2227 | icon_->big_icon = CopyIcon(big_icon); |
| 2228 | if (small_icon != NULL) |
| 2229 | icon_->small_icon = CopyIcon(small_icon); |
| 2230 | |
| 2231 | if (i) |
| 2232 | i->set_icons(); |
| 2233 | } |
| 2234 | |
| 2235 | //////////////////////////////////////////////////////////////// |
| 2236 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2237 | #ifndef IDC_HAND |
| 2238 | # define IDC_HAND MAKEINTRESOURCE(32649) |
| 2239 | #endif // !IDC_HAND |
| 2240 | |
| 2241 | int Fl_X::set_cursor(Fl_Cursor c) { |
| 2242 | LPSTR n; |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 2243 | HCURSOR new_cursor; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2244 | |
| 2245 | if (c == FL_CURSOR_NONE) |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 2246 | new_cursor = NULL; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2247 | else { |
| 2248 | switch (c) { |
| 2249 | case FL_CURSOR_ARROW: n = IDC_ARROW; break; |
| 2250 | case FL_CURSOR_CROSS: n = IDC_CROSS; break; |
| 2251 | case FL_CURSOR_WAIT: n = IDC_WAIT; break; |
| 2252 | case FL_CURSOR_INSERT: n = IDC_IBEAM; break; |
| 2253 | case FL_CURSOR_HAND: n = IDC_HAND; break; |
| 2254 | case FL_CURSOR_HELP: n = IDC_HELP; break; |
| 2255 | case FL_CURSOR_MOVE: n = IDC_SIZEALL; break; |
| 2256 | case FL_CURSOR_N: |
| 2257 | case FL_CURSOR_S: |
| 2258 | // FIXME: Should probably have fallbacks for these instead |
| 2259 | case FL_CURSOR_NS: n = IDC_SIZENS; break; |
| 2260 | case FL_CURSOR_NE: |
| 2261 | case FL_CURSOR_SW: |
| 2262 | // FIXME: Dito. |
| 2263 | case FL_CURSOR_NESW: n = IDC_SIZENESW; break; |
| 2264 | case FL_CURSOR_E: |
| 2265 | case FL_CURSOR_W: |
| 2266 | // FIXME: Dito. |
| 2267 | case FL_CURSOR_WE: n = IDC_SIZEWE; break; |
| 2268 | case FL_CURSOR_SE: |
| 2269 | case FL_CURSOR_NW: |
| 2270 | // FIXME: Dito. |
| 2271 | case FL_CURSOR_NWSE: n = IDC_SIZENWSE; break; |
| 2272 | default: |
| 2273 | return 0; |
| 2274 | } |
| 2275 | |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 2276 | new_cursor = LoadCursor(NULL, n); |
| 2277 | if (new_cursor == NULL) |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2278 | return 0; |
| 2279 | } |
| 2280 | |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 2281 | if ((cursor != NULL) && custom_cursor) |
| 2282 | DestroyIcon(cursor); |
| 2283 | |
| 2284 | cursor = new_cursor; |
| 2285 | custom_cursor = 0; |
| 2286 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2287 | SetCursor(cursor); |
| 2288 | |
| 2289 | return 1; |
| 2290 | } |
| 2291 | |
| 2292 | int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) { |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 2293 | HCURSOR new_cursor; |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2294 | |
Pierre Ossman | ab8aeed | 2012-04-25 14:57:22 +0000 | [diff] [blame] | 2295 | new_cursor = image_to_icon(image, false, hotx, hoty); |
Henrik Andersson | 485f419 | 2011-09-16 11:51:32 +0000 | [diff] [blame] | 2296 | if (new_cursor == NULL) |
| 2297 | return 0; |
| 2298 | |
| 2299 | if ((cursor != NULL) && custom_cursor) |
| 2300 | DestroyIcon(cursor); |
| 2301 | |
| 2302 | cursor = new_cursor; |
| 2303 | custom_cursor = 1; |
| 2304 | |
DRC | 685f17e | 2011-07-28 09:23:00 +0000 | [diff] [blame] | 2305 | SetCursor(cursor); |
| 2306 | |
| 2307 | return 1; |
| 2308 | } |
| 2309 | |
| 2310 | //////////////////////////////////////////////////////////////// |
DRC | 2ff39b8 | 2011-07-28 08:38:59 +0000 | [diff] [blame] | 2311 | // Implement the virtual functions for the base Fl_Window class: |
| 2312 | |
| 2313 | // If the box is a filled rectangle, we can make the redisplay *look* |
| 2314 | // faster by using X's background pixel erasing. We can make it |
| 2315 | // actually *be* faster by drawing the frame only, this is done by |
| 2316 | // setting fl_boxcheat, which is seen by code in fl_drawbox.cxx: |
| 2317 | // For WIN32 it looks like all windows share a background color, so |
| 2318 | // I use FL_GRAY for this and only do this cheat for windows that are |
| 2319 | // that color. |
| 2320 | // Actually it is totally disabled. |
| 2321 | // Fl_Widget *fl_boxcheat; |
| 2322 | //static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);} |
| 2323 | |
| 2324 | void Fl_Window::show() { |
| 2325 | image(Fl::scheme_bg_); |
| 2326 | if (Fl::scheme_bg_) { |
| 2327 | labeltype(FL_NORMAL_LABEL); |
| 2328 | align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP); |
| 2329 | } else { |
| 2330 | labeltype(FL_NO_LABEL); |
| 2331 | } |
| 2332 | Fl_Tooltip::exit(this); |
| 2333 | if (!shown()) { |
| 2334 | // if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color()); |
| 2335 | Fl_X::make(this); |
| 2336 | } else { |
| 2337 | // Once again, we would lose the capture if we activated the window. |
| 2338 | if (IsIconic(i->xid)) OpenIcon(i->xid); |
| 2339 | if (!fl_capture) BringWindowToTop(i->xid); |
| 2340 | //ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE); |
| 2341 | } |
| 2342 | #ifdef USE_PRINT_BUTTON |
| 2343 | void preparePrintFront(void); |
| 2344 | preparePrintFront(); |
| 2345 | #endif |
| 2346 | } |
| 2347 | |
| 2348 | Fl_Window *Fl_Window::current_; |
| 2349 | // the current context |
| 2350 | HDC fl_gc = 0; |
| 2351 | // the current window handle, initially set to -1 so we can correctly |
| 2352 | // allocate fl_GetDC(0) |
| 2353 | HWND fl_window = NULL; |
| 2354 | |
| 2355 | // Here we ensure only one GetDC is ever in place. |
| 2356 | HDC fl_GetDC(HWND w) { |
| 2357 | if (fl_gc) { |
| 2358 | if (w == fl_window && fl_window != NULL) return fl_gc; |
| 2359 | if (fl_window) fl_release_dc(fl_window, fl_gc); // ReleaseDC |
| 2360 | } |
| 2361 | fl_gc = GetDC(w); |
| 2362 | fl_save_dc(w, fl_gc); |
| 2363 | fl_window = w; |
| 2364 | // calling GetDC seems to always reset these: (?) |
| 2365 | SetTextAlign(fl_gc, TA_BASELINE|TA_LEFT); |
| 2366 | SetBkMode(fl_gc, TRANSPARENT); |
| 2367 | |
| 2368 | return fl_gc; |
| 2369 | } |
| 2370 | |
| 2371 | // make X drawing go into this window (called by subclass flush() impl.) |
| 2372 | void Fl_Window::make_current() { |
| 2373 | fl_GetDC(fl_xid(this)); |
| 2374 | |
| 2375 | #if USE_COLORMAP |
| 2376 | // Windows maintains a hardware and software color palette; the |
| 2377 | // SelectPalette() call updates the current soft->hard mapping |
| 2378 | // for all drawing calls, so we must select it here before any |
| 2379 | // code does any drawing... |
| 2380 | |
| 2381 | fl_select_palette(); |
| 2382 | #endif // USE_COLORMAP |
| 2383 | |
| 2384 | current_ = this; |
| 2385 | fl_clip_region(0); |
| 2386 | |
| 2387 | |
| 2388 | } |
| 2389 | |
| 2390 | /* Make sure that all allocated fonts are released. This works only if |
| 2391 | Fl::run() is allowed to exit by closing all windows. Calling 'exit(int)' |
| 2392 | will not automatically free any fonts. */ |
| 2393 | void fl_free_fonts(void) |
| 2394 | { |
| 2395 | // remove the Fl_Font_Descriptor chains |
| 2396 | int i; |
| 2397 | Fl_Fontdesc * s; |
| 2398 | Fl_Font_Descriptor * f; |
| 2399 | Fl_Font_Descriptor * ff; |
| 2400 | for (i=0; i<FL_FREE_FONT; i++) { |
| 2401 | s = fl_fonts + i; |
| 2402 | for (f=s->first; f; f=ff) { |
| 2403 | ff = f->next; |
| 2404 | delete f; |
| 2405 | s->first = ff; |
| 2406 | } |
| 2407 | } |
| 2408 | } |
| 2409 | |
| 2410 | |
| 2411 | /////////////////////////////////////////////////////////////////////// |
| 2412 | // |
| 2413 | // The following routines help fix a problem with the leaking of Windows |
| 2414 | // Device Context (DC) objects. The 'proper' protocol is for a program to |
| 2415 | // acquire a DC, save its state, do the modifications needed for drawing, |
| 2416 | // perform the drawing, restore the initial state, and release the DC. In |
| 2417 | // FLTK, the save and restore steps have previously been omitted and DCs are |
| 2418 | // not properly released, leading to a great number of DC leaks. As some |
| 2419 | // Windows "OSs" will hang when any process exceeds roughly 10,000 GDI objects, |
| 2420 | // it is important to control GDI leaks, which are much more important than memory |
| 2421 | // leaks. The following struct, global variable, and routines help implement |
| 2422 | // the above protocol for those cases where the GetDC and RestoreDC are not in |
| 2423 | // the same routine. For each GetDC, fl_save_dc is used to create an entry in |
| 2424 | // a linked list that saves the window handle, the DC handle, and the initial |
| 2425 | // state. When the DC is to be released, 'fl_release_dc' is called. It restores |
| 2426 | // the initial state and releases the DC. When the program exits, 'fl_cleanup_dc_list' |
| 2427 | // frees any remaining nodes in the list. |
| 2428 | |
| 2429 | struct Win_DC_List { // linked list |
| 2430 | HWND window; // window handle |
| 2431 | HDC dc; // device context handle |
| 2432 | int saved_dc; // initial state of DC |
| 2433 | Win_DC_List * next; // pointer to next item |
| 2434 | }; |
| 2435 | |
| 2436 | static Win_DC_List * win_DC_list = 0; |
| 2437 | |
| 2438 | void fl_save_dc( HWND w, HDC dc) { |
| 2439 | Win_DC_List * t; |
| 2440 | t = new Win_DC_List; |
| 2441 | t->window = w; |
| 2442 | t->dc = dc; |
| 2443 | t->saved_dc = SaveDC(dc); |
| 2444 | if (win_DC_list) |
| 2445 | t->next = win_DC_list; |
| 2446 | else |
| 2447 | t->next = NULL; |
| 2448 | win_DC_list = t; |
| 2449 | } |
| 2450 | |
| 2451 | void fl_release_dc(HWND w, HDC dc) { |
| 2452 | Win_DC_List * t= win_DC_list; |
| 2453 | Win_DC_List * prev = 0; |
| 2454 | if (!t) |
| 2455 | return; |
| 2456 | do { |
| 2457 | if (t->dc == dc) { |
| 2458 | RestoreDC(dc, t->saved_dc); |
| 2459 | ReleaseDC(w, dc); |
| 2460 | if (!prev) { |
| 2461 | win_DC_list = t->next; // delete first item |
| 2462 | } else { |
| 2463 | prev->next = t->next; // one in the middle |
| 2464 | } |
| 2465 | delete (t); |
| 2466 | return; |
| 2467 | } |
| 2468 | prev = t; |
| 2469 | t = t->next; |
| 2470 | } while (t); |
| 2471 | } |
| 2472 | |
| 2473 | void fl_cleanup_dc_list(void) { // clean up the list |
| 2474 | Win_DC_List * t = win_DC_list; |
| 2475 | if (!t)return; |
| 2476 | do { |
| 2477 | RestoreDC(t->dc, t->saved_dc); |
| 2478 | ReleaseDC(t->window, t->dc); |
| 2479 | win_DC_list = t->next; |
| 2480 | delete (t); |
| 2481 | t = win_DC_list; |
| 2482 | } while(t); |
| 2483 | } |
| 2484 | |
| 2485 | Fl_Region XRectangleRegion(int x, int y, int w, int h) { |
| 2486 | if (Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id) return CreateRectRgn(x,y,x+w,y+h); |
| 2487 | // because rotation may apply, the rectangle becomes a polygon in device coords |
| 2488 | POINT pt[4] = { {x, y}, {x + w, y}, {x + w, y + h}, {x, y + h} }; |
| 2489 | LPtoDP(fl_gc, pt, 4); |
| 2490 | return CreatePolygonRgn(pt, 4, ALTERNATE); |
| 2491 | } |
| 2492 | |
| 2493 | Window fl_xid_(const Fl_Window *w) { |
| 2494 | Fl_X *temp = Fl_X::i(w); |
| 2495 | return temp ? temp->xid : 0; |
| 2496 | } |
| 2497 | |
| 2498 | int Fl_Window::decorated_w() |
| 2499 | { |
| 2500 | if (!shown() || parent() || !border() || !visible()) return w(); |
| 2501 | int X, Y, bt, bx, by; |
| 2502 | Fl_X::fake_X_wm(this, X, Y, bt, bx, by); |
| 2503 | return w() + 2 * bx; |
| 2504 | } |
| 2505 | |
| 2506 | int Fl_Window::decorated_h() |
| 2507 | { |
| 2508 | if (!shown() || parent() || !border() || !visible()) return h(); |
| 2509 | int X, Y, bt, bx, by; |
| 2510 | Fl_X::fake_X_wm(this, X, Y, bt, bx, by); |
| 2511 | return h() + bt + 2 * by; |
| 2512 | } |
| 2513 | |
| 2514 | void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset) |
| 2515 | { |
| 2516 | if (!win->shown() || win->parent() || !win->border() || !win->visible()) { |
| 2517 | this->print_widget(win, x_offset, y_offset); |
| 2518 | return; |
| 2519 | } |
| 2520 | int X, Y, bt, bx, by, ww, wh; // compute the window border sizes |
| 2521 | Fl_X::fake_X_wm(win, X, Y, bt, bx, by); |
| 2522 | ww = win->w() + 2 * bx; |
| 2523 | wh = win->h() + bt + 2 * by; |
| 2524 | Fl_Display_Device::display_device()->set_current(); // make window current |
| 2525 | win->show(); |
| 2526 | Fl::check(); |
| 2527 | win->make_current(); |
| 2528 | HDC save_gc = fl_gc; |
| 2529 | fl_gc = GetDC(NULL); // get the screen device context |
| 2530 | // capture the 4 window sides from screen |
| 2531 | RECT r; GetWindowRect(fl_window, &r); |
| 2532 | uchar *top_image = fl_read_image(NULL, r.left, r.top, ww, bt + by); |
| 2533 | uchar *left_image = fl_read_image(NULL, r.left, r.top, bx, wh); |
| 2534 | uchar *right_image = fl_read_image(NULL, r.right - bx, r.top, bx, wh); |
| 2535 | uchar *bottom_image = fl_read_image(NULL, r.left, r.bottom-by, ww, by); |
| 2536 | ReleaseDC(NULL, fl_gc); fl_gc = save_gc; |
| 2537 | this->set_current(); |
| 2538 | // print the 4 window sides |
| 2539 | fl_draw_image(top_image, x_offset, y_offset, ww, bt + by, 3); |
| 2540 | fl_draw_image(left_image, x_offset, y_offset, bx, wh, 3); |
| 2541 | fl_draw_image(right_image, x_offset + win->w() + bx, y_offset, bx, wh, 3); |
| 2542 | fl_draw_image(bottom_image, x_offset, y_offset + win->h() + bt + by, ww, by, 3); |
| 2543 | delete[] top_image; |
| 2544 | delete[] left_image; |
| 2545 | delete[] right_image; |
| 2546 | delete[] bottom_image; |
| 2547 | // print the window inner part |
| 2548 | this->print_widget(win, x_offset + bx, y_offset + bt + by); |
| 2549 | fl_gc = GetDC(fl_xid(win)); |
| 2550 | ReleaseDC(fl_xid(win), fl_gc); |
| 2551 | } |
| 2552 | |
| 2553 | #ifdef USE_PRINT_BUTTON |
| 2554 | // to test the Fl_Printer class creating a "Print front window" button in a separate window |
| 2555 | // contains also preparePrintFront call above |
| 2556 | #include <FL/Fl_Printer.H> |
| 2557 | #include <FL/Fl_Button.H> |
| 2558 | void printFront(Fl_Widget *o, void *data) |
| 2559 | { |
| 2560 | Fl_Printer printer; |
| 2561 | o->window()->hide(); |
| 2562 | Fl_Window *win = Fl::first_window(); |
| 2563 | if(!win) return; |
| 2564 | int w, h; |
| 2565 | if( printer.start_job(1) ) { o->window()->show(); return; } |
| 2566 | if( printer.start_page() ) { o->window()->show(); return; } |
| 2567 | printer.printable_rect(&w,&h); |
| 2568 | int wh, ww; |
| 2569 | wh = win->decorated_h(); |
| 2570 | ww = win->decorated_w(); |
| 2571 | // scale the printer device so that the window fits on the page |
| 2572 | float scale = 1; |
| 2573 | if (ww > w || wh > h) { |
| 2574 | scale = (float)w/ww; |
| 2575 | if ((float)h/wh < scale) scale = (float)h/wh; |
| 2576 | printer.scale(scale, scale); |
| 2577 | } |
| 2578 | // #define ROTATE 20.0 |
| 2579 | #ifdef ROTATE |
| 2580 | printer.scale(scale * 0.8, scale * 0.8); |
| 2581 | printer.printable_rect(&w, &h); |
| 2582 | printer.origin(w/2, h/2 ); |
| 2583 | printer.rotate(ROTATE); |
| 2584 | printer.print_widget( win, - win->w()/2, - win->h()/2 ); |
| 2585 | //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 ); |
| 2586 | #else |
| 2587 | printer.print_window(win); |
| 2588 | #endif |
| 2589 | printer.end_page(); |
| 2590 | printer.end_job(); |
| 2591 | o->window()->show(); |
| 2592 | } |
| 2593 | |
| 2594 | void preparePrintFront(void) |
| 2595 | { |
| 2596 | static BOOL first=TRUE; |
| 2597 | if(!first) return; |
| 2598 | first=FALSE; |
| 2599 | static Fl_Window w(0,0,120,30); |
| 2600 | static Fl_Button b(0,0,w.w(),w.h(), "Print front window"); |
| 2601 | b.callback(printFront); |
| 2602 | w.end(); |
| 2603 | w.show(); |
| 2604 | } |
| 2605 | #endif // USE_PRINT_BUTTON |
| 2606 | |
| 2607 | #endif // FL_DOXYGEN |
| 2608 | |
| 2609 | // |
| 2610 | // End of "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $". |
| 2611 | // |