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