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