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