summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorculler <culler>2019-10-20 03:30:30 (GMT)
committerculler <culler>2019-10-20 03:30:30 (GMT)
commit1a24593842aae88beb8e408ac1770f7a36d91954 (patch)
tree5b2fd8fe60b2e6e3b854bb8461056c8cb13307cd
parentbe42ed39b45938000bdf6dc5b4f2c7cab5624927 (diff)
parentdaffadca1d118cf535300fea3c60baa6ee0dd5f7 (diff)
downloadtk-1a24593842aae88beb8e408ac1770f7a36d91954.zip
tk-1a24593842aae88beb8e408ac1770f7a36d91954.tar.gz
tk-1a24593842aae88beb8e408ac1770f7a36d91954.tar.bz2
Fix [ee946e4ebd]: on macOS local grabs only work for toplevels.
-rw-r--r--macosx/tkMacOSXMouseEvent.c67
-rw-r--r--macosx/tkMacOSXTest.c100
-rw-r--r--tests/grab.test36
3 files changed, 183 insertions, 20 deletions
diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c
index bc7c616..84b5fa1 100644
--- a/macosx/tkMacOSXMouseEvent.c
+++ b/macosx/tkMacOSXMouseEvent.c
@@ -36,6 +36,7 @@ static unsigned int ButtonModifiers2State(UInt32 buttonState,
enum {
NSWindowWillMoveEventType = 20
};
+
/*
* In OS X 10.6 an NSEvent of type NSMouseMoved would always have a non-Nil
* window attribute pointing to the active window. As of 10.8 this behavior
@@ -144,15 +145,28 @@ enum {
}
/*
- * If another toplevel has a grab, we ignore the event.
+ * Ignore the event if a local grab is in effect and the Tk event window is
+ * not in the grabber's subtree.
*/
grabWinPtr = winPtr->dispPtr->grabWinPtr;
- if (grabWinPtr &&
- grabWinPtr != winPtr &&
- !winPtr->dispPtr->grabFlags && /* this means the grab is local. */
- grabWinPtr->mainPtr == winPtr->mainPtr) {
- return theEvent;
+ if (grabWinPtr && /* There is a grab in effect ... */
+ !winPtr->dispPtr->grabFlags && /* and it is a local grab ... */
+ grabWinPtr->mainPtr == winPtr->mainPtr){ /* in the same application. */
+ Tk_Window tkwin2, tkEventWindow = Tk_CoordsToWindow(global.x, global.y, tkwin);
+ if (!tkEventWindow) {
+ return theEvent;
+ }
+ for (tkwin2 = tkEventWindow;
+ !Tk_IsTopLevel(tkwin2);
+ tkwin2 = Tk_Parent(tkwin2)) {
+ if (tkwin2 == (Tk_Window) grabWinPtr) {
+ break;
+ }
+ }
+ if (tkwin2 != (Tk_Window) grabWinPtr) {
+ return theEvent;
+ }
}
/*
@@ -226,6 +240,7 @@ enum {
}
if (eventType != NSScrollWheel) {
+
/*
* For normal mouse events, Tk_UpdatePointer will send the XEvent.
*/
@@ -236,6 +251,7 @@ enum {
#endif
Tk_UpdatePointer(tkwin, global.x, global.y, state);
} else {
+
/*
* For scroll wheel events we need to send the XEvent here.
*/
@@ -500,7 +516,7 @@ TkGenerateButtonEventForXPointer(
* TkGenerateButtonEvent --
*
* Given a global x & y position and the button key status this procedure
- * generates the appropiate X button event. It also handles the state
+ * generates the appropriate X button event. It also handles the state
* changes needed to implement implicit grabs.
*
* Results:
@@ -574,6 +590,7 @@ GenerateButtonEvent(
TkDisplay *dispPtr;
#if UNUSED
+
/*
* ButtonDown events will always occur in the front window. ButtonUp
* events, however, may occur anywhere on the screen. ButtonUp events
@@ -606,30 +623,46 @@ TkpWarpPointer(
TkDisplay *dispPtr)
{
CGPoint pt;
- UInt32 buttonState;
+ NSPoint loc;
+ int wNum;
if (dispPtr->warpWindow) {
int x, y;
-
+ TkWindow *winPtr = (TkWindow *) dispPtr->warpWindow;
+ TkWindow *topPtr = winPtr->privatePtr->toplevel->winPtr;
+ NSWindow *w = TkMacOSXDrawableWindow(winPtr->window);
+ wNum = [w windowNumber];
Tk_GetRootCoords(dispPtr->warpWindow, &x, &y);
pt.x = x + dispPtr->warpX;
pt.y = y + dispPtr->warpY;
+ loc.x = dispPtr->warpX;
+ loc.y = Tk_Height(topPtr) - dispPtr->warpY;
} else {
- pt.x = dispPtr->warpX;
+ wNum = 0;
+ pt.x = loc.x = dispPtr->warpX;
pt.y = dispPtr->warpY;
+ loc.y = tkMacOSXZeroScreenHeight - pt.y;
}
/*
- * Tell the OSX core to generate the events to make it happen.
+ * Generate an NSEvent of type NSMouseMoved.
+ *
+ * It is not clear why this is necessary. For example, calling
+ * event generate $w <Motion> -warp 1 -x $X -y $Y
+ * will cause two <Motion> events to be added to the Tcl queue.
*/
- buttonState = [NSEvent pressedMouseButtons];
- CGEventType type = kCGEventMouseMoved;
- CGEventRef theEvent = CGEventCreateMouseEvent(NULL, type, pt,
- buttonState);
CGWarpMouseCursorPosition(pt);
- CGEventPost(kCGHIDEventTap, theEvent);
- CFRelease(theEvent);
+ NSEvent *warpEvent = [NSEvent mouseEventWithType:NSMouseMoved
+ location:loc
+ modifierFlags:0
+ timestamp:GetCurrentEventTime()
+ windowNumber:wNum
+ context:nil
+ eventNumber:0
+ clickCount:1
+ pressure:0.0];
+ [NSApp postEvent:warpEvent atStart:NO];
}
/*
diff --git a/macosx/tkMacOSXTest.c b/macosx/tkMacOSXTest.c
index f109b7a..09736e6 100644
--- a/macosx/tkMacOSXTest.c
+++ b/macosx/tkMacOSXTest.c
@@ -13,6 +13,7 @@
*/
#include "tkMacOSXPrivate.h"
+#include "tkMacOSXConstants.h"
/*
* Forward declarations of procedures defined later in this file:
@@ -22,6 +23,8 @@
static int DebuggerObjCmd (ClientData dummy, Tcl_Interp *interp,
int objc, Tcl_Obj *const objv[]);
#endif
+static int PressButtonObjCmd (ClientData dummy, Tcl_Interp *interp,
+ int objc, Tcl_Obj *const objv[]);
/*
@@ -52,6 +55,7 @@ TkplatformtestInit(
#if MAC_OS_X_VERSION_MAX_ALLOWED < 1080
Tcl_CreateObjCommand(interp, "debugger", DebuggerObjCmd, NULL, NULL);
#endif
+ Tcl_CreateObjCommand(interp, "pressbutton", PressButtonObjCmd, NULL, NULL);
return TCL_OK;
}
@@ -119,6 +123,102 @@ TkTestLogDisplay(void) {
}
}
+/*
+ *----------------------------------------------------------------------
+ *
+ * PressButtonObjCmd --
+ *
+ * This Tcl command simulates a button press at a specific screen
+ * location. It injects NSEvents into the NSApplication event queue,
+ * as opposed to adding events to the Tcl queue as event generate
+ * would do. One application is for testing the grab command.
+ *
+ * Results:
+ * A standard Tcl result.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+PressButtonObjCmd(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const objv[])
+{
+ int x, y, i, value, wNum;
+ CGPoint pt;
+ NSPoint loc;
+ NSEvent *motion, *press, *release;
+ NSArray *screens = [NSScreen screens];
+ CGFloat ScreenHeight = 0;
+ enum {X=1, Y};
+
+ if (screens && [screens count]) {
+ ScreenHeight = [[screens objectAtIndex:0] frame].size.height;
+ }
+
+ if (objc != 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "x y");
+ return TCL_ERROR;
+ }
+ for (i = 1; i < objc; i++) {
+ if (Tcl_GetIntFromObj(interp,objv[i],&value) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ switch (i) {
+ case X:
+ x = value;
+ break;
+ case Y:
+ y = value;
+ break;
+ default:
+ break;
+ }
+ }
+ pt.x = loc.x = x;
+ pt.y = y;
+ loc.y = ScreenHeight - y;
+ wNum = 0;
+ CGWarpMouseCursorPosition(pt);
+ motion = [NSEvent mouseEventWithType:NSMouseMoved
+ location:loc
+ modifierFlags:0
+ timestamp:GetCurrentEventTime()
+ windowNumber:wNum
+ context:nil
+ eventNumber:0
+ clickCount:1
+ pressure:0.0];
+ [NSApp postEvent:motion atStart:NO];
+ press = [NSEvent mouseEventWithType:NSLeftMouseDown
+ location:loc
+ modifierFlags:0
+ timestamp:GetCurrentEventTime()
+ windowNumber:wNum
+ context:nil
+ eventNumber:1
+ clickCount:1
+ pressure:0.0];
+ [NSApp postEvent:press atStart:NO];
+ release = [NSEvent mouseEventWithType:NSLeftMouseUp
+ location:loc
+ modifierFlags:0
+ timestamp:GetCurrentEventTime()
+ windowNumber:wNum
+ context:nil
+ eventNumber:2
+ clickCount:1
+ pressure:0.0];
+ [NSApp postEvent:release atStart:NO];
+ return TCL_OK;
+}
+
/*
* Local Variables:
diff --git a/tests/grab.test b/tests/grab.test
index 33399cb..653d756 100644
--- a/tests/grab.test
+++ b/tests/grab.test
@@ -12,10 +12,14 @@ eval tcltest::configure $argv
tcltest::loadTestedCommands
namespace import -force tcltest::test
-# There's currently no way to test the actual grab effect, per se,
-# in an automated test. Therefore, this test suite only covers the
-# interface to the grab command (ie, error messages, etc.)
+# The macOS test module includes the pressbutton command to simulate a
+# mouse button press event by injecting events into the NSApplication
+# event queue. On other platforms there is currently no way to test
+# the actual grab effect, per se, in an automated test. Therefore,
+# this test suite only covers the interface to the grab command (ie,
+# error messages, etc.) on platforms other than macOS.
+testConstraint pressbutton [llength [info commands pressbutton]]
test grab-1.1 {Tk_GrabObjCmd} -body {
grab
@@ -182,6 +186,32 @@ test grab-5.2 {Tk_GrabObjCmd, grab set} -body {
grab release .
} -result {. global}
+test grab-6.1 {local grab on child window} -constraints {
+ pressbutton
+} -body {
+ wm geometry . 100x200+200+100
+ set result {}
+ frame .f -background red -padx 10 -pady 10 -height 100 -width 80
+ bind . <Button-1> {lappend result "outside"}
+ bind .f <Button-1> {lappend result "inside"}
+ pack .f
+ update idletasks
+ pressbutton 250 150
+ update
+ lappend result ":"
+ pressbutton 250 250
+ update
+ lappend result ":"
+ grab set .f
+ pressbutton 250 150
+ update
+ lappend result ":"
+ pressbutton 250 250
+ update
+ return $result
+} -cleanup {
+ grab release .f
+} -result {inside outside : outside : inside outside :}
cleanupTests
return