2 // "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $"
4 // WIN32-specific code for the Fast Light Tool Kit (FLTK).
6 // Copyright 1998-2010 by Bill Spitzak and others.
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
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.
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>
46 #include <sys/types.h>
49 # include <sys/time.h>
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 ...
60 # pragma comment (lib, "comctl32.lib")
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
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)
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() {
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");
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() {
145 s_imm_module
= LoadLibrary("IMM32.DLL");
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");
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
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
204 # define WM_SYNCPAINT 0x0088
207 #ifndef WM_MOUSELEAVE
208 # define WM_MOUSELEAVE 0x02a3
211 #ifndef WM_MOUSEWHEEL
212 # define WM_MOUSEWHEEL 0x020a
216 # define WHEEL_DELTA 120 // according to MSDN.
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
;
252 static int fd_array_size
= 0;
256 void (*cb
)(int, void*);
260 extern unsigned int fl_codepage
;
266 void fl_set_spot(int font
, int size
, int X
, int Y
, int W
, int H
, Fl_Window
*win
)
270 while (tw
->parent()) tw
= tw
->window(); // find top level window
273 HIMC himc
= flImmGetContext(fl_xid(tw
));
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
) {
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
));
298 fd
[i
].events
= (short)events
;
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
) {
314 for (i
=j
=0; i
<nfds
; i
++) {
316 short e
= fd
[i
].events
& ~events
;
317 if (!e
) continue; // if no events left, delete this fd
320 // move it down in the array if necessary:
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
) {
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_
;
349 IActiveIMMApp
*fl_aimm
= NULL
;
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
357 int fl_wait(double time_to_wait
) {
358 int have_message
= 0;
364 if (Fl::idle
&& !in_idle
) {
371 // For WIN32 we need to poll for socket input FIRST, since
372 // the event queue is not something we can select() on...
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
++) {
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
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())
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
)
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
);
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
;
422 while (Fl::get_awake_handler_(func
, data
)==0) {
427 TranslateMessage(&fl_msg
);
428 DispatchMessageW(&fl_msg
);
429 have_message
= PeekMessageW(&fl_msg
, NULL
, 0, 0, PM_REMOVE
);
434 // This should return 0 if only timer events were handled:
438 // fl_ready() is just like fl_wait(0.0) except no callbacks are done:
440 if (PeekMessage(&fl_msg
, NULL
, 0, 0, PM_NOREMOVE
)) return 1;
446 memcpy(fdt
, fdsets
, sizeof fdt
);
447 return get_wsock_mod() ? s_wsock_select(0,&fdt
[0],&fdt
[1],&fdt
[2],&t
) : 0;
450 ////////////////////////////////////////////////////////////////
456 SystemParametersInfo(SPI_GETWORKAREA
, 0, &r
, 0);
464 SystemParametersInfo(SPI_GETWORKAREA
, 0, &r
, 0);
472 SystemParametersInfo(SPI_GETWORKAREA
, 0, &r
, 0);
473 return r
.bottom
- r
.top
;
480 SystemParametersInfo(SPI_GETWORKAREA
, 0, &r
, 0);
481 return r
.right
- r
.left
;
484 void Fl::get_mouse(int &x
, int &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
)
502 buf
[GetLocaleInfo(id
, LOCALE_IDEFAULTANSICODEPAGE
, buf
, 8)] = 0;
506 // Convert \n -> \r\n
507 class Lf2CrlfConvert
{
511 Lf2CrlfConvert(const char *in
, int inlen
) {
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
520 else if ( *i
== '\n' ) // \n by itself? leave room to insert \r
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
++; }
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
;
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);
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);
572 SetClipboardData(CF_UNICODETEXT
, hMem
);
575 fl_i_own_selection
[clipboard
] = 0;
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
];
596 Fl::e_text
= new char[fl_selection_length
[clipboard
]+1];
597 char *o
= Fl::e_text
;
599 if ( *i
== '\r' && *(i
+1) == '\n') i
++;
603 Fl::e_length
= o
- Fl::e_text
;
604 receiver
.handle(FL_PASTE
);
605 delete [] Fl::e_text
;
608 if (!OpenClipboard(NULL
)) return;
609 HANDLE h
= GetClipboardData(CF_UNICODETEXT
);
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;
618 while (*a
) { // strip the CRLF pairs ($%$#@^)
619 if (*a
== '\r' && a
[1] == '\n') a
++;
623 Fl::e_length
= b
- Fl::e_text
;
624 receiver
.handle(FL_PASTE
);
633 ////////////////////////////////////////////////////////////////
635 void fl_get_codepage()
637 HKL hkl
= GetKeyboardLayout(0);
640 GetLocaleInfo (LOWORD(hkl
), LOCALE_IDEFAULTANSICODEPAGE
, ld
, 6);
641 DWORD ccp
= atol(ld
);
646 fl_aimm
->GetCodePageA(GetKeyboardLayout(0), &fl_codepage
);
647 } else if (get_imm_module() && flImmIsIME(hkl
)) {
654 static int mouse_event(Fl_Window
*window
, int what
, int button
,
655 WPARAM wParam
, LPARAM lParam
)
657 static int px
, py
, pmx
, pmy
;
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
);
664 #ifdef USE_CAPTURE_MOUSE_WIN
665 Fl_Window
*mouse_window
= window
; // save "mouse window"
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
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
;
679 if (wParam
& MK_LBUTTON
) state
|= FL_BUTTON1
;
680 if (wParam
& MK_MBUTTON
) state
|= FL_BUTTON2
;
681 if (wParam
& MK_RBUTTON
) state
|= FL_BUTTON3
;
685 case 1: // double-click
686 if (Fl::e_is_click
) {Fl::e_clicks
++; goto J1
;}
687 case 0: // single-click
690 #ifdef USE_CAPTURE_MOUSE_WIN
691 if (!fl_capture
) SetCapture(fl_xid(mouse_window
)); // use mouse window
693 if (!fl_capture
) SetCapture(fl_xid(window
)); // use main window
695 Fl::e_keysym
= FL_Button
+ button
;
697 px
= pmx
= Fl::e_x_root
; py
= pmy
= Fl::e_y_root
;
698 return Fl::handle(FL_PUSH
,window
);
701 if (!fl_capture
) ReleaseCapture();
702 Fl::e_keysym
= FL_Button
+ button
;
703 return Fl::handle(FL_RELEASE
,window
);
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
},
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
},
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
},
745 {VK_SLEEP
, FL_Sleep
},
746 {VK_MULTIPLY
, 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
},
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
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
];
800 extern HPALETTE
fl_select_palette(void); // in fl_color_win32.cxx
804 /////////////////////////////////////////////////////////////////////////////
811 Fl_Timeout_Handler callback
;
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
;
833 static void delete_timer(Win32Timer
& t
)
835 KillTimer(s_TimerWnd
, t
.handle
);
836 memset(&t
, 0, sizeof(Win32Timer
));
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.
853 fl_msg
.message
= uMsg
;
854 fl_msg
.wParam
= wParam
;
855 fl_msg
.lParam
= lParam
;
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
);
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...
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
) {
899 // Also tell WIN32 that we are drawing someplace else as well...
900 CombineRgn(i
->region
, i
->region
, R
, RGN_OR
);
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.
913 fl_GetDC(hWnd
); // Make sure we have a DC for this window...
917 window
->clear_damage();
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;
931 #ifdef USE_TRACK_MOUSE
932 if (track_mouse_win
!= window
) {
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
);
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
949 Fl::handle(FL_LEAVE
, tw
);
951 track_mouse_win
= 0; // force TrackMouseEvent() restart
955 Fl::handle(FL_FOCUS
, window
);
959 Fl::handle(FL_UNFOCUS
, window
);
960 Fl::flush(); // it never returns to main loop when deactivated...
964 if (!window
->parent()) {
965 Fl::handle(wParam
? FL_SHOW
: FL_HIDE
, window
);
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...)
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
;
990 case WM_INPUTLANGCHANGE
:
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;
1000 // ImmReleaseContext(hWnd, himc);
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
;
1017 case WM_SYSDEADCHAR
:
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)
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
;
1053 // Convert to special keypress...
1056 switch (Fl::e_keysym
) {
1058 Fl::e_keysym
= FL_Insert
;
1061 Fl::e_keysym
= FL_End
;
1064 Fl::e_keysym
= FL_Down
;
1067 Fl::e_keysym
= FL_Page_Down
;
1070 Fl::e_keysym
= FL_Left
;
1073 Fl::e_keysym
= FL_Right
;
1076 Fl::e_keysym
= FL_Home
;
1079 Fl::e_keysym
= FL_Up
;
1082 Fl::e_keysym
= FL_Page_Up
;
1085 Fl::e_keysym
= FL_Delete
;
1091 buffer
[0] = Fl::e_keysym
-FL_KP
;
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?)
1111 Fl::e_text
= buffer
;
1112 if (lParam
& (1<<31)) { // key up events.
1113 if (Fl::handle(FL_KEYUP
, window
)) return 0;
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;
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
);
1134 case WM_GETMINMAXINFO
:
1135 Fl_X::i(window
)->set_minmax((LPMINMAXINFO
)lParam
);
1139 if (!window
->parent()) {
1140 if (wParam
== SIZE_MINIMIZED
|| wParam
== SIZE_MAXHIDE
) {
1141 Fl::handle(FL_HIDE
, window
);
1143 Fl::handle(FL_SHOW
, window
);
1144 resize_bug_fix
= window
;
1145 window
->size(LOWORD(lParam
), HIWORD(lParam
));
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
); }
1160 if (LOWORD(lParam
) == HTCLIENT
) {
1161 while (window
->parent()) window
= window
->window();
1162 SetCursor(Fl_X::i(window
)->cursor
);
1168 case WM_QUERYNEWPALETTE
:
1170 if (fl_select_palette()) InvalidateRect(hWnd
, NULL
, FALSE
);
1173 case WM_PALETTECHANGED
:
1175 if ((HWND
)wParam
!= hWnd
&& fl_select_palette()) UpdateColors(fl_gc
);
1180 fl_select_palette();
1184 case WM_DESTROYCLIPBOARD
:
1185 fl_i_own_selection
[1] = 0;
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:
1194 OpenClipboard(NULL
);
1196 case WM_RENDERFORMAT
: {
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));
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));
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();
1217 if (Fl::handle(0,0)) return 0;
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
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;
1242 HWND hwnd
= fl_xid(w
);
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
);
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
);
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
1271 if (w
->size_range_set
&& (w
->maxw
!= w
->minw
|| w
->maxh
!= w
->minh
))
1279 // This is the original (pre 1.1.7) routine to calculate window border sizes.
1281 if (w
->border() && !w
->parent()) {
1282 if (w
->size_range_set
&& (w
->maxw
!= w
->minw
|| w
->maxh
!= w
->minh
)) {
1284 bx
= GetSystemMetrics(SM_CXSIZEFRAME
);
1285 by
= GetSystemMetrics(SM_CYSIZEFRAME
);
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
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)
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()) {
1337 if (!is_a_resize
) return;
1338 flags
|= SWP_NOMOVE
;
1341 Fl_Group::resize(X
,Y
,W
,H
);
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;
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
)) {
1365 // avoid zero size windows. A zero sized window on Win32
1366 // will cause continouly new redraw events.
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.
1382 NameList() { name
= (char**)malloc(sizeof(char**)); NName
= 1; nName
= 0; }
1385 for (i
=0; i
<nName
; i
++) free(name
[i
]);
1386 if (name
) free(name
);
1388 void add_name(const char *n
) {
1391 name
= (char**)realloc(name
, NName
* sizeof(char*));
1393 name
[nName
++] = strdup(n
);
1395 char has_name(const char *n
) {
1397 for (i
=0; i
<nName
; i
++) {
1398 if (strcmp(name
[i
], n
)==0) return 1;
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())) {
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
)) {
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
;
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
);
1473 DWORD style
= WS_CLIPCHILDREN
| WS_CLIPSIBLINGS
;
1474 DWORD styleEx
= WS_EX_LEFT
;
1485 styleEx
|= WS_EX_WINDOWEDGE
| WS_EX_CONTROLPARENT
;
1486 parent
= fl_xid(w
->window());
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);
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
;
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;
1513 if (!w
->modal()) style
|= WS_SYSMENU
| WS_MINIMIZEBOX
;
1517 if (!w
->force_position()) {
1518 xp
= yp
= CW_USEDEFAULT
;
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();
1534 if (!w
->visible()) showit
= 0;
1535 } else if (Fl::grab()) parent
= fl_xid(Fl::grab());
1543 x
->cursor
= fl_default_cursor
;
1544 if (!fl_codepage
) fl_get_codepage();
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);
1552 unsigned wlen
= fl_utf8toUtf16(w
->label(), l
, NULL
, 0); // Pass NULL to query length
1554 lab
= (WCHAR
*) malloc(sizeof(WCHAR
)*wlen
);
1555 wlen
= fl_utf8toUtf16(w
->label(), l
, (unsigned short*)lab
, wlen
);
1558 x
->xid
= CreateWindowExW(
1560 class_namew
, lab
, style
,
1565 NULL
// creation parameters
1569 x
->next
= Fl_X::first
;
1572 x
->wait_for_expose
= 1;
1573 if (fl_show_iconic
) {showit
= 0; fl_show_iconic
= 0;}
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
1588 RegisterDragDrop(x
->xid
, flIDropTarget
);
1591 CoCreateInstance(CLSID_CActiveIMM
, NULL
, CLSCTX_INPROC_SERVER
,
1592 IID_IActiveIMMApp
, (void**) &fl_aimm
);
1594 fl_aimm
->Activate(TRUE
);
1598 if (w
->modal()) {Fl::modal_
= w
; fl_fix_focus();}
1605 /////////////////////////////////////////////////////////////////////////////
1610 static LRESULT CALLBACK
s_TimerProc(HWND hwnd
, UINT msg
,
1611 WPARAM wParam
, LPARAM lParam
)
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
]);
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
)
1643 for (int i
= 0; i
< win32_timer_used
; ++i
) {
1644 if ( !win32_timers
[i
].handle
) {
1649 if (timer_id
== -1) {
1650 if (win32_timer_used
== win32_timer_alloc
) {
1653 timer_id
= win32_timer_used
++;
1655 unsigned int elapsed
= (unsigned int)(time
* 1000);
1657 if ( !s_TimerWnd
) {
1658 const char* timer_class
= "FLTimer";
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
,
1672 NULL
, NULL
, fl_display
, NULL
);
1673 // just in case this OS won't let us create a 0x0 size window:
1675 s_TimerWnd
= CreateWindowEx(WS_EX_LEFT
| WS_EX_TOOLWINDOW
,
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
) {
1701 void Fl::remove_timeout(Fl_Timeout_Handler cb
, void* data
)
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
)) {
1714 /////////////////////////////////////////////////////////////////////////////
1718 ////////////////////////////////////////////////////////////////
1720 HINSTANCE fl_display
= GetModuleHandle(NULL
);
1722 void Fl_Window::size_range_() {
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
);
1735 minmax
->ptMinTrackSize
.x
= w
->minw
+ wd
;
1736 minmax
->ptMinTrackSize
.y
= w
->minh
+ hd
;
1738 minmax
->ptMaxTrackSize
.x
= w
->maxw
+ wd
;
1739 minmax
->ptMaxSize
.x
= w
->maxw
+ wd
;
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
) {
1754 if (!name
) return (0);
1756 if (q
[0] && q
[1]==':') q
+= 2; // skip leading drive letter
1757 for (p
= q
; *p
; p
++) if (*p
== '/' || *p
== '\\') q
= p
+1;
1761 void Fl_Window::label(const char *name
,const char *iname
) {
1762 Fl_Widget::label(name
);
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
1771 unsigned short * lab
= (unsigned short*)malloc(sizeof(unsigned short)*wlen
);
1772 wlen
= fl_utf8toUtf16(name
, l
, lab
, wlen
);
1774 SetWindowTextW(i
->xid
, (WCHAR
*)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
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
);
1799 labeltype(FL_NO_LABEL
);
1801 Fl_Tooltip::exit(this);
1803 // if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color());
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();
1817 Fl_Window
*Fl_Window::current_
;
1818 // the current context
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
) {
1827 if (w
== fl_window
&& fl_window
!= NULL
) return fl_gc
;
1828 if (fl_window
) fl_release_dc(fl_window
, fl_gc
); // ReleaseDC
1831 fl_save_dc(w
, fl_gc
);
1833 // calling GetDC seems to always reset these: (?)
1834 SetTextAlign(fl_gc
, TA_BASELINE
|TA_LEFT
);
1835 SetBkMode(fl_gc
, TRANSPARENT
);
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));
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
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
1867 Fl_Font_Descriptor
* f
;
1868 Fl_Font_Descriptor
* ff
;
1869 for (i
=0; i
<FL_FREE_FONT
; i
++) {
1871 for (f
=s
->first
; f
; f
=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
) {
1909 t
= new Win_DC_List
;
1912 t
->saved_dc
= SaveDC(dc
);
1914 t
->next
= win_DC_list
;
1920 void fl_release_dc(HWND w
, HDC dc
) {
1921 Win_DC_List
* t
= win_DC_list
;
1922 Win_DC_List
* prev
= 0;
1927 RestoreDC(dc
, t
->saved_dc
);
1930 win_DC_list
= t
->next
; // delete first item
1932 prev
->next
= t
->next
; // one in the middle
1942 void fl_cleanup_dc_list(void) { // clean up the list
1943 Win_DC_List
* t
= win_DC_list
;
1946 RestoreDC(t
->dc
, t
->saved_dc
);
1947 ReleaseDC(t
->window
, t
->dc
);
1948 win_DC_list
= t
->next
;
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
);
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
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);
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
)
2030 o
->window()->hide();
2031 Fl_Window
*win
= Fl::first_window();
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
);
2038 wh
= win
->decorated_h();
2039 ww
= win
->decorated_w();
2040 // scale the printer device so that the window fits on the page
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
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 );
2056 printer
.print_window(win
);
2060 o
->window()->show();
2063 void preparePrintFront(void)
2065 static BOOL first
=TRUE
;
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
);
2074 #endif // USE_PRINT_BUTTON
2076 #endif // FL_DOXYGEN
2079 // End of "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $".