1/*
2 * tkMacOSXKeyEvent.c --
3 *
4 *	This file implements functions that decode & handle keyboard events on
5 *	MacOS X.
6 *
7 * Copyright 2001-2009, Apple Inc.
8 * Copyright (c) 2006-2009 Daniel A. Steffen <das@users.sourceforge.net>
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * RCS: @(#) $Id$
14 */
15
16#include "tkMacOSXPrivate.h"
17#include "tkMacOSXEvent.h"
18
19/*
20#ifdef TK_MAC_DEBUG
21#define TK_MAC_DEBUG_KEYBOARD
22#endif
23*/
24
25static Tk_Window grabWinPtr = NULL;
26				/* Current grab window, NULL if no grab. */
27static Tk_Window keyboardGrabWinPtr = NULL;
28				/* Current keyboard grab window. */
29static NSModalSession modalSession = NULL;
30
31#pragma mark TKApplication(TKKeyEvent)
32
33@implementation TKApplication(TKKeyEvent)
34- (NSEvent *)tkProcessKeyEvent:(NSEvent *)theEvent {
35#ifdef TK_MAC_DEBUG_EVENTS
36    TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent);
37#endif
38    NSWindow*	    w;
39    NSEventType	    type = [theEvent type];
40    NSUInteger	    modifiers, len;
41    BOOL	    repeat = NO;
42    unsigned short  keyCode;
43    NSString	    *characters = nil, *charactersIgnoringModifiers = nil;
44    static NSUInteger savedModifiers = 0;
45
46
47    switch (type) {
48    case NSKeyUp:
49    case NSKeyDown:
50	repeat = [theEvent isARepeat];
51	characters = [theEvent characters];
52	charactersIgnoringModifiers = [theEvent charactersIgnoringModifiers];
53    case NSFlagsChanged:
54	modifiers = [theEvent modifierFlags];
55	keyCode = [theEvent keyCode];
56	w = [self windowWithWindowNumber:[theEvent windowNumber]];
57#ifdef TK_MAC_DEBUG_EVENTS
58	TKLog(@"-[%@(%p) %s] %d %u %@ %@ %u %@", [self class], self, _cmd, repeat, modifiers, characters, charactersIgnoringModifiers, keyCode, w);
59#endif
60	break;
61
62    default:
63	return theEvent;
64	break;
65    }
66
67    unsigned int state = 0;
68
69    if (modifiers & NSAlphaShiftKeyMask) {
70	state |= LockMask;
71    }
72    if (modifiers & NSShiftKeyMask) {
73	state |= ShiftMask;
74    }
75    if (modifiers & NSControlKeyMask) {
76	state |= ControlMask;
77    }
78    if (modifiers & NSCommandKeyMask) {
79	state |= Mod1Mask;		/* command key */
80    }
81    if (modifiers & NSAlternateKeyMask) {
82	state |= Mod2Mask;		/* option key */
83    }
84    if (modifiers & NSNumericPadKeyMask) {
85	state |= Mod3Mask;
86    }
87    if (modifiers & NSFunctionKeyMask) {
88	state |= Mod4Mask;
89    }
90
91    /*
92     * The focus must be in the FrontWindow on the Macintosh. We then query Tk
93     * to determine the exact Tk window that owns the focus.
94     */
95
96    TkWindow *winPtr = TkMacOSXGetTkWindow(w);
97    Tk_Window tkwin = (Tk_Window) winPtr;
98
99    if (!tkwin) {
100	TkMacOSXDbgMsg("tkwin == NULL");
101	return theEvent;
102    }
103    tkwin = (Tk_Window) winPtr->dispPtr->focusPtr;
104    if (!tkwin) {
105	TkMacOSXDbgMsg("tkwin == NULL");
106	return theEvent;
107    }
108
109    XEvent xEvent;
110    memset(&xEvent, 0, sizeof(XEvent));
111    xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
112    xEvent.xany.send_event = false;
113    xEvent.xany.display = Tk_Display(tkwin);
114    xEvent.xany.window = Tk_WindowId(tkwin);
115
116    xEvent.xkey.root = XRootWindow(Tk_Display(tkwin), 0);
117    xEvent.xkey.subwindow = None;
118    xEvent.xkey.time = TkpGetMS();
119    xEvent.xkey.state = state;
120    xEvent.xkey.same_screen = true;
121    xEvent.xkey.trans_chars[0] = 0;
122    xEvent.xkey.nbytes = 0;
123
124    if (type == NSFlagsChanged) {
125	if (savedModifiers > modifiers) {
126	    xEvent.xany.type = KeyRelease;
127	} else {
128	    xEvent.xany.type = KeyPress;
129	}
130
131	/*
132	 * Use special '-1' to signify a special keycode to our platform
133	 * specific code in tkMacOSXKeyboard.c. This is rather like what
134	 * happens on Windows.
135	 */
136
137	xEvent.xany.send_event = -1;
138
139	/*
140	 * Set keycode (which was zero) to the changed modifier
141	 */
142
143	xEvent.xkey.keycode = (modifiers ^ savedModifiers);
144    } else {
145	if (type == NSKeyUp || repeat) {
146	    xEvent.xany.type = KeyRelease;
147	} else {
148	    xEvent.xany.type = KeyPress;
149	}
150	xEvent.xkey.keycode = (keyCode << 16) | (UInt16)
151		[characters characterAtIndex:0];
152	if (![characters getCString:xEvent.xkey.trans_chars
153		maxLength:XMaxTransChars encoding:NSUTF8StringEncoding]) {
154	    TkMacOSXDbgMsg("characters too long");
155	    return theEvent;
156	}
157	len = [charactersIgnoringModifiers length];
158	if (len) {
159	    xEvent.xkey.nbytes = [charactersIgnoringModifiers characterAtIndex:0];
160	    if (len > 1) {
161		TkMacOSXDbgMsg("more than one charactersIgnoringModifiers");
162	    }
163	}
164	if (repeat) {
165	    Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
166	    xEvent.xany.type = KeyPress;
167	    xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin));
168	}
169    }
170    Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL);
171    savedModifiers = modifiers;
172
173    return theEvent;
174}
175@end
176
177#pragma mark -
178
179/*
180 *----------------------------------------------------------------------
181 *
182 * XGrabKeyboard --
183 *
184 *	Simulates a keyboard grab by setting the focus.
185 *
186 * Results:
187 *	Always returns GrabSuccess.
188 *
189 * Side effects:
190 *	Sets the keyboard focus to the specified window.
191 *
192 *----------------------------------------------------------------------
193 */
194
195int
196XGrabKeyboard(
197    Display* display,
198    Window grab_window,
199    Bool owner_events,
200    int pointer_mode,
201    int keyboard_mode,
202    Time time)
203{
204    keyboardGrabWinPtr = Tk_IdToWindow(display, grab_window);
205    if (keyboardGrabWinPtr && grabWinPtr) {
206	NSWindow *w = TkMacOSXDrawableWindow(grab_window);
207	MacDrawable *macWin = (MacDrawable *) grab_window;
208
209	if (w && macWin->toplevel->winPtr == (TkWindow*) grabWinPtr) {
210	    if (modalSession) {
211		Tcl_Panic("XGrabKeyboard: already grabbed");
212	    }
213	    modalSession = [NSApp beginModalSessionForWindow:[w retain]];
214	}
215    }
216    return GrabSuccess;
217}
218
219/*
220 *----------------------------------------------------------------------
221 *
222 * XUngrabKeyboard --
223 *
224 *	Releases the simulated keyboard grab.
225 *
226 * Results:
227 *	None.
228 *
229 * Side effects:
230 *	Sets the keyboard focus back to the value before the grab.
231 *
232 *----------------------------------------------------------------------
233 */
234
235void
236XUngrabKeyboard(
237    Display* display,
238    Time time)
239{
240    if (modalSession) {
241	NSWindow *w = keyboardGrabWinPtr ? TkMacOSXDrawableWindow(
242		((TkWindow *) keyboardGrabWinPtr)->window) : nil;
243	[NSApp endModalSession:modalSession];
244	[w release];
245	modalSession = NULL;
246    }
247    keyboardGrabWinPtr = NULL;
248}
249
250/*
251 *----------------------------------------------------------------------
252 *
253 * TkMacOSXGetCapture --
254 *
255 * Results:
256 *	Returns the current grab window
257 *
258 * Side effects:
259 *	None.
260 *
261 *----------------------------------------------------------------------
262 */
263
264Tk_Window
265TkMacOSXGetCapture(void)
266{
267    return grabWinPtr;
268}
269
270/*
271 *----------------------------------------------------------------------
272 *
273 * TkMacOSXGetModalSession --
274 *
275 * Results:
276 *	Returns the current modal session
277 *
278 * Side effects:
279 *	None.
280 *
281 *----------------------------------------------------------------------
282 */
283
284MODULE_SCOPE NSModalSession
285TkMacOSXGetModalSession(void)
286{
287    return modalSession;
288}
289
290/*
291 *----------------------------------------------------------------------
292 *
293 * TkpSetCapture --
294 *
295 *	This function captures the mouse so that all future events will be
296 *	reported to this window, even if the mouse is outside the window. If
297 *	the specified window is NULL, then the mouse is released.
298 *
299 * Results:
300 *	None.
301 *
302 * Side effects:
303 *	Sets the capture flag and captures the mouse.
304 *
305 *----------------------------------------------------------------------
306 */
307
308void
309TkpSetCapture(
310    TkWindow *winPtr)		/* Capture window, or NULL. */
311{
312    while (winPtr && !Tk_IsTopLevel(winPtr)) {
313	winPtr = winPtr->parentPtr;
314    }
315    grabWinPtr = (Tk_Window) winPtr;
316}
317
318/*
319 *----------------------------------------------------------------------
320 *
321 * Tk_SetCaretPos --
322 *
323 *	This enables correct placement of the XIM caret. This is called by
324 *	widgets to indicate their cursor placement, and the caret location is
325 *	used by TkpGetString to place the XIM caret.
326 *
327 * Results:
328 *	None
329 *
330 * Side effects:
331 *	None
332 *
333 *----------------------------------------------------------------------
334 */
335
336void
337Tk_SetCaretPos(
338    Tk_Window tkwin,
339    int x,
340    int y,
341    int height)
342{
343}
344
345/*
346 * Local Variables:
347 * mode: c
348 * c-basic-offset: 4
349 * fill-column: 79
350 * coding: utf-8
351 * End:
352 */
353