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