summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/grab.n10
-rw-r--r--generic/tkGrab.c7
-rw-r--r--library/demos/dialog1.tcl14
-rw-r--r--library/demos/dialog2.tcl3
-rw-r--r--macosx/tkMacOSXKeyEvent.c23
-rw-r--r--macosx/tkMacOSXMouseEvent.c27
6 files changed, 60 insertions, 24 deletions
diff --git a/doc/grab.n b/doc/grab.n
index a6d0d19..c45e26a 100644
--- a/doc/grab.n
+++ b/doc/grab.n
@@ -61,6 +61,16 @@ The \fBfocus\fR command is still used to determine which window in the
application receives the keyboard events.
The keyboard grab is released when the grab is released.
.PP
+On macOS a global grab affects all windows created by one Tk process.
+No window in that process other than the grab window can even be
+focused, hence no other window receives key or mouse events. A local
+grab on macOS affects all windows created by one Tcl interpreter. It
+is possible to focus any window belonging to the Tk process during a
+local grab but the grab window is the only window created by its
+interpreter which receives key or mouse events. Windows belonging to the
+same process but created by different interpreters continue to receive
+key and mouse events normally.
+.PP
Grabs apply to particular displays. If an application has windows
on multiple displays then it can establish a separate grab on each
display.
diff --git a/generic/tkGrab.c b/generic/tkGrab.c
index 00d4511..5ea2906 100644
--- a/generic/tkGrab.c
+++ b/generic/tkGrab.c
@@ -426,12 +426,7 @@ Tk_Grab(
}
Tk_MakeWindowExist(tkwin);
-#ifndef MAC_OSX_TK
- if (!grabGlobal)
-#else
- if (0)
-#endif /* MAC_OSX_TK */
- {
+ if (!grabGlobal) {
Window dummy1, dummy2;
int dummy3, dummy4, dummy5, dummy6;
unsigned int state;
diff --git a/library/demos/dialog1.tcl b/library/demos/dialog1.tcl
index 5c572be..976e955 100644
--- a/library/demos/dialog1.tcl
+++ b/library/demos/dialog1.tcl
@@ -2,8 +2,16 @@
#
# This demonstration script creates a dialog box with a local grab.
+interp create slave
+load {} Tk slave
+slave eval {
+ wm title . slave
+ wm geometry . +700+30
+ pack [text .t -width 30 -height 10]
+}
+
after idle {.dialog1.msg configure -wraplength 4i}
-set i [tk_dialog .dialog1 "Dialog with local grab" {This is a modal dialog box. It uses Tk's "grab" command to create a "local grab" on the dialog box. The grab prevents any pointer-related events from getting to any other windows in the application until you have answered the dialog by invoking one of the buttons below. However, you can still interact with other applications.} \
+set i [tk_dialog .dialog1 "Dialog with local grab" {This is a modal dialog box. It uses Tk's "grab" command to create a "local grab" on the dialog box. The grab prevents any mouse or keyboard events from getting to any other windows in the application until you have answered the dialog by invoking one of the buttons below. However, you can still interact with other applications. For example, you should be able to edit text in the window named "slave" which was created by a slave interpreter.} \
info 0 OK Cancel {Show Code}]
switch $i {
@@ -11,3 +19,7 @@ switch $i {
1 {puts "You pressed Cancel"}
2 {showCode .dialog1}
}
+
+if {[interp exists slave]} {
+ interp delete slave
+}
diff --git a/library/demos/dialog2.tcl b/library/demos/dialog2.tcl
index 2f45da8..6ae27a8 100644
--- a/library/demos/dialog2.tcl
+++ b/library/demos/dialog2.tcl
@@ -8,7 +8,8 @@ after idle {
after 100 {
grab -global .dialog2
}
-set i [tk_dialog .dialog2 "Dialog with global grab" {This dialog box uses a global grab, so it prevents you from interacting with anything on your display until you invoke one of the buttons below. Global grabs are almost always a bad idea; don't use them unless you're truly desperate.} warning 0 OK Cancel {Show Code}]
+set i [tk_dialog .dialog2 "Dialog with global grab" {This dialog box uses a global grab. If you are using an X11 window manager you will be prevented from interacting with anything on your display until you invoke one of the buttons below. This is almost always a bad idea; don't use global grabs with X11 unless you're truly desperate. On macOS systems you will not be able to interact with any window belonging to this process, but interaction with other macOS Applications will still be possible.}\
+warning 0 OK Cancel {Show Code}]
switch $i {
0 {puts "You pressed OK"}
diff --git a/macosx/tkMacOSXKeyEvent.c b/macosx/tkMacOSXKeyEvent.c
index a153797..3327f0a 100644
--- a/macosx/tkMacOSXKeyEvent.c
+++ b/macosx/tkMacOSXKeyEvent.c
@@ -139,18 +139,26 @@ unsigned short releaseCode;
}
/*
- * The focus must be in the FrontWindow on the Macintosh. We then query Tk
- * to determine the exact Tk window that owns the focus.
+ * Events are only received for the front Window on the Macintosh.
+ * So to build an XEvent we look up the Tk window associated to the
+ * Front window. If a different window has a local grab we ignore
+ * the event.
*/
TkWindow *winPtr = TkMacOSXGetTkWindow(w);
Tk_Window tkwin = (Tk_Window) winPtr;
- if (!tkwin) {
- TkMacOSXDbgMsg("tkwin == NULL");
- return theEvent;
- }
- tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
+ if (tkwin) {
+ TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr;
+ if (grabWinPtr &&
+ grabWinPtr != winPtr &&
+ !winPtr->dispPtr->grabFlags && /* this means the grab is local. */
+ grabWinPtr->mainPtr == winPtr->mainPtr) {
+ return theEvent;
+ }
+ } else {
+ tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
+ }
if (!tkwin) {
TkMacOSXDbgMsg("tkwin == NULL");
return theEvent; /* Give up. No window for this event. */
@@ -160,6 +168,7 @@ unsigned short releaseCode;
* If it's a function key, or we have modifiers other than Shift or Alt,
* pass it straight to Tk. Otherwise we'll send for input processing.
*/
+
int code = (len == 0) ?
0 : [charactersIgnoringModifiers characterAtIndex: 0];
if (type != NSKeyDown || isFunctionKey(code)
diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c
index 828d874..f02df1d 100644
--- a/macosx/tkMacOSXMouseEvent.c
+++ b/macosx/tkMacOSXMouseEvent.c
@@ -106,23 +106,32 @@ enum {
}
}
- Window window = TkMacOSXGetXWindow(eventWindow);
- Tk_Window tkwin = window ? Tk_IdToWindow(TkGetDisplayList()->display,
- window) : NULL;
- if (!tkwin) {
+ TkWindow *winPtr = TkMacOSXGetTkWindow(eventWindow);
+ Tk_Window tkwin = (Tk_Window) winPtr;
+
+ if (tkwin) {
+ TkWindow *grabWinPtr = winPtr->dispPtr->grabWinPtr;
+ if (grabWinPtr &&
+ grabWinPtr != winPtr &&
+ !winPtr->dispPtr->grabFlags && /* this means the grab is local. */
+ grabWinPtr->mainPtr == winPtr->mainPtr) {
+ return theEvent;
+ }
+ } else {
tkwin = TkMacOSXGetCapture();
}
if (!tkwin) {
+ TkMacOSXDbgMsg("tkwin == NULL");
return theEvent; /* Give up. No window for this event. */
- }
-
- TkWindow *winPtr = (TkWindow *) tkwin;
+ } else {
+ winPtr = (TkWindow *)tkwin;
+ }
+
local.x -= winPtr->wmInfoPtr->xInParent;
local.y -= winPtr->wmInfoPtr->yInParent;
int win_x, win_y;
- tkwin = Tk_TopCoordsToWindow(tkwin, local.x, local.y,
- &win_x, &win_y);
+ tkwin = Tk_TopCoordsToWindow(tkwin, local.x, local.y, &win_x, &win_y);
unsigned int state = 0;
NSInteger button = [theEvent buttonNumber];