themes: Workaround for bug where a background color of RGB 0,0,0 in Black color schem...
[ntk.git] / src / Fl_win32.cxx
blob54d9c813d5ae0583c23e2756e564d80ab9c4df96
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.
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.
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.
23 // Please report all bugs and problems on the following page:
25 // http://www.fltk.org/str.php
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.
32 // This file must be #include'd in Fl.cxx and not compiled separately.
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
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
64 #if defined(__GNUC__)
65 # include <wchar.h>
66 #endif
68 #include <ole2.h>
69 #include <shellapi.h>
71 #include "aimm.h"
74 // USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()...
75 // USE_ASYNC_SELECT is OBSOLETED in 1.3 for the following reasons:
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.
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...
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
101 // dynamic wsock dll handling api:
102 #if defined(__CYGWIN__) && !defined(SOCKET)
103 # define SOCKET int
104 #endif
106 // note: winsock2.h has been #include'd in Fl.cxx
107 #define WSCK_DLL_NAME "WS2_32.DLL"
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 *);
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;
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");
124 return s_wsock_mod;
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.
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;
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");
154 return s_imm_module;
157 // USE_TRACK_MOUSE - define NO_TRACK_MOUSE if you don't have
158 // TrackMouseEvent()...
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.
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).
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).
178 // #define NO_TRACK_MOUSE
180 #if !defined(NO_TRACK_MOUSE)
181 # define USE_TRACK_MOUSE
182 #endif // NO_TRACK_MOUSE
184 static Fl_Window *track_mouse_win=0; // current TrackMouseEvent() window
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).
194 #ifdef USE_TRACK_MOUSE
195 #define USE_CAPTURE_MOUSE_WIN
196 #endif // USE_TRACK_MOUSE
199 // WM_SYNCPAINT is an "undocumented" message, which is finally defined in
200 // VC++ 6.0.
203 #ifndef WM_SYNCPAINT
204 # define WM_SYNCPAINT 0x0088
205 #endif
207 #ifndef WM_MOUSELEAVE
208 # define WM_MOUSELEAVE 0x02a3
209 #endif
211 #ifndef WM_MOUSEWHEEL
212 # define WM_MOUSEWHEEL 0x020a
213 #endif
215 #ifndef WHEEL_DELTA
216 # define WHEEL_DELTA 120 // according to MSDN.
217 #endif
221 // WM_FLSELECT is the user-defined message that we get when one of
222 // the sockets has pending data, etc.
225 #define WM_FLSELECT (WM_APP+1) // WM_APP is used for hide-window
228 ////////////////////////////////////////////////////////////////
229 // interface to poll/select call:
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...
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).
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().
246 static int maxfd = 0;
247 static fd_set fdsets[3];
249 extern IDropTarget *flIDropTarget;
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;
260 extern unsigned int fl_codepage;
262 void fl_reset_spot()
266 void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win)
268 if (!win) return;
269 Fl_Window* tw = win;
270 while (tw->parent()) tw = tw->window(); // find top level window
272 get_imm_module();
273 HIMC himc = flImmGetContext(fl_xid(tw));
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);
286 void fl_set_status(int x, int y, int w, int h)
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));
297 fd[i].fd = n;
298 fd[i].events = (short)events;
299 fd[i].cb = cb;
300 fd[i].arg = v;
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;
308 void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
309 Fl::add_fd(fd, FL_READ, cb, v);
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;
320 // move it down in the array if necessary:
321 if (j<i) {
322 fd[j]=fd[i];
324 j++;
326 nfds = j;
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]);
333 void Fl::remove_fd(int n) {
334 remove_fd(n, -1);
337 // these pointers are set by the Fl::lock() function:
338 static void nothing() {}
339 void (*fl_lock_function)() = nothing;
340 void (*fl_unlock_function)() = nothing;
342 static void* thread_message_;
343 void* Fl::thread_message() {
344 void* r = thread_message_;
345 thread_message_ = 0;
346 return r;
349 IActiveIMMApp *fl_aimm = NULL;
350 MSG fl_msg;
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.
357 int fl_wait(double time_to_wait) {
358 int have_message = 0;
360 run_checks();
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;
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;
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);
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;
396 if (Fl::idle || Fl::damage())
397 time_to_wait = 0.0;
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;
404 fl_unlock_function();
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);
410 fl_lock_function();
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);
427 TranslateMessage(&fl_msg);
428 DispatchMessageW(&fl_msg);
429 have_message = PeekMessageW(&fl_msg, NULL, 0, 0, PM_REMOVE);
432 Fl::flush();
434 // This should return 0 if only timer events were handled:
435 return 1;
438 // fl_ready() is just like fl_wait(0.0) except no callbacks are done:
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;
450 ////////////////////////////////////////////////////////////////
452 int Fl::x()
454 RECT r;
456 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
457 return r.left;
460 int Fl::y()
462 RECT r;
464 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
465 return r.top;
468 int Fl::h()
470 RECT r;
472 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
473 return r.bottom - r.top;
476 int Fl::w()
478 RECT r;
480 SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
481 return r.right - r.left;
484 void Fl::get_mouse(int &x, int &y) {
485 POINT p;
486 GetCursorPos(&p);
487 x = p.x;
488 y = p.y;
491 ////////////////////////////////////////////////////////////////
492 // code used for selections:
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];
499 UINT fl_get_lcid_codepage(LCID id)
501 char buf[8];
502 buf[GetLocaleInfo(id, LOCALE_IDEFAULTANSICODEPAGE, buf, 8)] = 0;
503 return atol(buf);
506 // Convert \n -> \r\n
507 class Lf2CrlfConvert {
508 char *out;
509 int outlen;
510 public:
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; }
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++; }
536 *o++ = 0;
538 ~Lf2CrlfConvert() {
539 delete[] out;
541 int GetLength() const { return(outlen); }
542 const char* GetValue() const { return(out); }
545 // call this when you create a selection:
546 void Fl::copy(const char *stuff, int len, int clipboard) {
547 if (!stuff || len<0) return;
549 // Convert \n -> \r\n (for old apps like Notepad, DOS)
550 Lf2CrlfConvert buf(stuff, len);
551 len = buf.GetLength();
552 stuff = buf.GetValue();
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;
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;
583 // Call this when a "paste" operation happens:
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!
590 // Convert \r\n -> \n
591 char *i = fl_selection_buffer[clipboard];
592 if (i==0L) {
593 Fl::e_text = 0;
594 return;
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++;
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++;
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;
629 CloseClipboard();
633 ////////////////////////////////////////////////////////////////
634 char fl_is_ime = 0;
635 void fl_get_codepage()
637 HKL hkl = GetKeyboardLayout(0);
638 TCHAR ld[8];
640 GetLocaleInfo (LOWORD(hkl), LOCALE_IDEFAULTANSICODEPAGE, ld, 6);
641 DWORD ccp = atol(ld);
642 fl_is_ime = 0;
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;
652 HWND fl_capture;
654 static int mouse_event(Fl_Window *window, int what, int button,
655 WPARAM wParam, LPARAM lParam)
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();
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;
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;
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);
700 case 2: // release:
701 if (!fl_capture) ReleaseCapture();
702 Fl::e_keysym = FL_Button + button;
703 return Fl::handle(FL_RELEASE,window);
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);
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, '\''}
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;
794 for (i = 0; i < 256; i++) if (!extendedlut[i]) extendedlut[i] = vklut[i];
796 return extended ? extendedlut[vk] : vklut[vk];
799 #if USE_COLORMAP
800 extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx
801 #endif
804 /////////////////////////////////////////////////////////////////////////////
805 /// Win32 timers
808 struct Win32Timer
810 UINT_PTR handle;
811 Fl_Timeout_Handler callback;
812 void *data;
814 static Win32Timer* win32_timers;
815 static int win32_timer_alloc;
816 static int win32_timer_used;
817 static HWND s_TimerWnd;
819 static void realloc_timers()
821 if (win32_timer_alloc == 0) {
822 win32_timer_alloc = 8;
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;
833 static void delete_timer(Win32Timer& t)
835 KillTimer(s_TimerWnd, t.handle);
836 memset(&t, 0, sizeof(Win32Timer));
839 /// END TIMERS
840 /////////////////////////////////////////////////////////////////////////////
842 static Fl_Window* resize_bug_fix;
844 extern void fl_save_pen(void);
845 extern void fl_restore_pen(void);
847 static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
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 = ???
860 Fl_Window *window = fl_find(hWnd);
862 if (window) switch (uMsg) {
864 case WM_QUIT: // this should not happen?
865 Fl::fatal("WM_QUIT message");
867 case WM_CLOSE: // user clicked close box
868 Fl::handle(FL_CLOSE, window);
869 PostQuitMessage(0);
870 return 0;
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;
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;
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;
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;
905 if (window->type() == FL_DOUBLE_WINDOW) ValidateRgn(hWnd,0);
906 else ValidateRgn(hWnd,i->region);
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;
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;
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;
940 #endif // USE_TRACK_MOUSE
941 mouse_event(window, 3, 0, wParam, lParam);
942 return 0;
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);
951 track_mouse_win = 0; // force TrackMouseEvent() restart
952 break;
954 case WM_SETFOCUS:
955 Fl::handle(FL_FOCUS, window);
956 break;
958 case WM_KILLFOCUS:
959 Fl::handle(FL_UNFOCUS, window);
960 Fl::flush(); // it never returns to main loop when deactivated...
961 break;
963 case WM_SHOWWINDOW:
964 if (!window->parent()) {
965 Fl::handle(wParam ? FL_SHOW : FL_HIDE, window);
967 break;
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)
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;
988 break;
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))
1012 uMsg = fl_msg.message;
1013 wParam = fl_msg.wParam;
1014 lParam = fl_msg.lParam;
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;
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) {
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;
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;
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;
1106 #else
1107 buffer[0] = 0;
1108 Fl::e_length = 0;
1109 #endif
1111 Fl::e_text = buffer;
1112 if (lParam & (1<<31)) { // key up events.
1113 if (Fl::handle(FL_KEYUP, window)) return 0;
1114 break;
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;
1123 break;}
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;
1134 case WM_GETMINMAXINFO:
1135 Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam);
1136 break;
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));
1148 break;
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;
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;
1165 break;
1167 #if USE_COLORMAP
1168 case WM_QUERYNEWPALETTE :
1169 fl_GetDC(hWnd);
1170 if (fl_select_palette()) InvalidateRect(hWnd, NULL, FALSE);
1171 break;
1173 case WM_PALETTECHANGED:
1174 fl_GetDC(hWnd);
1175 if ((HWND)wParam != hWnd && fl_select_palette()) UpdateColors(fl_gc);
1176 break;
1178 case WM_CREATE :
1179 fl_GetDC(hWnd);
1180 fl_select_palette();
1181 break;
1182 #endif
1184 case WM_DESTROYCLIPBOARD:
1185 fl_i_own_selection[1] = 0;
1186 return 1;
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;
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);
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;}
1216 default:
1217 if (Fl::handle(0,0)) return 0;
1218 break;
1222 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
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:
1231 // value | border | title bar
1232 // 0 | none | no
1233 // 1 | fix | yes
1234 // 2 | size | yes
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;
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;
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);
1291 bt = GetSystemMetrics(SM_CYCAPTION);
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;
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;
1323 return ret;
1326 ////////////////////////////////////////////////////////////////
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;
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;
1349 } else {
1350 x(X); y(Y);
1351 flags |= SWP_NOSIZE;
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;
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);
1373 ////////////////////////////////////////////////////////////////
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.
1380 class NameList {
1381 public:
1382 NameList() { name = (char**)malloc(sizeof(char**)); NName = 1; nName = 0; }
1383 ~NameList() {
1384 int i;
1385 for (i=0; i<nName; i++) free(name[i]);
1386 if (name) free(name);
1388 void add_name(const char *n) {
1389 if (NName==nName) {
1390 NName += 5;
1391 name = (char**)realloc(name, NName * sizeof(char*));
1393 name[nName++] = strdup(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;
1400 return 0;
1402 private:
1403 char **name;
1404 int nName, NName;
1407 void fl_fix_focus(); // in Fl.cxx
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
1415 Fl_X* Fl_X::make(Fl_Window* w) {
1416 Fl_Group::current(0); // get rid of very common user bug: forgot end()
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;
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;
1435 wchar_t class_namew[100]; // (limited) buffer for Windows class name
1437 // convert UTF-8 class_name to wchar_t for RegisterClassExW and CreateWindowExW
1439 fl_utf8toUtf16(class_name,strlen(class_name), // in
1440 (unsigned short*)class_namew, // out
1441 sizeof(class_namew)/sizeof(wchar_t)); // max. size
1443 if (!class_name_list.has_name(class_name)) {
1444 WNDCLASSEXW wcw;
1445 memset(&wcw, 0, sizeof(wcw));
1446 wcw.cbSize = sizeof(WNDCLASSEXW);
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);
1469 const wchar_t* message_namew = L"FLTK::ThreadWakeup";
1470 if (!fl_wake_msg) fl_wake_msg = RegisterWindowMessageW(message_namew);
1472 HWND parent;
1473 DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
1474 DWORD styleEx = WS_EX_LEFT;
1476 int xp = w->x();
1477 int yp = w->y();
1478 int wp = w->w();
1479 int hp = w->h();
1481 int showit = 1;
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());
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;
1506 // Thin border and title bar
1507 case 1: style |= WS_DLGFRAME | WS_CAPTION; break;
1509 // Thick, resizable border and title bar, with maximize button
1510 case 2: style |= WS_THICKFRAME | WS_MAXIMIZEBOX | WS_CAPTION ; break;
1512 if (by+bt) {
1513 if (!w->modal()) style |= WS_SYSMENU | WS_MINIMIZEBOX;
1514 wp += 2*bx;
1515 hp += 2*by+bt;
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);
1524 xp -= bx;
1525 yp -= by+bt;
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());
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();
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;
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
1567 if (lab) free(lab);
1569 x->next = Fl_X::first;
1570 Fl_X::first = x;
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
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);
1586 // Register all windows for potential drag'n'drop operations
1587 fl_OleInitialize();
1588 RegisterDragDrop(x->xid, flIDropTarget);
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);
1598 if (w->modal()) {Fl::modal_ = w; fl_fix_focus();}
1599 return x;
1605 /////////////////////////////////////////////////////////////////////////////
1606 /// Win32 timers
1610 static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg,
1611 WPARAM wParam, LPARAM lParam)
1613 switch (msg) {
1614 case WM_TIMER:
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);
1626 return 0;
1628 default:
1629 break;
1632 return DefWindowProc(hwnd, msg, wParam, lParam);
1635 void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
1637 repeat_timeout(time, cb, data);
1640 void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
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;
1649 if (timer_id == -1) {
1650 if (win32_timer_used == win32_timer_alloc) {
1651 realloc_timers();
1653 timer_id = win32_timer_used++;
1655 unsigned int elapsed = (unsigned int)(time * 1000);
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);
1683 win32_timers[timer_id].callback = cb;
1684 win32_timers[timer_id].data = data;
1686 win32_timers[timer_id].handle =
1687 SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL);
1690 int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
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;
1698 return 0;
1701 void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
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);
1713 /// END TIMERS
1714 /////////////////////////////////////////////////////////////////////////////
1718 ////////////////////////////////////////////////////////////////
1720 HINSTANCE fl_display = GetModuleHandle(NULL);
1722 void Fl_Window::size_range_() {
1723 size_range_set = 1;
1726 void Fl_X::set_minmax(LPMINMAXINFO minmax)
1728 int td, wd, hd, dummy_x, dummy_y;
1730 fake_X_wm(w, dummy_x, dummy_y, td, wd, hd);
1731 wd *= 2;
1732 hd *= 2;
1733 hd += td;
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;
1741 if (w->maxh) {
1742 minmax->ptMaxTrackSize.y = w->maxh + hd;
1743 minmax->ptMaxSize.y = w->maxh + hd;
1747 ////////////////////////////////////////////////////////////////
1749 #include <FL/filename.H> // need so FL_EXPORT fl_filename_name works
1751 // returns pointer to the filename, or null if name ends with '/'
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;
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);
1779 ////////////////////////////////////////////////////////////////
1780 // Implement the virtual functions for the base Fl_Window class:
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);}
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);
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);
1811 #ifdef USE_PRINT_BUTTON
1812 void preparePrintFront(void);
1813 preparePrintFront();
1814 #endif
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;
1824 // Here we ensure only one GetDC is ever in place.
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
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);
1837 return fl_gc;
1840 // make X drawing go into this window (called by subclass flush() impl.)
1841 void Fl_Window::make_current() {
1842 fl_GetDC(fl_xid(this));
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...
1850 fl_select_palette();
1851 #endif // USE_COLORMAP
1853 current_ = this;
1854 fl_clip_region(0);
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. */
1862 void fl_free_fonts(void)
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;
1880 ///////////////////////////////////////////////////////////////////////
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.
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
1905 static Win_DC_List * win_DC_list = 0;
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;
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
1934 delete (t);
1935 return;
1937 prev = t;
1938 t = t->next;
1939 } while (t);
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);
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);
1962 Window fl_xid_(const Fl_Window *w) {
1963 Fl_X *temp = Fl_X::i(w);
1964 return temp ? temp->xid : 0;
1967 int Fl_Window::decorated_w()
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;
1975 int Fl_Window::decorated_h()
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;
1983 void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
1985 if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
1986 this->print_widget(win, x_offset, y_offset);
1987 return;
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);
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>
2027 void printFront(Fl_Widget *o, void *data)
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);
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();
2063 void preparePrintFront(void)
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();
2074 #endif // USE_PRINT_BUTTON
2076 #endif // FL_DOXYGEN
2079 // End of "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $".