blob: b36f307c524cc05a2ae9b51acba3937ba186bff2 [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.
1182 if (!(keystate[VK_MENU] & (1<<31)))
1183 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);
DRC2ff39b82011-07-28 08:38:59 +00001779 if (!fl_codepage) fl_get_codepage();
1780
1781 WCHAR *lab = NULL;
1782 if (w->label()) {
1783 int l = strlen(w->label());
1784// lab = (WCHAR*) malloc((l + 1) * sizeof(short));
1785// l = fl_utf2unicode((unsigned char*)w->label(), l, (xchar*)lab);
1786// lab[l] = 0;
1787 unsigned wlen = fl_utf8toUtf16(w->label(), l, NULL, 0); // Pass NULL to query length
1788 wlen++;
1789 lab = (WCHAR *) malloc(sizeof(WCHAR)*wlen);
1790 wlen = fl_utf8toUtf16(w->label(), l, (unsigned short*)lab, wlen);
1791 lab[wlen] = 0;
1792 }
1793 x->xid = CreateWindowExW(
1794 styleEx,
1795 class_namew, lab, style,
1796 xp, yp, wp, hp,
1797 parent,
1798 NULL, // menu
1799 fl_display,
1800 NULL // creation parameters
1801 );
1802 if (lab) free(lab);
1803
DRC685f17e2011-07-28 09:23:00 +00001804 if (w->flags() & Fl_Widget::FULLSCREEN) {
1805 /* We need to make sure that the fullscreen is created on the
1806 default monitor, ie the desktop where the shortcut is located
1807 etc. This requires that CreateWindow is called with CW_USEDEFAULT
1808 for x and y. We can then use GetWindowRect to determine which
1809 monitor the window was placed on. */
1810 RECT rect;
1811 GetWindowRect(x->xid, &rect);
1812 make_fullscreen(w, x->xid, rect.left, rect.top,
1813 rect.right - rect.left, rect.bottom - rect.top);
1814 }
1815
DRC2ff39b82011-07-28 08:38:59 +00001816 x->next = Fl_X::first;
1817 Fl_X::first = x;
1818
DRC685f17e2011-07-28 09:23:00 +00001819 fl_clipboard_notify_target(x->xid);
1820
DRC2ff39b82011-07-28 08:38:59 +00001821 x->wait_for_expose = 1;
1822 if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;}
1823 if (showit) {
1824 w->set_visible();
1825 int old_event = Fl::e_number;
1826 w->handle(Fl::e_number = FL_SHOW); // get child windows to appear
1827 Fl::e_number = old_event;
1828 w->redraw(); // force draw to happen
1829 }
1830 // If we've captured the mouse, we dont want to activate any
1831 // other windows from the code, or we lose the capture.
1832 ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE :
DRC685f17e2011-07-28 09:23:00 +00001833 (Fl::grab() || (styleEx & WS_EX_TOOLWINDOW)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
DRC2ff39b82011-07-28 08:38:59 +00001834
1835 // Register all windows for potential drag'n'drop operations
1836 fl_OleInitialize();
1837 RegisterDragDrop(x->xid, flIDropTarget);
1838
1839 if (!fl_aimm) {
1840 CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_INPROC_SERVER,
1841 IID_IActiveIMMApp, (void**) &fl_aimm);
1842 if (fl_aimm) {
1843 fl_aimm->Activate(TRUE);
1844 }
1845 }
1846
1847 if (w->modal()) {Fl::modal_ = w; fl_fix_focus();}
1848 return x;
1849}
1850
1851
1852
1853
1854/////////////////////////////////////////////////////////////////////////////
1855/// Win32 timers
1856///
1857
1858
1859static LRESULT CALLBACK s_TimerProc(HWND hwnd, UINT msg,
1860 WPARAM wParam, LPARAM lParam)
1861{
1862 switch (msg) {
1863 case WM_TIMER:
1864 {
1865 unsigned int id = wParam - 1;
1866 if (id < (unsigned int)win32_timer_used && win32_timers[id].handle) {
1867 Fl_Timeout_Handler cb = win32_timers[id].callback;
1868 void* data = win32_timers[id].data;
1869 delete_timer(win32_timers[id]);
1870 if (cb) {
1871 (*cb)(data);
1872 }
1873 }
1874 }
1875 return 0;
1876
1877 default:
1878 break;
1879 }
1880
1881 return DefWindowProc(hwnd, msg, wParam, lParam);
1882}
1883
1884void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data)
1885{
1886 repeat_timeout(time, cb, data);
1887}
1888
1889void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data)
1890{
1891 int timer_id = -1;
1892 for (int i = 0; i < win32_timer_used; ++i) {
1893 if ( !win32_timers[i].handle ) {
1894 timer_id = i;
1895 break;
1896 }
1897 }
1898 if (timer_id == -1) {
1899 if (win32_timer_used == win32_timer_alloc) {
1900 realloc_timers();
1901 }
1902 timer_id = win32_timer_used++;
1903 }
1904 unsigned int elapsed = (unsigned int)(time * 1000);
1905
1906 if ( !s_TimerWnd ) {
1907 const char* timer_class = "FLTimer";
1908 WNDCLASSEX wc;
1909 memset(&wc, 0, sizeof(wc));
1910 wc.cbSize = sizeof (wc);
1911 wc.style = CS_CLASSDC;
1912 wc.lpfnWndProc = (WNDPROC)s_TimerProc;
1913 wc.hInstance = fl_display;
1914 wc.lpszClassName = timer_class;
1915 /*ATOM atom =*/ RegisterClassEx(&wc);
1916 // create a zero size window to handle timer events
1917 s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
1918 timer_class, "",
1919 WS_POPUP,
1920 0, 0, 0, 0,
1921 NULL, NULL, fl_display, NULL);
1922 // just in case this OS won't let us create a 0x0 size window:
1923 if (!s_TimerWnd)
1924 s_TimerWnd = CreateWindowEx(WS_EX_LEFT | WS_EX_TOOLWINDOW,
1925 timer_class, "",
1926 WS_POPUP,
1927 0, 0, 1, 1,
1928 NULL, NULL, fl_display, NULL);
1929 ShowWindow(s_TimerWnd, SW_SHOWNOACTIVATE);
1930 }
1931
1932 win32_timers[timer_id].callback = cb;
1933 win32_timers[timer_id].data = data;
1934
1935 win32_timers[timer_id].handle =
1936 SetTimer(s_TimerWnd, timer_id + 1, elapsed, NULL);
1937}
1938
1939int Fl::has_timeout(Fl_Timeout_Handler cb, void* data)
1940{
1941 for (int i = 0; i < win32_timer_used; ++i) {
1942 Win32Timer& t = win32_timers[i];
1943 if (t.handle && t.callback == cb && t.data == data) {
1944 return 1;
1945 }
1946 }
1947 return 0;
1948}
1949
1950void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data)
1951{
1952 int i;
1953 for (i = 0; i < win32_timer_used; ++i) {
1954 Win32Timer& t = win32_timers[i];
1955 if (t.handle && t.callback == cb &&
1956 (t.data == data || data == NULL)) {
1957 delete_timer(t);
1958 }
1959 }
1960}
1961
1962/// END TIMERS
1963/////////////////////////////////////////////////////////////////////////////
1964
1965
1966
1967////////////////////////////////////////////////////////////////
1968
1969HINSTANCE fl_display = GetModuleHandle(NULL);
1970
1971void Fl_Window::size_range_() {
1972 size_range_set = 1;
1973}
1974
1975void Fl_X::set_minmax(LPMINMAXINFO minmax)
1976{
1977 int td, wd, hd, dummy_x, dummy_y;
1978
1979 fake_X_wm(w, dummy_x, dummy_y, td, wd, hd);
1980 wd *= 2;
1981 hd *= 2;
1982 hd += td;
1983
1984 minmax->ptMinTrackSize.x = w->minw + wd;
1985 minmax->ptMinTrackSize.y = w->minh + hd;
1986 if (w->maxw) {
1987 minmax->ptMaxTrackSize.x = w->maxw + wd;
1988 minmax->ptMaxSize.x = w->maxw + wd;
1989 }
1990 if (w->maxh) {
1991 minmax->ptMaxTrackSize.y = w->maxh + hd;
1992 minmax->ptMaxSize.y = w->maxh + hd;
1993 }
1994}
1995
1996////////////////////////////////////////////////////////////////
1997
1998#include <FL/filename.H> // need so FL_EXPORT fl_filename_name works
1999
2000// returns pointer to the filename, or null if name ends with '/'
2001const char *fl_filename_name(const char *name) {
2002 const char *p,*q;
2003 if (!name) return (0);
2004 q = name;
2005 if (q[0] && q[1]==':') q += 2; // skip leading drive letter
2006 for (p = q; *p; p++) if (*p == '/' || *p == '\\') q = p+1;
2007 return q;
2008}
2009
2010void Fl_Window::label(const char *name,const char *iname) {
2011 Fl_Widget::label(name);
2012 iconlabel_ = iname;
2013 if (shown() && !parent()) {
2014 if (!name) name = "";
2015 int l = strlen(name);
2016// WCHAR *lab = (WCHAR*) malloc((l + 1) * sizeof(short));
2017// l = fl_utf2unicode((unsigned char*)name, l, (xchar*)lab);
2018 unsigned wlen = fl_utf8toUtf16(name, l, NULL, 0); // Pass NULL to query length
2019 wlen++;
2020 unsigned short * lab = (unsigned short*)malloc(sizeof(unsigned short)*wlen);
2021 wlen = fl_utf8toUtf16(name, l, lab, wlen);
2022 lab[wlen] = 0;
2023 SetWindowTextW(i->xid, (WCHAR *)lab);
2024 free(lab);
2025 }
2026}
2027
2028////////////////////////////////////////////////////////////////
DRC685f17e2011-07-28 09:23:00 +00002029
2030#ifndef IDC_HAND
2031# define IDC_HAND MAKEINTRESOURCE(32649)
2032#endif // !IDC_HAND
2033
2034int Fl_X::set_cursor(Fl_Cursor c) {
2035 LPSTR n;
2036
2037 if (c == FL_CURSOR_NONE)
2038 cursor = NULL;
2039 else {
2040 switch (c) {
2041 case FL_CURSOR_ARROW: n = IDC_ARROW; break;
2042 case FL_CURSOR_CROSS: n = IDC_CROSS; break;
2043 case FL_CURSOR_WAIT: n = IDC_WAIT; break;
2044 case FL_CURSOR_INSERT: n = IDC_IBEAM; break;
2045 case FL_CURSOR_HAND: n = IDC_HAND; break;
2046 case FL_CURSOR_HELP: n = IDC_HELP; break;
2047 case FL_CURSOR_MOVE: n = IDC_SIZEALL; break;
2048 case FL_CURSOR_N:
2049 case FL_CURSOR_S:
2050 // FIXME: Should probably have fallbacks for these instead
2051 case FL_CURSOR_NS: n = IDC_SIZENS; break;
2052 case FL_CURSOR_NE:
2053 case FL_CURSOR_SW:
2054 // FIXME: Dito.
2055 case FL_CURSOR_NESW: n = IDC_SIZENESW; break;
2056 case FL_CURSOR_E:
2057 case FL_CURSOR_W:
2058 // FIXME: Dito.
2059 case FL_CURSOR_WE: n = IDC_SIZEWE; break;
2060 case FL_CURSOR_SE:
2061 case FL_CURSOR_NW:
2062 // FIXME: Dito.
2063 case FL_CURSOR_NWSE: n = IDC_SIZENWSE; break;
2064 default:
2065 return 0;
2066 }
2067
2068 cursor = LoadCursor(NULL, n);
2069 if (cursor == NULL)
2070 return 0;
2071 }
2072
2073 SetCursor(cursor);
2074
2075 return 1;
2076}
2077
2078int Fl_X::set_cursor(const Fl_RGB_Image *image, int hotx, int hoty) {
2079 BITMAPV5HEADER bi;
2080 HBITMAP bitmap, mask;
2081 DWORD *bits;
2082
2083 if ((hotx < 0) || (hotx >= image->w()))
2084 return 0;
2085 if ((hoty < 0) || (hoty >= image->h()))
2086 return 0;
2087
2088 memset(&bi, 0, sizeof(BITMAPV5HEADER));
2089
2090 bi.bV5Size = sizeof(BITMAPV5HEADER);
2091 bi.bV5Width = image->w();
2092 bi.bV5Height = image->h();
2093 bi.bV5Planes = 1;
2094 bi.bV5BitCount = 32;
2095 bi.bV5Compression = BI_BITFIELDS;
2096 bi.bV5RedMask = 0x00FF0000;
2097 bi.bV5GreenMask = 0x0000FF00;
2098 bi.bV5BlueMask = 0x000000FF;
2099 bi.bV5AlphaMask = 0xFF000000;
2100
2101 HDC hdc;
2102
2103 hdc = GetDC(NULL);
2104 bitmap = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (void**)&bits, NULL, 0);
2105 ReleaseDC(NULL, hdc);
2106
2107 const uchar *i = (const uchar*)*image->data();
2108 for (int y = 0;y < image->h();y++) {
2109 for (int x = 0;x < image->w();x++) {
2110 switch (image->d()) {
2111 case 1:
2112 *bits = (0xff<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
2113 break;
2114 case 2:
2115 *bits = (i[1]<<24) | (i[0]<<16) | (i[0]<<8) | i[0];
2116 break;
2117 case 3:
2118 *bits = (0xff<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
2119 break;
2120 case 4:
2121 *bits = (i[3]<<24) | (i[0]<<16) | (i[1]<<8) | i[2];
2122 break;
2123 }
2124 i += image->d();
2125 bits++;
2126 }
2127 i += image->ld();
2128 }
2129
2130 // A mask bitmap is still needed even though it isn't used
2131 mask = CreateBitmap(image->w(),image->h(),1,1,NULL);
2132
2133 ICONINFO ii;
2134
2135 ii.fIcon = FALSE;
2136 ii.xHotspot = hotx;
2137 ii.yHotspot = hoty;
2138 ii.hbmMask = mask;
2139 ii.hbmColor = bitmap;
2140
2141 cursor = CreateIconIndirect(&ii);
2142
2143 DeleteObject(bitmap);
2144 DeleteObject(mask);
2145
2146 SetCursor(cursor);
2147
2148 return 1;
2149}
2150
2151////////////////////////////////////////////////////////////////
DRC2ff39b82011-07-28 08:38:59 +00002152// Implement the virtual functions for the base Fl_Window class:
2153
2154// If the box is a filled rectangle, we can make the redisplay *look*
2155// faster by using X's background pixel erasing. We can make it
2156// actually *be* faster by drawing the frame only, this is done by
2157// setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
2158// For WIN32 it looks like all windows share a background color, so
2159// I use FL_GRAY for this and only do this cheat for windows that are
2160// that color.
2161// Actually it is totally disabled.
2162// Fl_Widget *fl_boxcheat;
2163//static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);}
2164
2165void Fl_Window::show() {
2166 image(Fl::scheme_bg_);
2167 if (Fl::scheme_bg_) {
2168 labeltype(FL_NORMAL_LABEL);
2169 align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
2170 } else {
2171 labeltype(FL_NO_LABEL);
2172 }
2173 Fl_Tooltip::exit(this);
2174 if (!shown()) {
2175 // if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color());
2176 Fl_X::make(this);
2177 } else {
2178 // Once again, we would lose the capture if we activated the window.
2179 if (IsIconic(i->xid)) OpenIcon(i->xid);
2180 if (!fl_capture) BringWindowToTop(i->xid);
2181 //ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE);
2182 }
2183#ifdef USE_PRINT_BUTTON
2184void preparePrintFront(void);
2185preparePrintFront();
2186#endif
2187}
2188
2189Fl_Window *Fl_Window::current_;
2190// the current context
2191HDC fl_gc = 0;
2192// the current window handle, initially set to -1 so we can correctly
2193// allocate fl_GetDC(0)
2194HWND fl_window = NULL;
2195
2196// Here we ensure only one GetDC is ever in place.
2197HDC fl_GetDC(HWND w) {
2198 if (fl_gc) {
2199 if (w == fl_window && fl_window != NULL) return fl_gc;
2200 if (fl_window) fl_release_dc(fl_window, fl_gc); // ReleaseDC
2201 }
2202 fl_gc = GetDC(w);
2203 fl_save_dc(w, fl_gc);
2204 fl_window = w;
2205 // calling GetDC seems to always reset these: (?)
2206 SetTextAlign(fl_gc, TA_BASELINE|TA_LEFT);
2207 SetBkMode(fl_gc, TRANSPARENT);
2208
2209 return fl_gc;
2210}
2211
2212// make X drawing go into this window (called by subclass flush() impl.)
2213void Fl_Window::make_current() {
2214 fl_GetDC(fl_xid(this));
2215
2216#if USE_COLORMAP
2217 // Windows maintains a hardware and software color palette; the
2218 // SelectPalette() call updates the current soft->hard mapping
2219 // for all drawing calls, so we must select it here before any
2220 // code does any drawing...
2221
2222 fl_select_palette();
2223#endif // USE_COLORMAP
2224
2225 current_ = this;
2226 fl_clip_region(0);
2227
2228
2229}
2230
2231/* Make sure that all allocated fonts are released. This works only if
2232 Fl::run() is allowed to exit by closing all windows. Calling 'exit(int)'
2233 will not automatically free any fonts. */
2234void fl_free_fonts(void)
2235{
2236// remove the Fl_Font_Descriptor chains
2237 int i;
2238 Fl_Fontdesc * s;
2239 Fl_Font_Descriptor * f;
2240 Fl_Font_Descriptor * ff;
2241 for (i=0; i<FL_FREE_FONT; i++) {
2242 s = fl_fonts + i;
2243 for (f=s->first; f; f=ff) {
2244 ff = f->next;
2245 delete f;
2246 s->first = ff;
2247 }
2248 }
2249}
2250
2251
2252///////////////////////////////////////////////////////////////////////
2253//
2254// The following routines help fix a problem with the leaking of Windows
2255// Device Context (DC) objects. The 'proper' protocol is for a program to
2256// acquire a DC, save its state, do the modifications needed for drawing,
2257// perform the drawing, restore the initial state, and release the DC. In
2258// FLTK, the save and restore steps have previously been omitted and DCs are
2259// not properly released, leading to a great number of DC leaks. As some
2260// Windows "OSs" will hang when any process exceeds roughly 10,000 GDI objects,
2261// it is important to control GDI leaks, which are much more important than memory
2262// leaks. The following struct, global variable, and routines help implement
2263// the above protocol for those cases where the GetDC and RestoreDC are not in
2264// the same routine. For each GetDC, fl_save_dc is used to create an entry in
2265// a linked list that saves the window handle, the DC handle, and the initial
2266// state. When the DC is to be released, 'fl_release_dc' is called. It restores
2267// the initial state and releases the DC. When the program exits, 'fl_cleanup_dc_list'
2268// frees any remaining nodes in the list.
2269
2270struct Win_DC_List { // linked list
2271 HWND window; // window handle
2272 HDC dc; // device context handle
2273 int saved_dc; // initial state of DC
2274 Win_DC_List * next; // pointer to next item
2275};
2276
2277static Win_DC_List * win_DC_list = 0;
2278
2279void fl_save_dc( HWND w, HDC dc) {
2280 Win_DC_List * t;
2281 t = new Win_DC_List;
2282 t->window = w;
2283 t->dc = dc;
2284 t->saved_dc = SaveDC(dc);
2285 if (win_DC_list)
2286 t->next = win_DC_list;
2287 else
2288 t->next = NULL;
2289 win_DC_list = t;
2290}
2291
2292void fl_release_dc(HWND w, HDC dc) {
2293 Win_DC_List * t= win_DC_list;
2294 Win_DC_List * prev = 0;
2295 if (!t)
2296 return;
2297 do {
2298 if (t->dc == dc) {
2299 RestoreDC(dc, t->saved_dc);
2300 ReleaseDC(w, dc);
2301 if (!prev) {
2302 win_DC_list = t->next; // delete first item
2303 } else {
2304 prev->next = t->next; // one in the middle
2305 }
2306 delete (t);
2307 return;
2308 }
2309 prev = t;
2310 t = t->next;
2311 } while (t);
2312}
2313
2314void fl_cleanup_dc_list(void) { // clean up the list
2315 Win_DC_List * t = win_DC_list;
2316 if (!t)return;
2317 do {
2318 RestoreDC(t->dc, t->saved_dc);
2319 ReleaseDC(t->window, t->dc);
2320 win_DC_list = t->next;
2321 delete (t);
2322 t = win_DC_list;
2323 } while(t);
2324}
2325
2326Fl_Region XRectangleRegion(int x, int y, int w, int h) {
2327 if (Fl_Surface_Device::surface()->class_name() == Fl_Display_Device::class_id) return CreateRectRgn(x,y,x+w,y+h);
2328 // because rotation may apply, the rectangle becomes a polygon in device coords
2329 POINT pt[4] = { {x, y}, {x + w, y}, {x + w, y + h}, {x, y + h} };
2330 LPtoDP(fl_gc, pt, 4);
2331 return CreatePolygonRgn(pt, 4, ALTERNATE);
2332}
2333
2334Window fl_xid_(const Fl_Window *w) {
2335 Fl_X *temp = Fl_X::i(w);
2336 return temp ? temp->xid : 0;
2337}
2338
2339int Fl_Window::decorated_w()
2340{
2341 if (!shown() || parent() || !border() || !visible()) return w();
2342 int X, Y, bt, bx, by;
2343 Fl_X::fake_X_wm(this, X, Y, bt, bx, by);
2344 return w() + 2 * bx;
2345}
2346
2347int Fl_Window::decorated_h()
2348{
2349 if (!shown() || parent() || !border() || !visible()) return h();
2350 int X, Y, bt, bx, by;
2351 Fl_X::fake_X_wm(this, X, Y, bt, bx, by);
2352 return h() + bt + 2 * by;
2353}
2354
2355void Fl_Paged_Device::print_window(Fl_Window *win, int x_offset, int y_offset)
2356{
2357 if (!win->shown() || win->parent() || !win->border() || !win->visible()) {
2358 this->print_widget(win, x_offset, y_offset);
2359 return;
2360 }
2361 int X, Y, bt, bx, by, ww, wh; // compute the window border sizes
2362 Fl_X::fake_X_wm(win, X, Y, bt, bx, by);
2363 ww = win->w() + 2 * bx;
2364 wh = win->h() + bt + 2 * by;
2365 Fl_Display_Device::display_device()->set_current(); // make window current
2366 win->show();
2367 Fl::check();
2368 win->make_current();
2369 HDC save_gc = fl_gc;
2370 fl_gc = GetDC(NULL); // get the screen device context
2371 // capture the 4 window sides from screen
2372 RECT r; GetWindowRect(fl_window, &r);
2373 uchar *top_image = fl_read_image(NULL, r.left, r.top, ww, bt + by);
2374 uchar *left_image = fl_read_image(NULL, r.left, r.top, bx, wh);
2375 uchar *right_image = fl_read_image(NULL, r.right - bx, r.top, bx, wh);
2376 uchar *bottom_image = fl_read_image(NULL, r.left, r.bottom-by, ww, by);
2377 ReleaseDC(NULL, fl_gc); fl_gc = save_gc;
2378 this->set_current();
2379 // print the 4 window sides
2380 fl_draw_image(top_image, x_offset, y_offset, ww, bt + by, 3);
2381 fl_draw_image(left_image, x_offset, y_offset, bx, wh, 3);
2382 fl_draw_image(right_image, x_offset + win->w() + bx, y_offset, bx, wh, 3);
2383 fl_draw_image(bottom_image, x_offset, y_offset + win->h() + bt + by, ww, by, 3);
2384 delete[] top_image;
2385 delete[] left_image;
2386 delete[] right_image;
2387 delete[] bottom_image;
2388 // print the window inner part
2389 this->print_widget(win, x_offset + bx, y_offset + bt + by);
2390 fl_gc = GetDC(fl_xid(win));
2391 ReleaseDC(fl_xid(win), fl_gc);
2392}
2393
2394#ifdef USE_PRINT_BUTTON
2395// to test the Fl_Printer class creating a "Print front window" button in a separate window
2396// contains also preparePrintFront call above
2397#include <FL/Fl_Printer.H>
2398#include <FL/Fl_Button.H>
2399void printFront(Fl_Widget *o, void *data)
2400{
2401 Fl_Printer printer;
2402 o->window()->hide();
2403 Fl_Window *win = Fl::first_window();
2404 if(!win) return;
2405 int w, h;
2406 if( printer.start_job(1) ) { o->window()->show(); return; }
2407 if( printer.start_page() ) { o->window()->show(); return; }
2408 printer.printable_rect(&w,&h);
2409 int wh, ww;
2410 wh = win->decorated_h();
2411 ww = win->decorated_w();
2412 // scale the printer device so that the window fits on the page
2413 float scale = 1;
2414 if (ww > w || wh > h) {
2415 scale = (float)w/ww;
2416 if ((float)h/wh < scale) scale = (float)h/wh;
2417 printer.scale(scale, scale);
2418 }
2419// #define ROTATE 20.0
2420#ifdef ROTATE
2421 printer.scale(scale * 0.8, scale * 0.8);
2422 printer.printable_rect(&w, &h);
2423 printer.origin(w/2, h/2 );
2424 printer.rotate(ROTATE);
2425 printer.print_widget( win, - win->w()/2, - win->h()/2 );
2426 //printer.print_window_part( win, 0,0, win->w(), win->h(), - win->w()/2, - win->h()/2 );
2427#else
2428 printer.print_window(win);
2429#endif
2430 printer.end_page();
2431 printer.end_job();
2432 o->window()->show();
2433}
2434
2435void preparePrintFront(void)
2436{
2437 static BOOL first=TRUE;
2438 if(!first) return;
2439 first=FALSE;
2440 static Fl_Window w(0,0,120,30);
2441 static Fl_Button b(0,0,w.w(),w.h(), "Print front window");
2442 b.callback(printFront);
2443 w.end();
2444 w.show();
2445}
2446#endif // USE_PRINT_BUTTON
2447
2448#endif // FL_DOXYGEN
2449
2450//
2451// End of "$Id: Fl_win32.cxx 8759 2011-05-30 12:33:51Z manolo $".
2452//