2
// "$Id: Fl_win32.cxx,v 1.33.2.37.2.48 2004/04/11 04:38:59 easysw Exp $"
4
// WIN32-specific code for the Fast Light Tool Kit (FLTK).
6
// Copyright 1998-2004 by Bill Spitzak and others.
8
// This library is free software; you can redistribute it and/or
9
// modify it under the terms of the GNU Library General Public
10
// License as published by the Free Software Foundation; either
11
// version 2 of the License, or (at your option) any later version.
13
// This library is distributed in the hope that it will be useful,
14
// but WITHOUT ANY WARRANTY; without even the implied warranty of
15
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
// Library General Public License for more details.
18
// You should have received a copy of the GNU Library General Public
19
// License along with this library; if not, write to the Free Software
20
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23
// Please report all bugs and problems to "fltk-bugs@fltk.org".
26
// This file contains win32-specific code for fltk which is always linked
27
// in. Search other files for "WIN32" or filenames ending in _win32.cxx
28
// for other system-specific code.
32
#include <FL/Fl_Window.H>
36
#include <sys/types.h>
39
# include <sys/time.h>
47
// The following include files require GCC 3.x or a non-GNU compiler...
48
#if !defined(__GNUC__) || __GNUC__ >= 3
50
# include <ShellApi.h>
51
#endif // !__GNUC__ || __GNUC__ >= 3
55
// USE_ASYNC_SELECT - define it if you have WSAAsyncSelect()...
57
// This currently doesn't appear to work; needs to be fixed!
60
//#define USE_ASYNC_SELECT
64
// USE_TRACK_MOUSE - define it if you have TrackMouseEvent()...
66
// Apparently, at least some versions of Cygwin/MingW don't provide
67
// the TrackMouseEvent() function. You can define this by hand
68
// if you have it - this is only needed to support subwindow
69
// enter/leave notification under Windows.
72
//#define USE_TRACK_MOUSE
74
#if !defined(__GNUC__)
75
# define USE_TRACK_MOUSE
80
// WM_SYNCPAINT is an "undocumented" message, which is finally defined in
85
# define WM_SYNCPAINT 0x0088
89
# define WM_MOUSELEAVE 0x02a3
93
# define WM_MOUSEWHEEL 0x020a
97
# define WHEEL_DELTA 120 // according to MSDN.
102
// WM_FLSELECT is the user-defined message that we get when one of
103
// the sockets has pending data, etc.
106
#define WM_FLSELECT (WM_USER+0x0400)
109
////////////////////////////////////////////////////////////////
110
// interface to poll/select call:
112
// fd's are only implemented for sockets. Microsoft Windows does not
113
// have a unified IO system, so it doesn't support select() on files,
114
// devices, or pipes...
116
// Microsoft provides the Berkeley select() call and an asynchronous
117
// select function that sends a WIN32 message when the select condition
119
static int maxfd = 0;
120
#ifndef USE_ASYNC_SELECT
121
static fd_set fdsets[3];
122
#endif // !USE_ASYNC_SELECT
128
#if !defined(__GNUC__) || __GNUC__ >= 3
129
extern IDropTarget *flIDropTarget;
130
#endif // !__GNUC__ || __GNUC__ >= 3
133
static int fd_array_size = 0;
137
void (*cb)(int, void*);
141
void Fl::add_fd(int n, int events, void (*cb)(int, void*), void *v) {
144
if (i >= fd_array_size) {
145
fd_array_size = 2*fd_array_size+1;
146
fd = (FD*)realloc(fd, fd_array_size*sizeof(FD));
149
fd[i].events = (short)events;
153
#ifdef USE_ASYNC_SELECT
155
if (events & POLLIN) mask |= FD_READ;
156
if (events & POLLOUT) mask |= FD_WRITE;
157
if (events & POLLERR) mask |= FD_CLOSE;
158
WSAAsyncSelect(n, fl_window, WM_FLSELECT, mask);
160
if (events & POLLIN) FD_SET(n, &fdsets[0]);
161
if (events & POLLOUT) FD_SET(n, &fdsets[1]);
162
if (events & POLLERR) FD_SET(n, &fdsets[2]);
163
if (n > maxfd) maxfd = n;
164
#endif // USE_ASYNC_SELECT
167
void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) {
168
Fl::add_fd(fd, POLLIN, cb, v);
171
void Fl::remove_fd(int n, int events) {
173
for (i=j=0; i<nfds; i++) {
175
short e = fd[i].events & ~events;
176
if (!e) continue; // if no events left, delete this fd
179
// move it down in the array if necessary:
187
#ifdef USE_ASYNC_SELECT
188
WSAAsyncSelect(n, 0, 0, 0);
190
if (events & POLLIN) FD_CLR(unsigned(n), &fdsets[0]);
191
if (events & POLLOUT) FD_CLR(unsigned(n), &fdsets[1]);
192
if (events & POLLERR) FD_CLR(unsigned(n), &fdsets[2]);
193
#endif // USE_ASYNC_SELECT
196
void Fl::remove_fd(int n) {
200
// these pointers are set by the Fl::lock() function:
201
static void nothing() {}
202
void (*fl_lock_function)() = nothing;
203
void (*fl_unlock_function)() = nothing;
205
static void* thread_message_;
206
void* Fl::thread_message() {
207
void* r = thread_message_;
214
// This is never called with time_to_wait < 0.0.
215
// It *should* return negative on error, 0 if nothing happens before
216
// timeout, and >0 if any callbacks were done. This version only
217
// returns zero if nothing happens during a 0.0 timeout, otherwise
219
int fl_wait(double time_to_wait) {
220
int have_message = 0;
223
#ifndef USE_ASYNC_SELECT
225
// For WIN32 we need to poll for socket input FIRST, since
226
// the event queue is not something we can select() on...
235
if (::select(maxfd+1,&fdt[0],&fdt[1],&fdt[2],&t)) {
236
// We got something - do the callback!
237
for (int i = 0; i < nfds; i ++) {
240
if (FD_ISSET(f,&fdt[0])) revents |= POLLIN;
241
if (FD_ISSET(f,&fdt[1])) revents |= POLLOUT;
242
if (FD_ISSET(f,&fdt[2])) revents |= POLLERR;
243
if (fd[i].events & revents) fd[i].cb(f, fd[i].arg);
245
time_to_wait = 0.0; // just peek for any messages
250
// we need to check them periodically, so set a short timeout:
251
if (time_to_wait > .001) time_to_wait = .001;
255
#endif // USE_ASYNC_SELECT
257
fl_unlock_function();
259
if (time_to_wait < 2147483.648) {
260
// Perform the requested timeout...
261
have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
263
int t = (int)(time_to_wait * 1000.0 + .5);
264
if (t <= 0) { // too short to measure
268
timerid = SetTimer(NULL, 0, t, NULL);
269
have_message = GetMessage(&fl_msg, NULL, 0, 0);
270
KillTimer(NULL, timerid);
273
have_message = GetMessage(&fl_msg, NULL, 0, 0);
278
// Execute the message we got, and all other pending messages:
279
while (have_message) {
280
#ifdef USE_ASYNC_SELECT
281
if (fl_msg.message == WM_FLSELECT) {
282
// Got notification for socket
283
for (int i = 0; i < nfds; i ++)
284
if (fd[i].fd == (int)fl_msg.wParam) {
285
(fd[i].cb)(fd[i].fd, fd[i].arg);
288
// looks like it is best to do the dispatch-message anyway:
292
if (fl_msg.message == fl_wake_msg) // Used for awaking wait() from another thread
293
thread_message_ = (void*)fl_msg.wParam;
295
TranslateMessage(&fl_msg);
296
DispatchMessage(&fl_msg);
297
have_message = PeekMessage(&fl_msg, NULL, 0, 0, PM_REMOVE);
300
// This should return 0 if only timer events were handled:
304
// fl_ready() is just like fl_wait(0.0) except no callbacks are done:
306
if (PeekMessage(&fl_msg, NULL, 0, 0, PM_NOREMOVE)) return 1;
307
#ifdef USE_ASYNC_SELECT
317
return ::select(0,&fdt[0],&fdt[1],&fdt[2],&t);
318
#endif // USE_ASYNC_SELECT
321
////////////////////////////////////////////////////////////////
327
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
335
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
343
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
344
return r.bottom - r.top;
351
SystemParametersInfo(SPI_GETWORKAREA, 0, &r, 0);
352
return r.right - r.left;
355
void Fl::get_mouse(int &x, int &y) {
362
////////////////////////////////////////////////////////////////
363
// code used for selections:
365
char *fl_selection_buffer[2];
366
int fl_selection_length[2];
367
int fl_selection_buffer_length[2];
368
char fl_i_own_selection[2];
370
// call this when you create a selection:
371
void Fl::copy(const char *stuff, int len, int clipboard) {
372
if (!stuff || len<0) return;
373
if (len+1 > fl_selection_buffer_length[clipboard]) {
374
delete[] fl_selection_buffer[clipboard];
375
fl_selection_buffer[clipboard] = new char[len+100];
376
fl_selection_buffer_length[clipboard] = len+100;
378
memcpy(fl_selection_buffer[clipboard], stuff, len);
379
fl_selection_buffer[clipboard][len] = 0; // needed for direct paste
380
fl_selection_length[clipboard] = len;
382
// set up for "delayed rendering":
383
if (OpenClipboard(fl_xid(Fl::first_window()))) {
385
SetClipboardData(CF_TEXT, NULL);
388
fl_i_own_selection[clipboard] = 1;
392
// Call this when a "paste" operation happens:
393
void Fl::paste(Fl_Widget &receiver, int clipboard) {
394
if (!clipboard || fl_i_own_selection[clipboard]) {
395
// We already have it, do it quickly without window server.
396
// Notice that the text is clobbered if set_selection is
397
// called in response to FL_PASTE!
398
Fl::e_text = fl_selection_buffer[clipboard];
399
Fl::e_length = fl_selection_length[clipboard];
401
if (!Fl::e_text) Fl::e_text = (char *)"";
402
receiver.handle(FL_PASTE);
404
if (!OpenClipboard(NULL)) return;
405
HANDLE h = GetClipboardData(CF_TEXT);
407
Fl::e_text = (LPSTR)GlobalLock(h);
410
while (*a) { // strip the CRLF pairs ($%$#@^)
411
if (*a == '\r' && a[1] == '\n') a++;
415
Fl::e_length = b - Fl::e_text;
416
receiver.handle(FL_PASTE);
423
////////////////////////////////////////////////////////////////
427
static int mouse_event(Fl_Window *window, int what, int button,
428
WPARAM wParam, LPARAM lParam)
430
static int px, py, pmx, pmy;
432
Fl::e_x = pt.x = (signed short)LOWORD(lParam);
433
Fl::e_y = pt.y = (signed short)HIWORD(lParam);
434
ClientToScreen(fl_xid(window), &pt);
437
while (window->parent()) {
438
Fl::e_x += window->x();
439
Fl::e_y += window->y();
440
window = window->window();
443
ulong state = Fl::e_state & 0xff0000; // keep shift key states
445
// mouse event reports some shift flags, perhaps save them?
446
if (wParam & MK_SHIFT) state |= FL_SHIFT;
447
if (wParam & MK_CONTROL) state |= FL_CTRL;
449
if (wParam & MK_LBUTTON) state |= FL_BUTTON1;
450
if (wParam & MK_MBUTTON) state |= FL_BUTTON2;
451
if (wParam & MK_RBUTTON) state |= FL_BUTTON3;
455
case 1: // double-click
456
if (Fl::e_is_click) {Fl::e_clicks++; goto J1;}
457
case 0: // single-click
460
if (!fl_capture) SetCapture(fl_xid(window));
461
Fl::e_keysym = FL_Button + button;
463
px = pmx = Fl::e_x_root; py = pmy = Fl::e_y_root;
464
return Fl::handle(FL_PUSH,window);
467
if (!fl_capture) ReleaseCapture();
468
Fl::e_keysym = FL_Button + button;
469
return Fl::handle(FL_RELEASE,window);
472
default: // avoid compiler warning
473
// MSWindows produces extra events even if mouse does not move, ignore em:
474
if (Fl::e_x_root == pmx && Fl::e_y_root == pmy) return 1;
475
pmx = Fl::e_x_root; pmy = Fl::e_y_root;
476
if (abs(Fl::e_x_root-px)>5 || abs(Fl::e_y_root-py)>5) Fl::e_is_click = 0;
477
return Fl::handle(FL_MOVE,window);
482
// convert a MSWindows VK_x to an Fltk (X) Keysym:
483
// See also the inverse converter in Fl_get_key_win32.cxx
484
// This table is in numeric order by VK:
485
static const struct {unsigned short vk, fltk, extended;} vktab[] = {
486
{VK_BACK, FL_BackSpace},
488
{VK_CLEAR, FL_KP+'5', 0xff0b/*XK_Clear*/},
489
{VK_RETURN, FL_Enter, FL_KP_Enter},
490
{VK_SHIFT, FL_Shift_L, FL_Shift_R},
491
{VK_CONTROL, FL_Control_L, FL_Control_R},
492
{VK_MENU, FL_Alt_L, FL_Alt_R},
493
{VK_PAUSE, FL_Pause},
494
{VK_CAPITAL, FL_Caps_Lock},
495
{VK_ESCAPE, FL_Escape},
497
{VK_PRIOR, FL_KP+'9', FL_Page_Up},
498
{VK_NEXT, FL_KP+'3', FL_Page_Down},
499
{VK_END, FL_KP+'1', FL_End},
500
{VK_HOME, FL_KP+'7', FL_Home},
501
{VK_LEFT, FL_KP+'4', FL_Left},
502
{VK_UP, FL_KP+'8', FL_Up},
503
{VK_RIGHT, FL_KP+'6', FL_Right},
504
{VK_DOWN, FL_KP+'2', FL_Down},
505
{VK_SNAPSHOT, FL_Print}, // does not work on NT
506
{VK_INSERT, FL_KP+'0', FL_Insert},
507
{VK_DELETE, FL_KP+'.', FL_Delete},
508
{VK_LWIN, FL_Meta_L},
509
{VK_RWIN, FL_Meta_R},
511
{VK_MULTIPLY, FL_KP+'*'},
513
{VK_SUBTRACT, FL_KP+'-'},
514
{VK_DECIMAL, FL_KP+'.'},
515
{VK_DIVIDE, FL_KP+'/'},
516
{VK_NUMLOCK, FL_Num_Lock},
517
{VK_SCROLL, FL_Scroll_Lock},
530
static int ms2fltk(int vk, int extended) {
531
static unsigned short vklut[256];
532
static unsigned short extendedlut[256];
533
if (!vklut[1]) { // init the table
535
for (i = 0; i < 256; i++) vklut[i] = tolower(i);
536
for (i=VK_F1; i<=VK_F16; i++) vklut[i] = i+(FL_F-(VK_F1-1));
537
for (i=VK_NUMPAD0; i<=VK_NUMPAD9; i++) vklut[i] = i+(FL_KP+'0'-VK_NUMPAD0);
538
for (i = 0; i < sizeof(vktab)/sizeof(*vktab); i++) {
539
vklut[vktab[i].vk] = vktab[i].fltk;
540
extendedlut[vktab[i].vk] = vktab[i].extended;
542
for (i = 0; i < 256; i++) if (!extendedlut[i]) extendedlut[i] = vklut[i];
544
return extended ? extendedlut[vk] : vklut[vk];
548
extern HPALETTE fl_select_palette(void); // in fl_color_win32.cxx
551
static Fl_Window* resize_bug_fix;
553
extern void fl_save_pen(void);
554
extern void fl_restore_pen(void);
556
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
558
// Copy the message to fl_msg so add_handler code can see it, it is
559
// already there if this is called by DispatchMessage, but not if
560
// Windows calls this directly.
562
fl_msg.message = uMsg;
563
fl_msg.wParam = wParam;
564
fl_msg.lParam = lParam;
567
//fl_msg.lPrivate = ???
569
Fl_Window *window = fl_find(hWnd);
571
if (window) switch (uMsg) {
573
case WM_QUIT: // this should not happen?
574
Fl::fatal("WM_QUIT message");
576
case WM_CLOSE: // user clicked close box
577
Fl::handle(FL_CLOSE, window);
583
// Andreas Weitl - WM_SYNCPAINT needs to be passed to DefWindowProc
584
// so that Windows can generate the proper paint messages...
585
// Similarly, WM_NCPAINT and WM_ERASEBKGND need this, too...
590
Fl_X *i = Fl_X::i(window);
591
i->wait_for_expose = 0;
592
if (!i->region && window->damage()) {
593
// Redraw the whole window...
594
i->region = CreateRectRgn(0, 0, window->w(), window->h());
596
// We need to merge WIN32's damage into FLTK's damage.
597
R = CreateRectRgn(0,0,0,0);
598
GetUpdateRgn(hWnd,R,0);
601
// Also tell WIN32 that we are drawing someplace else as well...
602
InvalidateRgn(hWnd, i->region, FALSE);
603
CombineRgn(i->region, i->region, R, RGN_OR);
609
window->clear_damage((uchar)(window->damage()|FL_DAMAGE_EXPOSE));
610
// These next two statements should not be here, so that all update
611
// is deferred until Fl::flush() is called during idle. However WIN32
612
// apparently is very unhappy if we don't obey it and draw right now.
614
fl_GetDC(hWnd); // Make sure we have a DC for this window...
618
if (window->type() == FL_DOUBLE_WINDOW) ValidateRgn(hWnd,0);
619
else ValidateRgn(hWnd,i->region);
620
window->clear_damage();
623
case WM_LBUTTONDOWN: mouse_event(window, 0, 1, wParam, lParam); return 0;
624
case WM_LBUTTONDBLCLK:mouse_event(window, 1, 1, wParam, lParam); return 0;
625
case WM_LBUTTONUP: mouse_event(window, 2, 1, wParam, lParam); return 0;
626
case WM_MBUTTONDOWN: mouse_event(window, 0, 2, wParam, lParam); return 0;
627
case WM_MBUTTONDBLCLK:mouse_event(window, 1, 2, wParam, lParam); return 0;
628
case WM_MBUTTONUP: mouse_event(window, 2, 2, wParam, lParam); return 0;
629
case WM_RBUTTONDOWN: mouse_event(window, 0, 3, wParam, lParam); return 0;
630
case WM_RBUTTONDBLCLK:mouse_event(window, 1, 3, wParam, lParam); return 0;
631
case WM_RBUTTONUP: mouse_event(window, 2, 3, wParam, lParam); return 0;
634
#ifdef USE_TRACK_MOUSE
635
if (Fl::belowmouse() != window) {
637
tme.cbSize = sizeof(TRACKMOUSEEVENT);
638
tme.dwFlags = TME_LEAVE;
639
tme.hwndTrack = hWnd;
640
_TrackMouseEvent(&tme);
642
#endif // USE_TRACK_MOUSE
643
mouse_event(window, 3, 0, wParam, lParam);
648
if (!window->parent()) Fl::handle(FL_LEAVE, window);
652
Fl::handle(FL_FOCUS, window);
656
Fl::handle(FL_UNFOCUS, window);
657
Fl::flush(); // it never returns to main loop when deactivated...
661
if (!window->parent()) {
662
Fl::handle(wParam ? FL_SHOW : FL_HIDE, window);
667
// From eric@vfx.sel.sony.com, we should process WM_ACTIVATEAPP
668
// messages to restore the correct state of the shift/ctrl/alt/lock
669
// keys... Added control, shift, alt, and meta keys, and changed
670
// to use GetAsyncKeyState and do it when wParam is 1
671
// (that means we have focus...)
675
if (GetAsyncKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK;
676
if (GetAsyncKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK;
677
if (GetAsyncKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
678
if (GetAsyncKeyState(VK_CONTROL)&~1) state |= FL_CTRL;
679
if (GetAsyncKeyState(VK_SHIFT)&~1) state |= FL_SHIFT;
680
if (GetAsyncKeyState(VK_MENU)) state |= FL_ALT;
681
if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1) state |= FL_META;
691
// save the keysym until we figure out the characters:
692
Fl::e_keysym = ms2fltk(wParam,lParam&(1<<24));
693
// See if TranslateMessage turned it into a WM_*CHAR message:
694
if (PeekMessage(&fl_msg, hWnd, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
695
uMsg = fl_msg.message;
696
wParam = fl_msg.wParam;
697
lParam = fl_msg.lParam;
703
ulong state = Fl::e_state & 0xff000000; // keep the mouse button state
704
// if GetKeyState is expensive we might want to comment some of these out:
705
if (GetKeyState(VK_SHIFT)&~1) state |= FL_SHIFT;
706
if (GetKeyState(VK_CAPITAL)) state |= FL_CAPS_LOCK;
707
if (GetKeyState(VK_CONTROL)&~1) state |= FL_CTRL;
708
// Alt gets reported for the Alt-GR switch on foreign keyboards.
709
// so we need to check the event as well to get it right:
710
if ((lParam&(1<<29)) //same as GetKeyState(VK_MENU)
711
&& uMsg != WM_CHAR) state |= FL_ALT;
712
if (GetKeyState(VK_NUMLOCK)) state |= FL_NUM_LOCK;
713
if ((GetKeyState(VK_LWIN)|GetKeyState(VK_RWIN))&~1) {
714
// WIN32 bug? GetKeyState returns garbage if the user hit the
715
// meta key to pop up start menu. Sigh.
716
if ((GetAsyncKeyState(VK_LWIN)|GetAsyncKeyState(VK_RWIN))&~1)
719
if (GetKeyState(VK_SCROLL)) state |= FL_SCROLL_LOCK;
721
if (lParam & (1<<31)) { // key up events.
722
if (Fl::handle(FL_KEYUP, window)) return 0;
725
static char buffer[2];
726
if (uMsg == WM_CHAR || uMsg == WM_SYSCHAR) {
727
buffer[0] = char(wParam);
729
} else if (Fl::e_keysym >= FL_KP && Fl::e_keysym <= FL_KP_Last) {
730
buffer[0] = Fl::e_keysym-FL_KP;
737
// for (int i = lParam&0xff; i--;)
738
while (window->parent()) window = window->window();
739
if (Fl::handle(FL_KEYBOARD,window)) return 0;
742
case WM_MOUSEWHEEL: {
743
static int delta = 0; // running total of all motion
744
delta += (SHORT)(HIWORD(wParam));
745
Fl::e_dy = -delta / WHEEL_DELTA;
746
delta += Fl::e_dy * WHEEL_DELTA;
747
if (Fl::e_dy) Fl::handle(FL_MOUSEWHEEL, window);
751
case WM_GETMINMAXINFO:
752
Fl_X::i(window)->set_minmax((LPMINMAXINFO)lParam);
756
if (!window->parent()) {
757
if (wParam == SIZE_MINIMIZED || wParam == SIZE_MAXHIDE) {
758
Fl::handle(FL_HIDE, window);
760
Fl::handle(FL_SHOW, window);
761
resize_bug_fix = window;
762
window->size(LOWORD(lParam), HIWORD(lParam));
768
resize_bug_fix = window;
769
window->position(LOWORD(lParam), HIWORD(lParam));
773
if (LOWORD(lParam) == HTCLIENT) {
774
while (window->parent()) window = window->window();
775
SetCursor(Fl_X::i(window)->cursor);
781
case WM_QUERYNEWPALETTE :
783
if (fl_select_palette()) InvalidateRect(hWnd, NULL, FALSE);
786
case WM_PALETTECHANGED:
788
if ((HWND)wParam != hWnd && fl_select_palette()) UpdateColors(fl_gc);
797
case WM_DESTROYCLIPBOARD:
798
fl_i_own_selection[1] = 0;
801
case WM_RENDERALLFORMATS:
802
fl_i_own_selection[1] = 0;
803
// Windoze seems unhappy unless I do these two steps. Documentation
804
// seems to vary on whether opening the clipboard is necessary or
809
case WM_RENDERFORMAT: {
810
HANDLE h = GlobalAlloc(GHND, fl_selection_length[1]+1);
812
LPSTR p = (LPSTR)GlobalLock(h);
813
memcpy(p, fl_selection_buffer[1], fl_selection_length[1]);
814
p[fl_selection_length[1]] = 0;
816
SetClipboardData(CF_TEXT, h);
818
// Windoze also seems unhappy if I don't do this. Documentation very
819
// unclear on what is correct:
820
if (fl_msg.message == WM_RENDERALLFORMATS) CloseClipboard();
824
if (Fl::handle(0,0)) return 0;
828
return DefWindowProc(hWnd, uMsg, wParam, lParam);
831
////////////////////////////////////////////////////////////////
832
// This function gets the dimensions of the top/left borders and
833
// the title bar, if there is one, based on the FL_BORDER, FL_MODAL
834
// and FL_NONMODAL flags, and on the window's size range.
835
// It returns the following values:
837
// value | border | title bar
842
int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) {
843
int W, H, xoff, yoff, dx, dy;
844
int ret = bx = by = bt = 0;
845
if (w->border() && !w->parent()) {
846
if (w->size_range_set && (w->maxw != w->minw || w->maxh != w->minh)) {
848
bx = GetSystemMetrics(SM_CXSIZEFRAME);
849
by = GetSystemMetrics(SM_CYSIZEFRAME);
852
bx = GetSystemMetrics(SM_CXFIXEDFRAME);
853
by = GetSystemMetrics(SM_CYFIXEDFRAME);
855
bt = GetSystemMetrics(SM_CYCAPTION);
857
//The coordinates of the whole window, including non-client area
867
//Proceed to positioning the window fully inside the screen, if possible
868
//Make border's lower right corner visible
869
if (Fl::w() < X+W) X = Fl::w() - W;
870
if (Fl::h() < Y+H) Y = Fl::h() - H;
871
//Make border's upper left corner visible
874
//Make client area's lower right corner visible
875
if (Fl::w() < X+dx+ w->w()) X = Fl::w() - w->w() - dx;
876
if (Fl::h() < Y+dy+ w->h()) Y = Fl::h() - w->h() - dy;
877
//Make client area's upper left corner visible
878
if (X+xoff < 0) X = -xoff;
879
if (Y+yoff < 0) Y = -yoff;
880
//Return the client area's top left corner in (X,Y)
887
////////////////////////////////////////////////////////////////
889
void Fl_Window::resize(int X,int Y,int W,int H) {
890
UINT flags = SWP_NOSENDCHANGING | SWP_NOZORDER;
891
int is_a_resize = (W != w() || H != h());
892
int resize_from_program = (this != resize_bug_fix);
893
if (!resize_from_program) resize_bug_fix = 0;
894
if (X != x() || Y != y()) {
895
set_flag(FL_FORCE_POSITION);
897
if (!is_a_resize) return;
901
Fl_Group::resize(X,Y,W,H);
902
if (shown()) {redraw(); i->wait_for_expose = 1;}
907
if (!border()) flags |= SWP_NOACTIVATE;
908
if (resize_from_program && shown()) {
909
if (!resizable()) size_range(w(),h(),w(),h());
910
int dummy, bt, bx, by;
911
//Ignore window managing when resizing, so that windows (and more
912
//specifically menus) can be moved offscreen.
913
if (Fl_X::fake_X_wm(this, dummy, dummy, bt, bx, by)) {
919
SetWindowPos(i->xid, 0, X, Y, W, H, flags);
923
////////////////////////////////////////////////////////////////
925
void fl_fix_focus(); // in Fl.cxx
927
char fl_show_iconic; // hack for Fl_Window::iconic()
928
// int fl_background_pixel = -1; // color to use for background
929
HCURSOR fl_default_cursor;
930
UINT fl_wake_msg = 0;
931
int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR
933
Fl_X* Fl_X::make(Fl_Window* w) {
934
Fl_Group::current(0); // get rid of very common user bug: forgot end()
936
const char* class_name = /*w->xclass();
937
if (!class_name) class_name =*/ "FLTK"; // create a "FLTK" WNDCLASS
939
const char* message_name = "FLTK::ThreadWakeup";
942
// Documentation states a device context consumes about 800 bytes
943
// of memory... so who cares? If 800 bytes per window is what it
944
// takes to speed things up, I'm game.
945
//wc.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC | CS_DBLCLKS;
946
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC | CS_DBLCLKS;
947
wc.lpfnWndProc = (WNDPROC)WndProc;
948
wc.cbClsExtra = wc.cbWndExtra = 0;
949
wc.hInstance = fl_display;
951
w->icon((void *)LoadIcon(NULL, IDI_APPLICATION));
952
wc.hIcon = wc.hIconSm = (HICON)w->icon();
953
wc.hCursor = fl_default_cursor = LoadCursor(NULL, IDC_ARROW);
954
//uchar r,g,b; Fl::get_color(FL_GRAY,r,g,b);
955
//wc.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(r,g,b));
956
wc.hbrBackground = NULL;
957
wc.lpszMenuName = NULL;
958
wc.lpszClassName = class_name;
959
wc.cbSize = sizeof(WNDCLASSEX);
960
RegisterClassEx(&wc);
961
if (!fl_wake_msg) fl_wake_msg = RegisterWindowMessage(message_name);
964
DWORD style = WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
965
DWORD styleEx = WS_EX_LEFT;
976
styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
977
parent = fl_xid(w->window());
979
if (!w->size_range_set) {
980
if (w->resizable()) {
981
Fl_Widget *o = w->resizable();
982
int minw = o->w(); if (minw > 100) minw = 100;
983
int minh = o->h(); if (minh > 100) minh = 100;
984
w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0);
986
w->size_range(w->w(), w->h(), w->w(), w->h());
989
styleEx |= WS_EX_WINDOWEDGE | WS_EX_CONTROLPARENT;
990
int xwm = xp , ywm = yp , bt, bx, by;
991
switch (fake_X_wm(w, xwm, ywm, bt, bx, by)) {
992
// No border (used for menus)
993
case 0: style |= WS_POPUP;
994
styleEx |= WS_EX_TOOLWINDOW;
997
// Thin border and title bar
998
case 1: style |= WS_DLGFRAME | WS_CAPTION; break;
1000
// Thick, resizable border and title bar, with maximize button
1001
case 2: style |= WS_THICKFRAME | WS_MAXIMIZEBOX | WS_CAPTION ; break;
1004
if (!w->modal()) style |= WS_SYSMENU | WS_MINIMIZEBOX;
1008
if (!(w->flags() & Fl_Window::FL_FORCE_POSITION)) {
1009
xp = yp = CW_USEDEFAULT;
1020
if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) {
1021
// find some other window to be "transient for":
1022
Fl_Window* w = Fl_X::first->w;
1023
while (w->parent()) w = w->window();
1025
if (!w->visible()) showit = 0;
1026
} else if (Fl::grab()) parent = fl_xid(Fl::grab());
1034
x->cursor = fl_default_cursor;
1035
x->xid = CreateWindowEx(
1037
class_name, w->label(), style,
1042
NULL // creation parameters
1044
x->next = Fl_X::first;
1047
x->wait_for_expose = 1;
1048
if (fl_show_iconic) {showit = 0; fl_show_iconic = 0;}
1051
w->handle(FL_SHOW); // get child windows to appear
1052
w->redraw(); // force draw to happen
1054
// If we've captured the mouse, we dont want do activate any
1055
// other windows from the code, or we loose the capture.
1056
ShowWindow(x->xid, !showit ? SW_SHOWMINNOACTIVE :
1057
(Fl::grab() || (style & WS_POPUP)) ? SW_SHOWNOACTIVATE : SW_SHOWNORMAL);
1059
// Drag-n-drop requires GCC 3.x or a non-GNU compiler...
1060
#if !defined(__GNUC__) || __GNUC__ >= 3
1061
// Register all windows for potential drag'n'drop operations
1062
static char oleInitialized = 0;
1063
if (!oleInitialized) { OleInitialize(0L); oleInitialized=1; }
1065
RegisterDragDrop(x->xid, flIDropTarget);
1066
#endif // !__GNUC__ || __GNUC__ >= 3
1068
if (w->modal()) {Fl::modal_ = w; fl_fix_focus();}
1072
////////////////////////////////////////////////////////////////
1074
HINSTANCE fl_display = GetModuleHandle(NULL);
1076
void Fl_Window::size_range_() {
1080
void Fl_X::set_minmax(LPMINMAXINFO minmax)
1082
int td, wd, hd, dummy;
1084
fake_X_wm(w, dummy, dummy, td, wd, hd);
1089
minmax->ptMinTrackSize.x = w->minw + wd;
1090
minmax->ptMinTrackSize.y = w->minh + hd;
1092
minmax->ptMaxTrackSize.x = w->maxw + wd;
1093
minmax->ptMaxSize.x = w->maxw + wd;
1096
minmax->ptMaxTrackSize.y = w->maxh + hd;
1097
minmax->ptMaxSize.y = w->maxh + hd;
1101
////////////////////////////////////////////////////////////////
1103
#include <FL/filename.H> // need so FL_EXPORT fl_filename_name works
1105
// returns pointer to the filename, or null if name ends with '/'
1106
const char *fl_filename_name(const char *name) {
1108
if (!name) return (0);
1110
if (q[0] && q[1]==':') q += 2; // skip leading drive letter
1111
for (p = q; *p; p++) if (*p == '/' || *p == '\\') q = p+1;
1115
void Fl_Window::label(const char *name,const char *iname) {
1116
Fl_Widget::label(name);
1118
if (shown() && !parent()) {
1119
if (!name) name = "";
1120
SetWindowText(i->xid, name);
1121
// if (!iname) iname = fl_filename_name(name);
1122
// should do something with iname here...
1126
////////////////////////////////////////////////////////////////
1127
// Implement the virtual functions for the base Fl_Window class:
1129
// If the box is a filled rectangle, we can make the redisplay *look*
1130
// faster by using X's background pixel erasing. We can make it
1131
// actually *be* faster by drawing the frame only, this is done by
1132
// setting fl_boxcheat, which is seen by code in fl_drawbox.cxx:
1133
// For WIN32 it looks like all windows share a background color, so
1134
// I use FL_GRAY for this and only do this cheat for windows that are
1136
// Actually it is totally disabled.
1137
// Fl_Widget *fl_boxcheat;
1138
//static inline int can_boxcheat(uchar b) {return (b==1 || (b&2) && b<=15);}
1140
void Fl_Window::show() {
1141
image(Fl::scheme_bg_);
1142
if (Fl::scheme_bg_) {
1143
labeltype(FL_NORMAL_LABEL);
1144
align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP);
1146
labeltype(FL_NO_LABEL);
1149
// if (can_boxcheat(box())) fl_background_pixel = fl_xpixel(color());
1152
// Once again, we would lose the capture if we activated the window.
1153
if (IsIconic(i->xid)) OpenIcon(i->xid);
1154
if (!fl_capture) BringWindowToTop(i->xid);
1155
//ShowWindow(i->xid,fl_capture?SW_SHOWNOACTIVATE:SW_RESTORE);
1159
Fl_Window *Fl_Window::current_;
1160
// the current context
1162
// the current window handle, initially set to -1 so we can correctly
1163
// allocate fl_GetDC(0)
1164
HWND fl_window = (HWND)-1;
1166
// Here we ensure only one GetDC is ever in place.
1167
HDC fl_GetDC(HWND w) {
1169
if (w == fl_window) return fl_gc;
1170
ReleaseDC(fl_window, fl_gc);
1174
// calling GetDC seems to always reset these: (?)
1175
SetTextAlign(fl_gc, TA_BASELINE|TA_LEFT);
1176
SetBkMode(fl_gc, TRANSPARENT);
1180
// make X drawing go into this window (called by subclass flush() impl.)
1181
void Fl_Window::make_current() {
1182
fl_GetDC(fl_xid(this));
1185
// Windows maintains a hardware and software color palette; the
1186
// SelectPalette() call updates the current soft->hard mapping
1187
// for all drawing calls, so we must select it here before any
1188
// code does any drawing...
1190
fl_select_palette();
1191
#endif // USE_COLORMAP
1198
// End of "$Id: Fl_win32.cxx,v 1.33.2.37.2.48 2004/04/11 04:38:59 easysw Exp $".