1 /*
2 * tkWinX.c --
3 *
4 * This file contains Windows emulation procedures for X routines.
5 *
6 * Copyright (c) 1995-1996 Sun Microsystems, Inc.
7 * Copyright (c) 1994 Software Research Associates, Inc.
8 * Copyright (c) 1998-2000 by Scriptics Corporation.
9 *
10 * See the file "license.terms" for information on usage and redistribution
11 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id: tkWinX.c,v 1.25 2002/12/08 00:46:51 hobbs Exp $
14 */
15
16 #include "tkWinInt.h"
17
18 /*
19 * The w32api 1.1 package (included in Mingw 1.1) does not define _WIN32_IE
20 * by default. Define it here to gain access to the InitCommonControlsEx API
21 * in commctrl.h.
22 *
23 * For the addition of the __MINGW64_VERSION_MAJOR check see
24 * http://rt.cpan.org/Public/Bug/Display.html?id=53467
25 */
26
27 #if !defined( _WIN32_IE) && !defined(__MINGW64_VERSION_MAJOR)
28 #define _WIN32_IE 0x0300
29 #endif
30
31 #include <commctrl.h>
32
33 /*
34 * The zmouse.h file includes the definition for WM_MOUSEWHEEL.
35 */
36
37 #ifndef __BORLANDC__
38 #include <zmouse.h>
39 #endif
40 #ifndef WM_MOUSEWHEEL
41 #define WM_MOUSEWHEEL (WM_MOUSELAST+1) // message that will be supported
42 // by the OS
43 #endif
44
45
46 /*
47 * imm.h is needed by HandleIMEComposition
48 */
49
50 #include <imm.h>
51
52 static TkWinProcs asciiProcs = {
53 0,
54
55 (LRESULT (WINAPI *)(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg,
56 WPARAM wParam, LPARAM lParam)) CallWindowProcA,
57 (LRESULT (WINAPI *)(HWND hWnd, UINT Msg, WPARAM wParam,
58 LPARAM lParam)) DefWindowProcA,
59 (ATOM (WINAPI *)(CONST WNDCLASS *lpWndClass)) RegisterClassA,
60 (BOOL (WINAPI *)(HWND hWnd, LPCTSTR lpString)) SetWindowTextA,
61 (HWND (WINAPI *)(DWORD dwExStyle, LPCTSTR lpClassName,
62 LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
63 int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
64 HINSTANCE hInstance, LPVOID lpParam)) CreateWindowExA,
65 (BOOL (WINAPI *)(HMENU hMenu, UINT uPosition, UINT uFlags,
66 UINT uIDNewItem, LPCTSTR lpNewItem)) InsertMenuA,
67 };
68
69 static TkWinProcs unicodeProcs = {
70 1,
71
72 (LRESULT (WINAPI *)(WNDPROC lpPrevWndFunc, HWND hWnd, UINT Msg,
73 WPARAM wParam, LPARAM lParam)) CallWindowProcW,
74 (LRESULT (WINAPI *)(HWND hWnd, UINT Msg, WPARAM wParam,
75 LPARAM lParam)) DefWindowProcW,
76 (ATOM (WINAPI *)(CONST WNDCLASS *lpWndClass)) RegisterClassW,
77 (BOOL (WINAPI *)(HWND hWnd, LPCTSTR lpString)) SetWindowTextW,
78 (HWND (WINAPI *)(DWORD dwExStyle, LPCTSTR lpClassName,
79 LPCTSTR lpWindowName, DWORD dwStyle, int x, int y,
80 int nWidth, int nHeight, HWND hWndParent, HMENU hMenu,
81 HINSTANCE hInstance, LPVOID lpParam)) CreateWindowExW,
82 (BOOL (WINAPI *)(HMENU hMenu, UINT uPosition, UINT uFlags,
83 UINT uIDNewItem, LPCTSTR lpNewItem)) InsertMenuW,
84 };
85
86 TkWinProcs *tkWinProcs;
87
88 /*
89 * Declarations of static variables used in this file.
90 */
91
92 static char winScreenName[] = ":0"; /* Default name of windows display. */
93 static HINSTANCE tkInstance = NULL; /* Application instance handle. */
94 static int childClassInitialized; /* Registered child class? */
95 static WNDCLASS childClass; /* Window class for child windows. */
96 static WNDCLASS ownDCClass; /* Window class for child windows with private DC. */
97 static int tkPlatformId = 0; /* version of Windows platform */
98 static Tcl_Encoding keyInputEncoding = NULL;/* The current character
99 * encoding for keyboard input */
100 static int keyInputCharset = -1; /* The Win32 CHARSET for the keyboard
101 * encoding */
102 static Tcl_Encoding unicodeEncoding = NULL; /* unicode encoding */
103
104 /*
105 * Thread local storage. Notice that now each thread must have its
106 * own TkDisplay structure, since this structure contains most of
107 * the thread-specific date for threads.
108 */
109 typedef struct ThreadSpecificData {
110 TkDisplay *winDisplay; /* TkDisplay structure that *
111 * represents Windows screen. */
112 int updatingClipboard; /* If 1, we are updating the clipboard */
113 } ThreadSpecificData;
114 static Tcl_ThreadDataKey dataKey;
115
116 /*
117 * Forward declarations of procedures used in this file.
118 */
119
120 #ifdef __CYGWIN__
121 static void DisplayFileProc _ANSI_ARGS_((ClientData clientData,
122 int flags));
123 #endif
124 static void GenerateXEvent _ANSI_ARGS_((HWND hwnd, UINT message,
125 WPARAM wParam, LPARAM lParam));
126 static unsigned int GetState _ANSI_ARGS_((UINT message, WPARAM wParam,
127 LPARAM lParam));
128 static void GetTranslatedKey _ANSI_ARGS_((XKeyEvent *xkey));
129 static void UpdateInputLanguage _ANSI_ARGS_((int charset));
130 static int HandleIMEComposition _ANSI_ARGS_((HWND hwnd,
131 LPARAM lParam));
132
133 /*
134 *----------------------------------------------------------------------
135 *
136 * TkGetServerInfo --
137 *
138 * Given a window, this procedure returns information about
139 * the window server for that window. This procedure provides
140 * the guts of the "winfo server" command.
141 *
142 * Results:
143 * None.
144 *
145 * Side effects:
146 * None.
147 *
148 *----------------------------------------------------------------------
149 */
150
151 void
TkGetServerInfo(interp,tkwin)152 TkGetServerInfo(interp, tkwin)
153 Tcl_Interp *interp; /* The server information is returned in
154 * this interpreter's result. */
155 Tk_Window tkwin; /* Token for window; this selects a
156 * particular display and server. */
157 {
158 char buffer[60];
159 OSVERSIONINFO os;
160
161 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
162 GetVersionEx(&os);
163 sprintf(buffer, "Windows %d.%d %d %s", os.dwMajorVersion,
164 os.dwMinorVersion, os.dwBuildNumber,
165 #ifdef _WIN64
166 "Win64"
167 #else
168 "Win32"
169 #endif
170 );
171 Tcl_SetResult(interp, buffer, TCL_VOLATILE);
172 }
173
174 /*
175 *----------------------------------------------------------------------
176 *
177 * Tk_GetHINSTANCE --
178 *
179 * Retrieves the global instance handle used by the Tk library.
180 *
181 * Results:
182 * Returns the global instance handle.
183 *
184 * Side effects:
185 * None.
186 *
187 *----------------------------------------------------------------------
188 */
189
190 static BOOL CALLBACK
FindMyConsole(HWND win,LPARAM arg)191 FindMyConsole( HWND win, LPARAM arg )
192 {
193 DWORD mypid = *((DWORD *) arg);
194 DWORD pid;
195 GetWindowThreadProcessId(win,&pid);
196 if (pid == mypid)
197 {
198 ShowWindow(win,SW_SHOWMINNOACTIVE);
199 }
200 return TRUE;
201 }
202
203
204 HINSTANCE
Tk_GetHINSTANCE()205 Tk_GetHINSTANCE()
206 {
207 if (tkInstance == NULL) {
208 /* In dll case this is likely to be (perl or tcl) it is
209 here for static liked schemes
210 */
211 tkInstance = GetModuleHandle(NULL);
212 }
213 return tkInstance;
214 }
215
216 /*
217 *----------------------------------------------------------------------
218 *
219 * TkWinSetHINSTANCE --
220 *
221 * Sets the global instance handle used by the Tk library.
222 * This should be called by DllMain.
223 *
224 * Results:
225 * None.
226 *
227 * Side effects:
228 * None.
229 *
230 *----------------------------------------------------------------------
231 */
232
233 void
TkWinSetHINSTANCE(hInstance)234 TkWinSetHINSTANCE(hInstance)
235 HINSTANCE hInstance;
236 {
237 tkInstance = hInstance;
238 }
239
240 /*
241 *----------------------------------------------------------------------
242 *
243 * TkWinXInit --
244 *
245 * Initialize Xlib emulation layer.
246 *
247 * Results:
248 * None.
249 *
250 * Side effects:
251 * Sets up various data structures.
252 *
253 *----------------------------------------------------------------------
254 */
255
256 void
TkWinXInit(hInstance)257 TkWinXInit(hInstance)
258 HINSTANCE hInstance;
259 {
260 CHARSETINFO lpCs;
261 DWORD lpCP;
262
263 if (childClassInitialized != 0) {
264 return;
265 }
266 childClassInitialized = 1;
267
268 if (TkWinGetPlatformId() == VER_PLATFORM_WIN32_NT) {
269 /*
270 * This is necessary to enable the use of themeable elements on XP,
271 * so we don't even try and call it for Win9*.
272 */
273
274 INITCOMMONCONTROLSEX comctl;
275 ZeroMemory(&comctl, sizeof(comctl));
276 (void) InitCommonControlsEx(&comctl);
277
278 tkWinProcs = &unicodeProcs;
279 } else {
280 tkWinProcs = &asciiProcs;
281 }
282
283 #ifdef _LANG
284 TclWinSetInterfaces(TkWinGetPlatformId() == VER_PLATFORM_WIN32_NT);
285 #endif
286
287 tkInstance = hInstance;
288
289 /*
290 * When threads are enabled, we cannot use CLASSDC because
291 * threads will then write into the same device context.
292 *
293 * This is a hack; we should add a subsystem that manages
294 * device context on a per-thread basis. See also tkWinWm.c,
295 * which also initializes a WNDCLASS structure.
296 */
297
298 #ifdef TCL_THREADS
299 childClass.style = CS_HREDRAW | CS_VREDRAW;
300 #else
301 childClass.style = CS_HREDRAW | CS_VREDRAW | CS_CLASSDC;
302 #endif
303
304 childClass.cbClsExtra = 0;
305 childClass.cbWndExtra = 0;
306 childClass.hInstance = hInstance;
307 childClass.hbrBackground = NULL;
308 childClass.lpszMenuName = NULL;
309
310 /*
311 * Register the Child window class.
312 */
313
314 childClass.lpszClassName = TK_WIN_CHILD_CLASS_NAME;
315 childClass.lpfnWndProc = TkWinChildProc;
316 childClass.hIcon = NULL;
317 childClass.hCursor = NULL;
318
319 if (!RegisterClass(&childClass)) {
320 panic("Unable to register TkChild class");
321 }
322
323 ownDCClass = childClass;
324 ownDCClass.lpszClassName = TK_WIN_OWNDC_CLASS_NAME;
325 ownDCClass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
326 if (!RegisterClass(&ownDCClass)) {
327 panic("Unable to register TkOwnDC class");
328 }
329
330 /*
331 * Initialize input language info
332 */
333 if(GetLocaleInfo(LANGIDFROMLCID(GetKeyboardLayout(0)),
334 LOCALE_IDEFAULTANSICODEPAGE | LOCALE_RETURN_NUMBER,
335 (LPWSTR) &lpCP,sizeof(lpCP)/sizeof(TCHAR))==0)
336 return CP_ACP;
337 if (TranslateCharsetInfo((DWORD *)lpCP, &lpCs, TCI_SRCCODEPAGE)==0)
338 return CP_ACP;
339 UpdateInputLanguage(lpCs.ciCharset);
340
341 /*
342 * Make sure we cleanup on finalize.
343 */
344 Tcl_CreateExitHandler((Tcl_ExitProc *) TkWinXCleanup,
345 (ClientData) hInstance);
346 }
347
348 /*
349 *----------------------------------------------------------------------
350 *
351 * TkWinXCleanup --
352 *
353 * Removes the registered classes for Tk.
354 *
355 * Results:
356 * None.
357 *
358 * Side effects:
359 * Removes window classes from the system.
360 *
361 *----------------------------------------------------------------------
362 */
363
364 void
TkWinXCleanup(hInstance)365 TkWinXCleanup(hInstance)
366 HINSTANCE hInstance;
367 {
368 /*
369 * Clean up our own class.
370 */
371
372 if (childClassInitialized) {
373 childClassInitialized = 0;
374 UnregisterClass(TK_WIN_CHILD_CLASS_NAME, hInstance);
375 UnregisterClass(TK_WIN_OWNDC_CLASS_NAME, hInstance);
376 }
377
378 if (unicodeEncoding != NULL) {
379 Tcl_FreeEncoding(unicodeEncoding);
380 unicodeEncoding = NULL;
381 }
382
383 /*
384 * And let the window manager clean up its own class(es).
385 */
386
387 TkWinWmCleanup(hInstance);
388 }
389
390 /*
391 *----------------------------------------------------------------------
392 *
393 * TkWinGetPlatformId --
394 *
395 * Determines whether running under NT, 95, or Win32s, to allow
396 * runtime conditional code. Win32s is no longer supported.
397 *
398 * Results:
399 * The return value is one of:
400 * VER_PLATFORM_WIN32s Win32s on Windows 3.1.
401 * VER_PLATFORM_WIN32_WINDOWS Win32 on Windows 95.
402 * VER_PLATFORM_WIN32_NT Win32 on Windows NT
403 *
404 * Side effects:
405 * None.
406 *
407 *----------------------------------------------------------------------
408 */
409
410 int
TkWinGetPlatformId()411 TkWinGetPlatformId()
412 {
413 if (tkPlatformId == 0) {
414 OSVERSIONINFO os;
415
416 os.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
417 GetVersionEx(&os);
418 tkPlatformId = os.dwPlatformId;
419 }
420 return tkPlatformId;
421 }
422
423 /*
424 *----------------------------------------------------------------------
425 *
426 * TkGetDefaultScreenName --
427 *
428 * Returns the name of the screen that Tk should use during
429 * initialization.
430 *
431 * Results:
432 * Returns a statically allocated string.
433 *
434 * Side effects:
435 * None.
436 *
437 *----------------------------------------------------------------------
438 */
439
440 CONST char *
TkGetDefaultScreenName(interp,screenName)441 TkGetDefaultScreenName(interp, screenName)
442 Tcl_Interp *interp; /* Not used. */
443 CONST char *screenName; /* If NULL, use default string. */
444 {
445 if ((screenName == NULL) || (screenName[0] == '\0')) {
446 screenName = winScreenName;
447 }
448 return screenName;
449 }
450
451 /*
452 *----------------------------------------------------------------------
453 *
454 * TkpOpenDisplay --
455 *
456 * Create the Display structure and fill it with device
457 * specific information.
458 *
459 * Results:
460 * Returns a TkDisplay structure on success or NULL on failure.
461 *
462 * Side effects:
463 * Allocates a new TkDisplay structure.
464 *
465 *----------------------------------------------------------------------
466 */
467
468 TkDisplay *
TkpOpenDisplay(display_name)469 TkpOpenDisplay(display_name)
470 CONST char *display_name;
471 {
472 Screen *screen;
473 HDC dc;
474 TkWinDrawable *twdPtr;
475 Display *display;
476 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
477 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
478
479 if (tsdPtr->winDisplay != NULL) {
480 if (strcmp(tsdPtr->winDisplay->display->display_name, display_name)
481 == 0) {
482 return tsdPtr->winDisplay;
483 } else {
484 return NULL;
485 }
486 }
487
488 display = (Display *) ckalloc(sizeof(Display));
489 ZeroMemory(display, sizeof(Display));
490
491 display->display_name = (char *) ckalloc(strlen(display_name)+1);
492 strcpy(display->display_name, display_name);
493
494 display->cursor_font = 1;
495 display->nscreens = 1;
496 display->request = 1;
497 display->qlen = 0;
498
499 screen = (Screen *) ckalloc(sizeof(Screen));
500 screen->display = display;
501
502 dc = GetDC(NULL);
503 screen->width = GetDeviceCaps(dc, HORZRES);
504 screen->height = GetDeviceCaps(dc, VERTRES);
505 screen->mwidth = MulDiv(screen->width, 254,
506 GetDeviceCaps(dc, LOGPIXELSX) * 10);
507 screen->mheight = MulDiv(screen->height, 254,
508 GetDeviceCaps(dc, LOGPIXELSY) * 10);
509
510 /*
511 * Set up the root window.
512 */
513
514 twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable));
515 if (twdPtr == NULL) {
516 return None;
517 }
518 twdPtr->type = TWD_WINDOW;
519 twdPtr->window.winPtr = NULL;
520 twdPtr->window.handle = NULL;
521 screen->root = (Window)twdPtr;
522
523 /*
524 * On windows, when creating a color bitmap, need two pieces of
525 * information: the number of color planes and the number of
526 * pixels per plane. Need to remember both quantities so that
527 * when constructing an HBITMAP for offscreen rendering, we can
528 * specify the correct value for the number of planes. Otherwise
529 * the HBITMAP won't be compatible with the HWND and we'll just
530 * get blank spots copied onto the screen.
531 */
532
533 screen->ext_data = (XExtData *) GetDeviceCaps(dc, PLANES);
534 screen->root_depth = GetDeviceCaps(dc, BITSPIXEL) * (int) screen->ext_data;
535
536 screen->root_visual = (Visual *) ckalloc(sizeof(Visual));
537 screen->root_visual->visualid = 0;
538 if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) {
539 screen->root_visual->map_entries = GetDeviceCaps(dc, SIZEPALETTE);
540 screen->root_visual->class = PseudoColor;
541 screen->root_visual->red_mask = 0x0;
542 screen->root_visual->green_mask = 0x0;
543 screen->root_visual->blue_mask = 0x0;
544 } else {
545 if (screen->root_depth == 4) {
546 screen->root_visual->class = StaticColor;
547 screen->root_visual->map_entries = 16;
548 } else if (screen->root_depth == 8) {
549 screen->root_visual->class = StaticColor;
550 screen->root_visual->map_entries = 256;
551 } else if (screen->root_depth == 12) {
552 screen->root_visual->class = TrueColor;
553 screen->root_visual->map_entries = 32;
554 screen->root_visual->red_mask = 0xf0;
555 screen->root_visual->green_mask = 0xf000;
556 screen->root_visual->blue_mask = 0xf00000;
557 } else if (screen->root_depth == 16) {
558 screen->root_visual->class = TrueColor;
559 screen->root_visual->map_entries = 64;
560 screen->root_visual->red_mask = 0xf8;
561 screen->root_visual->green_mask = 0xfc00;
562 screen->root_visual->blue_mask = 0xf80000;
563 } else if (screen->root_depth >= 24) {
564 screen->root_visual->class = TrueColor;
565 screen->root_visual->map_entries = 256;
566 screen->root_visual->red_mask = 0xff;
567 screen->root_visual->green_mask = 0xff00;
568 screen->root_visual->blue_mask = 0xff0000;
569 }
570 }
571 screen->root_visual->bits_per_rgb = screen->root_depth;
572 ReleaseDC(NULL, dc);
573
574 /*
575 * Note that these pixel values are not palette relative.
576 */
577
578 screen->white_pixel = RGB(255, 255, 255);
579 screen->black_pixel = RGB(0, 0, 0);
580
581 display->screens = screen;
582 display->nscreens = 1;
583 display->default_screen = 0;
584 screen->cmap = XCreateColormap(display, None, screen->root_visual,
585 AllocNone);
586 tsdPtr->winDisplay = (TkDisplay *) ckalloc(sizeof(TkDisplay));
587 ZeroMemory(tsdPtr->winDisplay, sizeof(TkDisplay));
588 tsdPtr->winDisplay->display = display;
589 tsdPtr->updatingClipboard = FALSE;
590 return tsdPtr->winDisplay;
591 }
592
593 /*
594 *----------------------------------------------------------------------
595 *
596 * TkpCloseDisplay --
597 *
598 * Closes and deallocates a Display structure created with the
599 * TkpOpenDisplay function.
600 *
601 * Results:
602 * None.
603 *
604 * Side effects:
605 * Frees up memory.
606 *
607 *----------------------------------------------------------------------
608 */
609
610 void
TkpCloseDisplay(dispPtr)611 TkpCloseDisplay(dispPtr)
612 TkDisplay *dispPtr;
613 {
614 Display *display = dispPtr->display;
615 HWND hwnd;
616 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
617 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
618
619 if (dispPtr != tsdPtr->winDisplay) {
620 panic("TkpCloseDisplay: tried to call TkpCloseDisplay on another display");
621 return;
622 }
623
624 /*
625 * Force the clipboard to be rendered if we are the clipboard owner.
626 */
627
628 if (dispPtr->clipWindow) {
629 hwnd = Tk_GetHWND(Tk_WindowId(dispPtr->clipWindow));
630 if (GetClipboardOwner() == hwnd) {
631 OpenClipboard(hwnd);
632 EmptyClipboard();
633 TkWinClipboardRender(dispPtr, CF_TEXT);
634 CloseClipboard();
635 }
636 }
637
638 tsdPtr->winDisplay = NULL;
639
640 if (display->display_name != (char *) NULL) {
641 ckfree(display->display_name);
642 }
643 if (display->screens != (Screen *) NULL) {
644 if (display->screens->root_visual != NULL) {
645 ckfree((char *) display->screens->root_visual);
646 }
647 if (display->screens->root != None) {
648 ckfree((char *) display->screens->root);
649 }
650 if (display->screens->cmap != None) {
651 XFreeColormap(display, display->screens->cmap);
652 }
653 ckfree((char *) display->screens);
654 }
655 #ifdef __CYGWIN__
656 if (dispPtr->display != 0) {
657 Tcl_DeleteFileHandler(ConnectionNumber(dispPtr->display));
658 close(ConnectionNumber(dispPtr->display));
659 }
660 #endif
661 ckfree((char *) display);
662 }
663
664
665 #ifdef __CYGWIN__
666 /*
667 *----------------------------------------------------------------------
668 *
669 * DisplayFileProc --
670 *
671 * This procedure implements the file handler for the /dev/windows
672 * connection.
673 *
674 * Results:
675 * None.
676 *
677 * Side effects:
678 * Process Win32 message queue. Compare to tclWin/tclWinNotify.c
679 * Tcl_WaitForEvent() event loop.
680 *
681 *----------------------------------------------------------------------
682 */
683
684 static void
DisplayFileProc(clientData,flags)685 DisplayFileProc(clientData, flags)
686 ClientData clientData; /* The display pointer. */
687 int flags; /* Should be TCL_READABLE. */
688 {
689 TkDisplay *dispPtr = (TkDisplay *) clientData;
690 Display *display = dispPtr->display;
691 MSG msg;
692 int n;
693
694 /* NOTE: read returns the result of GetMessage */
695 /* *not* the number of bytes read */
696 n = read(ConnectionNumber(display), &msg, sizeof(MSG));
697 if(n == 0) {
698 /*
699 * The application is exiting, so repost the quit message
700 * and start unwinding.
701 */
702
703 PostQuitMessage(msg.wParam);
704 return;
705 }
706 if(n > 0) {
707 TranslateMessage(&msg);
708 DispatchMessage(&msg);
709 }
710 }
711 #endif
712
713 /*
714 *----------------------------------------------------------------------
715 *
716 * XBell --
717 *
718 * Generate a beep.
719 *
720 * Results:
721 * None.
722 *
723 * Side effects:
724 * Plays a sounds out the system speakers.
725 *
726 *----------------------------------------------------------------------
727 */
728
729 void
XBell(display,percent)730 XBell(display, percent)
731 Display* display;
732 int percent;
733 {
734 MessageBeep(MB_OK);
735 }
736
737 /*
738 *----------------------------------------------------------------------
739 *
740 * TkWinChildProc --
741 *
742 * Callback from Windows whenever an event occurs on a child
743 * window.
744 *
745 * Results:
746 * Standard Windows return value.
747 *
748 * Side effects:
749 * May process events off the Tk event queue.
750 *
751 *----------------------------------------------------------------------
752 */
753
754 LRESULT CALLBACK
TkWinChildProc(hwnd,message,wParam,lParam)755 TkWinChildProc(hwnd, message, wParam, lParam)
756 HWND hwnd;
757 UINT message;
758 WPARAM wParam;
759 LPARAM lParam;
760 {
761 LRESULT result;
762
763 switch (message) {
764 case WM_INPUTLANGCHANGE:
765 UpdateInputLanguage(wParam);
766 result = 1;
767 break;
768
769 case WM_IME_COMPOSITION:
770 result = 0;
771 if (HandleIMEComposition(hwnd, lParam) == 0) {
772 result = DefWindowProc(hwnd, message, wParam, lParam);
773 }
774 break;
775
776 case WM_SETCURSOR:
777 /*
778 * Short circuit the WM_SETCURSOR message since we set
779 * the cursor elsewhere.
780 */
781
782 result = TRUE;
783 break;
784
785 case WM_CREATE:
786 case WM_ERASEBKGND:
787 result = 0;
788 break;
789
790 case WM_PAINT:
791 GenerateXEvent(hwnd, message, wParam, lParam);
792 result = DefWindowProc(hwnd, message, wParam, lParam);
793 break;
794
795 case TK_CLAIMFOCUS:
796 case TK_GEOMETRYREQ:
797 case TK_ATTACHWINDOW:
798 case TK_DETACHWINDOW:
799 result = TkWinEmbeddedEventProc(hwnd, message, wParam, lParam);
800 break;
801
802 default:
803 if (!Tk_TranslateWinEvent(hwnd, message, wParam, lParam,
804 &result)) {
805 result = DefWindowProc(hwnd, message, wParam, lParam);
806 }
807 break;
808 }
809
810 /*
811 * Handle any newly queued events before returning control to Windows.
812 */
813
814 Tcl_ServiceAll();
815 return result;
816 }
817
818 /*
819 *----------------------------------------------------------------------
820 *
821 * Tk_TranslateWinEvent --
822 *
823 * This function is called by widget window procedures to handle
824 * the translation from Win32 events to Tk events.
825 *
826 * Results:
827 * Returns 1 if the event was handled, else 0.
828 *
829 * Side effects:
830 * Depends on the event.
831 *
832 *----------------------------------------------------------------------
833 */
834
835 int
Tk_TranslateWinEvent(hwnd,message,wParam,lParam,resultPtr)836 Tk_TranslateWinEvent(hwnd, message, wParam, lParam, resultPtr)
837 HWND hwnd;
838 UINT message;
839 WPARAM wParam;
840 LPARAM lParam;
841 LRESULT *resultPtr;
842 {
843 *resultPtr = 0;
844 switch (message) {
845 default: {
846 Tk_Window tkwin = (Tk_Window) Tk_HWNDToWindow(hwnd);
847 if (tkwin) {
848 if (Lang_WinEvent(tkwin, message, wParam, lParam, resultPtr))
849 return 1;
850 }
851 break;
852 }
853
854 case WM_RENDERFORMAT: {
855 TkWindow *winPtr = (TkWindow *) Tk_HWNDToWindow(hwnd);
856 if (winPtr) {
857 TkWinClipboardRender(winPtr->dispPtr, wParam);
858 }
859 return 1;
860 }
861
862 case WM_COMMAND:
863 case WM_NOTIFY:
864 case WM_VSCROLL:
865 case WM_HSCROLL: {
866 /*
867 * Reflect these messages back to the sender so that they
868 * can be handled by the window proc for the control. Note
869 * that we need to be careful not to reflect a message that
870 * is targeted to this window, or we will loop.
871 */
872
873 HWND target = (message == WM_NOTIFY)
874 ? ((NMHDR*)lParam)->hwndFrom : (HWND) lParam;
875 if (target && target != hwnd) {
876 *resultPtr = SendMessage(target, message, wParam, lParam);
877 return 1;
878 }
879 break;
880 }
881
882 case WM_LBUTTONDOWN:
883 case WM_LBUTTONDBLCLK:
884 case WM_MBUTTONDOWN:
885 case WM_MBUTTONDBLCLK:
886 case WM_RBUTTONDOWN:
887 case WM_RBUTTONDBLCLK:
888 case WM_LBUTTONUP:
889 case WM_MBUTTONUP:
890 case WM_RBUTTONUP:
891 case WM_MOUSEMOVE:
892 Tk_PointerEvent(hwnd, (short) LOWORD(lParam),
893 (short) HIWORD(lParam));
894 return 1;
895
896 case WM_CLOSE:
897 case WM_SETFOCUS:
898 case WM_KILLFOCUS:
899 case WM_DESTROYCLIPBOARD:
900 case WM_CHAR:
901 case WM_SYSKEYDOWN:
902 case WM_SYSKEYUP:
903 case WM_KEYDOWN:
904 case WM_KEYUP:
905 case WM_MOUSEWHEEL:
906 GenerateXEvent(hwnd, message, wParam, lParam);
907 return 1;
908 case WM_MENUCHAR:
909 GenerateXEvent(hwnd, message, wParam, lParam);
910 /* MNC_CLOSE is the only one that looks right. This is a hack. */
911 *resultPtr = MAKELONG (0, MNC_CLOSE);
912 return 1;
913 }
914 return 0;
915 }
916
917 /*
918 *----------------------------------------------------------------------
919 *
920 * GenerateXEvent --
921 *
922 * This routine generates an X event from the corresponding
923 * Windows event.
924 *
925 * Results:
926 * None.
927 *
928 * Side effects:
929 * Queues one or more X events.
930 *
931 *----------------------------------------------------------------------
932 */
933
934 static void
GenerateXEvent(hwnd,message,wParam,lParam)935 GenerateXEvent(hwnd, message, wParam, lParam)
936 HWND hwnd;
937 UINT message;
938 WPARAM wParam;
939 LPARAM lParam;
940 {
941 XEvent event;
942 TkWindow *winPtr = (TkWindow *)Tk_HWNDToWindow(hwnd);
943 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
944 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
945
946 if (!winPtr || winPtr->window == None) {
947 return;
948 }
949
950 event.xany.serial = winPtr->display->request++;
951 event.xany.send_event = False;
952 event.xany.display = winPtr->display;
953 event.xany.window = winPtr->window;
954
955 switch (message) {
956 case WM_PAINT: {
957 PAINTSTRUCT ps;
958
959 event.type = Expose;
960 BeginPaint(hwnd, &ps);
961 event.xexpose.x = ps.rcPaint.left;
962 event.xexpose.y = ps.rcPaint.top;
963 #ifdef __OPEN32__
964 event.xexpose.width = max(ps.rcPaint.right - ps.rcPaint.left,0);
965 event.xexpose.height = max(ps.rcPaint.bottom - ps.rcPaint.top,0);
966 #else
967 event.xexpose.width = ps.rcPaint.right - ps.rcPaint.left;
968 event.xexpose.height = ps.rcPaint.bottom - ps.rcPaint.top;
969 #endif
970 EndPaint(hwnd, &ps);
971 event.xexpose.count = 0;
972 break;
973 }
974
975 case WM_CLOSE:
976 event.type = ClientMessage;
977 event.xclient.message_type =
978 Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS");
979 event.xclient.format = 32;
980 event.xclient.data.l[0] =
981 Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW");
982 break;
983
984 case WM_SETFOCUS:
985 case WM_KILLFOCUS: {
986 TkWindow *otherWinPtr = (TkWindow *)Tk_HWNDToWindow((HWND) wParam);
987
988 /*
989 * Compare toplevel windows to avoid reporting focus
990 * changes within the same toplevel.
991 */
992
993 while (!(winPtr->flags & TK_TOP_LEVEL)) {
994 winPtr = winPtr->parentPtr;
995 if (winPtr == NULL) {
996 return;
997 }
998 }
999 while (otherWinPtr && !(otherWinPtr->flags & TK_TOP_LEVEL)) {
1000 otherWinPtr = otherWinPtr->parentPtr;
1001 }
1002
1003 /*
1004 * Do a catch-all Tk_SetCaretPos here to make sure that the
1005 * window receiving focus sets the caret at least once.
1006 */
1007 if (message == WM_SETFOCUS) {
1008 Tk_SetCaretPos((Tk_Window) winPtr, 0, 0, 0);
1009 }
1010
1011 if (otherWinPtr == winPtr) {
1012 return;
1013 }
1014
1015 event.xany.window = winPtr->window;
1016 event.type = (message == WM_SETFOCUS) ? FocusIn : FocusOut;
1017 event.xfocus.mode = NotifyNormal;
1018 event.xfocus.detail = NotifyNonlinear;
1019
1020 /*
1021 * Destroy the caret if we own it. If we are moving to another Tk
1022 * window, it will reclaim and reposition it with Tk_SetCaretPos.
1023 */
1024 if (message == WM_KILLFOCUS) {
1025 DestroyCaret();
1026 }
1027 break;
1028 }
1029
1030 case WM_DESTROYCLIPBOARD:
1031 if (tsdPtr->updatingClipboard == TRUE) {
1032 /*
1033 * We want to avoid this event if we are the ones that caused
1034 * this event.
1035 */
1036 return;
1037 }
1038 event.type = SelectionClear;
1039 event.xselectionclear.selection =
1040 Tk_InternAtom((Tk_Window)winPtr, "CLIPBOARD");
1041 event.xselectionclear.time = TkpGetMS();
1042 break;
1043
1044 case WM_MOUSEWHEEL:
1045 /*
1046 * The mouse wheel event is closer to a key event than a
1047 * mouse event in that the message is sent to the window
1048 * that has focus.
1049 */
1050
1051 case WM_CHAR:
1052 case WM_SYSKEYDOWN:
1053 case WM_SYSKEYUP:
1054 case WM_KEYDOWN:
1055 case WM_KEYUP: {
1056 unsigned int state = GetState(message, wParam, lParam);
1057 Time time = TkpGetMS();
1058 POINT clientPoint;
1059 POINTS rootPoint; /* Note: POINT and POINTS are different */
1060 DWORD msgPos;
1061
1062 /*
1063 * Compute the screen and window coordinates of the event.
1064 */
1065
1066 msgPos = GetMessagePos();
1067 rootPoint = MAKEPOINTS(msgPos);
1068 clientPoint.x = rootPoint.x;
1069 clientPoint.y = rootPoint.y;
1070 ScreenToClient(hwnd, &clientPoint);
1071
1072 /*
1073 * Set up the common event fields.
1074 */
1075
1076 event.xbutton.root = RootWindow(winPtr->display,
1077 winPtr->screenNum);
1078 event.xbutton.subwindow = None;
1079 event.xbutton.x = clientPoint.x;
1080 event.xbutton.y = clientPoint.y;
1081 event.xbutton.x_root = rootPoint.x;
1082 event.xbutton.y_root = rootPoint.y;
1083 event.xbutton.state = state;
1084 event.xbutton.time = time;
1085 event.xbutton.same_screen = True;
1086
1087 /*
1088 * Now set up event specific fields.
1089 */
1090
1091 switch (message) {
1092 case WM_MOUSEWHEEL:
1093 /*
1094 * We have invented a new X event type to handle
1095 * this event. It still uses the KeyPress struct.
1096 * However, the keycode field has been overloaded
1097 * to hold the zDelta of the wheel.
1098 */
1099
1100 event.type = MouseWheelEvent;
1101 event.xany.send_event = -1;
1102 event.xkey.keycode = (short) HIWORD(wParam);
1103 break;
1104 case WM_SYSKEYDOWN:
1105 case WM_KEYDOWN:
1106 /*
1107 * Check for translated characters in the event queue.
1108 * Setting xany.send_event to -1 indicates to the
1109 * Windows implementation of TkpGetString() that this
1110 * event was generated by windows and that the Windows
1111 * extension xkey.trans_chars is filled with the
1112 * MBCS characters that came from the TranslateMessage
1113 * call.
1114 */
1115
1116 #ifdef __OPEN32__
1117 StashedKey = 0;
1118 #endif
1119 event.type = KeyPress;
1120 event.xany.send_event = -1;
1121 event.xkey.keycode = wParam;
1122 GetTranslatedKey(&event.xkey);
1123 break;
1124
1125 case WM_SYSKEYUP:
1126 case WM_KEYUP:
1127 /*
1128 * We don't check for translated characters on keyup
1129 * because Tk won't know what to do with them. Instead, we
1130 * wait for the WM_CHAR messages which will follow.
1131 */
1132 #ifdef __OPEN32__
1133 StashedKey = 0;
1134 #endif
1135 event.type = KeyRelease;
1136 event.xkey.keycode = wParam;
1137 event.xkey.nbytes = 0;
1138 break;
1139
1140 case WM_CHAR:
1141 /*
1142 * Synthesize both a KeyPress and a KeyRelease.
1143 * Strings generated by Input Method Editor are handled
1144 * in the following manner:
1145 * 1. A series of WM_KEYDOWN & WM_KEYUP messages that
1146 * cause GetTranslatedKey() to be called and return
1147 * immediately because the WM_KEYDOWNs have no
1148 * associated WM_CHAR messages -- the IME window is
1149 * accumulating the characters and translating them
1150 * itself. In the "bind" command, you get an event
1151 * with a mystery keysym and %A == "" for each
1152 * WM_KEYDOWN that actually was meant for the IME.
1153 * 2. A WM_KEYDOWN corresponding to the "confirm typing"
1154 * character. This causes GetTranslatedKey() to be
1155 * called.
1156 * 3. A WM_IME_NOTIFY message saying that the IME is
1157 * done. A side effect of this message is that
1158 * GetTranslatedKey() thinks this means that there
1159 * are no WM_CHAR messages and returns immediately.
1160 * In the "bind" command, you get an another event
1161 * with a mystery keysym and %A == "".
1162 * 4. A sequence of WM_CHAR messages that correspond to
1163 * the characters in the IME window. A bunch of
1164 * simulated KeyPress/KeyRelease events will be
1165 * generated, one for each character. Adjacent
1166 * WM_CHAR messages may actually specify the high
1167 * and low bytes of a multi-byte character -- in that
1168 * case the two WM_CHAR messages will be combined into
1169 * one event. It is the event-consumer's
1170 * responsibility to convert the string returned from
1171 * XLookupString from system encoding to UTF-8.
1172 * 5. And finally we get the WM_KEYUP for the "confirm
1173 * typing" character.
1174 */
1175
1176 #ifdef __OPEN32__
1177 StashedKey = (char) lParam;
1178 #endif
1179 event.type = KeyPress;
1180 event.xany.send_event = -1;
1181 event.xkey.keycode = 0;
1182 event.xkey.nbytes = 1;
1183 event.xkey.trans_chars[0] = (char) wParam;
1184
1185 if (IsDBCSLeadByte((BYTE) wParam)) {
1186 MSG msg;
1187
1188 if ((PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE) != 0)
1189 && (msg.message == WM_CHAR)) {
1190 GetMessage(&msg, NULL, 0, 0);
1191 event.xkey.nbytes = 2;
1192 event.xkey.trans_chars[1] = (char) msg.wParam;
1193 }
1194 }
1195 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
1196 event.type = KeyRelease;
1197 break;
1198 }
1199 break;
1200 }
1201
1202 default:
1203 return;
1204 }
1205 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
1206 }
1207
1208 /*
1209 *----------------------------------------------------------------------
1210 *
1211 * GetState --
1212 *
1213 * This function constructs a state mask for the mouse buttons
1214 * and modifier keys as they were before the event occured.
1215 *
1216 * Results:
1217 * Returns a composite value of all the modifier and button state
1218 * flags that were set at the time the event occurred.
1219 *
1220 * Side effects:
1221 * None.
1222 *
1223 *----------------------------------------------------------------------
1224 */
1225
1226 static unsigned int
GetState(message,wParam,lParam)1227 GetState(message, wParam, lParam)
1228 UINT message; /* Win32 message type */
1229 WPARAM wParam; /* wParam of message, used if key message */
1230 LPARAM lParam; /* lParam of message, used if key message */
1231 {
1232 int mask;
1233 int prevState; /* 1 if key was previously down */
1234 unsigned int state = TkWinGetModifierState();
1235
1236 /*
1237 * If the event is a key press or release, we check for modifier
1238 * keys so we can report the state of the world before the event.
1239 */
1240
1241 if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN
1242 || message == WM_SYSKEYUP || message == WM_KEYUP) {
1243 mask = 0;
1244 prevState = HIWORD(lParam) & KF_REPEAT;
1245 switch(wParam) {
1246 case VK_SHIFT:
1247 mask = ShiftMask;
1248 break;
1249 case VK_CONTROL:
1250 mask = ControlMask;
1251 break;
1252 case VK_MENU:
1253 mask = ALT_MASK;
1254 break;
1255 case VK_CAPITAL:
1256 if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) {
1257 mask = LockMask;
1258 prevState = ((state & mask) ^ prevState) ? 0 : 1;
1259 }
1260 break;
1261 case VK_NUMLOCK:
1262 if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) {
1263 mask = Mod1Mask;
1264 prevState = ((state & mask) ^ prevState) ? 0 : 1;
1265 }
1266 break;
1267 case VK_SCROLL:
1268 if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN) {
1269 mask = Mod3Mask;
1270 prevState = ((state & mask) ^ prevState) ? 0 : 1;
1271 }
1272 break;
1273 }
1274 if (prevState) {
1275 state |= mask;
1276 } else {
1277 state &= ~mask;
1278 }
1279 }
1280 return state;
1281 }
1282
1283 /*
1284 *----------------------------------------------------------------------
1285 *
1286 * GetTranslatedKey --
1287 *
1288 * Retrieves WM_CHAR messages that are placed on the system queue
1289 * by the TranslateMessage system call and places them in the
1290 * given KeyPress event.
1291 *
1292 * Results:
1293 * Sets the trans_chars and nbytes member of the key event.
1294 *
1295 * Side effects:
1296 * Removes any WM_CHAR messages waiting on the top of the system
1297 * event queue.
1298 *
1299 *----------------------------------------------------------------------
1300 */
1301
1302 static void
GetTranslatedKey(xkey)1303 GetTranslatedKey(xkey)
1304 XKeyEvent *xkey;
1305 {
1306 MSG msg;
1307
1308 xkey->nbytes = 0;
1309
1310 while ((xkey->nbytes < XMaxTransChars)
1311 && PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
1312 if ((msg.message == WM_CHAR) || (msg.message == WM_SYSCHAR)) {
1313 GetMessage(&msg, NULL, 0, 0);
1314
1315 /*
1316 * If this is a normal character message, we may need to strip
1317 * off the Alt modifier (e.g. Alt-digits). Note that we don't
1318 * want to do this for system messages, because those were
1319 * presumably generated as an Alt-char sequence (e.g. accelerator
1320 * keys).
1321 */
1322
1323 if ((msg.message == WM_CHAR) && (msg.lParam & 0x20000000)) {
1324 xkey->state = 0;
1325 }
1326 xkey->trans_chars[xkey->nbytes] = (char) msg.wParam;
1327 xkey->nbytes++;
1328
1329 if (((unsigned short) msg.wParam) > ((unsigned short) 0xff)) {
1330 /*
1331 * Some "addon" input devices, such as the popular
1332 * PenPower Chinese writing pad, generate 16 bit
1333 * values in WM_CHAR messages (instead of passing them
1334 * in two separate WM_CHAR messages containing two
1335 * 8-bit values.
1336 */
1337
1338 xkey->trans_chars[xkey->nbytes] = (char) (msg.wParam >> 8);
1339 xkey->nbytes ++;
1340 }
1341 } else {
1342 break;
1343 }
1344 }
1345 }
1346
1347 /*
1348 *----------------------------------------------------------------------
1349 *
1350 * UpdateInputLanguage --
1351 *
1352 * Gets called when a WM_INPUTLANGCHANGE message is received
1353 * by the TK child window procedure. This message is sent
1354 * by the Input Method Editor system when the user chooses
1355 * a different input method. All subsequent WM_CHAR
1356 * messages will contain characters in the new encoding. We record
1357 * the new encoding so that TkpGetString() knows how to
1358 * correctly translate the WM_CHAR into unicode.
1359 *
1360 * Results:
1361 * Records the new encoding in keyInputEncoding.
1362 *
1363 * Side effects:
1364 * Old value of keyInputEncoding is freed.
1365 *
1366 *----------------------------------------------------------------------
1367 */
1368
1369 static void
UpdateInputLanguage(charset)1370 UpdateInputLanguage(charset)
1371 int charset;
1372 {
1373 CHARSETINFO charsetInfo;
1374 Tcl_Encoding encoding;
1375 char codepage[4 + TCL_INTEGER_SPACE];
1376
1377 if (keyInputCharset == charset) {
1378 return;
1379 }
1380 if (TranslateCharsetInfo((DWORD*)charset, &charsetInfo, TCI_SRCCHARSET)
1381 == 0) {
1382 /*
1383 * Some mysterious failure.
1384 */
1385
1386 return;
1387 }
1388
1389 wsprintfA(codepage, "cp%d", charsetInfo.ciACP);
1390
1391 if ((encoding = Tcl_GetEncoding(NULL, codepage)) == NULL) {
1392 /*
1393 * The encoding is not supported by Tcl.
1394 */
1395
1396 return;
1397 }
1398
1399 if (keyInputEncoding != NULL) {
1400 Tcl_FreeEncoding(keyInputEncoding);
1401 }
1402
1403 keyInputEncoding = encoding;
1404 keyInputCharset = charset;
1405 }
1406
1407 /*
1408 *----------------------------------------------------------------------
1409 *
1410 * TkWinGetKeyInputEncoding --
1411 *
1412 * Returns the current keyboard input encoding selected by the
1413 * user (with WM_INPUTLANGCHANGE events).
1414 *
1415 * Results:
1416 * The current keyboard input encoding.
1417 *
1418 * Side effects:
1419 * None.
1420 *
1421 *----------------------------------------------------------------------
1422 */
1423
1424 Tcl_Encoding
TkWinGetKeyInputEncoding()1425 TkWinGetKeyInputEncoding()
1426 {
1427 return keyInputEncoding;
1428 }
1429
1430 /*
1431 *----------------------------------------------------------------------
1432 *
1433 * TkWinGetUnicodeEncoding --
1434 *
1435 * Returns the cached unicode encoding.
1436 *
1437 * Results:
1438 * The unicode encoding.
1439 *
1440 * Side effects:
1441 * None.
1442 *
1443 *----------------------------------------------------------------------
1444 */
1445
1446 Tcl_Encoding
TkWinGetUnicodeEncoding()1447 TkWinGetUnicodeEncoding()
1448 {
1449 if (unicodeEncoding == NULL) {
1450 unicodeEncoding = Tcl_GetEncoding(NULL, "unicode");
1451 }
1452 return unicodeEncoding;
1453 }
1454
1455 /*
1456 *----------------------------------------------------------------------
1457 *
1458 * HandleIMEComposition --
1459 *
1460 * This function works around a definciency in some versions
1461 * of Windows 2000 to make it possible to entry multi-lingual
1462 * characters under all versions of Windows 2000.
1463 *
1464 * When an Input Method Editor (IME) is ready to send input
1465 * characters to an application, it sends a WM_IME_COMPOSITION
1466 * message with the GCS_RESULTSTR. However, The DefWindowProc()
1467 * on English Windows 2000 arbitrarily converts all non-Latin-1
1468 * characters in the composition to "?".
1469 *
1470 * This function correctly processes the composition data and
1471 * sends the UNICODE values of the composed characters to
1472 * TK's event queue.
1473 *
1474 * Results:
1475 * If this function has processed the composition data, returns 1.
1476 * Otherwise returns 0.
1477 *
1478 * Side effects:
1479 * Key events are put into the TK event queue.
1480 *
1481 *----------------------------------------------------------------------
1482 */
1483
1484 static int
HandleIMEComposition(hwnd,lParam)1485 HandleIMEComposition(hwnd, lParam)
1486 HWND hwnd; /* Window receiving the message. */
1487 LPARAM lParam; /* Flags for the WM_IME_COMPOSITION
1488 * message */
1489 {
1490 HIMC hIMC;
1491 int i, n;
1492 XEvent event;
1493 char * buff;
1494 TkWindow *winPtr;
1495 Tcl_Encoding unicodeEncoding = TkWinGetUnicodeEncoding();
1496 BOOL isWinNT = (TkWinGetPlatformId() == VER_PLATFORM_WIN32_NT);
1497
1498 if ((lParam & GCS_RESULTSTR) == 0) {
1499 /*
1500 * Composition is not finished yet.
1501 */
1502
1503 return 0;
1504 }
1505
1506 hIMC = ImmGetContext(hwnd);
1507 if (hIMC) {
1508 if (isWinNT) {
1509 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
1510 } else {
1511 n = ImmGetCompositionStringA(hIMC, GCS_RESULTSTR, NULL, 0);
1512 }
1513
1514 if ((n > 0) && ((buff = (char *) ckalloc(n)) != NULL)) {
1515 if (isWinNT) {
1516 n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n);
1517 } else {
1518 Tcl_DString utfString, unicodeString;
1519
1520 n = ImmGetCompositionStringA(hIMC, GCS_RESULTSTR, buff, n);
1521 Tcl_DStringInit(&utfString);
1522 Tcl_ExternalToUtfDString(keyInputEncoding, buff, n,
1523 &utfString);
1524 Tcl_UtfToExternalDString(unicodeEncoding,
1525 Tcl_DStringValue(&utfString), -1, &unicodeString);
1526 i = Tcl_DStringLength(&unicodeString);
1527 if (n < i) {
1528 /*
1529 * Only alloc more space if we need, otherwise just
1530 * use what we've created. Don't realloc as that may
1531 * copy data we no longer need.
1532 */
1533 ckfree((char *) buff);
1534 buff = (char *) ckalloc(i);
1535 }
1536 n = i;
1537 memcpy(buff, Tcl_DStringValue(&unicodeString), n);
1538 Tcl_DStringFree(&utfString);
1539 Tcl_DStringFree(&unicodeString);
1540 }
1541
1542 /*
1543 * Set up the fields pertinent to key event.
1544 *
1545 * We set send_event to the special value of -2, so that
1546 * TkpGetString() in tkWinKey.c knows that trans_chars[]
1547 * already contains a UNICODE char and there's no need to
1548 * do encoding conversion.
1549 */
1550
1551 winPtr = (TkWindow *)Tk_HWNDToWindow(hwnd);
1552
1553 event.xkey.serial = winPtr->display->request++;
1554 event.xkey.send_event = -2;
1555 event.xkey.display = winPtr->display;
1556 event.xkey.window = winPtr->window;
1557 event.xkey.root = RootWindow(winPtr->display, winPtr->screenNum);
1558 event.xkey.subwindow = None;
1559 event.xkey.state = TkWinGetModifierState();
1560 event.xkey.time = TkpGetMS();
1561 event.xkey.same_screen = True;
1562 event.xkey.keycode = 0;
1563 event.xkey.nbytes = 2;
1564
1565 for (i=0; i<n;) {
1566 /*
1567 * Simulate a pair of KeyPress and KeyRelease events
1568 * for each UNICODE character in the composition.
1569 */
1570
1571 event.xkey.trans_chars[0] = (char) buff[i++];
1572 event.xkey.trans_chars[1] = (char) buff[i++];
1573
1574 event.type = KeyPress;
1575 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
1576
1577 event.type = KeyRelease;
1578 Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
1579 }
1580
1581 ckfree(buff);
1582 }
1583 ImmReleaseContext(hwnd, hIMC);
1584 return 1;
1585 }
1586
1587 return 0;
1588 }
1589
1590 /*
1591 *----------------------------------------------------------------------
1592 *
1593 * Tk_FreeXId --
1594 *
1595 * This interface is not needed under Windows.
1596 *
1597 * Results:
1598 * None.
1599 *
1600 * Side effects:
1601 * None.
1602 *
1603 *----------------------------------------------------------------------
1604 */
1605
1606 void
Tk_FreeXId(display,xid)1607 Tk_FreeXId(display, xid)
1608 Display *display;
1609 XID xid;
1610 {
1611 }
1612
1613 /*
1614 *----------------------------------------------------------------------
1615 *
1616 * TkWinResendEvent --
1617 *
1618 * This function converts an X event into a Windows event and
1619 * invokes the specified windo procedure.
1620 *
1621 * Results:
1622 * A standard Windows result.
1623 *
1624 * Side effects:
1625 * Invokes the window procedure
1626 *
1627 *----------------------------------------------------------------------
1628 */
1629
1630 LRESULT
TkWinResendEvent(wndproc,hwnd,eventPtr)1631 TkWinResendEvent(wndproc, hwnd, eventPtr)
1632 WNDPROC wndproc;
1633 HWND hwnd;
1634 XEvent *eventPtr;
1635 {
1636 UINT msg;
1637 WPARAM wparam;
1638 LPARAM lparam;
1639
1640 if (eventPtr->type == ButtonPress) {
1641 switch (eventPtr->xbutton.button) {
1642 case Button1:
1643 msg = WM_LBUTTONDOWN;
1644 wparam = MK_LBUTTON;
1645 break;
1646 case Button2:
1647 msg = WM_MBUTTONDOWN;
1648 wparam = MK_MBUTTON;
1649 break;
1650 case Button3:
1651 msg = WM_RBUTTONDOWN;
1652 wparam = MK_RBUTTON;
1653 break;
1654 default:
1655 return 0;
1656 }
1657 if (eventPtr->xbutton.state & Button1Mask) {
1658 wparam |= MK_LBUTTON;
1659 }
1660 if (eventPtr->xbutton.state & Button2Mask) {
1661 wparam |= MK_MBUTTON;
1662 }
1663 if (eventPtr->xbutton.state & Button3Mask) {
1664 wparam |= MK_RBUTTON;
1665 }
1666 if (eventPtr->xbutton.state & ShiftMask) {
1667 wparam |= MK_SHIFT;
1668 }
1669 if (eventPtr->xbutton.state & ControlMask) {
1670 wparam |= MK_CONTROL;
1671 }
1672 lparam = MAKELPARAM((short) eventPtr->xbutton.x,
1673 (short) eventPtr->xbutton.y);
1674 } else {
1675 return 0;
1676 }
1677 return CallWindowProc(wndproc, hwnd, msg, wparam, lparam);
1678 }
1679
1680 /*
1681 *----------------------------------------------------------------------
1682 *
1683 * TkpGetMS --
1684 *
1685 * Return a relative time in milliseconds. It doesn't matter
1686 * when the epoch was.
1687 *
1688 * Results:
1689 * Number of milliseconds.
1690 *
1691 * Side effects:
1692 * None.
1693 *
1694 *----------------------------------------------------------------------
1695 */
1696
1697 unsigned long
TkpGetMS()1698 TkpGetMS()
1699 {
1700 return GetTickCount();
1701 }
1702
1703 /*
1704 *----------------------------------------------------------------------
1705 *
1706 * TkWinUpdatingClipboard --
1707 *
1708 *
1709 * Results:
1710 * Number of milliseconds.
1711 *
1712 * Side effects:
1713 * None.
1714 *
1715 *----------------------------------------------------------------------
1716 */
1717
1718 void
TkWinUpdatingClipboard(int mode)1719 TkWinUpdatingClipboard(int mode)
1720 {
1721 ThreadSpecificData *tsdPtr = (ThreadSpecificData *)
1722 Tcl_GetThreadData(&dataKey, sizeof(ThreadSpecificData));
1723
1724 tsdPtr->updatingClipboard = mode;
1725 }
1726
1727 /*
1728 *----------------------------------------------------------------------
1729 *
1730 * Tk_SetCaretPos --
1731 *
1732 * This enables correct movement of focus in the MS Magnifier, as well
1733 * as allowing us to correctly position the IME Window. The following
1734 * Win32 APIs are used to work with MS caret:
1735 *
1736 * CreateCaret DestroyCaret SetCaretPos GetCaretPos
1737 *
1738 * Only one instance of caret can be active at any time
1739 * (e.g. DestroyCaret API does not take any argument such as handle).
1740 * Since do-it-right approach requires to track the create/destroy
1741 * caret status all the time in a global scope among windows (or
1742 * widgets), we just implement this minimal setup to get the job done.
1743 *
1744 * Results:
1745 * None
1746 *
1747 * Side effects:
1748 * Sets the global Windows caret position.
1749 *
1750 *----------------------------------------------------------------------
1751 */
1752
1753 void
Tk_SetCaretPos(Tk_Window tkwin,int x,int y,int height)1754 Tk_SetCaretPos(Tk_Window tkwin, int x, int y, int height)
1755 {
1756 static HWND caretHWND = NULL;
1757 TkCaret *caretPtr = &(((TkWindow *) tkwin)->dispPtr->caret);
1758 Window win;
1759
1760 /*
1761 * Prevent processing anything if the values haven't changed.
1762 * Windows only has one display, so we can do this with statics.
1763 */
1764 if ((caretPtr->winPtr == ((TkWindow *) tkwin))
1765 && (caretPtr->x == x) && (caretPtr->y == y)) {
1766 return;
1767 }
1768
1769 caretPtr->winPtr = ((TkWindow *) tkwin);
1770 caretPtr->x = x;
1771 caretPtr->y = y;
1772 caretPtr->height = height;
1773
1774 /*
1775 * We adjust to the toplevel to get the coords right, as setting
1776 * the IME composition window is based on the toplevel hwnd, so
1777 * ignore height.
1778 */
1779
1780 while (!Tk_IsTopLevel(tkwin)) {
1781 x += Tk_X(tkwin);
1782 y += Tk_Y(tkwin);
1783 tkwin = Tk_Parent(tkwin);
1784 if (tkwin == NULL) {
1785 return;
1786 }
1787 }
1788
1789 win = Tk_WindowId(tkwin);
1790 if (win) {
1791 HIMC hIMC;
1792 HWND hwnd = Tk_GetHWND(win);
1793
1794 if (hwnd != caretHWND) {
1795 DestroyCaret();
1796 if (CreateCaret(hwnd, NULL, 0, 0)) {
1797 caretHWND = hwnd;
1798 }
1799 }
1800
1801 if (!SetCaretPos(x, y) && CreateCaret(hwnd, NULL, 0, 0)) {
1802 caretHWND = hwnd;
1803 SetCaretPos(x, y);
1804 }
1805
1806 /*
1807 * The IME composition window should be updated whenever the caret
1808 * position is changed because a clause of the composition string may
1809 * be converted to the final characters and the other clauses still
1810 * stay on the composition window. -- yamamoto
1811 */
1812 hIMC = ImmGetContext(hwnd);
1813 if (hIMC) {
1814 COMPOSITIONFORM cform;
1815 cform.dwStyle = CFS_POINT;
1816 cform.ptCurrentPos.x = x;
1817 cform.ptCurrentPos.y = y;
1818 ImmSetCompositionWindow(hIMC, &cform);
1819 ImmReleaseContext(hwnd, hIMC);
1820 }
1821 }
1822 }
1823
1824