/* * tkWinPointer.c -- * * Windows specific mouse tracking code. * * Copyright (c) 1995 Sun Microsystems, Inc. * * See the file "license.terms" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * SCCS: @(#) tkWinPointer.c 1.14 96/02/15 18:56:05 */ #include "tkWinInt.h" /* * Check for enter/leave events every MOUSE_TIMER_INTERVAL milliseconds. */ #define MOUSE_TIMER_INTERVAL 250 /* * Mask that selects any of the state bits corresponding to buttons, * plus masks that select individual buttons' bits: */ #define ALL_BUTTONS \ (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask) static unsigned int buttonStates[] = { Button1Mask, Button2Mask, Button3Mask, Button4Mask, Button5Mask }; /* * Declarations of static variables used in the grab module. */ static int captured; /* 1 if mouse events outside of Tk windows * will be reported, else 0. */ static TkWindow *grabWinPtr; /* Window that defines the top of the grab * tree in a global grab. */ static TkWindow *keyboardWinPtr;/* Current keyboard grab window. */ static TkWindow *restrictWinPtr; /* Window to which all mouse events will be reported. */ /* * Declarations of static variables used in mouse position tracking. */ static POINT lastMousePos; /* Last known mouse position. */ static HWND lastMouseWindow; /* Last known mouse window. */ static TkWindow *lastMouseWinPtr; /* Last window mouse was seen in. Used to * detect Enter/Leave events. */ static Tcl_TimerToken mouseTimer; /* Handle to the latest mouse timer. */ static int mouseTimerSet; /* Non-zero if the mouse timer is active. */ /* * Forward declarations of procedures used in this file. */ static void InitializeCrossingEvent _ANSI_ARGS_(( XEvent* eventPtr, TkWindow *winPtr, long x, long y)); static void MouseTimerProc _ANSI_ARGS_((ClientData clientData)); static int UpdateMousePosition _ANSI_ARGS_((HWND hwnd, TkWindow *winPtr, long x, long y)); /* *---------------------------------------------------------------------- * * TkWinPointerInit -- * * Initialize the mouse pointer module. * * Results: * None. * * Side effects: * Initializes various static variables. * *---------------------------------------------------------------------- */ void TkWinPointerInit() { captured = 0; grabWinPtr = NULL; keyboardWinPtr = NULL; restrictWinPtr = NULL; mouseTimerSet = 0; GetCursorPos(&lastMousePos); lastMouseWindow = WindowFromPoint(lastMousePos); lastMouseWinPtr = NULL; } /* *---------------------------------------------------------------------- * * TkWinPointerDeadWindow -- * * Clean up pointer module state when a window is destroyed. * * Results: * None. * * Side effects: * May change the grab module settings. * *---------------------------------------------------------------------- */ void TkWinPointerDeadWindow(winPtr) TkWindow *winPtr; { if (winPtr == lastMouseWinPtr) { lastMouseWinPtr = NULL; } if (winPtr == grabWinPtr) { grabWinPtr = NULL; } if (winPtr == restrictWinPtr) { restrictWinPtr = NULL; } if (!(restrictWinPtr || grabWinPtr)) { captured = 0; ReleaseCapture(); } } /* *---------------------------------------------------------------------- * * TkWinPointerEvent -- * * This procedure is called for each pointer-related event, * before the event is queued. It simulates X style automatic * grabs so that button release events are not lost. It also * updates the pointer position so enter/leave events will be * correctly generated. * * Results: * Returns 0 if the event should be discarded. * * Side effects: * Changes the current mouse capture window. * *---------------------------------------------------------------------- */ void TkWinPointerEvent(eventPtr, winPtr) XEvent *eventPtr; /* Event to process */ TkWindow *winPtr; /* Window to which event was reported. */ { POINT pos; HWND hwnd; TkWinDrawable *twdPtr; /* * 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 winPtr can be NULL. */ if (captured) { pos.x = eventPtr->xmotion.x_root; pos.y = eventPtr->xmotion.y_root; hwnd = WindowFromPoint(pos); twdPtr = TkWinGetDrawableFromHandle(hwnd); if (twdPtr && (twdPtr->type == TWD_WINDOW)) { winPtr = TkWinGetWinPtr(twdPtr); } else { winPtr = NULL; } } else { hwnd = TkWinGetHWND(Tk_WindowId(winPtr)); } switch (eventPtr->type) { case MotionNotify: /* * If updating the mouse position caused an enter or leave * event to be generated, we discard the motion event. */ if (UpdateMousePosition(hwnd, winPtr, eventPtr->xmotion.x_root, eventPtr->xmotion.y_root)) { return; } break; case ButtonPress: /* * Set mouse capture and the restrict window if we are * currently unrestricted. However, If this is not the * first button pressed and we are already grabbed, do not * change anything. */ if (!restrictWinPtr) { if (!grabWinPtr) { /* * Mouse was ungrabbed, so set a button grab. */ restrictWinPtr = winPtr; captured = 1; SetCapture(hwnd); } else if ((eventPtr->xmotion.state & ALL_BUTTONS) == 0) { /* * Mouse was grabbed, but not in a button grab. * Make sure the new restrict window is inside the * current grab tree. */ if (TkPositionInTree(winPtr, grabWinPtr) == TK_GRAB_IN_TREE) { restrictWinPtr = winPtr; } else { restrictWinPtr = grabWinPtr; } captured = 1; SetCapture(TkWinGetHWND(Tk_WindowId(restrictWinPtr))); } } break; case ButtonRelease: /* * Release the mouse capture when the last button is * released and we aren't in a global grab. */ if ((eventPtr->xbutton.state & ALL_BUTTONS) == buttonStates[eventPtr->xbutton.button - Button1]) { if (!grabWinPtr) { captured = 0; ReleaseCapture(); } /* * If we are releasing a restrict window, then we need * to send the button event followed by mouse motion from * the restrict window the the current mouse position. */ if (restrictWinPtr) { if (Tk_WindowId(restrictWinPtr) != eventPtr->xany.window) { TkChangeEventWindow(eventPtr, restrictWinPtr); } Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL); lastMouseWinPtr = restrictWinPtr; restrictWinPtr = NULL; UpdateMousePosition(hwnd, winPtr, eventPtr->xmotion.x_root, eventPtr->xmotion.y_root); return; } } break; } /* * If a restrict window is set, make sure the pointer event is reported * relative to that window. Otherwise, if a global grab is in effect * then events outside of window managed by Tk should be reported to the * grab window. */ if (restrictWinPtr) { winPtr = restrictWinPtr; } else if (grabWinPtr && !winPtr) { winPtr = grabWinPtr; } /* * If the target window has changed, update the coordinates in the event. */ if (winPtr && Tk_WindowId(winPtr) != eventPtr->xany.window) { TkChangeEventWindow(eventPtr, winPtr); } Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_TAIL); } /* *---------------------------------------------------------------------- * * XGrabPointer -- * * Capture the mouse so event are reported outside of toplevels. * Note that this is a very limited implementation that only * supports GrabModeAsync and owner_events True. * * Results: * Always returns GrabSuccess. * * Side effects: * Turns on mouse capture, sets the global grab pointer, and * clears any window restrictions. * *---------------------------------------------------------------------- */ int XGrabPointer(display, grab_window, owner_events, event_mask, pointer_mode, keyboard_mode, confine_to, cursor, time) Display* display; Window grab_window; Bool owner_events; unsigned int event_mask; int pointer_mode; int keyboard_mode; Window confine_to; Cursor cursor; Time time; { HWND hwnd = TkWinGetHWND(grab_window); grabWinPtr = TkWinGetWinPtr(grab_window); captured = 1; restrictWinPtr = NULL; SetCapture(hwnd); if (TkPositionInTree(lastMouseWinPtr, grabWinPtr) == TK_GRAB_IN_TREE) { TkWinUpdateCursor(lastMouseWinPtr); } else { TkWinUpdateCursor(grabWinPtr); } return GrabSuccess; } /* *---------------------------------------------------------------------- * * XUngrabPointer -- * * Release the current grab. * * Results: * None. * * Side effects: * Releases the mouse capture. * *---------------------------------------------------------------------- */ void XUngrabPointer(display, time) Display* display; Time time; { captured = 0; grabWinPtr = NULL; restrictWinPtr = NULL; ReleaseCapture(); TkWinUpdateCursor(lastMouseWinPtr); } /* *---------------------------------------------------------------------- * * 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, grab_window, owner_events, pointer_mode, keyboard_mode, time) 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. * *---------------------------------------------------------------------- */ void XUngrabKeyboard(display, time) Display* display; Time time; { keyboardWinPtr = NULL; } /* *---------------------------------------------------------------------- * * InitializeCrossingEvent -- * * Initializes the common fields for enter/leave events. * * Results: * None. * * Side effects: * Fills in the specified event structure. * *---------------------------------------------------------------------- */ static void InitializeCrossingEvent(eventPtr, winPtr, x, y) XEvent* eventPtr; /* Event structure to initialize. */ TkWindow *winPtr; /* Window to make event relative to. */ long x, y; /* Root coords of event. */ { eventPtr->xcrossing.serial = LastKnownRequestProcessed(winPtr->display); eventPtr->xcrossing.send_event = 0; eventPtr->xcrossing.display = winPtr->display; eventPtr->xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum); eventPtr->xcrossing.time = TkCurrentTime(winPtr->dispPtr); eventPtr->xcrossing.x_root = x; eventPtr->xcrossing.y_root = y; eventPtr->xcrossing.state = TkWinGetModifierState(WM_MOUSEMOVE, 0, 0); eventPtr->xcrossing.mode = NotifyNormal; eventPtr->xcrossing.focus = False; } /* *---------------------------------------------------------------------- * * UpdateMousePosition -- * * Update the current mouse window and position, and generate * any enter/leave events that are needed. Will schedule a * timer to check the mouse position if the pointer is still * inside a Tk window. * * Results: * Returns 1 if enter/leave events were generated. * * Side effects: * May generate enter/leave events and schedule a timer. * *---------------------------------------------------------------------- */ int UpdateMousePosition(hwnd, winPtr, x, y) HWND hwnd; /* current mouse window */ TkWindow *winPtr; /* current Tk window (or NULL) */ long x; /* current mouse position in */ long y; /* root coordinates */ { int crossed = 0; /* 1 if mouse crossed a window boundary */ TkWindow *cursorWinPtr; if (winPtr != lastMouseWinPtr) { if (restrictWinPtr) { int newPos, oldPos; newPos = TkPositionInTree(winPtr, restrictWinPtr); oldPos = TkPositionInTree(lastMouseWinPtr, restrictWinPtr); /* * Check if the mouse crossed into or out of the restrict * window. If so, we need to generate an Enter or Leave event. */ if ((newPos != oldPos) && ((newPos == TK_GRAB_IN_TREE) || (oldPos == TK_GRAB_IN_TREE))) { XEvent event; InitializeCrossingEvent(&event, restrictWinPtr, x, y); if (newPos == TK_GRAB_IN_TREE) { event.type = EnterNotify; } else { event.type = LeaveNotify; } if ((oldPos == TK_GRAB_ANCESTOR) || (newPos == TK_GRAB_ANCESTOR)) { event.xcrossing.detail = NotifyAncestor; } else { event.xcrossing.detail = NotifyVirtual; } TkChangeEventWindow(&event, restrictWinPtr); Tk_QueueWindowEvent(&event, TCL_QUEUE_TAIL); } } else { TkWindow *targetPtr; if ((lastMouseWinPtr == NULL) || (lastMouseWinPtr->window == None)) { targetPtr = winPtr; } else { targetPtr = lastMouseWinPtr; } if (targetPtr && (targetPtr->window != None)) { XEvent event; /* * Generate appropriate Enter/Leave events. */ InitializeCrossingEvent(&event, targetPtr, x, y); TkInOutEvents(&event, lastMouseWinPtr, winPtr, LeaveNotify, EnterNotify, TCL_QUEUE_TAIL); if (TkPositionInTree(winPtr, grabWinPtr) == TK_GRAB_IN_TREE) { cursorWinPtr = winPtr; } else { cursorWinPtr = grabWinPtr; } crossed = 1; } } lastMouseWinPtr = winPtr; } /* * Make sure the cursor reflects the current mouse position. */ if (restrictWinPtr) { cursorWinPtr = restrictWinPtr; } else if (grabWinPtr) { cursorWinPtr = (TkPositionInTree(winPtr, grabWinPtr) == TK_GRAB_IN_TREE) ? winPtr : grabWinPtr; } else { cursorWinPtr = winPtr; } TkWinUpdateCursor(cursorWinPtr); lastMouseWindow = hwnd; lastMousePos.x = x; lastMousePos.y = y; /* * Ensure the mouse timer is set if we are still inside a Tk window. */ if (winPtr != NULL && !mouseTimerSet) { mouseTimerSet = 1; mouseTimer = Tcl_CreateTimerHandler(MOUSE_TIMER_INTERVAL, MouseTimerProc, NULL); } return crossed; } /* *---------------------------------------------------------------------- * * 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 clientData; { POINT pos; HWND hwnd; TkWinDrawable *twdPtr; TkWindow *winPtr; mouseTimerSet = 0; /* * Get the current mouse position and window. Don't do anything * if the mouse hasn't moved since the last time we looked. */ GetCursorPos(&pos); if (pos.x == lastMousePos.y && pos.y == lastMousePos.y) { hwnd = lastMouseWindow; } else { hwnd = WindowFromPoint(pos); } /* * Check to see if the current window is managed by Tk. */ if (hwnd == lastMouseWindow) { winPtr = lastMouseWinPtr; } else { twdPtr = TkWinGetDrawableFromHandle(hwnd); if (twdPtr && (twdPtr->type == TWD_WINDOW)) { winPtr = TkWinGetWinPtr(twdPtr); } else { winPtr = NULL; } } /* * Generate enter/leave events. */ UpdateMousePosition(hwnd, winPtr, pos.x, pos.y); } /* *---------------------------------------------------------------------- * * 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(tkwin, xPtr, yPtr) Tk_Window tkwin; /* Window that identifies screen on which * lookup is to be done. */ int *xPtr, *yPtr; /* Store pointer coordinates here. */ { DWORD msgPos; POINTS rootPoint; msgPos = GetMessagePos(); rootPoint = MAKEPOINTS(msgPos); *xPtr = rootPoint.x; *yPtr = rootPoint.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, w, root_return, child_return, root_x_return, root_y_return, win_x_return, win_y_return, mask_return) 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; { TkGetPointerCoords(NULL, root_x_return, root_y_return); *mask_return = TkWinGetModifierState(WM_MOUSEMOVE, 0, 0); return True; } /* *---------------------------------------------------------------------- * * XGetInputFocus -- * * Retrieves the current keyboard focus window. * * Results: * Returns the current focus window. * * Side effects: * None. * *---------------------------------------------------------------------- */ void XGetInputFocus(display, focus_return, revert_to_return) Display *display; Window *focus_return; int *revert_to_return; { HWND hwnd = GetFocus(); TkWinDrawable *twdPtr = TkWinGetDrawableFromHandle(hwnd); /* * The focus window may be a Tk window or a window manager decorative * frame. */ if (twdPtr) { *focus_return = Tk_WindowId(TkWinGetWinPtr(twdPtr)); } else { *focus_return = NULL; } *revert_to_return = RevertToParent; } /* *---------------------------------------------------------------------- * * XSetInputFocus -- * * Set the current focus window. * * Results: * None. * * Side effects: * Changes the keyboard focus and causes the selected window to * be activated. * *---------------------------------------------------------------------- */ void XSetInputFocus(display, focus, revert_to, time) Display* display; Window focus; int revert_to; Time time; { HWND hwnd = TkWinGetHWND(focus); SetFocus(hwnd); } /* *---------------------------------------------------------------------- * * XDefineCursor -- * * This function is called to update the cursor on a window. * Since the mouse might be in the specified window, we need to * check the specified window against the current mouse position * and grab state. * * Results: * None. * * Side effects: * May update the cursor. * *---------------------------------------------------------------------- */ void XDefineCursor(display, w, cursor) Display* display; Window w; Cursor cursor; { TkWindow *winPtr = TkWinGetWinPtr(w); if (restrictWinPtr) { /* * If there is a restrict window, then we only update the cursor * if the restrict window is the window being modified. */ if (winPtr == restrictWinPtr) { goto update; } } else if (grabWinPtr) { /* * If a grab is in effect, then we only update the cursor if the mouse * pointer is outside the grab tree and the specified window is the * grab window, or the pointer is inside the grab tree and the * specified window is also the pointer window. */ if (TkPositionInTree(lastMouseWinPtr, grabWinPtr) == TK_GRAB_IN_TREE) { if (winPtr == lastMouseWinPtr) { goto update; } } else if (winPtr == grabWinPtr) { goto update; } } else { /* * Otherwise, we only update the cursor if the specified window * contains the mouse pointer. */ if (winPtr == lastMouseWinPtr) { goto update; } } return; update: TkWinUpdateCursor(winPtr); }