1133 lines
27 KiB
C
1133 lines
27 KiB
C
|
/*
|
|||
|
* tkWinX.c --
|
|||
|
*
|
|||
|
* This file contains Windows emulation procedures for X routines.
|
|||
|
*
|
|||
|
* Copyright (c) 1995-1996 Sun Microsystems, Inc.
|
|||
|
* Copyright (c) 1994 Software Research Associates, Inc.
|
|||
|
*
|
|||
|
* See the file "license.terms" for information on usage and redistribution
|
|||
|
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
|||
|
*
|
|||
|
* SCCS: @(#) tkWinX.c 1.31 96/10/14 13:26:55
|
|||
|
*/
|
|||
|
|
|||
|
#include "tkInt.h"
|
|||
|
#include "tkWinInt.h"
|
|||
|
|
|||
|
/*
|
|||
|
* The following declaration is a special purpose backdoor into the
|
|||
|
* Tcl notifier. It is used to process events on the Tcl event queue,
|
|||
|
* without reentering the system event queue.
|
|||
|
*/
|
|||
|
|
|||
|
extern void TclWinFlushEvents _ANSI_ARGS_((void));
|
|||
|
|
|||
|
/*
|
|||
|
* Declarations of static variables used in this file.
|
|||
|
*/
|
|||
|
|
|||
|
static HINSTANCE appInstance = (HINSTANCE) NULL;
|
|||
|
/* Global application instance handle. */
|
|||
|
static Display *winDisplay; /* Display that represents Windows screen. */
|
|||
|
static Tcl_HashTable windowTable;
|
|||
|
/* Table of child windows indexed by handle. */
|
|||
|
static char winScreenName[] = ":0";
|
|||
|
/* Default name of windows display. */
|
|||
|
static ATOM topLevelAtom, childAtom;
|
|||
|
/* Atoms for the classes registered by Tk. */
|
|||
|
static isInModal = 0; /* True if TK can entered a modal loop */
|
|||
|
|
|||
|
/*
|
|||
|
* Forward declarations of procedures used in this file.
|
|||
|
*/
|
|||
|
|
|||
|
static void DeleteWindow _ANSI_ARGS_((HWND hwnd));
|
|||
|
static void GetTranslatedKey _ANSI_ARGS_((XKeyEvent *xkey));
|
|||
|
static void TranslateEvent _ANSI_ARGS_((HWND hwnd, UINT message,
|
|||
|
WPARAM wParam, LPARAM lParam));
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkGetServerInfo --
|
|||
|
*
|
|||
|
* Given a window, this procedure returns information about
|
|||
|
* the window server for that window. This procedure provides
|
|||
|
* the guts of the "winfo server" command.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
TkGetServerInfo(interp, tkwin)
|
|||
|
Tcl_Interp *interp; /* The server information is returned in
|
|||
|
* this interpreter's result. */
|
|||
|
Tk_Window tkwin; /* Token for window; this selects a
|
|||
|
* particular display and server. */
|
|||
|
{
|
|||
|
char buffer[50];
|
|||
|
OSVERSIONINFO info;
|
|||
|
int index = 0;
|
|||
|
static char* os[] = {"Win32", "Win32s", "Win32c"};
|
|||
|
|
|||
|
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|||
|
GetVersionEx(&info);
|
|||
|
if (info.dwPlatformId == VER_PLATFORM_WIN32s) {
|
|||
|
index = 1;
|
|||
|
} else if (info.dwPlatformId == VER_PLATFORM_WIN32s) {
|
|||
|
index = 2;
|
|||
|
}
|
|||
|
sprintf(buffer, "Windows %d.%d %d ", info.dwMajorVersion,
|
|||
|
info.dwMinorVersion, info.dwBuildNumber);
|
|||
|
Tcl_AppendResult(interp, buffer, os[index], (char *) NULL);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinGetTkModule --
|
|||
|
*
|
|||
|
* This function returns the module handle for the Tk DLL.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns the library module handle.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
HMODULE
|
|||
|
TkWinGetTkModule()
|
|||
|
{
|
|||
|
char libName[13];
|
|||
|
sprintf(libName, "tk%d%d.dll", TK_MAJOR_VERSION, TK_MINOR_VERSION);
|
|||
|
return GetModuleHandle(libName);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinGetAppInstance --
|
|||
|
*
|
|||
|
* Retrieves the global application instance handle.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns the global application instance handle.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
HINSTANCE
|
|||
|
TkWinGetAppInstance()
|
|||
|
{
|
|||
|
return appInstance;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinXInit --
|
|||
|
*
|
|||
|
* Initialize Xlib emulation layer.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Sets up various data structures.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
TkWinXInit(hInstance)
|
|||
|
HINSTANCE hInstance;
|
|||
|
{
|
|||
|
WNDCLASS class;
|
|||
|
static initialized = 0;
|
|||
|
|
|||
|
if (initialized != 0) {
|
|||
|
return;
|
|||
|
}
|
|||
|
initialized = 1;
|
|||
|
|
|||
|
appInstance = hInstance;
|
|||
|
|
|||
|
class.style = CS_HREDRAW | CS_VREDRAW;
|
|||
|
class.cbClsExtra = 0;
|
|||
|
class.cbWndExtra = 0;
|
|||
|
class.hInstance = hInstance;
|
|||
|
class.hbrBackground = NULL;
|
|||
|
class.lpszMenuName = NULL;
|
|||
|
|
|||
|
/*
|
|||
|
* Register the TopLevel window class.
|
|||
|
*/
|
|||
|
|
|||
|
class.lpszClassName = TK_WIN_TOPLEVEL_CLASS_NAME;
|
|||
|
class.lpfnWndProc = TkWinTopLevelProc;
|
|||
|
class.hIcon = LoadIcon(TkWinGetTkModule(), "tk");
|
|||
|
class.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|||
|
|
|||
|
topLevelAtom = RegisterClass(&class);
|
|||
|
if (topLevelAtom == 0) {
|
|||
|
panic("Unable to register TkTopLevel class");
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Register the Child window class.
|
|||
|
*/
|
|||
|
|
|||
|
class.lpszClassName = TK_WIN_CHILD_CLASS_NAME;
|
|||
|
class.lpfnWndProc = TkWinChildProc;
|
|||
|
class.hIcon = NULL;
|
|||
|
class.hCursor = NULL;
|
|||
|
|
|||
|
childAtom = RegisterClass(&class);
|
|||
|
if (childAtom == 0) {
|
|||
|
UnregisterClass((LPCTSTR)topLevelAtom, hInstance);
|
|||
|
panic("Unable to register TkChild class");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkGetDefaultScreenName --
|
|||
|
*
|
|||
|
* Returns the name of the screen that Tk should use during
|
|||
|
* initialization.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a statically allocated string.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
char *
|
|||
|
TkGetDefaultScreenName(interp, screenName)
|
|||
|
Tcl_Interp *interp; /* Not used. */
|
|||
|
char *screenName; /* If NULL, use default string. */
|
|||
|
{
|
|||
|
if ((screenName == NULL) || (screenName[0] == '\0')) {
|
|||
|
screenName = winScreenName;
|
|||
|
}
|
|||
|
return screenName;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* XOpenDisplay --
|
|||
|
*
|
|||
|
* Create the Display structure and fill it with device
|
|||
|
* specific information.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a Display structure on success or NULL on failure.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Allocates a new Display structure.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
Display *
|
|||
|
XOpenDisplay(display_name)
|
|||
|
_Xconst char *display_name;
|
|||
|
{
|
|||
|
Screen *screen;
|
|||
|
HDC dc;
|
|||
|
TkWinDrawable *twdPtr;
|
|||
|
|
|||
|
TkWinPointerInit();
|
|||
|
|
|||
|
Tcl_InitHashTable(&windowTable, TCL_ONE_WORD_KEYS);
|
|||
|
|
|||
|
if (winDisplay != NULL) {
|
|||
|
if (strcmp(winDisplay->display_name, display_name) == 0) {
|
|||
|
return winDisplay;
|
|||
|
} else {
|
|||
|
panic("XOpenDisplay: tried to open multiple displays");
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
winDisplay = (Display *) ckalloc(sizeof(Display));
|
|||
|
winDisplay->display_name = (char *) ckalloc(strlen(display_name)+1);
|
|||
|
strcpy(winDisplay->display_name, display_name);
|
|||
|
|
|||
|
winDisplay->cursor_font = 1;
|
|||
|
winDisplay->nscreens = 1;
|
|||
|
winDisplay->request = 1;
|
|||
|
winDisplay->qlen = 0;
|
|||
|
|
|||
|
screen = (Screen *) ckalloc(sizeof(Screen));
|
|||
|
screen->display = winDisplay;
|
|||
|
|
|||
|
dc = GetDC(NULL);
|
|||
|
screen->width = GetDeviceCaps(dc, HORZRES);
|
|||
|
screen->height = GetDeviceCaps(dc, VERTRES);
|
|||
|
screen->mwidth = GetDeviceCaps(dc, HORZSIZE);
|
|||
|
screen->mheight = GetDeviceCaps(dc, VERTSIZE);
|
|||
|
|
|||
|
/*
|
|||
|
* Set up the root window.
|
|||
|
*/
|
|||
|
|
|||
|
twdPtr = (TkWinDrawable*) ckalloc(sizeof(TkWinDrawable));
|
|||
|
if (twdPtr == NULL) {
|
|||
|
return None;
|
|||
|
}
|
|||
|
twdPtr->type = TWD_WINDOW;
|
|||
|
twdPtr->window.winPtr = NULL;
|
|||
|
twdPtr->window.handle = NULL;
|
|||
|
screen->root = (Window)twdPtr;
|
|||
|
|
|||
|
screen->root_depth = GetDeviceCaps(dc, BITSPIXEL);
|
|||
|
screen->root_visual = (Visual *) ckalloc(sizeof(Visual));
|
|||
|
screen->root_visual->visualid = 0;
|
|||
|
if (GetDeviceCaps(dc, RASTERCAPS) & RC_PALETTE) {
|
|||
|
screen->root_visual->map_entries = GetDeviceCaps(dc, SIZEPALETTE);
|
|||
|
screen->root_visual->class = PseudoColor;
|
|||
|
screen->root_visual->red_mask = 0x0;
|
|||
|
screen->root_visual->green_mask = 0x0;
|
|||
|
screen->root_visual->blue_mask = 0x0;
|
|||
|
} else {
|
|||
|
if (screen->root_depth == 4) {
|
|||
|
screen->root_visual->class = StaticColor;
|
|||
|
screen->root_visual->map_entries = 16;
|
|||
|
} else if (screen->root_depth == 8) {
|
|||
|
screen->root_visual->class = StaticColor;
|
|||
|
screen->root_visual->map_entries = 256;
|
|||
|
} else if (screen->root_depth == 16) {
|
|||
|
screen->root_visual->class = TrueColor;
|
|||
|
screen->root_visual->map_entries = 64;
|
|||
|
screen->root_visual->red_mask = 0xf8;
|
|||
|
screen->root_visual->green_mask = 0xfc00;
|
|||
|
screen->root_visual->blue_mask = 0xf80000;
|
|||
|
} else if (screen->root_depth >= 24) {
|
|||
|
screen->root_visual->class = TrueColor;
|
|||
|
screen->root_visual->map_entries = 256;
|
|||
|
screen->root_visual->red_mask = 0xff;
|
|||
|
screen->root_visual->green_mask = 0xff00;
|
|||
|
screen->root_visual->blue_mask = 0xff0000;
|
|||
|
}
|
|||
|
}
|
|||
|
screen->root_visual->bits_per_rgb = screen->root_depth;
|
|||
|
ReleaseDC(NULL, dc);
|
|||
|
|
|||
|
/*
|
|||
|
* Note that these pixel values are not palette relative.
|
|||
|
*/
|
|||
|
|
|||
|
screen->white_pixel = RGB(255, 255, 255);
|
|||
|
screen->black_pixel = RGB(0, 0, 0);
|
|||
|
|
|||
|
winDisplay->screens = screen;
|
|||
|
winDisplay->nscreens = 1;
|
|||
|
winDisplay->default_screen = 0;
|
|||
|
screen->cmap = XCreateColormap(winDisplay, None, screen->root_visual,
|
|||
|
AllocNone);
|
|||
|
return winDisplay;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* XBell --
|
|||
|
*
|
|||
|
* Generate a beep.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Plays a sounds out the system speakers.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
XBell(display, percent)
|
|||
|
Display* display;
|
|||
|
int percent;
|
|||
|
{
|
|||
|
MessageBeep(MB_OK);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinTopLevelProc --
|
|||
|
*
|
|||
|
* Callback from Windows whenever an event occurs on a top level
|
|||
|
* window.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Standard Windows return value.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Default window behavior.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
LRESULT CALLBACK
|
|||
|
TkWinTopLevelProc(hwnd, message, wParam, lParam)
|
|||
|
HWND hwnd;
|
|||
|
UINT message;
|
|||
|
WPARAM wParam;
|
|||
|
LPARAM lParam;
|
|||
|
{
|
|||
|
static inMoveSize = 0;
|
|||
|
|
|||
|
if (inMoveSize) {
|
|||
|
TclWinFlushEvents();
|
|||
|
}
|
|||
|
|
|||
|
switch (message) {
|
|||
|
case WM_ENTERSIZEMOVE:
|
|||
|
inMoveSize = 1;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_EXITSIZEMOVE:
|
|||
|
inMoveSize = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_CREATE: {
|
|||
|
CREATESTRUCT *info = (CREATESTRUCT *) lParam;
|
|||
|
TkWinDrawable *twdPtr = (TkWinDrawable *)info->lpCreateParams;
|
|||
|
Tcl_HashEntry *hPtr;
|
|||
|
int new;
|
|||
|
|
|||
|
/*
|
|||
|
* Add the window and handle to the window table.
|
|||
|
*/
|
|||
|
|
|||
|
twdPtr->window.handle = hwnd;
|
|||
|
hPtr = Tcl_CreateHashEntry(&windowTable, (char *)hwnd, &new);
|
|||
|
if (!new) {
|
|||
|
panic("Duplicate window handle: %p", hwnd);
|
|||
|
}
|
|||
|
Tcl_SetHashValue(hPtr, twdPtr);
|
|||
|
|
|||
|
/*
|
|||
|
* Store the pointer to the drawable structure passed into
|
|||
|
* CreateWindow in the user data slot of the window.
|
|||
|
*/
|
|||
|
|
|||
|
SetWindowLong(hwnd, GWL_USERDATA, (DWORD)twdPtr);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
case WM_DESTROY:
|
|||
|
DeleteWindow(hwnd);
|
|||
|
return 0;
|
|||
|
|
|||
|
case WM_GETMINMAXINFO:
|
|||
|
TkWinWmSetLimits(hwnd, (MINMAXINFO *) lParam);
|
|||
|
return 0;
|
|||
|
|
|||
|
case WM_PALETTECHANGED:
|
|||
|
return TkWinWmInstallColormaps(hwnd, WM_PALETTECHANGED,
|
|||
|
hwnd == (HWND)wParam);
|
|||
|
|
|||
|
case WM_QUERYNEWPALETTE:
|
|||
|
return TkWinWmInstallColormaps(hwnd, WM_QUERYNEWPALETTE, TRUE);
|
|||
|
|
|||
|
case WM_WINDOWPOSCHANGED: {
|
|||
|
WINDOWPOS *pos = (WINDOWPOS *) lParam;
|
|||
|
TkWinDrawable *twdPtr =
|
|||
|
(TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA);
|
|||
|
|
|||
|
TkWinWmConfigure(TkWinGetWinPtr(twdPtr), pos);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
case WM_CLOSE:
|
|||
|
case WM_LBUTTONDOWN:
|
|||
|
case WM_MBUTTONDOWN:
|
|||
|
case WM_RBUTTONDOWN:
|
|||
|
case WM_LBUTTONUP:
|
|||
|
case WM_MBUTTONUP:
|
|||
|
case WM_RBUTTONUP:
|
|||
|
case WM_MOUSEMOVE:
|
|||
|
case WM_CHAR:
|
|||
|
case WM_SYSCHAR:
|
|||
|
case WM_KEYDOWN:
|
|||
|
case WM_KEYUP:
|
|||
|
case WM_SETFOCUS:
|
|||
|
case WM_KILLFOCUS:
|
|||
|
TranslateEvent(hwnd, message, wParam, lParam);
|
|||
|
return 0;
|
|||
|
|
|||
|
case WM_SYSKEYDOWN:
|
|||
|
case WM_SYSKEYUP:
|
|||
|
case WM_DESTROYCLIPBOARD:
|
|||
|
TranslateEvent(hwnd, message, wParam, lParam);
|
|||
|
|
|||
|
/*
|
|||
|
* We need to pass these messages to the default window
|
|||
|
* procedure in order to get the system menu to work.
|
|||
|
*/
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinChildProc --
|
|||
|
*
|
|||
|
* Callback from Windows whenever an event occurs on a child
|
|||
|
* window.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Standard Windows return value.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Default window behavior.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
LRESULT CALLBACK
|
|||
|
TkWinChildProc(hwnd, message, wParam, lParam)
|
|||
|
HWND hwnd;
|
|||
|
UINT message;
|
|||
|
WPARAM wParam;
|
|||
|
LPARAM lParam;
|
|||
|
{
|
|||
|
switch (message) {
|
|||
|
case WM_CREATE: {
|
|||
|
CREATESTRUCT *info = (CREATESTRUCT *) lParam;
|
|||
|
Tcl_HashEntry *hPtr;
|
|||
|
int new;
|
|||
|
|
|||
|
/*
|
|||
|
* Add the window and handle to the window table.
|
|||
|
*/
|
|||
|
|
|||
|
hPtr = Tcl_CreateHashEntry(&windowTable, (char *)hwnd, &new);
|
|||
|
if (!new) {
|
|||
|
panic("Duplicate window handle: %p", hwnd);
|
|||
|
}
|
|||
|
Tcl_SetHashValue(hPtr, info->lpCreateParams);
|
|||
|
|
|||
|
/*
|
|||
|
* Store the pointer to the drawable structure passed into
|
|||
|
* CreateWindow in the user data slot of the window. Then set
|
|||
|
* the Z stacking order so the window appears on top.
|
|||
|
*/
|
|||
|
|
|||
|
SetWindowLong(hwnd, GWL_USERDATA, (DWORD)info->lpCreateParams);
|
|||
|
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0,
|
|||
|
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
case WM_DESTROY:
|
|||
|
DeleteWindow(hwnd);
|
|||
|
return 0;
|
|||
|
|
|||
|
case WM_ERASEBKGND:
|
|||
|
case WM_WINDOWPOSCHANGED:
|
|||
|
return 0;
|
|||
|
|
|||
|
case WM_RENDERFORMAT: {
|
|||
|
TkWinDrawable *twdPtr;
|
|||
|
twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA);
|
|||
|
TkWinClipboardRender(TkWinGetWinPtr(twdPtr), wParam);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
case WM_DESTROYCLIPBOARD:
|
|||
|
case WM_PAINT:
|
|||
|
case WM_LBUTTONDOWN:
|
|||
|
case WM_MBUTTONDOWN:
|
|||
|
case WM_RBUTTONDOWN:
|
|||
|
case WM_LBUTTONUP:
|
|||
|
case WM_MBUTTONUP:
|
|||
|
case WM_RBUTTONUP:
|
|||
|
case WM_MOUSEMOVE:
|
|||
|
case WM_CHAR:
|
|||
|
case WM_SYSCHAR:
|
|||
|
case WM_SYSKEYDOWN:
|
|||
|
case WM_SYSKEYUP:
|
|||
|
case WM_KEYDOWN:
|
|||
|
case WM_KEYUP:
|
|||
|
case WM_SETFOCUS:
|
|||
|
case WM_KILLFOCUS:
|
|||
|
TranslateEvent(hwnd, message, wParam, lParam);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TranslateEvent --
|
|||
|
*
|
|||
|
* This function is called by the window procedures to handle
|
|||
|
* the translation from Win32 events to Tk events.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Queues a new Tk event.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
TranslateEvent(hwnd, message, wParam, lParam)
|
|||
|
HWND hwnd;
|
|||
|
UINT message;
|
|||
|
WPARAM wParam;
|
|||
|
LPARAM lParam;
|
|||
|
{
|
|||
|
TkWindow *winPtr;
|
|||
|
XEvent event;
|
|||
|
TkWinDrawable *twdPtr;
|
|||
|
|
|||
|
/*
|
|||
|
* Retrieve the window information, and reset the hwnd pointer in
|
|||
|
* case the original window was a toplevel decorative frame.
|
|||
|
*/
|
|||
|
|
|||
|
twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA);
|
|||
|
if (twdPtr == NULL) {
|
|||
|
return;
|
|||
|
}
|
|||
|
winPtr = TkWinGetWinPtr(twdPtr);
|
|||
|
|
|||
|
/*
|
|||
|
* TranslateEvent may get called even after Tk has deleted the window.
|
|||
|
* So we must check for a dead window before proceeding.
|
|||
|
*/
|
|||
|
|
|||
|
if (winPtr == NULL || winPtr->window == None) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
hwnd = TkWinGetHWND(winPtr->window);
|
|||
|
|
|||
|
event.xany.serial = winPtr->display->request++;
|
|||
|
event.xany.send_event = False;
|
|||
|
event.xany.display = winPtr->display;
|
|||
|
event.xany.window = (Window) winPtr->window;
|
|||
|
|
|||
|
switch (message) {
|
|||
|
case WM_PAINT: {
|
|||
|
PAINTSTRUCT ps;
|
|||
|
|
|||
|
event.type = Expose;
|
|||
|
BeginPaint(hwnd, &ps);
|
|||
|
event.xexpose.x = ps.rcPaint.left;
|
|||
|
event.xexpose.y = ps.rcPaint.top;
|
|||
|
event.xexpose.width = ps.rcPaint.right - ps.rcPaint.left;
|
|||
|
event.xexpose.height = ps.rcPaint.bottom - ps.rcPaint.top;
|
|||
|
EndPaint(hwnd, &ps);
|
|||
|
event.xexpose.count = 0;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case WM_CLOSE:
|
|||
|
event.type = ClientMessage;
|
|||
|
event.xclient.message_type =
|
|||
|
Tk_InternAtom((Tk_Window) winPtr, "WM_PROTOCOLS");
|
|||
|
event.xclient.format = 32;
|
|||
|
event.xclient.data.l[0] =
|
|||
|
Tk_InternAtom((Tk_Window) winPtr, "WM_DELETE_WINDOW");
|
|||
|
break;
|
|||
|
|
|||
|
case WM_SETFOCUS:
|
|||
|
event.type = FocusIn;
|
|||
|
event.xfocus.mode = NotifyNormal;
|
|||
|
event.xfocus.detail = NotifyAncestor;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_KILLFOCUS:
|
|||
|
event.type = FocusOut;
|
|||
|
event.xfocus.mode = NotifyNormal;
|
|||
|
event.xfocus.detail = NotifyAncestor;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_DESTROYCLIPBOARD:
|
|||
|
event.type = SelectionClear;
|
|||
|
event.xselectionclear.selection =
|
|||
|
Tk_InternAtom((Tk_Window)winPtr, "CLIPBOARD");
|
|||
|
event.xselectionclear.time = GetCurrentTime();
|
|||
|
break;
|
|||
|
|
|||
|
case WM_LBUTTONDOWN:
|
|||
|
case WM_MBUTTONDOWN:
|
|||
|
case WM_RBUTTONDOWN:
|
|||
|
case WM_LBUTTONUP:
|
|||
|
case WM_MBUTTONUP:
|
|||
|
case WM_RBUTTONUP:
|
|||
|
case WM_MOUSEMOVE:
|
|||
|
case WM_CHAR:
|
|||
|
case WM_SYSCHAR:
|
|||
|
case WM_SYSKEYDOWN:
|
|||
|
case WM_SYSKEYUP:
|
|||
|
case WM_KEYDOWN:
|
|||
|
case WM_KEYUP: {
|
|||
|
unsigned int state = TkWinGetModifierState(message,
|
|||
|
wParam, lParam);
|
|||
|
Time time = GetCurrentTime();
|
|||
|
POINT clientPoint;
|
|||
|
POINTS rootPoint; /* Note: POINT and POINTS are different */
|
|||
|
DWORD msgPos;
|
|||
|
|
|||
|
/*
|
|||
|
* Compute the screen and window coordinates of the event.
|
|||
|
*/
|
|||
|
|
|||
|
msgPos = GetMessagePos();
|
|||
|
rootPoint = MAKEPOINTS(msgPos);
|
|||
|
clientPoint.x = rootPoint.x;
|
|||
|
clientPoint.y = rootPoint.y;
|
|||
|
ScreenToClient(hwnd, &clientPoint);
|
|||
|
|
|||
|
/*
|
|||
|
* Set up the common event fields.
|
|||
|
*/
|
|||
|
|
|||
|
event.xbutton.root = RootWindow(winPtr->display,
|
|||
|
winPtr->screenNum);
|
|||
|
event.xbutton.subwindow = None;
|
|||
|
event.xbutton.x = clientPoint.x;
|
|||
|
event.xbutton.y = clientPoint.y;
|
|||
|
event.xbutton.x_root = rootPoint.x;
|
|||
|
event.xbutton.y_root = rootPoint.y;
|
|||
|
event.xbutton.state = state;
|
|||
|
event.xbutton.time = time;
|
|||
|
event.xbutton.same_screen = True;
|
|||
|
|
|||
|
/*
|
|||
|
* Now set up event specific fields.
|
|||
|
*/
|
|||
|
|
|||
|
switch (message) {
|
|||
|
case WM_LBUTTONDOWN:
|
|||
|
event.type = ButtonPress;
|
|||
|
event.xbutton.button = Button1;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_MBUTTONDOWN:
|
|||
|
event.type = ButtonPress;
|
|||
|
event.xbutton.button = Button2;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_RBUTTONDOWN:
|
|||
|
event.type = ButtonPress;
|
|||
|
event.xbutton.button = Button3;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_LBUTTONUP:
|
|||
|
event.type = ButtonRelease;
|
|||
|
event.xbutton.button = Button1;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_MBUTTONUP:
|
|||
|
event.type = ButtonRelease;
|
|||
|
event.xbutton.button = Button2;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_RBUTTONUP:
|
|||
|
event.type = ButtonRelease;
|
|||
|
event.xbutton.button = Button3;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_MOUSEMOVE:
|
|||
|
event.type = MotionNotify;
|
|||
|
event.xmotion.is_hint = NotifyNormal;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_SYSKEYDOWN:
|
|||
|
case WM_KEYDOWN:
|
|||
|
/*
|
|||
|
* Check for translated characters in the event queue.
|
|||
|
* Setting xany.send_event to -1 indicates to the
|
|||
|
* Windows implementation of XLookupString that this
|
|||
|
* event was generated by windows and that the Windows
|
|||
|
* extension xkey.trans_chars is filled with the
|
|||
|
* characters that came from the TranslateMessage
|
|||
|
* call. If it is not -1, xkey.keycode is the
|
|||
|
* virtual key being sent programmatically by generic
|
|||
|
* code.
|
|||
|
*/
|
|||
|
|
|||
|
event.type = KeyPress;
|
|||
|
event.xany.send_event = -1;
|
|||
|
event.xkey.keycode = wParam;
|
|||
|
GetTranslatedKey(&event.xkey);
|
|||
|
break;
|
|||
|
|
|||
|
case WM_SYSKEYUP:
|
|||
|
case WM_KEYUP:
|
|||
|
/*
|
|||
|
* We don't check for translated characters on keyup
|
|||
|
* because Tk won't know what to do with them. Instead, we
|
|||
|
* wait for the WM_CHAR messages which will follow.
|
|||
|
*/
|
|||
|
event.type = KeyRelease;
|
|||
|
event.xkey.keycode = wParam;
|
|||
|
event.xkey.nchars = 0;
|
|||
|
break;
|
|||
|
|
|||
|
case WM_CHAR:
|
|||
|
case WM_SYSCHAR:
|
|||
|
/*
|
|||
|
* Synthesize both a KeyPress and a KeyRelease.
|
|||
|
*/
|
|||
|
|
|||
|
event.type = KeyPress;
|
|||
|
event.xany.send_event = -1;
|
|||
|
event.xkey.keycode = 0;
|
|||
|
event.xkey.nchars = 1;
|
|||
|
event.xkey.trans_chars[0] = (char) wParam;
|
|||
|
Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
|
|||
|
event.type = KeyRelease;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((event.type == MotionNotify)
|
|||
|
|| (event.type == ButtonPress)
|
|||
|
|| (event.type == ButtonRelease)) {
|
|||
|
TkWinPointerEvent(&event, winPtr);
|
|||
|
goto done;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default:
|
|||
|
goto done;
|
|||
|
}
|
|||
|
Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL);
|
|||
|
|
|||
|
done:
|
|||
|
if (isInModal) {
|
|||
|
TclWinFlushEvents();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinGetModifierState --
|
|||
|
*
|
|||
|
* This function constructs a state mask for the mouse buttons
|
|||
|
* and modifier keys.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a composite value of all the modifier and button state
|
|||
|
* flags that were set at the time the event occurred.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
unsigned int
|
|||
|
TkWinGetModifierState(message, wParam, lParam)
|
|||
|
UINT message; /* Win32 message type */
|
|||
|
WPARAM wParam; /* wParam of message, used if key message */
|
|||
|
LPARAM lParam; /* lParam of message, used if key message */
|
|||
|
{
|
|||
|
unsigned int state = 0; /* accumulated state flags */
|
|||
|
int isKeyEvent; /* 1 if message is a key press or release */
|
|||
|
int prevState; /* 1 if key was previously down */
|
|||
|
|
|||
|
/*
|
|||
|
* If the event is a key press or release, we check for autorepeat.
|
|||
|
*/
|
|||
|
|
|||
|
if (message == WM_SYSKEYDOWN || message == WM_KEYDOWN
|
|||
|
|| message == WM_SYSKEYUP || message == WM_KEYUP) {
|
|||
|
isKeyEvent = TRUE;
|
|||
|
prevState = HIWORD(lParam) & KF_REPEAT;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If the key being pressed or released is a modifier key, then
|
|||
|
* we use its previous state, otherwise we look at the current state.
|
|||
|
*/
|
|||
|
|
|||
|
if (isKeyEvent && (wParam == VK_SHIFT)) {
|
|||
|
state |= prevState ? ShiftMask : 0;
|
|||
|
} else {
|
|||
|
state |= (GetKeyState(VK_SHIFT) & 0x8000) ? ShiftMask : 0;
|
|||
|
}
|
|||
|
if (isKeyEvent && (wParam == VK_CONTROL)) {
|
|||
|
state |= prevState ? ControlMask : 0;
|
|||
|
} else {
|
|||
|
state |= (GetKeyState(VK_CONTROL) & 0x8000) ? ControlMask : 0;
|
|||
|
}
|
|||
|
if (isKeyEvent && (wParam == VK_MENU)) {
|
|||
|
state |= prevState ? Mod2Mask : 0;
|
|||
|
} else {
|
|||
|
state |= (GetKeyState(VK_MENU) & 0x8000) ? Mod2Mask : 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* For toggle keys, we have to check both the previous key state
|
|||
|
* and the current toggle state. The result is the state of the
|
|||
|
* toggle before the event.
|
|||
|
*/
|
|||
|
|
|||
|
if ((wParam == VK_CAPITAL)
|
|||
|
&& (message == WM_SYSKEYDOWN || message == WM_KEYDOWN)) {
|
|||
|
state = (prevState ^ (GetKeyState(VK_CAPITAL) & 0x0001))
|
|||
|
? 0 : LockMask;
|
|||
|
} else {
|
|||
|
state |= (GetKeyState(VK_CAPITAL) & 0x0001) ? LockMask : 0;
|
|||
|
}
|
|||
|
if ((wParam == VK_NUMLOCK)
|
|||
|
&& (message == WM_SYSKEYDOWN || message == WM_KEYDOWN)) {
|
|||
|
state = (prevState ^ (GetKeyState(VK_NUMLOCK) & 0x0001))
|
|||
|
? 0 : Mod1Mask;
|
|||
|
} else {
|
|||
|
state |= (GetKeyState(VK_NUMLOCK) & 0x0001) ? Mod1Mask : 0;
|
|||
|
}
|
|||
|
if ((wParam == VK_SCROLL)
|
|||
|
&& (message == WM_SYSKEYDOWN || message == WM_KEYDOWN)) {
|
|||
|
state = (prevState ^ (GetKeyState(VK_SCROLL) & 0x0001))
|
|||
|
? 0 : Mod3Mask;
|
|||
|
} else {
|
|||
|
state |= (GetKeyState(VK_SCROLL) & 0x0001) ? Mod3Mask : 0;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* If a mouse button is being pressed or released, we use the previous
|
|||
|
* state of the button.
|
|||
|
*/
|
|||
|
|
|||
|
if (message == WM_LBUTTONUP || (message != WM_LBUTTONDOWN
|
|||
|
&& GetKeyState(VK_LBUTTON) & 0x8000)) {
|
|||
|
state |= Button1Mask;
|
|||
|
}
|
|||
|
if (message == WM_MBUTTONUP || (message != WM_MBUTTONDOWN
|
|||
|
&& GetKeyState(VK_MBUTTON) & 0x8000)) {
|
|||
|
state |= Button2Mask;
|
|||
|
}
|
|||
|
if (message == WM_RBUTTONUP || (message != WM_RBUTTONDOWN
|
|||
|
&& GetKeyState(VK_RBUTTON) & 0x8000)) {
|
|||
|
state |= Button3Mask;
|
|||
|
}
|
|||
|
return state;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* GetTranslatedKey --
|
|||
|
*
|
|||
|
* Retrieves WM_CHAR messages that are placed on the system queue
|
|||
|
* by the TranslateMessage system call and places them in the
|
|||
|
* given KeyPress event.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Sets the trans_chars and nchars member of the key event.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Removes any WM_CHAR messages waiting on the top of the system
|
|||
|
* event queue.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
GetTranslatedKey(xkey)
|
|||
|
XKeyEvent *xkey;
|
|||
|
{
|
|||
|
MSG msg;
|
|||
|
|
|||
|
xkey->nchars = 0;
|
|||
|
|
|||
|
while (xkey->nchars < XMaxTransChars
|
|||
|
&& PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) {
|
|||
|
if ((msg.message == WM_CHAR) || (msg.message == WM_SYSCHAR)) {
|
|||
|
xkey->trans_chars[xkey->nchars] = (char) msg.wParam;
|
|||
|
xkey->nchars++;
|
|||
|
GetMessage(&msg, NULL, 0, 0);
|
|||
|
if ((msg.message == WM_CHAR) && (msg.lParam & 0x20000000)) {
|
|||
|
xkey->state = 0;
|
|||
|
}
|
|||
|
} else {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* TkWinGetDrawableFromHandle --
|
|||
|
*
|
|||
|
* Find the drawable associated with the given window handle.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* Returns a drawable pointer if the window is managed by Tk.
|
|||
|
* Otherwise it returns NULL.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
TkWinDrawable *
|
|||
|
TkWinGetDrawableFromHandle(hwnd)
|
|||
|
HWND hwnd; /* Win32 window handle */
|
|||
|
{
|
|||
|
Tcl_HashEntry *hPtr;
|
|||
|
|
|||
|
hPtr = Tcl_FindHashEntry(&windowTable, (char *)hwnd);
|
|||
|
if (hPtr) {
|
|||
|
return (TkWinDrawable *)Tcl_GetHashValue(hPtr);
|
|||
|
}
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* DeleteWindow --
|
|||
|
*
|
|||
|
* Remove a window from the window table, and free the resources
|
|||
|
* associated with the drawable.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* Frees the resources associated with a window handle.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
static void
|
|||
|
DeleteWindow(hwnd)
|
|||
|
HWND hwnd;
|
|||
|
{
|
|||
|
TkWinDrawable *twdPtr;
|
|||
|
Tcl_HashEntry *hPtr;
|
|||
|
|
|||
|
/*
|
|||
|
* Remove the window from the window table.
|
|||
|
*/
|
|||
|
|
|||
|
hPtr = Tcl_FindHashEntry(&windowTable, (char *)hwnd);
|
|||
|
if (hPtr) {
|
|||
|
Tcl_DeleteHashEntry(hPtr);
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
* Free the drawable associated with this window, unless the drawable
|
|||
|
* is still in use by a TkWindow. This only happens in the case of
|
|||
|
* a top level window, since the window gets destroyed when the
|
|||
|
* decorative frame is destroyed.
|
|||
|
*/
|
|||
|
|
|||
|
twdPtr = (TkWinDrawable *) GetWindowLong(hwnd, GWL_USERDATA);
|
|||
|
if (twdPtr) {
|
|||
|
if (twdPtr->window.winPtr == NULL) {
|
|||
|
ckfree((char *) twdPtr);
|
|||
|
} else if (!(twdPtr->window.winPtr->flags & TK_TOP_LEVEL)) {
|
|||
|
panic("Non-toplevel window destroyed before its drawable");
|
|||
|
} else {
|
|||
|
twdPtr->window.handle = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*
|
|||
|
* Tk_FreeXId --
|
|||
|
*
|
|||
|
* This inteface is not needed under Windows.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* None.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void
|
|||
|
Tk_FreeXId(display, xid)
|
|||
|
Display *display;
|
|||
|
XID xid;
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
/*----------------------------------------------------------------------
|
|||
|
* TkWinEnterModalLoop --
|
|||
|
*
|
|||
|
* Notifies the Windows event handler that TK is about to enter a
|
|||
|
* modal loop. Window Events are handled immediately if TK has
|
|||
|
* enter a modal loop.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* The isInModal counter is incremented.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void TkWinEnterModalLoop(interp)
|
|||
|
Tcl_Interp * interp;
|
|||
|
{
|
|||
|
isInModal ++;
|
|||
|
}
|
|||
|
|
|||
|
/*----------------------------------------------------------------------
|
|||
|
* TkWinLeaveModalLoop --
|
|||
|
*
|
|||
|
* Complement to the TkWinEnterModalLoop function.
|
|||
|
*
|
|||
|
* Results:
|
|||
|
* None.
|
|||
|
*
|
|||
|
* Side effects:
|
|||
|
* The isInModal counter is decremented.
|
|||
|
*
|
|||
|
*----------------------------------------------------------------------
|
|||
|
*/
|
|||
|
|
|||
|
void TkWinLeaveModalLoop(interp)
|
|||
|
Tcl_Interp * interp;
|
|||
|
{
|
|||
|
isInModal --;
|
|||
|
}
|