diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-12-21 22:56:39 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-12-21 22:56:39 (GMT) |
commit | 2bc8acacaa385fe4e607a99569b502032f98bc64 (patch) | |
tree | 7e70af1addc0d893b8daf4339f277cbf939998d8 /tk8.6/macosx/tkMacOSXMouseEvent.c | |
parent | d1a6de55efc90f190dee42ab8c4fa9070834e77d (diff) | |
parent | 1741f1b6324ead16eb1eeaa16e1f18cf0a2abb4f (diff) | |
download | blt-2bc8acacaa385fe4e607a99569b502032f98bc64.zip blt-2bc8acacaa385fe4e607a99569b502032f98bc64.tar.gz blt-2bc8acacaa385fe4e607a99569b502032f98bc64.tar.bz2 |
Merge commit '1741f1b6324ead16eb1eeaa16e1f18cf0a2abb4f' as 'tk8.6'
Diffstat (limited to 'tk8.6/macosx/tkMacOSXMouseEvent.c')
-rw-r--r-- | tk8.6/macosx/tkMacOSXMouseEvent.c | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/tk8.6/macosx/tkMacOSXMouseEvent.c b/tk8.6/macosx/tkMacOSXMouseEvent.c new file mode 100644 index 0000000..c4197f7 --- /dev/null +++ b/tk8.6/macosx/tkMacOSXMouseEvent.c @@ -0,0 +1,580 @@ +/* + * tkMacOSXMouseEvent.c -- + * + * This file implements functions that decode & handle mouse events on + * MacOS X. + * + * Copyright 2001-2009, Apple Inc. + * Copyright (c) 2005-2009 Daniel A. Steffen <das@users.sourceforge.net> + * + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + +#include "tkMacOSXPrivate.h" +#include "tkMacOSXWm.h" +#include "tkMacOSXEvent.h" +#include "tkMacOSXDebug.h" + +typedef struct { + unsigned int state; + long delta; + Window window; + Point global; + Point local; +} MouseEventData; + +static int GenerateButtonEvent(MouseEventData *medPtr); +static unsigned int ButtonModifiers2State(UInt32 buttonState, + UInt32 keyModifiers); + +#pragma mark TKApplication(TKMouseEvent) + +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 + * had changed. The new behavior was that if the mouse were ever moved outside + * of a window, all subsequent NSMouseMoved NSEvents would have a Nil window + * attribute. To work around this the TKApplication remembers the last non-Nil + * window that it received in a mouse event. If it receives an NSEvent with a + * Nil window attribute then the saved window is used. + */ + +@implementation TKApplication(TKMouseEvent) +- (NSEvent *) tkProcessMouseEvent: (NSEvent *) theEvent +{ +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); +#endif + NSWindow* eventWindow = [theEvent window]; + NSEventType eventType = [theEvent type]; +#if 0 + NSTrackingArea *trackingArea = nil; + NSInteger eventNumber, clickCount, buttonNumber; +#endif + switch (eventType) { + case NSMouseEntered: + case NSMouseExited: + case NSCursorUpdate: + case NSLeftMouseDown: + case NSLeftMouseUp: + case NSRightMouseDown: + case NSRightMouseUp: + case NSOtherMouseDown: + case NSOtherMouseUp: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + case NSMouseMoved: + case NSTabletPoint: + case NSTabletProximity: + case NSScrollWheel: + break; + default: /* Unrecognized mouse event. */ + return theEvent; + } + + /* Remember the window in case we need it next time. */ + if (eventWindow && eventWindow != _windowWithMouse) { + if (_windowWithMouse) { + [_windowWithMouse release]; + } + _windowWithMouse = eventWindow; + [_windowWithMouse retain]; + } + + /* Create an Xevent to add to the Tk queue. */ + NSPoint global, local = [theEvent locationInWindow]; + if (eventWindow) { /* local will be in window coordinates. */ + global = [eventWindow convertPointToScreen: local]; + local.y = [eventWindow frame].size.height - local.y; + global.y = tkMacOSXZeroScreenHeight - global.y; + } else { /* local will be in screen coordinates. */ + if (_windowWithMouse ) { + eventWindow = _windowWithMouse; + global = local; + local = [eventWindow convertPointFromScreen: local]; + local.y = [eventWindow frame].size.height - local.y; + 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); + + if (err == noErr) { + state |= (buttons & ((1<<5) - 1)) << 8; + } else if (button < 5) { + switch (eventType) { + case NSLeftMouseDown: + case NSRightMouseDown: + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDown: + state |= 1 << (button + 8); + break; + default: + break; + } + } + + NSUInteger modifiers = [theEvent modifierFlags]; + + if (modifiers & NSAlphaShiftKeyMask) { + state |= LockMask; + } + if (modifiers & NSShiftKeyMask) { + state |= ShiftMask; + } + if (modifiers & NSControlKeyMask) { + state |= ControlMask; + } + if (modifiers & NSCommandKeyMask) { + state |= Mod1Mask; /* command key */ + } + if (modifiers & NSAlternateKeyMask) { + state |= Mod2Mask; /* option key */ + } + if (modifiers & NSNumericPadKeyMask) { + state |= Mod3Mask; + } + if (modifiers & NSFunctionKeyMask) { + state |= Mod4Mask; + } + + if (eventType != NSScrollWheel) { +#ifdef TK_MAC_DEBUG_EVENTS + TKLog(@"UpdatePointer %p x %f.0 y %f.0 %d", tkwin, global.x, global.y, state); +#endif + Tk_UpdatePointer(tkwin, global.x, global.y, state); + } else { /* handle scroll wheel event */ + CGFloat delta; + int coarseDelta; + XEvent xEvent; + + xEvent.type = MouseWheelEvent; + xEvent.xbutton.x = local.x; + xEvent.xbutton.y = local.y; + xEvent.xbutton.x_root = global.x; + xEvent.xbutton.y_root = global.y; + xEvent.xany.send_event = false; + xEvent.xany.display = Tk_Display(tkwin); + xEvent.xany.window = Tk_WindowId(tkwin); + + delta = [theEvent deltaY]; + if (delta != 0.0) { + coarseDelta = (delta > -1.0 && delta < 1.0) ? + (signbit(delta) ? -1 : 1) : lround(delta); + xEvent.xbutton.state = state; + xEvent.xkey.keycode = coarseDelta; + xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); + Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); + } + delta = [theEvent deltaX]; + if (delta != 0.0) { + coarseDelta = (delta > -1.0 && delta < 1.0) ? + (signbit(delta) ? -1 : 1) : lround(delta); + xEvent.xbutton.state = state | ShiftMask; + xEvent.xkey.keycode = coarseDelta; + xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); + Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); + } + } + return theEvent; +} +@end + +#pragma mark - + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXKeyModifiers -- + * + * Returns the current state of the modifier keys. + * + * Results: + * An OS Modifier state. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +EventModifiers +TkMacOSXModifierState(void) +{ + UInt32 keyModifiers; + int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront()); + + keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() : + GetCurrentKeyModifiers(); + + return (EventModifiers) (keyModifiers & USHRT_MAX); +} + +/* + *---------------------------------------------------------------------- + * + * TkMacOSXButtonKeyState -- + * + * Returns the current state of the button & modifier keys. + * + * Results: + * A bitwise inclusive OR of a subset of the following: Button1Mask, + * ShiftMask, LockMask, ControlMask, Mod*Mask. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +unsigned int +TkMacOSXButtonKeyState(void) +{ + UInt32 buttonState = 0, keyModifiers; + int isFrontProcess = (GetCurrentEvent() && Tk_MacOSXIsAppInFront()); + + buttonState = isFrontProcess ? GetCurrentEventButtonState() : + GetCurrentButtonState(); + keyModifiers = isFrontProcess ? GetCurrentEventKeyModifiers() : + GetCurrentKeyModifiers(); + + return ButtonModifiers2State(buttonState, keyModifiers); +} + +/* + *---------------------------------------------------------------------- + * + * ButtonModifiers2State -- + * + * Converts Carbon mouse button state and modifier values into a Tk + * button/modifier state. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static unsigned int +ButtonModifiers2State( + UInt32 buttonState, + UInt32 keyModifiers) +{ + unsigned int state; + + /* + * Tk supports at most 5 buttons. + */ + + state = (buttonState & ((1<<5) - 1)) << 8; + + if (keyModifiers & alphaLock) { + state |= LockMask; + } + if (keyModifiers & shiftKey) { + state |= ShiftMask; + } + if (keyModifiers & controlKey) { + state |= ControlMask; + } + if (keyModifiers & cmdKey) { + state |= Mod1Mask; /* command key */ + } + if (keyModifiers & optionKey) { + state |= Mod2Mask; /* option key */ + } + if (keyModifiers & kEventKeyModifierNumLockMask) { + state |= Mod3Mask; + } + if (keyModifiers & kEventKeyModifierFnMask) { + state |= Mod4Mask; + } + + return state; +} + +/* + *---------------------------------------------------------------------- + * + * XQueryPointer -- + * + * Check the current state of the mouse. This is not a complete + * implementation of this function. It only computes the root coordinates + * and the current mask. + * + * Results: + * Sets root_x_return, root_y_return, and mask_return. Returns true on + * success. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +Bool +XQueryPointer( + Display *display, + Window w, + Window *root_return, + Window *child_return, + int *root_x_return, + int *root_y_return, + int *win_x_return, + int *win_y_return, + unsigned int *mask_return) +{ + int getGlobal = (root_x_return && root_y_return); + int getLocal = (win_x_return && win_y_return && w != None); + + if (getGlobal || getLocal) { + NSPoint global = [NSEvent mouseLocation]; + + if (getLocal) { + MacDrawable *macWin = (MacDrawable *) w; + NSWindow *win = TkMacOSXDrawableWindow(w); + + if (win) { + NSPoint local; + + local = [win convertPointFromScreen:global]; + local.y = [win frame].size.height - local.y; + if (macWin->winPtr && macWin->winPtr->wmInfoPtr) { + local.x -= macWin->winPtr->wmInfoPtr->xInParent; + local.y -= macWin->winPtr->wmInfoPtr->yInParent; + } + *win_x_return = local.x; + *win_y_return = local.y; + } + } + if (getGlobal) { + *root_x_return = global.x; + *root_y_return = tkMacOSXZeroScreenHeight - global.y; + } + } + if (mask_return) { + *mask_return = TkMacOSXButtonKeyState(); + } + return True; +} + +/* + *---------------------------------------------------------------------- + * + * TkGenerateButtonEventForXPointer -- + * + * This procedure generates an X button event for the current pointer + * state as reported by XQueryPointer(). + * + * Results: + * True if event(s) are generated - false otherwise. + * + * Side effects: + * Additional events may be place on the Tk event queue. Grab state may + * also change. + * + *---------------------------------------------------------------------- + */ + +MODULE_SCOPE int +TkGenerateButtonEventForXPointer( + Window window) /* X Window containing button event. */ +{ + MouseEventData med; + int global_x, global_y, local_x, local_y; + + bzero(&med, sizeof(MouseEventData)); + XQueryPointer(NULL, window, NULL, NULL, &global_x, &global_y, + &local_x, &local_y, &med.state); + med.global.h = global_x; + med.global.v = global_y; + med.local.h = local_x; + med.local.v = local_y; + med.window = window; + + return GenerateButtonEvent(&med); +} + +/* + *---------------------------------------------------------------------- + * + * 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 + * changes needed to implement implicit grabs. + * + * Results: + * True if event(s) are generated, false otherwise. + * + * Side effects: + * Additional events may be place on the Tk event queue. Grab state may + * also change. + * + *---------------------------------------------------------------------- + */ + +int +TkGenerateButtonEvent( + int x, /* X location of mouse, */ + int y, /* Y location of mouse. */ + Window window, /* X Window containing button event. */ + unsigned int state) /* Button Key state suitable for X event. */ +{ + MacDrawable *macWin = (MacDrawable *) window; + NSWindow *win = TkMacOSXDrawableWindow(window); + MouseEventData med; + + bzero(&med, sizeof(MouseEventData)); + med.state = state; + med.window = window; + med.global.h = x; + med.global.v = y; + med.local = med.global; + + if (win) { + NSPoint local = NSMakePoint(x, tkMacOSXZeroScreenHeight - y); + + local = [win convertPointFromScreen:local]; + local.y = [win frame].size.height - local.y; + if (macWin->winPtr && macWin->winPtr->wmInfoPtr) { + local.x -= macWin->winPtr->wmInfoPtr->xInParent; + local.y -= macWin->winPtr->wmInfoPtr->yInParent; + } + med.local.h = local.x; + med.local.v = tkMacOSXZeroScreenHeight - local.y; + } + + return GenerateButtonEvent(&med); +} + +/* + *---------------------------------------------------------------------- + * + * GenerateButtonEvent -- + * + * Generate an X button event from a MouseEventData structure. Handles + * the state changes needed to implement implicit grabs. + * + * Results: + * True if event(s) are generated - false otherwise. + * + * Side effects: + * Additional events may be place on the Tk event queue. Grab state may + * also change. + * + *---------------------------------------------------------------------- + */ + +static int +GenerateButtonEvent( + MouseEventData *medPtr) +{ + Tk_Window tkwin; + int dummy; + TkDisplay *dispPtr; + +#if UNUSED + /* + * ButtonDown events will always occur in the front window. ButtonUp + * events, however, may occur anywhere on the screen. ButtonUp events + * should only be sent to Tk if in the front window or during an implicit + * grab. + */ + + if ((medPtr->activeNonFloating == NULL) + || ((!(TkpIsWindowFloating(medPtr->whichWin)) + && (medPtr->activeNonFloating != medPtr->whichWin)) + && TkMacOSXGetCapture() == NULL)) { + return false; + } +#endif + + dispPtr = TkGetDisplayList(); + tkwin = Tk_IdToWindow(dispPtr->display, medPtr->window); + + if (tkwin != NULL) { + tkwin = Tk_TopCoordsToWindow(tkwin, medPtr->local.h, medPtr->local.v, + &dummy, &dummy); + } + + Tk_UpdatePointer(tkwin, medPtr->global.h, medPtr->global.v, medPtr->state); + return true; +} + +void +TkpWarpPointer( + TkDisplay *dispPtr) +{ + CGPoint pt; + UInt32 buttonState; + + if (dispPtr->warpWindow) { + int x, y; + + Tk_GetRootCoords(dispPtr->warpWindow, &x, &y); + pt.x = x + dispPtr->warpX; + pt.y = y + dispPtr->warpY; + } else { + pt.x = dispPtr->warpX; + pt.y = dispPtr->warpY; + } + + /* + * Tell the OSX core to generate the events to make it happen. + */ + + buttonState = [NSEvent pressedMouseButtons]; + CGEventType type = kCGEventMouseMoved; + CGEventRef theEvent = CGEventCreateMouseEvent(NULL, + type, + pt, + buttonState); + CGWarpMouseCursorPosition(pt); + CGEventPost(kCGHIDEventTap, theEvent); + CFRelease(theEvent); +} + +/* + * Local Variables: + * mode: objc + * c-basic-offset: 4 + * fill-column: 79 + * coding: utf-8 + * End: + */ |