📄 tkgrab.c
字号:
* 3. If a button press or release occurs outside the window where * the first button was pressed, retarget the event so it's reported * to the window where the first button was pressed. * 4. If the last button is released in a window different than where * the first button was pressed, generate Enter/Leave events to * move the mouse from the button window to its current window. * 5. If the grab is set at a time when a button is already down, or * if the window where the button was pressed was deleted, then * dispPtr->buttonWinPtr will stay NULL. Just forget about the * auto-grab for the button press; events will go to whatever * window contains the pointer. If this window isn't in the grab * tree then redirect events to the grab window. * 6. When a button is pressed during a local grab, the X server sets * a grab of its own, since it doesn't even know about our local * grab. This causes enter and leave events no longer to be * generated in the same way as for global grabs. To eliminate this * problem, set a temporary global grab when the first button goes * down and release it when the last button comes up. */ if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) { winPtr2 = dispPtr->buttonWinPtr; if (winPtr2 == NULL) { if (outsideGrabTree) { winPtr2 = dispPtr->grabWinPtr; /* Note 5. */ } else { winPtr2 = winPtr; /* Note 5. */ } } if (eventPtr->type == ButtonPress) { if ((eventPtr->xbutton.state & ALL_BUTTONS) == 0) { if (outsideGrabTree) { TkChangeEventWindow(eventPtr, dispPtr->grabWinPtr); Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); return 0; /* Note 2. */ } if (!(dispPtr->grabFlags & GRAB_GLOBAL)) { /* Note 6. */ serial = NextRequest(dispPtr->display); if (XGrabPointer(dispPtr->display, dispPtr->grabWinPtr->window, True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == 0) { EatGrabEvents(dispPtr, serial); if (XGrabKeyboard(dispPtr->display, winPtr->window, False, GrabModeAsync, GrabModeAsync, CurrentTime) == 0) { dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; } else { XUngrabPointer(dispPtr->display, CurrentTime); } } } dispPtr->buttonWinPtr = winPtr; return 1; } } else { if ((eventPtr->xbutton.state & ALL_BUTTONS) == buttonStates[eventPtr->xbutton.button - Button1]) { ReleaseButtonGrab(dispPtr); /* Note 4. */ } } if (winPtr2 != winPtr) { TkChangeEventWindow(eventPtr, winPtr2); Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); return 0; /* Note 3. */ } } return 1;}/* *---------------------------------------------------------------------- * * TkChangeEventWindow -- * * Given an event and a new window to which the event should be * retargeted, modify fields of the event so that the event is * properly retargeted to the new window. * * Results: * The following fields of eventPtr are modified: window, * subwindow, x, y, same_screen. * * Side effects: * None. * *---------------------------------------------------------------------- */voidTkChangeEventWindow(eventPtr, winPtr) register XEvent *eventPtr; /* Event to retarget. Must have * type ButtonPress, ButtonRelease, KeyPress, * KeyRelease, MotionNotify, EnterNotify, * or LeaveNotify. */ TkWindow *winPtr; /* New target window for event. */{ int x, y, sameScreen, bd; register TkWindow *childPtr; eventPtr->xmotion.window = Tk_WindowId(winPtr); if (eventPtr->xmotion.root == RootWindow(winPtr->display, winPtr->screenNum)) { Tk_GetRootCoords((Tk_Window) winPtr, &x, &y); eventPtr->xmotion.x = eventPtr->xmotion.x_root - x; eventPtr->xmotion.y = eventPtr->xmotion.y_root - y; eventPtr->xmotion.subwindow = None; for (childPtr = winPtr->childList; childPtr != NULL; childPtr = childPtr->nextPtr) { if (childPtr->flags & TK_TOP_LEVEL) { continue; } x = eventPtr->xmotion.x - childPtr->changes.x; y = eventPtr->xmotion.y - childPtr->changes.y; bd = childPtr->changes.border_width; if ((x >= -bd) && (y >= -bd) && (x < (childPtr->changes.width + bd)) && (y < (childPtr->changes.height + bd))) { eventPtr->xmotion.subwindow = childPtr->window; } } sameScreen = 1; } else { eventPtr->xmotion.x = 0; eventPtr->xmotion.y = 0; eventPtr->xmotion.subwindow = None; sameScreen = 0; } if (eventPtr->type == MotionNotify) { eventPtr->xmotion.same_screen = sameScreen; } else { eventPtr->xbutton.same_screen = sameScreen; }}/* *---------------------------------------------------------------------- * * TkInOutEvents -- * * This procedure synthesizes EnterNotify and LeaveNotify events * to correctly transfer the pointer from one window to another. * It can also be used to generate FocusIn and FocusOut events * to move the input focus. * * Results: * None. * * Side effects: * Synthesized events may be pushed back onto the event queue. * The event pointed to by eventPtr is modified. * *---------------------------------------------------------------------- */voidTkInOutEvents(eventPtr, sourcePtr, destPtr, leaveType, enterType, position) XEvent *eventPtr; /* A template X event. Must have all fields * properly set except for type, window, * subwindow, x, y, detail, and same_screen * (Not all of these fields are valid for * FocusIn/FocusOut events; x_root and y_root * must be valid for Enter/Leave events, even * though x and y needn't be valid). */ TkWindow *sourcePtr; /* Window that used to have the pointer or * focus (NULL means it was not in a window * managed by this process). */ TkWindow *destPtr; /* Window that is to end up with the pointer * or focus (NULL means it's not one managed * by this process). */ int leaveType; /* Type of events to generate for windows * being left (LeaveNotify or FocusOut). 0 * means don't generate leave events. */ int enterType; /* Type of events to generate for windows * being entered (EnterNotify or FocusIn). 0 * means don't generate enter events. */ Tcl_QueuePosition position; /* Position at which events are added to * the system event queue. */{ register TkWindow *winPtr; int upLevels, downLevels, i, j, focus; /* * There are four possible cases to deal with: * * 1. SourcePtr and destPtr are the same. There's nothing to do in * this case. * 2. SourcePtr is an ancestor of destPtr in the same top-level * window. Must generate events down the window tree from source * to dest. * 3. DestPtr is an ancestor of sourcePtr in the same top-level * window. Must generate events up the window tree from sourcePtr * to destPtr. * 4. All other cases. Must first generate events up the window tree * from sourcePtr to its top-level, then down from destPtr's * top-level to destPtr. This form is called "non-linear." * * The call to FindCommonAncestor separates these four cases and decides * how many levels up and down events have to be generated for. */ if (sourcePtr == destPtr) { return; } if ((leaveType == FocusOut) || (enterType == FocusIn)) { focus = 1; } else { focus = 0; } FindCommonAncestor(sourcePtr, destPtr, &upLevels, &downLevels); /* * Generate enter/leave events and add them to the grab event queue. */#define QUEUE(w, t, d) \ if (w->window != None) { \ eventPtr->type = t; \ if (focus) { \ eventPtr->xfocus.window = w->window; \ eventPtr->xfocus.detail = d; \ } else { \ eventPtr->xcrossing.detail = d; \ TkChangeEventWindow(eventPtr, w); \ } \ Tk_QueueWindowEvent(eventPtr, position); \ } if (downLevels == 0) { /* * SourcePtr is an inferior of destPtr. */ if (leaveType != 0) { QUEUE(sourcePtr, leaveType, NotifyAncestor); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { QUEUE(winPtr, leaveType, NotifyVirtual); } } if ((enterType != 0) && (destPtr != NULL)) { QUEUE(destPtr, enterType, NotifyInferior); } } else if (upLevels == 0) { /* * DestPtr is an inferior of sourcePtr. */ if ((leaveType != 0) && (sourcePtr != NULL)) { QUEUE(sourcePtr, leaveType, NotifyInferior); } if (enterType != 0) { for (i = downLevels-1; i > 0; i--) { for (winPtr = destPtr->parentPtr, j = 1; j < i; winPtr = winPtr->parentPtr, j++) { } QUEUE(winPtr, enterType, NotifyVirtual); } if (destPtr != NULL) { QUEUE(destPtr, enterType, NotifyAncestor); } } } else { /* * Non-linear: neither window is an inferior of the other. */ if (leaveType != 0) { QUEUE(sourcePtr, leaveType, NotifyNonlinear); for (winPtr = sourcePtr->parentPtr, i = upLevels-1; i > 0; winPtr = winPtr->parentPtr, i--) { QUEUE(winPtr, leaveType, NotifyNonlinearVirtual); } } if (enterType != 0) { for (i = downLevels-1; i > 0; i--) { for (winPtr = destPtr->parentPtr, j = 1; j < i; winPtr = winPtr->parentPtr, j++) { } QUEUE(winPtr, enterType, NotifyNonlinearVirtual); } if (destPtr != NULL) { QUEUE(destPtr, enterType, NotifyNonlinear); } } }}/* *---------------------------------------------------------------------- * * MovePointer2 -- * * This procedure synthesizes EnterNotify and LeaveNotify events * to correctly transfer the pointer from one window to another. * It is different from TkInOutEvents in that no template X event * needs to be supplied; this procedure generates the template * event and calls TkInOutEvents. * * Results: * None. * * Side effects: * Synthesized events may be pushed back onto the event queue. * *---------------------------------------------------------------------- */static voidMovePointer2(sourcePtr, destPtr, mode, leaveEvents, enterEvents) TkWindow *sourcePtr; /* Window currently containing pointer (NULL * means it's not one managed by this * process). */ TkWindow *destPtr; /* Window that is to end up containing the * pointer (NULL means it's not one managed * by this process). */ int mode; /* Mode for enter/leave events, such as * NotifyNormal or NotifyUngrab. */ int leaveEvents; /* Non-zero means generate leave events for the * windows being left. Zero means don't * generate leave events. */ int enterEvents; /* Non-zero means generate enter events for the * windows being entered. Zero means don't * generate enter events. */{ XEvent event; Window dummy1, dummy2; int dummy3, dummy4; TkWindow *winPtr; winPtr = sourcePtr; if ((winPtr == NULL) || (winPtr->window == None)) { winPtr = destPtr; if ((winPtr == NULL) || (winPtr->window == None)) { return; } } event.xcrossing.serial = LastKnownRequestProcessed( winPtr->display); event.xcrossing.send_event = GENERATED_EVENT_MAGIC; event.xcrossing.display = winPtr->display; event.xcrossing.root = RootWindow(winPtr->display, winPtr->screenNum); event.xcrossing.time = TkCurrentTime(winPtr->dispPtr); XQueryPointer(winPtr->display, winPtr->window, &dummy1, &dummy2, &event.xcrossing.x_root, &event.xcrossing.y_root, &dummy3, &dummy4, &event.xcrossing.state); event.xcrossing.mode = mode; event.xcrossing.focus = False; TkInOutEvents(&event, sourcePtr, destPtr, (leaveEvents) ? LeaveNotify : 0, (enterEvents) ? EnterNotify : 0, TCL_QUEUE_MARK);}/* *---------------------------------------------------------------------- * * TkGrabDeadWindow -- * * This procedure is invoked whenever a window is deleted, so that * grab-related cleanup can be performed. * * Results: * None. * * Side effects: * Various cleanups happen, such as generating events to move the * pointer back to its "natural" window as if an ungrab had been * done. See the code. * *---------------------------------------------------------------------- */voidTkGrabDeadWindow(winPtr) register TkWindow *winPtr; /* Window that is in the process * of being deleted. */{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -