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