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