📄 tkgrab.c
字号:
/* * tkGrab.c -- * * This file provides procedures that implement grabs for Tk. * * Copyright (c) 1992-1994 The Regents of the University of California. * Copyright (c) 1994-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: @(#) tkGrab.c 1.52 97/03/21 11:14:34 */#include "tkPort.h"#include "tkInt.h"/* * The grab state machine has four states: ungrabbed, button pressed, * grabbed, and button pressed while grabbed. In addition, there are * three pieces of grab state information: the current grab window, * the current restrict window, and whether the mouse is captured. * * The current grab window specifies the point in the Tk window * heirarchy above which pointer events will not be reported. Any * window within the subtree below the grab window will continue to * receive events as normal. Events outside of the grab tree will be * reported to the grab window. * * If the current restrict window is set, then all pointer events will * be reported only to the restrict window. The restrict window is * normally set during an automatic button grab. * * The mouse capture state specifies whether the window system will * report mouse events outside of any Tk toplevels. This is set * during a global grab or an automatic button grab. * * The transitions between different states is given in the following * table: * * Event\State U B G GB * ----------- -- -- -- -- * FirstPress B B GB GB * Press B B G GB * Release U B G GB * LastRelease U U G G * Grab G G G G * Ungrab U B U U * * Note: U=Ungrabbed, B=Button, G=Grabbed, GB=Grab and Button * * In addition, the following conditions are always true: * * State\Variable Grab Restrict Capture * -------------- ---- -------- ------- * Ungrabbed 0 0 0 * Button 0 1 1 * Grabbed 1 0 b/g * Grab and Button 1 1 1 * * Note: 0 means variable is set to NULL, 1 means variable is set to * some window, b/g means the variable is set to a window if a button * is currently down or a global grab is in effect. * * The final complication to all of this is enter and leave events. * In order to correctly handle all of the various cases, Tk cannot * rely on X enter/leave events in all situations. The following * describes the correct sequence of enter and leave events that * should be observed by Tk scripts: * * Event(state) Enter/Leave From -> To * ------------ ---------------------- * LastRelease(B | GB): restrict window -> anc(grab window, event window) * Grab(U | B): event window -> anc(grab window, event window) * Grab(G): anc(old grab window, event window) -> * anc(new grab window, event window) * Grab(GB): restrict window -> anc(new grab window, event window) * Ungrab(G): anc(grab window, event window) -> event window * Ungrab(GB): restrict window -> event window * * Note: anc(x,y) returns the least ancestor of y that is in the tree * of x, terminating at toplevels. *//* * The following structure is used to pass information to * GrabRestrictProc from EatGrabEvents. */typedef struct { Display *display; /* Display from which to discard events. */ unsigned int serial; /* Serial number with which to compare. */} GrabInfo;/* * Bit definitions for grabFlags field of TkDisplay structures: * * GRAB_GLOBAL 1 means this is a global grab (we grabbed via * the server so all applications are locked out). * 0 means this is a local grab that affects * only this application. * GRAB_TEMP_GLOBAL 1 means we've temporarily grabbed via the * server because a button is down and we want * to make sure that we get the button-up * event. The grab will be released when the * last mouse button goes up. */#define GRAB_GLOBAL 1#define GRAB_TEMP_GLOBAL 4/* * The following structure is a Tcl_Event that triggers a change in * the grabWinPtr field of a display. This event guarantees that * the change occurs in the proper order relative to enter and leave * events. */typedef struct NewGrabWinEvent { Tcl_Event header; /* Standard information for all Tcl events. */ TkDisplay *dispPtr; /* Display whose grab window is to change. */ Window grabWindow; /* New grab window for display. This is * recorded instead of a (TkWindow *) because * it will allow us to detect cases where * the window is destroyed before this event * is processed. */} NewGrabWinEvent;/* * The following magic value is stored in the "send_event" field of * EnterNotify and LeaveNotify events that are generated in this * file. This allows us to separate "real" events coming from the * server from those that we generated. */#define GENERATED_EVENT_MAGIC ((Bool) 0x147321ac)/* * 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};/* * Forward declarations for procedures declared later in this file: */static void EatGrabEvents _ANSI_ARGS_((TkDisplay *dispPtr, unsigned int serial));static TkWindow * FindCommonAncestor _ANSI_ARGS_((TkWindow *winPtr1, TkWindow *winPtr2, int *countPtr1, int *countPtr2));static Tk_RestrictAction GrabRestrictProc _ANSI_ARGS_((ClientData arg, XEvent *eventPtr));static int GrabWinEventProc _ANSI_ARGS_((Tcl_Event *evPtr, int flags));static void MovePointer2 _ANSI_ARGS_((TkWindow *sourcePtr, TkWindow *destPtr, int mode, int leaveEvents, int EnterEvents));static void QueueGrabWindowChange _ANSI_ARGS_((TkDisplay *dispPtr, TkWindow *grabWinPtr));static void ReleaseButtonGrab _ANSI_ARGS_((TkDisplay *dispPtr));/* *---------------------------------------------------------------------- * * Tk_GrabCmd -- * * This procedure is invoked to process the "grab" Tcl command. * See the user documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * *---------------------------------------------------------------------- */ /* ARGSUSED */intTk_GrabCmd(clientData, interp, argc, argv) ClientData clientData; /* Main window associated with * interpreter. */ Tcl_Interp *interp; /* Current interpreter. */ int argc; /* Number of arguments. */ char **argv; /* Argument strings. */{ int globalGrab, c; Tk_Window tkwin; TkDisplay *dispPtr; size_t length; if (argc < 2) { badArgs: Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " ?-global? window\" or \"", argv[0], " option ?arg arg ...?\"", (char *) NULL); return TCL_ERROR; } c = argv[1][0]; length = strlen(argv[1]); if (c == '.') { if (argc != 2) { goto badArgs; } tkwin = Tk_NameToWindow(interp, argv[1], (Tk_Window) clientData); if (tkwin == NULL) { return TCL_ERROR; } return Tk_Grab(interp, tkwin, 0); } else if ((c == '-') && (strncmp(argv[1], "-global", length) == 0) && (length >= 2)) { if (argc != 3) { goto badArgs; } tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); if (tkwin == NULL) { return TCL_ERROR; } return Tk_Grab(interp, tkwin, 1); } else if ((c == 'c') && (strncmp(argv[1], "current", length) == 0)) { if (argc > 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " current ?window?\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); if (tkwin == NULL) { return TCL_ERROR; } dispPtr = ((TkWindow *) tkwin)->dispPtr; if (dispPtr->eventualGrabWinPtr != NULL) { interp->result = dispPtr->eventualGrabWinPtr->pathName; } } else { for (dispPtr = tkDisplayList; dispPtr != NULL; dispPtr = dispPtr->nextPtr) { if (dispPtr->eventualGrabWinPtr != NULL) { Tcl_AppendElement(interp, dispPtr->eventualGrabWinPtr->pathName); } } } return TCL_OK; } else if ((c == 'r') && (strncmp(argv[1], "release", length) == 0)) { if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " release window\"", (char *) NULL); return TCL_ERROR; } tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); if (tkwin == NULL) { Tcl_ResetResult(interp); } else { Tk_Ungrab(tkwin); } } else if ((c == 's') && (strncmp(argv[1], "set", length) == 0) && (length >= 2)) { if ((argc != 3) && (argc != 4)) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " set ?-global? window\"", (char *) NULL); return TCL_ERROR; } if (argc == 3) { globalGrab = 0; tkwin = Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); } else { globalGrab = 1; length = strlen(argv[2]); if ((strncmp(argv[2], "-global", length) != 0) || (length < 2)) { Tcl_AppendResult(interp, "bad argument \"", argv[2], "\": must be \"", argv[0], " set ?-global? window\"", (char *) NULL); return TCL_ERROR; } tkwin = Tk_NameToWindow(interp, argv[3], (Tk_Window) clientData); } if (tkwin == NULL) { return TCL_ERROR; } return Tk_Grab(interp, tkwin, globalGrab); } else if ((c == 's') && (strncmp(argv[1], "status", length) == 0) && (length >= 2)) { TkWindow *winPtr; if (argc != 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " status window\"", (char *) NULL); return TCL_ERROR; } winPtr = (TkWindow *) Tk_NameToWindow(interp, argv[2], (Tk_Window) clientData); if (winPtr == NULL) { return TCL_ERROR; } dispPtr = winPtr->dispPtr; if (dispPtr->eventualGrabWinPtr != winPtr) { interp->result = "none"; } else if (dispPtr->grabFlags & GRAB_GLOBAL) { interp->result = "global"; } else { interp->result = "local"; } } else { Tcl_AppendResult(interp, "unknown or ambiguous option \"", argv[1], "\": must be current, release, set, or status", (char *) NULL); return TCL_ERROR; } return TCL_OK;}/* *---------------------------------------------------------------------- * * Tk_Grab -- * * Grabs the pointer and keyboard, so that mouse-related events are * only reported relative to a given window and its descendants. * * Results: * A standard Tcl result is returned. TCL_OK is the normal return * value; if the grab could not be set then TCL_ERROR is returned * and interp->result will hold an error message. * * Side effects: * Once this call completes successfully, no window outside the * tree rooted at tkwin will receive pointer- or keyboard-related * events until the next call to Tk_Ungrab. If a previous grab was * in effect within this application, then it is replaced with a new * one. * *---------------------------------------------------------------------- */intTk_Grab(interp, tkwin, grabGlobal) Tcl_Interp *interp; /* Used for error reporting. */ Tk_Window tkwin; /* Window on whose behalf the pointer * is to be grabbed. */ int grabGlobal; /* Non-zero means issue a grab to the * server so that no other application * gets mouse or keyboard events. * Zero means the grab only applies * within this application. */{ int grabResult, numTries; TkWindow *winPtr = (TkWindow *) tkwin; TkDisplay *dispPtr = winPtr->dispPtr; TkWindow *winPtr2; unsigned int serial; ReleaseButtonGrab(dispPtr); if (dispPtr->eventualGrabWinPtr != NULL) { if ((dispPtr->eventualGrabWinPtr == winPtr) && (grabGlobal == ((dispPtr->grabFlags & GRAB_GLOBAL) != 0))) { return TCL_OK; } if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) { alreadyGrabbed: interp->result = "grab failed: another application has grab"; return TCL_ERROR; } Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr); } Tk_MakeWindowExist(tkwin); if (!grabGlobal) { Window dummy1, dummy2; int dummy3, dummy4, dummy5, dummy6; unsigned int state; /* * Local grab. However, if any mouse buttons are down, turn * it into a global grab temporarily, until the last button * goes up. This does two things: (a) it makes sure that we
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -