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