Tk Source Code

Check-in [1edd8b7a]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Implement local grabs on macOS.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-6-branch
Files: files | file ages | folders
SHA3-256: 1edd8b7a1638926304bbbfef3688863bf7d97e6e10f3683f982cbda2b1eda71d
User & Date: culler 2019-01-08 23:01:32
References
2022-04-02
20:44 Ticket [dc4c5573] tk_popup entry index not working on unix status still Open with 4 other changes artifact: 583222ce user: fvogel
Context
2019-01-10
08:10
Fix [9e31fd9449]: X11/X.h and Windows.h have conflicting symbols. *** POTENTIAL INCOMPATIBILITY *** on Windows only: gcc/clang/MSVC will generate new warnings in extensions when the "None" symbol is used incorrectly. Those warnings are all fixed in the core, that's what most of this commit is doing. check-in: c707c501 user: jan.nijtmans tags: core-8-6-branch
2019-01-09
14:28
Fix bug [70e531918e]: geometry issues with menubuttons on macOS check-in: 06cc21c0 user: culler tags: bug-70e531918e
13:01
Implement local grabs on macOS. check-in: 10f2f6f2 user: culler tags: trunk
2019-01-08
23:01
Implement local grabs on macOS. check-in: 1edd8b7a user: culler tags: core-8-6-branch
22:53
Update the demos to give a more complete explanation of local and global grabs. Closed-Leaf check-in: 97a0b2b5 user: culler tags: mac-local-grabs
2019-01-04
23:27
Merge 8.5 check-in: dbb582dd user: jan.nijtmans tags: core-8-6-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to doc/grab.n.

56
57
58
59
60
61
62










63
64
65
66
67
68
69
if they are sent to any window in the grabbing application then they are
redirected to the focus window.
During a global grab Tk grabs the keyboard so that all keyboard events
are always sent to the grabbing application.
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
Grabs apply to particular displays.  If an application has windows
on multiple displays then it can establish a separate grab on each
display.
The grab on a particular display affects only the windows on
that display.
It is possible for different applications on a single display to have







>
>
>
>
>
>
>
>
>
>







56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
if they are sent to any window in the grabbing application then they are
redirected to the focus window.
During a global grab Tk grabs the keyboard so that all keyboard events
are always sent to the grabbing application.
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.
The grab on a particular display affects only the windows on
that display.
It is possible for different applications on a single display to have

Changes to generic/tkGrab.c.

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
	if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) {
	    goto alreadyGrabbed;
	}
	Tk_Ungrab((Tk_Window) dispPtr->eventualGrabWinPtr);
    }

    Tk_MakeWindowExist(tkwin);
#ifndef MAC_OSX_TK
    if (!grabGlobal)
#else
    if (0)
#endif /* MAC_OSX_TK */
    {
	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







<
|
<
<
<
<







422
423
424
425
426
427
428

429




430
431
432
433
434
435
436
	if (dispPtr->eventualGrabWinPtr->mainPtr != winPtr->mainPtr) {
	    goto alreadyGrabbed;
	}
	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

Changes to library/demos/dialog1.tcl.

1
2
3
4








5
6
7
8
9
10
11
12
13




# dialog1.tcl --
#
# This demonstration script creates a dialog box with a local grab.









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.} \
info 0 OK Cancel {Show Code}]

switch $i {
    0 {puts "You pressed OK"}
    1 {puts "You pressed Cancel"}
    2 {showCode .dialog1}
}








>
>
>
>
>
>
>
>

|







>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# dialog1.tcl --
#
# 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 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 {
    0 {puts "You pressed OK"}
    1 {puts "You pressed Cancel"}
    2 {showCode .dialog1}
}

if {[interp exists slave]} {
    interp delete slave
}

Changes to library/demos/dialog2.tcl.

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
# dialog2.tcl --
#
# This demonstration script creates a dialog box with a global grab.

after idle {
    .dialog2.msg configure -wraplength 4i
}
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}]


switch $i {
    0 {puts "You pressed OK"}
    1 {puts "You pressed Cancel"}
    2 {showCode .dialog2}
}










|
>






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# dialog2.tcl --
#
# This demonstration script creates a dialog box with a global grab.

after idle {
    .dialog2.msg configure -wraplength 4i
}
after 100 {
    grab -global .dialog2
}
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"}
    1 {puts "You pressed Cancel"}
    2 {showCode .dialog2}
}

Changes to macosx/tkMacOSXKeyEvent.c.

135
136
137
138
139
140
141
142


143
144
145
146
147
148
149


150


151
152

153

154
155
156
157
158
159
160
161
162

163
164
165
166
167
168
169
          state |= Mod3Mask;
        }
        if (modifiers & NSFunctionKeyMask) {
          state |= Mod4Mask;
        }

        /*
         * The focus must be in the FrontWindow on the Macintosh. We then query Tk


         * to determine the exact Tk window that owns the focus.
         */

        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) {
          TkMacOSXDbgMsg("tkwin == NULL");
          return theEvent;  /* Give up. No window for this event. */
        }

        /*
         * 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)
            || (len > 0 && state & (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask))) {

            XEvent xEvent;
            setupXEvent(&xEvent, w, state);







|
>
>
|





|
>
>
|
>
>
|
|
>
|
>









>







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
          state |= Mod3Mask;
        }
        if (modifiers & NSFunctionKeyMask) {
          state |= Mod4Mask;
        }

        /*
         * 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) {
	    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. */
        }

        /*
         * 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)
            || (len > 0 && state & (ControlMask | Mod1Mask | Mod3Mask | Mod4Mask))) {

            XEvent xEvent;
            setupXEvent(&xEvent, w, state);

Changes to macosx/tkMacOSXMouseEvent.c.

102
103
104
105
106
107
108
109
110
111
112








113
114
115

116


117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
	    global.y = tkMacOSXZeroScreenHeight - global.y;
	} else { /* We have no window. Use the screen???*/
	    local.y = tkMacOSXZeroScreenHeight - local.y;
	    global = local;
	}
    }

    Window window = TkMacOSXGetXWindow(eventWindow);
    Tk_Window tkwin = window ? Tk_IdToWindow(TkGetDisplayList()->display,
	    window) : NULL;
    if (!tkwin) {








	tkwin = TkMacOSXGetCapture();
    }
    if (!tkwin) {

	return theEvent; /* Give up.  No window for this event. */


    }

    TkWindow  *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);

    unsigned int state = 0;
    NSInteger button = [theEvent buttonNumber];
    EventRef eventRef = (EventRef)[theEvent eventRef];
    UInt32 buttons;
    OSStatus err = GetEventParameter(eventRef, kEventParamMouseChord,
				     typeUInt32, NULL, sizeof(UInt32), NULL, &buttons);







|
|
|
|
>
>
>
>
>
>
>
>



>

>
>
|
|
<




|
<







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

130
131
132
133
134

135
136
137
138
139
140
141
	    global.y = tkMacOSXZeroScreenHeight - global.y;
	} else { /* We have no window. Use the screen???*/
	    local.y = tkMacOSXZeroScreenHeight - local.y;
	    global = local;
	}
    }

    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. */
    } 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);


    unsigned int state = 0;
    NSInteger button = [theEvent buttonNumber];
    EventRef eventRef = (EventRef)[theEvent eventRef];
    UInt32 buttons;
    OSStatus err = GetEventParameter(eventRef, kEventParamMouseChord,
				     typeUInt32, NULL, sizeof(UInt32), NULL, &buttons);