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