1 /*
2 * tkMacOSXTest.c --
3 *
4 * Contains commands for platform specific tests for
5 * the Macintosh platform.
6 *
7 * Copyright © 1996 Sun Microsystems, Inc.
8 * Copyright © 2001-2009 Apple Inc.
9 * Copyright © 2005-2009 Daniel A. Steffen <das@users.sourceforge.net>
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 */
14
15 #include "tkMacOSXPrivate.h"
16 #include "tkMacOSXConstants.h"
17 #include "tkMacOSXWm.h"
18
19
20 /*
21 * Forward declarations of procedures defined later in this file:
22 */
23
24 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1080
25 static int DebuggerObjCmd (ClientData dummy, Tcl_Interp *interp,
26 int objc, Tcl_Obj *const objv[]);
27 #endif
28 static int PressButtonObjCmd (ClientData dummy, Tcl_Interp *interp,
29 int objc, Tcl_Obj *const *objv);
30 static int InjectKeyEventObjCmd (ClientData dummy, Tcl_Interp *interp,
31 int objc, Tcl_Obj *const *objv);
32 static int MenuBarHeightObjCmd (ClientData dummy, Tcl_Interp *interp,
33 int objc, Tcl_Obj *const *objv);
34
35
36 /*
37 *----------------------------------------------------------------------
38 *
39 * TkplatformtestInit --
40 *
41 * Defines commands that test platform specific functionality for
42 * Unix platforms.
43 *
44 * Results:
45 * A standard Tcl result.
46 *
47 * Side effects:
48 * Defines new commands.
49 *
50 *----------------------------------------------------------------------
51 */
52
53 int
TkplatformtestInit(Tcl_Interp * interp)54 TkplatformtestInit(
55 Tcl_Interp *interp) /* Interpreter to add commands to. */
56 {
57 /*
58 * Add commands for platform specific tests on MacOS here.
59 */
60
61 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1080
62 Tcl_CreateObjCommand(interp, "debugger", DebuggerObjCmd, NULL, NULL);
63 #endif
64 Tcl_CreateObjCommand(interp, "pressbutton", PressButtonObjCmd, NULL, NULL);
65 Tcl_CreateObjCommand(interp, "injectkeyevent", InjectKeyEventObjCmd, NULL, NULL);
66 Tcl_CreateObjCommand(interp, "menubarheight", MenuBarHeightObjCmd, NULL, NULL);
67 return TCL_OK;
68 }
69
70 /*
71 *----------------------------------------------------------------------
72 *
73 * DebuggerObjCmd --
74 *
75 * This procedure simply calls the low level debugger, which was
76 * deprecated in OSX 10.8.
77 *
78 * Results:
79 * A standard Tcl result.
80 *
81 * Side effects:
82 * None.
83 *
84 *----------------------------------------------------------------------
85 */
86
87 #if MAC_OS_X_VERSION_MAX_ALLOWED < 1080
88 static int
DebuggerObjCmd(ClientData clientData,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])89 DebuggerObjCmd(
90 ClientData clientData, /* Not used. */
91 Tcl_Interp *interp, /* Not used. */
92 int objc, /* Not used. */
93 Tcl_Obj *const objv[]) /* Not used. */
94 {
95 Debugger();
96 return TCL_OK;
97 }
98 #endif
99
100 /*
101 *----------------------------------------------------------------------
102 *
103 * MenuBarHeightObjCmd --
104 *
105 * This procedure calls [NSMenu menuBarHeight] and returns the result
106 * as an integer. Windows can never be placed to overlap the MenuBar,
107 * so tests need to be aware of its size.
108 *
109 * Results:
110 * A standard Tcl result.
111 *
112 * Side effects:
113 * None.
114 *
115 *----------------------------------------------------------------------
116 */
117
118 static int
MenuBarHeightObjCmd(TCL_UNUSED (void *),Tcl_Interp * interp,TCL_UNUSED (int),TCL_UNUSED (Tcl_Obj * const *))119 MenuBarHeightObjCmd(
120 TCL_UNUSED(void *), /* Not used. */
121 Tcl_Interp *interp, /* Not used. */
122 TCL_UNUSED(int), /* Not used. */
123 TCL_UNUSED(Tcl_Obj *const *)) /* Not used. */
124 {
125 static int height = 0;
126 if (height == 0) {
127 height = (int) [[NSApp mainMenu] menuBarHeight];
128 }
129 Tcl_SetObjResult(interp, Tcl_NewWideIntObj(height));
130 return TCL_OK;
131 }
132
133 /*
134 *----------------------------------------------------------------------
135 *
136 * TkTestLogDisplay --
137 *
138 * The test image display procedure calls this to determine whether it
139 * should write a log message recording that it has being run.
140 *
141 * Results:
142 * Returns true if and only if the NSView of the drawable is the
143 * current focusView, which on 10.14 and newer systems can only be the
144 * case when within [NSView drawRect].
145 *
146 * Side effects:
147 * None
148 *
149 *----------------------------------------------------------------------
150 */
151 MODULE_SCOPE Bool
TkTestLogDisplay(Drawable drawable)152 TkTestLogDisplay(
153 Drawable drawable)
154 {
155 MacDrawable *macWin = (MacDrawable *)drawable;
156 NSWindow *win = nil;
157 if (macWin->toplevel && macWin->toplevel->winPtr &&
158 macWin->toplevel->winPtr->wmInfoPtr &&
159 macWin->toplevel->winPtr->wmInfoPtr->window) {
160 win = macWin->toplevel->winPtr->wmInfoPtr->window;
161 } else if (macWin->winPtr && macWin->winPtr->wmInfoPtr &&
162 macWin->winPtr->wmInfoPtr->window) {
163 win = macWin->winPtr->wmInfoPtr->window;
164 }
165 if (win) {
166 return ([win contentView] == [NSView focusView]);
167 } else {
168 return True;
169 }
170 }
171
172 /*
173 *----------------------------------------------------------------------
174 *
175 * PressButtonObjCmd --
176 *
177 * This Tcl command simulates a button press at a specific screen
178 * location. It injects NSEvents into the NSApplication event queue, as
179 * opposed to adding events to the Tcl queue as event generate would do.
180 * One application is for testing the grab command. These events have
181 * their unused context property set to 1 as a signal indicating that they
182 * should not be ignored by [NSApp tkProcessMouseEvent].
183 *
184 * Results:
185 * A standard Tcl result.
186 *
187 * Side effects:
188 * None.
189 *
190 *----------------------------------------------------------------------
191 */
192
193 static int
PressButtonObjCmd(TCL_UNUSED (void *),Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])194 PressButtonObjCmd(
195 TCL_UNUSED(void *),
196 Tcl_Interp *interp,
197 int objc,
198 Tcl_Obj *const objv[])
199 {
200 int x = 0, y = 0, i, value;
201 NSInteger signal = -1;
202 CGPoint pt;
203 NSPoint loc;
204 NSEvent *motion, *press, *release;
205 NSArray *screens = [NSScreen screens];
206 CGFloat ScreenHeight = 0;
207 enum {X=1, Y};
208
209 if (screens && [screens count]) {
210 ScreenHeight = [[screens objectAtIndex:0] frame].size.height;
211 }
212
213 if (objc != 3) {
214 Tcl_WrongNumArgs(interp, 1, objv, "x y");
215 return TCL_ERROR;
216 }
217 for (i = 1; i < objc; i++) {
218 if (Tcl_GetIntFromObj(interp,objv[i],&value) != TCL_OK) {
219 return TCL_ERROR;
220 }
221 switch (i) {
222 case X:
223 x = value;
224 break;
225 case Y:
226 y = value;
227 break;
228 default:
229 break;
230 }
231 }
232 pt.x = loc.x = x;
233 pt.y = y;
234 loc.y = ScreenHeight - y;
235
236 /*
237 * We set the window number and the eventNumber to -1 as a signal to
238 * processMouseEvent.
239 */
240
241 CGWarpMouseCursorPosition(pt);
242 motion = [NSEvent mouseEventWithType:NSMouseMoved
243 location:loc
244 modifierFlags:0
245 timestamp:GetCurrentEventTime()
246 windowNumber:signal
247 context:nil
248 eventNumber:signal
249 clickCount:1
250 pressure:0.0];
251 [NSApp postEvent:motion atStart:NO];
252 press = [NSEvent mouseEventWithType:NSLeftMouseDown
253 location:loc
254 modifierFlags:0
255 timestamp:GetCurrentEventTime()
256 windowNumber:signal
257 context:nil
258 eventNumber:signal
259 clickCount:1
260 pressure:0.0];
261 [NSApp postEvent:press atStart:NO];
262 release = [NSEvent mouseEventWithType:NSLeftMouseUp
263 location:loc
264 modifierFlags:0
265 timestamp:GetCurrentEventTime()
266 windowNumber:signal
267 context:nil
268 eventNumber:signal
269 clickCount:1
270 pressure:-1.0];
271 [NSApp postEvent:release atStart:NO];
272 return TCL_OK;
273 }
274
275 static int
InjectKeyEventObjCmd(TCL_UNUSED (void *),Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])276 InjectKeyEventObjCmd(
277 TCL_UNUSED(void *),
278 Tcl_Interp *interp,
279 int objc,
280 Tcl_Obj *const objv[])
281 {
282 static const char *const optionStrings[] = {
283 "press", "release", "flagschanged", NULL};
284 NSUInteger types[3] = {NSKeyDown, NSKeyUp, NSFlagsChanged};
285 static const char *const argStrings[] = {
286 "-shift", "-control", "-option", "-command", "-function", "-x", "-y", NULL};
287 enum args {KEYEVENT_SHIFT, KEYEVENT_CONTROL, KEYEVENT_OPTION, KEYEVENT_COMMAND,
288 KEYEVENT_FUNCTION, KEYEVENT_X, KEYEVENT_Y};
289 int i, index, keysym, mods = 0, x = 0, y = 0;
290 NSString *chars = nil, *unmod = nil, *upper, *lower;
291 NSEvent *keyEvent;
292 NSUInteger type;
293 MacKeycode macKC;
294
295 if (objc < 3) {
296 wrongArgs:
297 Tcl_WrongNumArgs(interp, 1, objv, "option keysym ?arg?");
298 return TCL_ERROR;
299 }
300 if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
301 &index) != TCL_OK) {
302 return TCL_ERROR;
303 }
304 type = types[index];
305 if (Tcl_GetIntFromObj(interp, objv[2], &keysym) != TCL_OK) {
306 Tcl_SetObjResult(interp, Tcl_ObjPrintf(
307 "keysym must be an integer"));
308 Tcl_SetErrorCode(interp, "TK", "TEST", "INJECT", "KEYSYM", NULL);
309 return TCL_ERROR;
310 }
311 macKC.uint = XKeysymToKeycode(NULL, keysym);
312 for (i = 3; i < objc; i++) {
313 if (Tcl_GetIndexFromObjStruct(interp, objv[i], argStrings,
314 sizeof(char *), "option", TCL_EXACT, &index) != TCL_OK) {
315 return TCL_ERROR;
316 }
317 switch ((enum args) index) {
318 case KEYEVENT_SHIFT:
319 mods |= NSShiftKeyMask;
320 break;
321 case KEYEVENT_CONTROL:
322 mods |= NSControlKeyMask;
323 break;
324 case KEYEVENT_OPTION:
325 mods |= NSAlternateKeyMask;
326 break;
327 case KEYEVENT_COMMAND:
328 mods |= NSCommandKeyMask;
329 break;
330 case KEYEVENT_FUNCTION:
331 mods |= NSFunctionKeyMask;
332 break;
333 case KEYEVENT_X:
334 if (++i >= objc) {
335 goto wrongArgs;
336 }
337 if (Tcl_GetIntFromObj(interp,objv[i], &x) != TCL_OK) {
338 return TCL_ERROR;
339 }
340 break;
341 case KEYEVENT_Y:
342 if (++i >= objc) {
343 goto wrongArgs;
344 }
345 if (Tcl_GetIntFromObj(interp,objv[i], &y) != TCL_OK) {
346 return TCL_ERROR;
347 }
348 break;
349 }
350 }
351 if (type != NSFlagsChanged) {
352 UniChar keychar = macKC.v.keychar;
353 chars = [[NSString alloc] initWithCharacters: &keychar length:1];
354 upper = [chars uppercaseString];
355 lower = [chars lowercaseString];
356 if (![upper isEqual: lower] && [chars isEqual: upper]) {
357 mods |= NSShiftKeyMask;
358 }
359 if (mods & NSShiftKeyMask) {
360 chars = upper;
361 unmod = lower;
362 macKC.v.o_s |= INDEX_SHIFT;
363 } else {
364 unmod = chars;
365 }
366 if (macKC.v.o_s & INDEX_OPTION) {
367 mods |= NSAlternateKeyMask;
368 }
369 }
370 keyEvent = [NSEvent keyEventWithType:type
371 location:NSMakePoint(x, y)
372 modifierFlags:mods
373 timestamp:GetCurrentEventTime()
374 windowNumber:0
375 context:nil
376 characters:chars
377 charactersIgnoringModifiers:unmod
378 isARepeat:NO
379 keyCode:macKC.v.virt];
380 [NSApp postEvent:keyEvent atStart:NO];
381 return TCL_OK;
382 }
383 /*
384 * Local Variables:
385 * mode: objc
386 * c-basic-offset: 4
387 * fill-column: 79
388 * coding: utf-8
389 * End:
390 */
391