1 |
/* $Header$ */ |
2 |
|
3 |
/* |
4 |
* tkWinX.c -- |
5 |
* |
6 |
* This file contains Windows emulation procedures for X routines. |
7 |
* |
8 |
* Copyright (c) 1995-1996 Sun Microsystems, Inc. |
9 |
* Copyright (c) 1994 Software Research Associates, Inc. |
10 |
* Copyright (c) 1998-2000 by Scriptics Corporation. |
11 |
* |
12 |
* See the file "license.terms" for information on usage and redistribution |
13 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES. |
14 |
* |
15 |
* RCS: @(#) $Id: tkWinX.c,v 1.10 2000/04/19 01:06:51 ericm Exp $ |
16 |
*/ |
17 |
|
18 |
#include "tkWinInt.h" |
19 |
|
20 |
/* |
21 |
* The zmouse.h file includes the definition for WM_MOUSEWHEEL. |
22 |
*/ |
23 |
|
24 |
#include <zmouse.h> |
25 |
|
26 |
/* |
27 |
* Declarations of static variables used in this file. |
28 |
*/ |
29 |
|
30 |
static char winScreenName[] = ":0"; /* Default name of windows display. */ |
31 |
static HINSTANCE tkInstance; /* Application instance handle. */ |
32 |
static int childClassInitialized; /* Registered child class? */ |
33 |
static WNDCLASS childClass; /* Window class for child windows. */ |
34 |
static int tkPlatformId; /* version of Windows platform */ |
35 |
|
36 |
TCL_DECLARE_MUTEX(winXMutex) |
37 |
|
38 |
/* |
39 |
* Thread local storage. Notice that now each thread must have its |
40 |
* own TkDisplay structure, since this structure contains most of |
41 |
* the thread-specific date for threads. |
42 |
*/ |
43 |
typedef struct ThreadSpecificData { |
44 |
TkDisplay *winDisplay; /* TkDisplay structure that * |
45 |
* represents Windows screen. */ |
46 |
int updatingClipboard; /* If 1, we are updating the clipboard */ |
47 |
} ThreadSpecificData; |
48 |
static Tcl_ThreadDataKey dataKey; |
49 |
|
50 |
/* |
51 |
* Forward declarations of procedures used in this file. |
52 |
*/ |
53 |
|
54 |
static void GenerateXEvent _ANSI_ARGS_((HWND hwnd, UINT message, |
55 |
WPARAM wParam, LPARAM lParam)); |
56 |
static unsigned int GetState _ANSI_ARGS_((UINT message, WPARAM wParam, |
57 |
LPARAM lParam)); |
58 |
static void GetTranslatedKey _ANSI_ARGS_((XKeyEvent *xkey)); |
59 |
|
60 |
/* |
61 |
*---------------------------------------------------------------------- |
62 |
* |
63 |
* TkGetServerInfo -- |
64 |
* |
65 |
* Given a window, this procedure returns information about |
66 |
* the window server for that window. This procedure provides |
67 |
* the guts of the "winfo server" command. |
68 |
* |
69 |
* Results: |
70 |
* None. |
71 |
* |
72 |
* Side effects: |
73 |
* None. |
74 |
* |
75 |
*---------------------------------------------------------------------- |
76 |
*/ |
77 |
|
78 |
void |
79 |
TkGetServerInfo(interp, tkwin) |
80 |
Tcl_Interp *interp; /* The server information is returned in |
81 |
* this interpreter's result. */ |
82 |
Tk_Window tkwin; /* Token for window; this selects a |
83 |
* particular display and server. */ |
84 |
{ |
85 |
char buffer[60]; |
86 |
OSVERSIONINFO os; |
87 |
|
88 |
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
89 |
GetVersionEx(&os); |
90 |
sprintf(buffer, "Windows %d.%d %d Win32", os.dwMajorVersion, |
91 |
os.dwMinorVersion, os.dwBuildNumber); |
92 |
Tcl_SetResult(interp, buffer, TCL_VOLATILE); |
93 |
} |
94 |
|
95 |
/* |
96 |
*---------------------------------------------------------------------- |
97 |
* |
98 |
* Tk_GetHINSTANCE -- |
99 |
* |
100 |
* Retrieves the global instance handle used by the Tk library. |
101 |
* |
102 |
* Results: |
103 |
* Returns the global instance handle. |
104 |
* |
105 |
* Side effects: |
106 |
* None. |
107 |
* |
108 |
*---------------------------------------------------------------------- |
109 |
*/ |
110 |
|
111 |
HINSTANCE |
112 |
Tk_GetHINSTANCE() |
113 |
{ |
114 |
return tkInstance; |
115 |
} |
116 |
|
117 |
/* |
118 |
*---------------------------------------------------------------------- |
119 |
* |
120 |
* TkWinXInit -- |
121 |
* |
122 |
* Initialize Xlib emulation layer. |
123 |
* |
124 |
* Results: |
125 |
* None. |
126 |
* |
127 |
* Side effects: |
128 |
* Sets up various data structures. |
129 |
* |
130 |
*---------------------------------------------------------------------- |
131 |
*/ |
132 |
|
133 |
void |
134 |
TkWinXInit(hInstance) |
135 |
HINSTANCE hInstance; |
136 |
{ |
137 |
OSVERSIONINFO os; |
138 |
|
139 |
if (childClassInitialized != 0) { |
140 |
return; |
141 |
} |
142 |
childClassInitialized = 1; |
143 |
|
144 |
tkInstance = hInstance; |
145 |
|
146 |
os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); |
147 |
GetVersionEx(&os); |
148 |
tkPlatformId = os.dwPlatformId; |
149 |
|
150 |
/* |
151 |
* When threads are enabled, we cannot use CLASSDC because |
152 |
* threads will then write into the same device context. |
153 |
* |
154 |
* This is a hack; we should add a subsystem that manages |
155 |
* device context on a per-thread basis. See also tkWinWm.c, |
156 |
* which also initializes a WNDCLASS structure. |
157 |
*/ |
158 |
|
159 |
#ifdef TCL_THREADS |
160 |
childClass.style = CS_HREDRAW | CS_VREDRAW; |
161 |
#else |
162 |
childClass.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC; |
163 |
#endif |
164 |
|
165 |
childClass.cbClsExtra = 0; |
166 |
childClass.cbWndExtra = 0; |
167 |
childClass.hInstance = hInstance; |
168 |
childClass.hbrBackground = NULL; |
169 |
childClass.lpszMenuName = NULL; |
170 |
|
171 |
/* |
172 |
* Register the Child window class. |
173 |
*/ |
174 |
|
175 |
childClass.lpszClassName = TK_WIN_CHILD_CLASS_NAME; |
176 |
childClass.lpfnWndProc = TkWinChildProc; |
177 |
childClass.hIcon = NULL; |
178 |
childClass.hCursor = NULL; |
179 |
|
180 |
if (!RegisterClass(&childClass)) { |
181 |
panic("Unable to register TkChild class"); |
182 |
} |
183 |
} |
184 |
|
185 |
/* |
186 |
*---------------------------------------------------------------------- |
187 |
* |
188 |
* TkWinXCleanup -- |
189 |
* |
190 |
* Removes the registered classes for Tk. |
191 |
* |
192 |
* Results: |
193 |
* None. |
194 |
* |
195 |
* Side effects: |
196 |
* Removes window classes from the system. |
197 |
* |
198 |
*---------------------------------------------------------------------- |
199 |
*/ |
200 |
|
201 |
void |
202 |
TkWinXCleanup(hInstance) |
203 |
HINSTANCE hInstance; |
204 |
{ |
205 |
/* |
206 |
* Clean up our own class. |
207 |
*/ |
208 |
|
209 |
if (childClassInitialized) { |
210 |
childClassInitialized = 0; |
211 |
UnregisterClass(TK_WIN_CHILD_CLASS_NAME, hInstance); |
212 |
} |
213 |
|
214 |
/* |
215 |
* And let the window manager clean up its own class(es). |
216 |
*/ |
217 |
|
218 |
TkWinWmCleanup(hInstance); |
219 |
} |
220 |
|
221 |
/* |
222 |
*---------------------------------------------------------------------- |
223 |
* |
224 |
* TkWinGetPlatformId -- |
225 |
* |
226 |
* Determines whether running under NT, 95, or Win32s, to allow |
227 |
* runtime conditional code. |
228 |
* |
229 |
* Results: |
230 |
* The return value is one of: |
231 |
* VER_PLATFORM_WIN32s Win32s on Windows 3.1. |
232 |
* VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95. |
233 |
* VER_PLATFORM_WIN32_NT Win32 on Windows NT |
234 |
* |
235 |
* Side effects: |
236 |
* None. |
237 |
* |
238 |
*---------------------------------------------------------------------- |
239 |
*/ |
240 |
|
241 |
int |
242 |
TkWinGetPlatformId() |
243 |
{ |
244 |
return tkPlatformId; |
245 |
} |
246 |
|
247 |
/* |
248 |
*---------------------------------------------------------------------- |
249 |
* |
250 |
* TkGetDefaultScreenName -- |
251 |
* |
252 |
* Returns the name of the screen that Tk should use during |
253 |
* initialization. |
254 |
* |
255 |
* Results: |
256 |
* Returns a statically allocated string. |
257 |
* |
258 |
* Side effects: |
259 |
* None. |
260 |
* |
261 |
*---------------------------------------------------------------------- |
262 |
*/ |
263 |
|
264 |
char * |
265 |
TkGetDefaultScreenName(interp, screenName) |
266 |
Tcl_Interp *interp; /* Not used. */ |
267 |
char *screenName; /* If NULL, use default string. */ |
268 |
{ |
269 |
if ((screenName == NULL) || (screenName[0] == '\0')) { |
270 |
screenName = winScreenName; |
271 |
} |
272 |
return screenName; |
273 |
} |
274 |
|
275 |
/* |
276 |
*---------------------------------------------------------------------- |
277 |
* |
278 |
* TkpOpenDisplay -- |
279 |
* |
280 |
* Create the Display structure and fill it with device |
281 |
* specific information. |
282 |
* |
283 |
* Results: |
284 |
* Returns a TkDisplay structure on success or NULL on failure. |
285 |
* |
286 |
* Side effects: |
287 |
* Allocates a new TkDisplay structure. |
288 |
* |
289 |
*---------------------------------------------------------------------- |
290 |
*/ |
291 |
|
292 |
TkDisplay * |
293 |
TkpOpenDisplay(display_name) |
294 |
char *display_name; |
295 |
{ |
296 |
Screen *screen; |
297 |
HDC dc; |
298 |
TkWinDrawable *twdPtr; |
299 |
Display *display; |
300 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
301 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
302 |
|
303 |
if (tsdPtr->winDisplay != NULL) { |
304 |
if (strcmp(tsdPtr->winDisplay->display->display_name, display_name) |
305 |
== 0) { |
306 |
return tsdPtr->winDisplay; |
307 |
} else { |
308 |
return NULL; |
309 |
} |
310 |
} |
311 |
|
312 |
display = (Display *) ckalloc(sizeof(Display)); |
313 |
display->display_name = (char *) ckalloc(strlen(display_name)+1); |
314 |
strcpy(display->display_name, display_name); |
315 |
|
316 |
display->cursor_font = 1; |
317 |
display->nscreens = 1; |
318 |
display->request = 1; |
319 |
display->qlen = 0; |
320 |
|
321 |
screen = (Screen *) ckalloc(sizeof(Screen)); |
322 |
screen->display = display; |
323 |
|
324 |
dc = GetDC(NULL); |
325 |
screen->width = GetDeviceCaps(dc, HORZRES); |
326 |
screen->height = GetDeviceCaps(dc, VERTRES); |
327 |
screen->mwidth = MulDiv(screen->width, 254, |
328 |
GetDeviceCaps(dc, LOGPIXELSX) * 10); |
329 |
screen->mheight = MulDiv(screen->height, 254, |
330 |
GetDeviceCaps(dc, LOGPIXELSY) * 10); |
331 |
|
332 |
/* |
333 |
* Set up the root window. |
334 |
*/ |
335 |
|
336 |
twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable)); |
337 |
if (twdPtr == NULL) { |
338 |
return None; |
339 |
} |
340 |
twdPtr->type = TWD_WINDOW; |
341 |
twdPtr->window.winPtr = NULL; |
342 |
twdPtr->window.handle = NULL; |
343 |
screen->root = (Window)twdPtr; |
344 |
|
345 |
/* |
346 |
* On windows, when creating a color bitmap, need two pieces of |
347 |
* information: the number of color planes and the number of |
348 |
* pixels per plane. Need to remember both quantities so that |
349 |
* when constructing an HBITMAP for offscreen rendering, we can |
350 |
* specify the correct value for the number of planes. Otherwise |
351 |
* the HBITMAP won't be compatible with the HWND and we'll just |
352 |
* get blank spots copied onto the screen. |
353 |
*/ |
354 |
|
355 |
screen->ext_data = (XExtData *) GetDeviceCaps(dc, PLANES); |
356 |
screen->root_depth = GetDeviceCaps(dc, BITSPIXEL) * (int) screen->ext_data; |
357 |
|
358 |
screen->root_visual = (Visual *) ckalloc(sizeof(Visual)); |
359 |
screen->root_visual->visualid = 0; |
360 |
if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) { |
361 |
screen->root_visual->map_entries = GetDeviceCaps(dc, SIZEPALETTE); |
362 |
screen->root_visual->class = PseudoColor; |
363 |
screen->root_visual->red_mask = 0x0; |
364 |
screen->root_visual->green_mask = 0x0; |
365 |
screen->root_visual->blue_mask = 0x0; |
366 |
} else { |
367 |
if (screen->root_depth == 4) { |
368 |
screen->root_visual->class = StaticColor; |
369 |
screen->root_visual->map_entries = 16; |
370 |
} else if (screen->root_depth == 8) { |
371 |
screen->root_visual->class = StaticColor; |
372 |
screen->root_visual->map_entries = 256; |
373 |
} else if (screen->root_depth == 12) { |
374 |
screen->root_visual->class = TrueColor; |
375 |
screen->root_visual->map_entries = 32; |
376 |
screen->root_visual->red_mask = 0xf0; |
377 |
screen->root_visual->green_mask = 0xf000; |
378 |
screen->root_visual->blue_mask = 0xf00000; |
379 |
} else if (screen->root_depth == 16) { |
380 |
screen->root_visual->class = TrueColor; |
381 |
screen->root_visual->map_entries = 64; |
382 |
screen->root_visual->red_mask = 0xf8; |
383 |
screen->root_visual->green_mask = 0xfc00; |
384 |
screen->root_visual->blue_mask = 0xf80000; |
385 |
} else if (screen->root_depth >= 24) { |
386 |
screen->root_visual->class = TrueColor; |
387 |
screen->root_visual->map_entries = 256; |
388 |
screen->root_visual->red_mask = 0xff; |
389 |
screen->root_visual->green_mask = 0xff00; |
390 |
screen->root_visual->blue_mask = 0xff0000; |
391 |
} |
392 |
} |
393 |
screen->root_visual->bits_per_rgb = screen->root_depth; |
394 |
ReleaseDC(NULL, dc); |
395 |
|
396 |
/* |
397 |
* Note that these pixel values are not palette relative. |
398 |
*/ |
399 |
|
400 |
screen->white_pixel = RGB(255, 255, 255); |
401 |
screen->black_pixel = RGB(0, 0, 0); |
402 |
|
403 |
display->screens = screen; |
404 |
display->nscreens = 1; |
405 |
display->default_screen = 0; |
406 |
screen->cmap = XCreateColormap(display, None, screen->root_visual, |
407 |
AllocNone); |
408 |
tsdPtr->winDisplay = (TkDisplay *) ckalloc(sizeof(TkDisplay)); |
409 |
tsdPtr->winDisplay->display = display; |
410 |
tsdPtr->updatingClipboard = FALSE; |
411 |
return tsdPtr->winDisplay; |
412 |
} |
413 |
|
414 |
/* |
415 |
*---------------------------------------------------------------------- |
416 |
* |
417 |
* TkpCloseDisplay -- |
418 |
* |
419 |
* Closes and deallocates a Display structure created with the |
420 |
* TkpOpenDisplay function. |
421 |
* |
422 |
* Results: |
423 |
* None. |
424 |
* |
425 |
* Side effects: |
426 |
* Frees up memory. |
427 |
* |
428 |
*---------------------------------------------------------------------- |
429 |
*/ |
430 |
|
431 |
void |
432 |
TkpCloseDisplay(dispPtr) |
433 |
TkDisplay *dispPtr; |
434 |
{ |
435 |
Display *display = dispPtr->display; |
436 |
HWND hwnd; |
437 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
438 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
439 |
|
440 |
if (dispPtr != tsdPtr->winDisplay) { |
441 |
panic("TkpCloseDisplay: tried to call TkpCloseDisplay on another display"); |
442 |
return; |
443 |
} |
444 |
|
445 |
/* |
446 |
* Force the clipboard to be rendered if we are the clipboard owner. |
447 |
*/ |
448 |
|
449 |
if (dispPtr->clipWindow) { |
450 |
hwnd = Tk_GetHWND(Tk_WindowId(dispPtr->clipWindow)); |
451 |
if (GetClipboardOwner() == hwnd) { |
452 |
OpenClipboard(hwnd); |
453 |
EmptyClipboard(); |
454 |
TkWinClipboardRender(dispPtr, CF_TEXT); |
455 |
CloseClipboard(); |
456 |
} |
457 |
} |
458 |
|
459 |
tsdPtr->winDisplay = NULL; |
460 |
|
461 |
if (display->display_name != (char *) NULL) { |
462 |
ckfree(display->display_name); |
463 |
} |
464 |
if (display->screens != (Screen *) NULL) { |
465 |
if (display->screens->root_visual != NULL) { |
466 |
ckfree((char *) display->screens->root_visual); |
467 |
} |
468 |
if (display->screens->root != None) { |
469 |
ckfree((char *) display->screens->root); |
470 |
} |
471 |
if (display->screens->cmap != None) { |
472 |
XFreeColormap(display, display->screens->cmap); |
473 |
} |
474 |
ckfree((char *) display->screens); |
475 |
} |
476 |
ckfree((char *) display); |
477 |
ckfree((char *) dispPtr); |
478 |
} |
479 |
|
480 |
/* |
481 |
*---------------------------------------------------------------------- |
482 |
* |
483 |
* XBell -- |
484 |
* |
485 |
* Generate a beep. |
486 |
* |
487 |
* Results: |
488 |
* None. |
489 |
* |
490 |
* Side effects: |
491 |
* Plays a sounds out the system speakers. |
492 |
* |
493 |
*---------------------------------------------------------------------- |
494 |
*/ |
495 |
|
496 |
void |
497 |
XBell(display, percent) |
498 |
Display* display; |
499 |
int percent; |
500 |
{ |
501 |
MessageBeep(MB_OK); |
502 |
} |
503 |
|
504 |
/* |
505 |
*---------------------------------------------------------------------- |
506 |
* |
507 |
* TkWinChildProc -- |
508 |
* |
509 |
* Callback from Windows whenever an event occurs on a child |
510 |
* window. |
511 |
* |
512 |
* Results: |
513 |
* Standard Windows return value. |
514 |
* |
515 |
* Side effects: |
516 |
* May process events off the Tk event queue. |
517 |
* |
518 |
*---------------------------------------------------------------------- |
519 |
*/ |
520 |
|
521 |
LRESULT CALLBACK |
522 |
TkWinChildProc(hwnd, message, wParam, lParam) |
523 |
HWND hwnd; |
524 |
UINT message; |
525 |
WPARAM wParam; |
526 |
LPARAM lParam; |
527 |
{ |
528 |
LRESULT result; |
529 |
|
530 |
switch (message) { |
531 |
case WM_SETCURSOR: |
532 |
/* |
533 |
* Short circuit the WM_SETCURSOR message since we set |
534 |
* the cursor elsewhere. |
535 |
*/ |
536 |
|
537 |
result = TRUE; |
538 |
break; |
539 |
|
540 |
case WM_CREATE: |
541 |
case WM_ERASEBKGND: |
542 |
result = 0; |
543 |
break; |
544 |
|
545 |
case WM_PAINT: |
546 |
GenerateXEvent(hwnd, message, wParam, lParam); |
547 |
result = DefWindowProc(hwnd, message, wParam, lParam); |
548 |
break; |
549 |
|
550 |
case TK_CLAIMFOCUS: |
551 |
case TK_GEOMETRYREQ: |
552 |
case TK_ATTACHWINDOW: |
553 |
case TK_DETACHWINDOW: |
554 |
result = TkWinEmbeddedEventProc(hwnd, message, wParam, lParam); |
555 |
break; |
556 |
|
557 |
default: |
558 |
if (!Tk_TranslateWinEvent(hwnd, message, wParam, lParam, |
559 |
&result)) { |
560 |
result = DefWindowProc(hwnd, message, wParam, lParam); |
561 |
} |
562 |
break; |
563 |
} |
564 |
|
565 |
/* |
566 |
* Handle any newly queued events before returning control to Windows. |
567 |
*/ |
568 |
|
569 |
Tcl_ServiceAll(); |
570 |
return result; |
571 |
} |
572 |
|
573 |
/* |
574 |
*---------------------------------------------------------------------- |
575 |
* |
576 |
* Tk_TranslateWinEvent -- |
577 |
* |
578 |
* This function is called by widget window procedures to handle |
579 |
* the translation from Win32 events to Tk events. |
580 |
* |
581 |
* Results: |
582 |
* Returns 1 if the event was handled, else 0. |
583 |
* |
584 |
* Side effects: |
585 |
* Depends on the event. |
586 |
* |
587 |
*---------------------------------------------------------------------- |
588 |
*/ |
589 |
|
590 |
int |
591 |
Tk_TranslateWinEvent(hwnd, message, wParam, lParam, resultPtr) |
592 |
HWND hwnd; |
593 |
UINT message; |
594 |
WPARAM wParam; |
595 |
LPARAM lParam; |
596 |
LRESULT *resultPtr; |
597 |
{ |
598 |
*resultPtr = 0; |
599 |
switch (message) { |
600 |
case WM_RENDERFORMAT: { |
601 |
TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd); |
602 |
if (winPtr) { |
603 |
TkWinClipboardRender(winPtr->dispPtr, wParam); |
604 |
} |
605 |
return 1; |
606 |
} |
607 |
|
608 |
case WM_COMMAND: |
609 |
case WM_NOTIFY: |
610 |
case WM_VSCROLL: |
611 |
case WM_HSCROLL: { |
612 |
/* |
613 |
* Reflect these messages back to the sender so that they |
614 |
* can be handled by the window proc for the control. Note |
615 |
* that we need to be careful not to reflect a message that |
616 |
* is targeted to this window, or we will loop. |
617 |
*/ |
618 |
|
619 |
HWND target = (message == WM_NOTIFY) |
620 |
? ((NMHDR*)lParam)->hwndFrom : (HWND) lParam; |
621 |
if (target && target != hwnd) { |
622 |
*resultPtr = SendMessage(target, message, wParam, lParam); |
623 |
return 1; |
624 |
} |
625 |
break; |
626 |
} |
627 |
|
628 |
case WM_LBUTTONDOWN: |
629 |
case WM_LBUTTONDBLCLK: |
630 |
case WM_MBUTTONDOWN: |
631 |
case WM_MBUTTONDBLCLK: |
632 |
case WM_RBUTTONDOWN: |
633 |
case WM_RBUTTONDBLCLK: |
634 |
case WM_LBUTTONUP: |
635 |
case WM_MBUTTONUP: |
636 |
case WM_RBUTTONUP: |
637 |
case WM_MOUSEMOVE: |
638 |
Tk_PointerEvent(hwnd, (short) LOWORD(lParam), |
639 |
(short) HIWORD(lParam)); |
640 |
return 1; |
641 |
|
642 |
case WM_CLOSE: |
643 |
case WM_SETFOCUS: |
644 |
case WM_KILLFOCUS: |
645 |
case WM_DESTROYCLIPBOARD: |
646 |
case WM_CHAR: |
647 |
case WM_SYSKEYDOWN: |
648 |
case WM_SYSKEYUP: |
649 |
case WM_KEYDOWN: |
650 |
case WM_KEYUP: |
651 |
case WM_MOUSEWHEEL: |
652 |
GenerateXEvent(hwnd, message, wParam, lParam); |
653 |
return 1; |
654 |
case WM_MENUCHAR: |
655 |
GenerateXEvent(hwnd, message, wParam, lParam); |
656 |
/* MNC_CLOSE is the only one that looks right. This is a hack. */ |
657 |
*resultPtr = MAKELONG (0, MNC_CLOSE); |
658 |
return 1; |
659 |
} |
660 |
return 0; |
661 |
} |
662 |
|
663 |
/* |
664 |
*---------------------------------------------------------------------- |
665 |
* |
666 |
* GenerateXEvent -- |
667 |
* |
668 |
* This routine generates an X event from the corresponding |
669 |
* Windows event. |
670 |
* |
671 |
* Results: |
672 |
* None. |
673 |
* |
674 |
* Side effects: |
675 |
* Queues one or more X events. |
676 |
* |
677 |
*---------------------------------------------------------------------- |
678 |
*/ |
679 |
|
680 |
static void |
681 |
GenerateXEvent(hwnd, message, wParam, lParam) |
682 |
HWND hwnd; |
683 |
UINT message; |
684 |
WPARAM wParam; |
685 |
LPARAM lParam; |
686 |
{ |
687 |
XEvent event; |
688 |
TkWindow *winPtr = (TkWindow *)Tk_HWNDToWindow(hwnd); |
689 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
690 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
691 |
|
692 |
if (!winPtr || winPtr->window == None) { |
693 |
return; |
694 |
} |
695 |
|
696 |
event.xany.serial = winPtr->display->request++; |
697 |
event.xany.send_event = False; |
698 |
event.xany.display = winPtr->display; |
699 |
event.xany.window = winPtr->window; |
700 |
|
701 |
switch (message) { |
702 |
case WM_PAINT: { |
703 |
PAINTSTRUCT ps; |
704 |
|
705 |
event.type = Expose; |
706 |
BeginPaint(hwnd, &ps); |
707 |
event.xexpose.x = ps.rcPaint.left; |
708 |
event.xexpose.y = ps.rcPaint.top; |
709 |
event.xexpose.width = ps.rcPaint.right - ps.rcPaint.left; |
710 |
event.xexpose.height = ps.rcPaint.bottom - ps.rcPaint.top; |
711 |
EndPaint(hwnd, &ps); |
712 |
event.xexpose.count = 0; |
713 |
break; |
714 |
} |
715 |
|
716 |
case WM_CLOSE: |
717 |
event.type = ClientMessage; |
718 |
event.xclient.message_type = |
719 |
Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS"); |
720 |
event.xclient.format = 32; |
721 |
event.xclient.data.l[0] = |
722 |
Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW"); |
723 |
break; |
724 |
|
725 |
case WM_SETFOCUS: |
726 |
case WM_KILLFOCUS: { |
727 |
TkWindow *otherWinPtr = (TkWindow *)Tk_HWNDToWindow((HWND) wParam); |
728 |
|
729 |
/* |
730 |
* Compare toplevel windows to avoid reporting focus |
731 |
* changes within the same toplevel. |
732 |
*/ |
733 |
|
734 |
while (!(winPtr->flags & TK_TOP_LEVEL)) { |
735 |
winPtr = winPtr->parentPtr; |
736 |
if (winPtr == NULL) { |
737 |
return; |
738 |
} |
739 |
} |
740 |
while (otherWinPtr && !(otherWinPtr->flags & TK_TOP_LEVEL)) { |
741 |
otherWinPtr = otherWinPtr->parentPtr; |
742 |
} |
743 |
if (otherWinPtr == winPtr) { |
744 |
return; |
745 |
} |
746 |
|
747 |
event.xany.window = winPtr->window; |
748 |
event.type = (message == WM_SETFOCUS) ? FocusIn : FocusOut; |
749 |
event.xfocus.mode = NotifyNormal; |
750 |
event.xfocus.detail = NotifyNonlinear; |
751 |
break; |
752 |
} |
753 |
|
754 |
case WM_DESTROYCLIPBOARD: |
755 |
if (tsdPtr->updatingClipboard == TRUE) { |
756 |
/* |
757 |
* We want to avoid this event if we are the ones that caused |
758 |
* this event. |
759 |
*/ |
760 |
return; |
761 |
} |
762 |
event.type = SelectionClear; |
763 |
event.xselectionclear.selection = |
764 |
Tk_InternAtom((Tk_Window)winPtr, "CLIPBOARD"); |
765 |
event.xselectionclear.time = TkpGetMS(); |
766 |
break; |
767 |
|
768 |
case WM_MOUSEWHEEL: |
769 |
/* |
770 |
* The mouse wheel event is closer to a key event than a |
771 |
* mouse event in that the message is sent to the window |
772 |
* that has focus. |
773 |
*/ |
774 |
|
775 |
case WM_CHAR: |
776 |
case WM_SYSKEYDOWN: |
777 |
case WM_SYSKEYUP: |
778 |
case WM_KEYDOWN: |
779 |
case WM_KEYUP: { |
780 |
unsigned int state = GetState(message, wParam, lParam); |
781 |
Time time = TkpGetMS(); |
782 |
POINT clientPoint; |
783 |
POINTS rootPoint; /* Note: POINT and POINTS are different */ |
784 |
DWORD msgPos; |
785 |
|
786 |
/* |
787 |
* Compute the screen and window coordinates of the event. |
788 |
*/ |
789 |
|
790 |
msgPos = GetMessagePos(); |
791 |
rootPoint = MAKEPOINTS(msgPos); |
792 |
clientPoint.x = rootPoint.x; |
793 |
clientPoint.y = rootPoint.y; |
794 |
ScreenToClient(hwnd, &clientPoint); |
795 |
|
796 |
/* |
797 |
* Set up the common event fields. |
798 |
*/ |
799 |
|
800 |
event.xbutton.root = RootWindow(winPtr->display, |
801 |
winPtr->screenNum); |
802 |
event.xbutton.subwindow = None; |
803 |
event.xbutton.x = clientPoint.x; |
804 |
event.xbutton.y = clientPoint.y; |
805 |
event.xbutton.x_root = rootPoint.x; |
806 |
event.xbutton.y_root = rootPoint.y; |
807 |
event.xbutton.state = state; |
808 |
event.xbutton.time = time; |
809 |
event.xbutton.same_screen = True; |
810 |
|
811 |
/* |
812 |
* Now set up event specific fields. |
813 |
*/ |
814 |
|
815 |
switch (message) { |
816 |
case WM_MOUSEWHEEL: |
817 |
/* |
818 |
* We have invented a new X event type to handle |
819 |
* this event. It still uses the KeyPress struct. |
820 |
* However, the keycode field has been overloaded |
821 |
* to hold the zDelta of the wheel. |
822 |
*/ |
823 |
|
824 |
event.type = MouseWheelEvent; |
825 |
event.xany.send_event = -1; |
826 |
event.xkey.keycode = (short) HIWORD(wParam); |
827 |
break; |
828 |
case WM_SYSKEYDOWN: |
829 |
case WM_KEYDOWN: |
830 |
/* |
831 |
* Check for translated characters in the event queue. |
832 |
* Setting xany.send_event to -1 indicates to the |
833 |
* Windows implementation of XLookupString that this |
834 |
* event was generated by windows and that the Windows |
835 |
* extension xkey.trans_chars is filled with the |
836 |
* characters that came from the TranslateMessage |
837 |
* call. If it is not -1, xkey.keycode is the |
838 |
* virtual key being sent programmatically by generic |
839 |
* code. |
840 |
*/ |
841 |
|
842 |
event.type = KeyPress; |
843 |
event.xany.send_event = -1; |
844 |
event.xkey.keycode = wParam; |
845 |
GetTranslatedKey(&event.xkey); |
846 |
break; |
847 |
|
848 |
case WM_SYSKEYUP: |
849 |
case WM_KEYUP: |
850 |
/* |
851 |
* We don't check for translated characters on keyup |
852 |
* because Tk won't know what to do with them. Instead, we |
853 |
* wait for the WM_CHAR messages which will follow. |
854 |
*/ |
855 |
event.type = KeyRelease; |
856 |
event.xkey.keycode = wParam; |
857 |
event.xkey.nbytes = 0; |
858 |
break; |
859 |
|
860 |
case WM_CHAR: |
861 |
/* |
862 |
* Synthesize both a KeyPress and a KeyRelease. |
863 |
* Strings generated by Input Method Editor are handled |
864 |
* in the following manner: |
865 |
* 1. A series of WM_KEYDOWN & WM_KEYUP messages that |
866 |
* cause GetTranslatedKey() to be called and return |
867 |
* immediately because the WM_KEYDOWNs have no |
868 |
* associated WM_CHAR messages -- the IME window is |
869 |
* accumulating the characters and translating them |
870 |
* itself. In the "bind" command, you get an event |
871 |
* with a mystery keysym and %A == "" for each |
872 |
* WM_KEYDOWN that actually was meant for the IME. |
873 |
* 2. A WM_KEYDOWN corresponding to the "confirm typing" |
874 |
* character. This causes GetTranslatedKey() to be |
875 |
* called. |
876 |
* 3. A WM_IME_NOTIFY message saying that the IME is |
877 |
* done. A side effect of this message is that |
878 |
* GetTranslatedKey() thinks this means that there |
879 |
* are no WM_CHAR messages and returns immediately. |
880 |
* In the "bind" command, you get an another event |
881 |
* with a mystery keysym and %A == "". |
882 |
* 4. A sequence of WM_CHAR messages that correspond to |
883 |
* the characters in the IME window. A bunch of |
884 |
* simulated KeyPress/KeyRelease events will be |
885 |
* generated, one for each character. Adjacent |
886 |
* WM_CHAR messages may actually specify the high |
887 |
* and low bytes of a multi-byte character -- in that |
888 |
* case the two WM_CHAR messages will be combined into |
889 |
* one event. It is the event-consumer's |
890 |
* responsibility to convert the string returned from |
891 |
* XLookupString from system encoding to UTF-8. |
892 |
* 5. And finally we get the WM_KEYUP for the "confirm |
893 |
* typing" character. |
894 |
*/ |
895 |
|
896 |
event.type = KeyPress; |
897 |
event.xany.send_event = -1; |
898 |
event.xkey.keycode = 0; |
899 |
event.xkey.nbytes = 1; |
900 |
event.xkey.trans_chars[0] = (char) wParam; |
901 |
|
902 |
if (IsDBCSLeadByte((BYTE) wParam)) { |
903 |
MSG msg; |
904 |
|
905 |
if ((PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) != 0) |
906 |
&& (msg.message == WM_CHAR)) { |
907 |
GetMessage(&msg, NULL, 0, 0); |
908 |
event.xkey.nbytes = 2; |
909 |
event.xkey.trans_chars[1] = (char) msg.wParam; |
910 |
} |
911 |
} |
912 |
Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); |
913 |
event.type = KeyRelease; |
914 |
break; |
915 |
} |
916 |
break; |
917 |
} |
918 |
|
919 |
default: |
920 |
return; |
921 |
} |
922 |
Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); |
923 |
} |
924 |
|
925 |
/* |
926 |
*---------------------------------------------------------------------- |
927 |
* |
928 |
* GetState -- |
929 |
* |
930 |
* This function constructs a state mask for the mouse buttons |
931 |
* and modifier keys as they were before the event occured. |
932 |
* |
933 |
* Results: |
934 |
* Returns a composite value of all the modifier and button state |
935 |
* flags that were set at the time the event occurred. |
936 |
* |
937 |
* Side effects: |
938 |
* None. |
939 |
* |
940 |
*---------------------------------------------------------------------- |
941 |
*/ |
942 |
|
943 |
static unsigned int |
944 |
GetState(message, wParam, lParam) |
945 |
UINT message; /* Win32 message type */ |
946 |
WPARAM wParam; /* wParam of message, used if key message */ |
947 |
LPARAM lParam; /* lParam of message, used if key message */ |
948 |
{ |
949 |
int mask; |
950 |
int prevState; /* 1 if key was previously down */ |
951 |
unsigned int state = TkWinGetModifierState(); |
952 |
|
953 |
/* |
954 |
* If the event is a key press or release, we check for modifier |
955 |
* keys so we can report the state of the world before the event. |
956 |
*/ |
957 |
|
958 |
if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN |
959 |
|| message == WM_SYSKEYUP || message == WM_KEYUP) { |
960 |
mask = 0; |
961 |
prevState = HIWORD(lParam) & KF_REPEAT; |
962 |
switch(wParam) { |
963 |
case VK_SHIFT: |
964 |
mask = ShiftMask; |
965 |
break; |
966 |
case VK_CONTROL: |
967 |
mask = ControlMask; |
968 |
break; |
969 |
case VK_MENU: |
970 |
mask = ALT_MASK; |
971 |
break; |
972 |
case VK_CAPITAL: |
973 |
if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) { |
974 |
mask = LockMask; |
975 |
prevState = ((state & mask) ^ prevState) ? 0 : 1; |
976 |
} |
977 |
break; |
978 |
case VK_NUMLOCK: |
979 |
if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) { |
980 |
mask = Mod1Mask; |
981 |
prevState = ((state & mask) ^ prevState) ? 0 : 1; |
982 |
} |
983 |
break; |
984 |
case VK_SCROLL: |
985 |
if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) { |
986 |
mask = Mod3Mask; |
987 |
prevState = ((state & mask) ^ prevState) ? 0 : 1; |
988 |
} |
989 |
break; |
990 |
} |
991 |
if (prevState) { |
992 |
state |= mask; |
993 |
} else { |
994 |
state &= ~mask; |
995 |
} |
996 |
} |
997 |
return state; |
998 |
} |
999 |
|
1000 |
/* |
1001 |
*---------------------------------------------------------------------- |
1002 |
* |
1003 |
* GetTranslatedKey -- |
1004 |
* |
1005 |
* Retrieves WM_CHAR messages that are placed on the system queue |
1006 |
* by the TranslateMessage system call and places them in the |
1007 |
* given KeyPress event. |
1008 |
* |
1009 |
* Results: |
1010 |
* Sets the trans_chars and nbytes member of the key event. |
1011 |
* |
1012 |
* Side effects: |
1013 |
* Removes any WM_CHAR messages waiting on the top of the system |
1014 |
* event queue. |
1015 |
* |
1016 |
*---------------------------------------------------------------------- |
1017 |
*/ |
1018 |
|
1019 |
static void |
1020 |
GetTranslatedKey(xkey) |
1021 |
XKeyEvent *xkey; |
1022 |
{ |
1023 |
MSG msg; |
1024 |
char buf[XMaxTransChars]; |
1025 |
|
1026 |
xkey->nbytes = 0; |
1027 |
|
1028 |
while ((xkey->nbytes < XMaxTransChars) |
1029 |
&& PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { |
1030 |
if ((msg.message == WM_CHAR) || (msg.message == WM_SYSCHAR)) { |
1031 |
GetMessage(&msg, NULL, 0, 0); |
1032 |
|
1033 |
/* |
1034 |
* If this is a normal character message, we may need to strip |
1035 |
* off the Alt modifier (e.g. Alt-digits). Note that we don't |
1036 |
* want to do this for system messages, because those were |
1037 |
* presumably generated as an Alt-char sequence (e.g. accelerator |
1038 |
* keys). |
1039 |
*/ |
1040 |
|
1041 |
if ((msg.message == WM_CHAR) && (msg.lParam & 0x20000000)) { |
1042 |
xkey->state = 0; |
1043 |
} |
1044 |
buf[xkey->nbytes] = (char) msg.wParam; |
1045 |
xkey->trans_chars[xkey->nbytes] = (char) msg.wParam; |
1046 |
xkey->nbytes++; |
1047 |
} else { |
1048 |
break; |
1049 |
} |
1050 |
} |
1051 |
} |
1052 |
|
1053 |
/* |
1054 |
*---------------------------------------------------------------------- |
1055 |
* |
1056 |
* Tk_FreeXId -- |
1057 |
* |
1058 |
* This inteface is not needed under Windows. |
1059 |
* |
1060 |
* Results: |
1061 |
* None. |
1062 |
* |
1063 |
* Side effects: |
1064 |
* None. |
1065 |
* |
1066 |
*---------------------------------------------------------------------- |
1067 |
*/ |
1068 |
|
1069 |
void |
1070 |
Tk_FreeXId(display, xid) |
1071 |
Display *display; |
1072 |
XID xid; |
1073 |
{ |
1074 |
} |
1075 |
|
1076 |
/* |
1077 |
*---------------------------------------------------------------------- |
1078 |
* |
1079 |
* TkWinResendEvent -- |
1080 |
* |
1081 |
* This function converts an X event into a Windows event and |
1082 |
* invokes the specified windo procedure. |
1083 |
* |
1084 |
* Results: |
1085 |
* A standard Windows result. |
1086 |
* |
1087 |
* Side effects: |
1088 |
* Invokes the window procedure |
1089 |
* |
1090 |
*---------------------------------------------------------------------- |
1091 |
*/ |
1092 |
|
1093 |
LRESULT |
1094 |
TkWinResendEvent(wndproc, hwnd, eventPtr) |
1095 |
WNDPROC wndproc; |
1096 |
HWND hwnd; |
1097 |
XEvent *eventPtr; |
1098 |
{ |
1099 |
UINT msg; |
1100 |
WPARAM wparam; |
1101 |
LPARAM lparam; |
1102 |
|
1103 |
if (eventPtr->type == ButtonPress) { |
1104 |
switch (eventPtr->xbutton.button) { |
1105 |
case Button1: |
1106 |
msg = WM_LBUTTONDOWN; |
1107 |
wparam = MK_LBUTTON; |
1108 |
break; |
1109 |
case Button2: |
1110 |
msg = WM_MBUTTONDOWN; |
1111 |
wparam = MK_MBUTTON; |
1112 |
break; |
1113 |
case Button3: |
1114 |
msg = WM_RBUTTONDOWN; |
1115 |
wparam = MK_RBUTTON; |
1116 |
break; |
1117 |
default: |
1118 |
return 0; |
1119 |
} |
1120 |
if (eventPtr->xbutton.state & Button1Mask) { |
1121 |
wparam |= MK_LBUTTON; |
1122 |
} |
1123 |
if (eventPtr->xbutton.state & Button2Mask) { |
1124 |
wparam |= MK_MBUTTON; |
1125 |
} |
1126 |
if (eventPtr->xbutton.state & Button3Mask) { |
1127 |
wparam |= MK_RBUTTON; |
1128 |
} |
1129 |
if (eventPtr->xbutton.state & ShiftMask) { |
1130 |
wparam |= MK_SHIFT; |
1131 |
} |
1132 |
if (eventPtr->xbutton.state & ControlMask) { |
1133 |
wparam |= MK_CONTROL; |
1134 |
} |
1135 |
lparam = MAKELPARAM((short) eventPtr->xbutton.x, |
1136 |
(short) eventPtr->xbutton.y); |
1137 |
} else { |
1138 |
return 0; |
1139 |
} |
1140 |
return CallWindowProc(wndproc, hwnd, msg, wparam, lparam); |
1141 |
} |
1142 |
|
1143 |
/* |
1144 |
*---------------------------------------------------------------------- |
1145 |
* |
1146 |
* TkpGetMS -- |
1147 |
* |
1148 |
* Return a relative time in milliseconds. It doesn't matter |
1149 |
* when the epoch was. |
1150 |
* |
1151 |
* Results: |
1152 |
* Number of milliseconds. |
1153 |
* |
1154 |
* Side effects: |
1155 |
* None. |
1156 |
* |
1157 |
*---------------------------------------------------------------------- |
1158 |
*/ |
1159 |
|
1160 |
unsigned long |
1161 |
TkpGetMS() |
1162 |
{ |
1163 |
return GetTickCount(); |
1164 |
} |
1165 |
|
1166 |
/* |
1167 |
*---------------------------------------------------------------------- |
1168 |
* |
1169 |
* TkWinUpdatingClipboard -- |
1170 |
* |
1171 |
* |
1172 |
* Results: |
1173 |
* Number of milliseconds. |
1174 |
* |
1175 |
* Side effects: |
1176 |
* None. |
1177 |
* |
1178 |
*---------------------------------------------------------------------- |
1179 |
*/ |
1180 |
|
1181 |
void |
1182 |
TkWinUpdatingClipboard(int mode) |
1183 |
{ |
1184 |
ThreadSpecificData *tsdPtr = (ThreadSpecificData *) |
1185 |
Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData)); |
1186 |
|
1187 |
tsdPtr->updatingClipboard = mode; |
1188 |
} |
1189 |
|
1190 |
/* End of tkwinx.c */ |