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