/* * tkWinPointer.c -- * * Windows specific mouse tracking code. * * Copyright (c) 1995-1997 Sun Microsystems, Inc. * Copyright (c) 1998-1999 by Scriptics Corporation. * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ #include "tkWinInt.h" /* * Check for enter/leave events every MOUSE_TIMER_INTERVAL milliseconds. */ #define MOUSE_TIMER_INTERVAL 250 /* * Declarations of static variables used in this file. */ static int captured = 0; /* 1 if mouse is currently captured. */ static TkWindow *keyboardWinPtr = NULL; /* Current keyboard grab window. */ static Tcl_TimerToken mouseTimer; /* Handle to the latest mouse timer. */ static int mouseTimerSet = 0; /* 1 if the mouse timer is active. */ /* * Forward declarations of procedures used in this file. */ static void MouseTimerProc(ClientData clientData); /* *---------------------------------------------------------------------- * * TkWinGetModifierState -- * * Return the modifier state as of the last message. * * Results: * Returns the X modifier mask. * * Side effects: * None. * *---------------------------------------------------------------------- */ int TkWinGetModifierState(void) { int state = 0; if (GetKeyState(VK_SHIFT) & 0x8000) { state |= ShiftMask; } if (GetKeyState(VK_CONTROL) & 0x8000) { state |= ControlMask; } if (GetKeyState(VK_MENU) & 0x8000) { state |= ALT_MASK; } if (GetKeyState(VK_CAPITAL) & 0x0001) { state |= LockMask; } if (GetKeyState(VK_NUMLOCK) & 0x0001) { state |= Mod1Mask; } if (GetKeyState(VK_SCROLL) & 0x0001) { state |= Mod3Mask; } if (GetKeyState(VK_LBUTTON) & 0x8000) { state |= Button1Mask; } if (GetKeyState(VK_MBUTTON) & 0x8000) { state |= Button2Mask; } if (GetKeyState(VK_RBUTTON) & 0x8000) { state |= Button3Mask; } if (GetKeyState(VK_XBUTTON1) & 0x8000) { state |= Button4Mask; } if (GetKeyState(VK_XBUTTON2) & 0x8000) { state |= Button5Mask; } return state; } /* *---------------------------------------------------------------------- * * Tk_PointerEvent -- * * This procedure is called for each pointer-related event. It converts * the position to root coords and updates the global pointer state * machine. It also ensures that the mouse timer is scheduled. * * Results: * None. * * Side effects: * May queue events and change the grab state. * *---------------------------------------------------------------------- */ void Tk_PointerEvent( HWND hwnd, /* Window for coords, or NULL for the root * window. */ int x, int y) /* Coords relative to hwnd, or screen if hwnd * is NULL. */ { POINT pos; int state; Tk_Window tkwin; pos.x = x; pos.y = y; /* * Convert client coords to root coords if we were given a window. */ if (hwnd) { ClientToScreen(hwnd, &pos); } /* * If the mouse is captured, Windows will report all pointer events to the * capture window. So, we need to determine which window the mouse is * really over and change the event. Note that the computed hwnd may point * to a window not owned by Tk, or a toplevel decorative frame, so tkwin * can be NULL. */ if (captured || hwnd == NULL) { hwnd = WindowFromPoint(pos); } tkwin = Tk_HWNDToWindow(hwnd); state = TkWinGetModifierState(); Tk_UpdatePointer(tkwin, pos.x, pos.y, state); if ((captured || tkwin) && !mouseTimerSet) { mouseTimerSet = 1; mouseTimer = Tcl_CreateTimerHandler(MOUSE_TIMER_INTERVAL, MouseTimerProc, NULL); } } /* *---------------------------------------------------------------------- * * XGrabKeyboard -- * * Simulates a keyboard grab by setting the focus. * * Results: * Always returns GrabSuccess. * * Side effects: * Sets the keyboard focus to the specified window. * *---------------------------------------------------------------------- */ int XGrabKeyboard( Display *display, Window grab_window, Bool owner_events, int pointer_mode, int keyboard_mode, Time time) { keyboardWinPtr = TkWinGetWinPtr(grab_window); return GrabSuccess; } /* *---------------------------------------------------------------------- * * XUngrabKeyboard -- * * Releases the simulated keyboard grab. * * Results: * None. * * Side effects: * Sets the keyboard focus back to the value before the grab. * *---------------------------------------------------------------------- */ int XUngrabKeyboard( Display *display, Time time) { keyboardWinPtr = NULL; return Success; } /* *---------------------------------------------------------------------- * * MouseTimerProc -- * * Check the current mouse position and look for enter/leave events. * * Results: * None. * * Side effects: * May schedule a new timer and/or generate enter/leave events. * *---------------------------------------------------------------------- */ void MouseTimerProc( ClientData clientData) { POINT pos; mouseTimerSet = 0; GetCursorPos(&pos); Tk_PointerEvent(NULL, pos.x, pos.y); } /* *---------------------------------------------------------------------- * * TkWinCancelMouseTimer -- * * If the mouse timer is set, cancel it. * * Results: * None. * * Side effects: * May cancel the mouse timer. * *---------------------------------------------------------------------- */ void TkWinCancelMouseTimer(void) { if (mouseTimerSet) { Tcl_DeleteTimerHandler(mouseTimer); mouseTimerSet = 0; } } /* *---------------------------------------------------------------------- * * TkGetPointerCoords -- * * Fetch the position of the mouse pointer. * * Results: * *xPtr and *yPtr are filled in with the root coordinates of the mouse * pointer for the display. * * Side effects: * None. * *---------------------------------------------------------------------- */ void TkGetPointerCoords( Tk_Window tkwin, /* Window that identifies screen on which * lookup is to be done. */ int *xPtr, int *yPtr) /* Store pointer coordinates here. */ { POINT point; GetCursorPos(&point); *xPtr = point.x; *yPtr = point.y; } /* *---------------------------------------------------------------------- * * XQueryPointer -- * * Check the current state of the mouse. This is not a complete * implementation of this function. It only computes the root coordinates * and the current mask. * * Results: * Sets root_x_return, root_y_return, and mask_return. Returns true on * success. * * Side effects: * None. * *---------------------------------------------------------------------- */ Bool XQueryPointer( Display *display, Window w, Window *root_return, Window *child_return, int *root_x_return, int *root_y_return, int *win_x_return, int *win_y_return, unsigned int *mask_return) { LastKnownRequestProcessed(display)++; TkGetPointerCoords(NULL, root_x_return, root_y_return); *mask_return = TkWinGetModifierState(); return True; } /* *---------------------------------------------------------------------- * * XWarpPointer, TkpWarpPointer -- * * Move pointer to new location. Note that implementation of XWarpPointer * is incomplete. * * Results: * None. * * Side effects: * Mouse pointer changes position on screen. * *---------------------------------------------------------------------- */ /* * TkSetCursorPos is a helper function replacing SetCursorPos since this * latter Windows function appears to have been broken by Microsoft * since Win10 Falls Creator Update - See ticket [69b48f427e] along with * several other Internet reports about this breakage. */ void TkSetCursorPos( int x, int y) { INPUT input; int xscreen = (int)(GetSystemMetrics(SM_CXSCREEN) - 1); int yscreen = (int)(GetSystemMetrics(SM_CYSCREEN) - 1); /* * A multi-screen system may have different logical pixels/inch, with * Windows applying behind-the-scenes scaling on secondary screens. * Don't try and emulate that, instead fall back to SetCursor if the * requested position is off the primary screen. */ if ( x < 0 || x > xscreen || y < 0 || y > yscreen ) { SetCursorPos(x, y); return; } input.type = INPUT_MOUSE; input.mi.dx = (x * 65535 + xscreen/2) / xscreen; input.mi.dy = (y * 65535 + yscreen/2) / yscreen; /* * Horrible workaround here. There is a bug on Win 10: when warping to * pixel (x = 0, y = 0) the SendInput() below just does not move the * mouse pointer. However, as soon as dx or dy is non zero it moves as * expected. Given the scaling factor of 65535 (see above), * (dx = 1 , dy = 0) still means pixel (x = 0, y = 0). * See ticket [69b48f427e]. */ if (input.mi.dx == 0 && input.mi.dy == 0) { input.mi.dx = 1; } input.mi.mouseData = 0; input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE; input.mi.time = 0; input.mi.dwExtraInfo = 0; SendInput(1, &input, sizeof(input)); } int XWarpPointer( Display *display, Window src_w, Window dest_w, int src_x, int src_y, unsigned int src_width, unsigned int src_height, int dest_x, int dest_y) { RECT r; GetWindowRect(Tk_GetHWND(dest_w), &r); TkSetCursorPos(r.left+dest_x, r.top+dest_y); return Success; } void TkpWarpPointer( TkDisplay *dispPtr) { if (dispPtr->warpWindow) { RECT r; GetWindowRect(Tk_GetHWND(Tk_WindowId(dispPtr->warpWindow)), &r); TkSetCursorPos(r.left + dispPtr->warpX, r.top + dispPtr->warpY); } else { TkSetCursorPos(dispPtr->warpX, dispPtr->warpY); } } /* *---------------------------------------------------------------------- * * XGetInputFocus -- * * Retrieves the current keyboard focus window. * * Results: * Returns the current focus window. * * Side effects: * None. * *---------------------------------------------------------------------- */ int XGetInputFocus( Display *display, Window *focus_return, int *revert_to_return) { Tk_Window tkwin = Tk_HWNDToWindow(GetFocus()); *focus_return = tkwin ? Tk_WindowId(tkwin) : None; *revert_to_return = RevertToParent; LastKnownRequestProcessed(display)++; return Success; } /* *---------------------------------------------------------------------- * * XSetInputFocus -- * * Set the current focus window. * * Results: * None. * * Side effects: * Changes the keyboard focus and causes the selected window to * be activated. * *---------------------------------------------------------------------- */ int XSetInputFocus( Display *display, Window focus, int revert_to, Time time) { LastKnownRequestProcessed(display)++; if (focus != None) { SetFocus(Tk_GetHWND(focus)); } return Success; } /* *---------------------------------------------------------------------- * * TkpChangeFocus -- * * This procedure is invoked to move the system focus from one window to * another. * * Results: * The return value is the serial number of the command that changed the * focus. It may be needed by the caller to filter out focus change * events that were queued before the command. If the procedure doesn't * actually change the focus then it returns 0. * * Side effects: * The official Windows focus window changes; the application's focus * window isn't changed by this procedure. * *---------------------------------------------------------------------- */ int TkpChangeFocus( TkWindow *winPtr, /* Window that is to receive the X focus. */ int force) /* Non-zero means claim the focus even if it * didn't originally belong to topLevelPtr's * application. */ { TkDisplay *dispPtr = winPtr->dispPtr; Window focusWindow; int dummy, serial; TkWindow *winPtr2; if (!force) { XGetInputFocus(dispPtr->display, &focusWindow, &dummy); winPtr2 = (TkWindow *) Tk_IdToWindow(dispPtr->display, focusWindow); if ((winPtr2 == NULL) || (winPtr2->mainPtr != winPtr->mainPtr)) { return 0; } } if (winPtr->window == None) { Tcl_Panic("ChangeXFocus got null X window"); } /* * Change the foreground window so the focus window is raised to the top * of the system stacking order and gets the keyboard focus. */ if (force) { TkWinSetForegroundWindow(winPtr); } XSetInputFocus(dispPtr->display, winPtr->window, RevertToParent, CurrentTime); /* * Remember the current serial number for the X server and issue a dummy * server request. This marks the position at which we changed the focus, * so we can distinguish FocusIn and FocusOut events on either side of the * mark. */ serial = NextRequest(winPtr->display); XNoOp(winPtr->display); return serial; } /* *---------------------------------------------------------------------- * * TkpSetCapture -- * * This function captures the mouse so that all future events will be * reported to this window, even if the mouse is outside the window. If * the specified window is NULL, then the mouse is released. * * Results: * None. * * Side effects: * Sets the capture flag and captures the mouse. * *---------------------------------------------------------------------- */ void TkpSetCapture( TkWindow *winPtr) /* Capture window, or NULL. */ { if (winPtr) { SetCapture(Tk_GetHWND(Tk_WindowId(winPtr))); captured = 1; } else { captured = 0; ReleaseCapture(); } } /* *---------------------------------------------------------------------- * * TkpGetCapture -- * * This function requests which window is capturing the mouse. * * Results: * The return value is a pointer to the capture window, if there is * one, otherwise it is NULL. * * Side effects: * None. * *---------------------------------------------------------------------- */ Tk_Window TkpGetCapture(void) { return Tk_HWNDToWindow(GetCapture()); } /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */