📄 tkgrab.c
字号:
* see the button-up event; and (b) it allows us to track mouse * motion among all of the windows of this application. */ dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL); XQueryPointer(dispPtr->display, winPtr->window, &dummy1, &dummy2, &dummy3, &dummy4, &dummy5, &dummy6, &state); if ((state & ALL_BUTTONS) != 0) { dispPtr->grabFlags |= GRAB_TEMP_GLOBAL; goto setGlobalGrab; } } else { dispPtr->grabFlags |= GRAB_GLOBAL; setGlobalGrab: /* * Tricky point: must ungrab before grabbing. This is needed * in case there is a button auto-grab already in effect. If * there is, and the mouse has moved to a different window, X * won't generate enter and leave events to move the mouse if * we grab without ungrabbing. */ XUngrabPointer(dispPtr->display, CurrentTime); serial = NextRequest(dispPtr->display); /* * Another tricky point: there are races with some window * managers that can cause grabs to fail because the window * manager hasn't released its grab quickly enough. To work * around this problem, retry a few times after AlreadyGrabbed * errors to give the grab release enough time to register with * the server. */ grabResult = 0; /* Needed only to prevent gcc * compiler warnings. */ for (numTries = 0; numTries < 10; numTries++) { grabResult = XGrabPointer(dispPtr->display, winPtr->window, True, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask |PointerMotionMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime); if (grabResult != AlreadyGrabbed) { break; } Tcl_Sleep(100); } if (grabResult != 0) { grabError: if (grabResult == GrabNotViewable) { interp->result = "grab failed: window not viewable"; } else if (grabResult == AlreadyGrabbed) { goto alreadyGrabbed; } else if (grabResult == GrabFrozen) { interp->result = "grab failed: keyboard or pointer frozen"; } else if (grabResult == GrabInvalidTime) { interp->result = "grab failed: invalid time"; } else { char msg[100]; sprintf(msg, "grab failed for unknown reason (code %d)", grabResult); Tcl_AppendResult(interp, msg, (char *) NULL); } return TCL_ERROR; } grabResult = XGrabKeyboard(dispPtr->display, Tk_WindowId(tkwin), False, GrabModeAsync, GrabModeAsync, CurrentTime); if (grabResult != 0) { XUngrabPointer(dispPtr->display, CurrentTime); goto grabError; } /* * Eat up any grab-related events generated by the server for the * grab. There are several reasons for doing this: * * 1. We have to synthesize the events for local grabs anyway, since * the server doesn't participate in them. * 2. The server doesn't always generate the right events for global * grabs (e.g. it generates events even if the current window is * in the grab tree, which we don't want). * 3. We want all the grab-related events to be processed immediately * (before other events that are already queued); events coming * from the server will be in the wrong place, but events we * synthesize here will go to the front of the queue. */ EatGrabEvents(dispPtr, serial); } /* * Synthesize leave events to move the pointer from its current window * up to the lowest ancestor that it has in common with the grab window. * However, only do this if the pointer is outside the grab window's * subtree but inside the grab window's application. */ if ((dispPtr->serverWinPtr != NULL) && (dispPtr->serverWinPtr->mainPtr == winPtr->mainPtr)) { for (winPtr2 = dispPtr->serverWinPtr; ; winPtr2 = winPtr2->parentPtr) { if (winPtr2 == winPtr) { break; } if (winPtr2 == NULL) { MovePointer2(dispPtr->serverWinPtr, winPtr, NotifyGrab, 1, 0); break; } } } QueueGrabWindowChange(dispPtr, winPtr); return TCL_OK;}/* *---------------------------------------------------------------------- * * Tk_Ungrab -- * * Releases a grab on the mouse pointer and keyboard, if there * is one set on the specified window. * * Results: * None. * * Side effects: * Pointer and keyboard events will start being delivered to other * windows again. * *---------------------------------------------------------------------- */voidTk_Ungrab(tkwin) Tk_Window tkwin; /* Window whose grab should be * released. */{ TkDisplay *dispPtr; TkWindow *grabWinPtr, *winPtr; unsigned int serial; grabWinPtr = (TkWindow *) tkwin; dispPtr = grabWinPtr->dispPtr; if (grabWinPtr != dispPtr->eventualGrabWinPtr) { return; } ReleaseButtonGrab(dispPtr); QueueGrabWindowChange(dispPtr, (TkWindow *) NULL); if (dispPtr->grabFlags & (GRAB_GLOBAL|GRAB_TEMP_GLOBAL)) { dispPtr->grabFlags &= ~(GRAB_GLOBAL|GRAB_TEMP_GLOBAL); serial = NextRequest(dispPtr->display); XUngrabPointer(dispPtr->display, CurrentTime); XUngrabKeyboard(dispPtr->display, CurrentTime); EatGrabEvents(dispPtr, serial); } /* * Generate events to move the pointer back to the window where it * really is. Some notes: * 1. As with grabs, only do this if the "real" window is not a * descendant of the grab window, since in this case the pointer * is already where it's supposed to be. * 2. If the "real" window is in some other application then don't * generate any events at all, since everything's already been * reported correctly. * 3. Only generate enter events. Don't generate leave events, * because we never told the lower-level windows that they * had the pointer in the first place. */ for (winPtr = dispPtr->serverWinPtr; ; winPtr = winPtr->parentPtr) { if (winPtr == grabWinPtr) { break; } if (winPtr == NULL) { if ((dispPtr->serverWinPtr == NULL) || (dispPtr->serverWinPtr->mainPtr == grabWinPtr->mainPtr)) { MovePointer2(grabWinPtr, dispPtr->serverWinPtr, NotifyUngrab, 0, 1); } break; } }}/* *---------------------------------------------------------------------- * * ReleaseButtonGrab -- * * This procedure is called to release a simulated button grab, if * there is one in effect. A button grab is present whenever * dispPtr->buttonWinPtr is non-NULL or when the GRAB_TEMP_GLOBAL * flag is set. * * Results: * None. * * Side effects: * DispPtr->buttonWinPtr is reset to NULL, and enter and leave * events are generated if necessary to move the pointer from * the button grab window to its current window. * *---------------------------------------------------------------------- */static voidReleaseButtonGrab(dispPtr) register TkDisplay *dispPtr; /* Display whose button grab is to be * released. */{ unsigned int serial; if (dispPtr->buttonWinPtr != NULL) { if (dispPtr->buttonWinPtr != dispPtr->serverWinPtr) { MovePointer2(dispPtr->buttonWinPtr, dispPtr->serverWinPtr, NotifyUngrab, 1, 1); } dispPtr->buttonWinPtr = NULL; } if (dispPtr->grabFlags & GRAB_TEMP_GLOBAL) { dispPtr->grabFlags &= ~GRAB_TEMP_GLOBAL; serial = NextRequest(dispPtr->display); XUngrabPointer(dispPtr->display, CurrentTime); XUngrabKeyboard(dispPtr->display, CurrentTime); EatGrabEvents(dispPtr, serial); }}/* *---------------------------------------------------------------------- * * TkPointerEvent -- * * This procedure is called for each pointer-related event, before * the event has been processed. It does various things to make * grabs work correctly. * * Results: * If the return value is 1 it means the event should be processed * (event handlers should be invoked). If the return value is 0 * it means the event should be ignored in order to make grabs * work correctly. In some cases this procedure modifies the event. * * Side effects: * Grab state information may be updated. New events may also be * pushed back onto the event queue to replace or augment the * one passed in here. * *---------------------------------------------------------------------- */intTkPointerEvent(eventPtr, winPtr) register XEvent *eventPtr; /* Pointer to the event. */ TkWindow *winPtr; /* Tk's information for window * where event was reported. */{ register TkWindow *winPtr2; TkDisplay *dispPtr = winPtr->dispPtr; unsigned int serial; int outsideGrabTree = 0; int ancestorOfGrab = 0; int appGrabbed = 0; /* Non-zero means event is being * reported to an application that is * affected by the grab. */ /* * Collect information about the grab (if any). */ switch (TkGrabState(winPtr)) { case TK_GRAB_IN_TREE: appGrabbed = 1; break; case TK_GRAB_ANCESTOR: appGrabbed = 1; outsideGrabTree = 1; ancestorOfGrab = 1; break; case TK_GRAB_EXCLUDED: appGrabbed = 1; outsideGrabTree = 1; break; } if ((eventPtr->type == EnterNotify) || (eventPtr->type == LeaveNotify)) { /* * Keep track of what window the mouse is *really* over. * Any events that we generate have a special send_event value, * which is detected below and used to ignore the event for * purposes of setting serverWinPtr. */ if (eventPtr->xcrossing.send_event != GENERATED_EVENT_MAGIC) { if ((eventPtr->type == LeaveNotify) && (winPtr->flags & TK_TOP_LEVEL)) { dispPtr->serverWinPtr = NULL; } else { dispPtr->serverWinPtr = winPtr; } } /* * When a grab is active, X continues to report enter and leave * events for windows outside the tree of the grab window: * 1. Detect these events and ignore them except for * windows above the grab window. * 2. Allow Enter and Leave events to pass through the * windows above the grab window, but never let them * end up with the pointer *in* one of those windows. */ if (dispPtr->grabWinPtr != NULL) { if (outsideGrabTree && appGrabbed) { if (!ancestorOfGrab) { return 0; } switch (eventPtr->xcrossing.detail) { case NotifyInferior: return 0; case NotifyAncestor: eventPtr->xcrossing.detail = NotifyVirtual; break; case NotifyNonlinear: eventPtr->xcrossing.detail = NotifyNonlinearVirtual; break; } } /* * Make buttons have the same grab-like behavior inside a grab * as they do outside a grab: do this by ignoring enter and * leave events except for the window in which the button was * pressed. */ if ((dispPtr->buttonWinPtr != NULL) && (winPtr != dispPtr->buttonWinPtr)) { return 0; } } return 1; } if (!appGrabbed) { return 1; } if (eventPtr->type == MotionNotify) { /* * When grabs are active, X reports motion events relative to the * window under the pointer. Instead, it should report the events * relative to the window the button went down in, if there is a * button down. Otherwise, if the pointer window is outside the * subtree of the grab window, the events should be reported * relative to the grab window. Otherwise, the event should be * reported to the pointer window. */ winPtr2 = winPtr; if (dispPtr->buttonWinPtr != NULL) { winPtr2 = dispPtr->buttonWinPtr; } else if (outsideGrabTree || (dispPtr->serverWinPtr == NULL)) { winPtr2 = dispPtr->grabWinPtr; } if (winPtr2 != winPtr) { TkChangeEventWindow(eventPtr, winPtr2); Tk_QueueWindowEvent(eventPtr, TCL_QUEUE_HEAD); return 0; } return 1; } /* * Process ButtonPress and ButtonRelease events: * 1. Keep track of whether a button is down and what window it * went down in. * 2. If the first button goes down outside the grab tree, pretend * it went down in the grab window. Note: it's important to * redirect events to the grab window like this in order to make * things like menus work, where button presses outside the * grabbed menu need to be seen. An application can always * ignore the events if they occur outside its window.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -