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