diff options
Diffstat (limited to 'macosx/tkMacOSXMouseEvent.c')
-rw-r--r-- | macosx/tkMacOSXMouseEvent.c | 402 |
1 files changed, 201 insertions, 201 deletions
diff --git a/macosx/tkMacOSXMouseEvent.c b/macosx/tkMacOSXMouseEvent.c index 7dcfa10..83eef3d 100644 --- a/macosx/tkMacOSXMouseEvent.c +++ b/macosx/tkMacOSXMouseEvent.c @@ -24,6 +24,7 @@ typedef struct { Point global; Point local; } MouseEventData; + static Tk_Window captureWinPtr = NULL; /* Current capture window; may be * NULL. */ @@ -42,9 +43,9 @@ enum { * 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. + * attribute until the mouse returned to the window. In 11.1 it changed again. + * The window attribute can be non-nil, but referencing a window which does not + * belong to the application. */ @implementation TKApplication(TKMouseEvent) @@ -52,151 +53,167 @@ enum { { NSWindow *eventWindow = [theEvent window]; NSEventType eventType = [theEvent type]; + NSRect viewFrame = [[eventWindow contentView] frame]; + NSPoint location = [theEvent locationInWindow]; TkWindow *winPtr = NULL, *grabWinPtr; - Tk_Window tkwin; + Tk_Window tkwin = NULL, capture, target; NSPoint local, global; - NSInteger button = -1; -#if 0 - NSTrackingArea *trackingArea = nil; - NSInteger eventNumber, clickCount, buttonNumber; -#endif - [NSEvent stopPeriodicEvents]; + NSInteger button; + Bool inTitleBar = NO; + int win_x, win_y; + unsigned int buttonState = 0; + static int validPresses = 0, ignoredPresses = 0; #ifdef TK_MAC_DEBUG_EVENTS TKLog(@"-[%@(%p) %s] %@", [self class], self, _cmd, theEvent); #endif + + /* + * If this event is not for a Tk toplevel, it should just be passed up the + * responder chain. However, there is an exception for synthesized events, + * which are used in testing. Those events are recognized by having their + * both the windowNumber and the eventNumber set to -1. + */ + + if (eventWindow && ![eventWindow isMemberOfClass:[TKWindow class]]) { + if ([theEvent windowNumber] != -1 || [theEvent eventNumber] != -1) + return theEvent; + } + + /* + * Check if the event is located in the titlebar. + */ + + if (eventWindow) { + inTitleBar = viewFrame.size.height < location.y; + } + + button = [theEvent buttonNumber] + Button1; + if ((button & -2) == Button2) { + button ^= 1; /* Swap buttons 2/3 */ + } switch (eventType) { - case NSLeftMouseDown: - case NSRightMouseDown: - case NSOtherMouseDown: + case NSRightMouseUp: + case NSOtherMouseUp: + buttonState &= ~Tk_GetButtonMask(button); + break; case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged: - button = [theEvent buttonNumber] + Button1; + case NSRightMouseDown: + case NSOtherMouseDown: + buttonState |= Tk_GetButtonMask(button); + break; case NSMouseEntered: + if ([eventWindow respondsToSelector:@selector(mouseInResizeArea)] && + !inTitleBar) { + [(TKWindow *)eventWindow setMouseInResizeArea:YES]; + } + break; case NSMouseExited: - case NSCursorUpdate: + if ([eventWindow respondsToSelector:@selector(mouseInResizeArea)]) { + [(TKWindow *)eventWindow setMouseInResizeArea:NO]; + break; + } case NSLeftMouseUp: - case NSRightMouseUp: - case NSOtherMouseUp: + case NSLeftMouseDown: case NSMouseMoved: + case NSScrollWheel: +#if 0 + case NSCursorUpdate: case NSTabletPoint: case NSTabletProximity: - case NSScrollWheel: +#endif break; - default: /* Unrecognized mouse event. */ + default: /* This type of event is ignored. */ return theEvent; } /* - * Compute the mouse position in Tk screen coordinates (global) and in the - * Tk coordinates of its containing Tk Window (local). If a grab is in effect, - * the local coordinates should be relative to the grab window. + * Update the button state. We ignore left button presses that start a + * resize or occur in the title bar. See tickets [d72abe6b54] and + * [39cbacb9e8]. */ - if (eventWindow) { - local = [theEvent locationInWindow]; + if (eventType == NSLeftMouseDown) { + if ([eventWindow respondsToSelector:@selector(mouseInResizeArea)] && + [(TKWindow *) eventWindow mouseInResizeArea]) { - /* - * Do not send ButtonPress XEvents for MouseDown NSEvents that start a - * resize. (The MouseUp will be handled during LiveResize.) See - * ticket [d72abe6b54]. - */ + /* + * When the left button is pressed in the resize area, we receive + * NSMouseDown, but when it is released we do not receive + * NSMouseUp. So ignore the event and clear the button state but + * do not change the ignoredPresses count. + */ - if (eventType == NSLeftMouseDown && - ([eventWindow styleMask] & NSResizableWindowMask) && - [NSApp macMinorVersion] > 6) { - NSRect frame = [eventWindow frame]; - if (local.x < 3 || local.x > frame.size.width - 3 || local.y < 3) { - return theEvent; - } + buttonState &= ~Tk_GetButtonMask(Button1); + return theEvent; } - global = [eventWindow tkConvertPointToScreen: local]; - tkwin = TkpGetCapture(); - if (tkwin) { - winPtr = (TkWindow *) tkwin; - eventWindow = TkMacOSXDrawableWindow(winPtr->window); - if (eventWindow) { - local = [eventWindow tkConvertPointFromScreen: global]; - } else { - return theEvent; - } + if (inTitleBar) { + ignoredPresses++; + return theEvent; } - local.y = [eventWindow frame].size.height - local.y; - global.y = TkMacOSXZeroScreenHeight() - global.y; - } else { - - /* - * If the event has no NSWindow, the location is in screen coordinates. - */ - - global = [theEvent locationInWindow]; - tkwin = TkpGetCapture(); - if (tkwin) { - winPtr = (TkWindow *) tkwin; - eventWindow = TkMacOSXDrawableWindow(winPtr->window); - } else { - eventWindow = [NSApp mainWindow]; + validPresses++; + buttonState |= Tk_GetButtonMask(Button1); + } + if (eventType == NSLeftMouseUp) { + if (ignoredPresses > 0) { + ignoredPresses--; + } else if (validPresses > 0) { + validPresses--; } - if (!eventWindow) { - return theEvent; + if (validPresses == 0) { + buttonState &= ~Tk_GetButtonMask(Button1); } - local = [eventWindow tkConvertPointFromScreen: global]; - local.y = [eventWindow frame].size.height - local.y; - global.y = TkMacOSXZeroScreenHeight() - global.y; } /* - * If we still don't have a window, try using the toplevel that - * manages the NSWindow. + * Find an appropriate NSWindow to attach to this event, and its + * associated Tk window. */ - if (!tkwin) { - winPtr = TkMacOSXGetTkWindow(eventWindow); - tkwin = (Tk_Window) winPtr; + capture = TkpGetCapture(); + if (capture) { + winPtr = (TkWindow *) capture; + eventWindow = TkMacOSXGetNSWindowForDrawable(winPtr->window); + if (!eventWindow) { + return theEvent; + } + } else { + if (eventWindow) { + winPtr = TkMacOSXGetTkWindow(eventWindow); + } + if (!winPtr) { + eventWindow = [NSApp mainWindow]; + winPtr = TkMacOSXGetTkWindow(eventWindow); + } } - if (!tkwin) { + if (!winPtr) { /* - * We can't find a window for this event. We have to ignore it. + * We couldn't find a Tk window for this event. We have to ignore it. */ #ifdef TK_MAC_DEBUG_EVENTS - TkMacOSXDbgMsg("tkwin == NULL"); + TkMacOSXDbgMsg("Event received with no Tk window."); #endif return theEvent; } + tkwin = (Tk_Window) winPtr; /* - * Ignore the event if a local grab is in effect and the Tk event window is - * not in the grabber's subtree. - */ - - grabWinPtr = winPtr->dispPtr->grabWinPtr; - if (grabWinPtr && /* There is a grab in effect ... */ - !winPtr->dispPtr->grabFlags && /* and it is a local grab ... */ - grabWinPtr->mainPtr == winPtr->mainPtr){ /* in the same application. */ - Tk_Window tkwin2, tkEventWindow = Tk_CoordsToWindow(global.x, global.y, tkwin); - if (!tkEventWindow) { - return theEvent; - } - for (tkwin2 = tkEventWindow; - !Tk_IsTopLevel(tkwin2); - tkwin2 = Tk_Parent(tkwin2)) { - if (tkwin2 == (Tk_Window) grabWinPtr) { - break; - } - } - if (tkwin2 != (Tk_Window) grabWinPtr) { - return theEvent; - } - } - - /* - * Convert local from NSWindow flipped coordinates to the toplevel's - * coordinates. + * Compute the mouse position in local (window) and global (screen) + * coordinates. These are Tk coordinates, meaning that the local origin is + * at the top left corner of the containing toplevel and the global origin + * is at top left corner of the primary screen. */ + global = [NSEvent mouseLocation]; + local = [eventWindow tkConvertPointFromScreen: global]; + global.x = floor(global.x); + global.y = floor(TkMacOSXZeroScreenHeight() - global.y); + local.x = floor(local.x); + local.y = floor([eventWindow frame].size.height - local.y); if (Tk_IsEmbedded(winPtr)) { TkWindow *contPtr = TkpGetOtherWindow(winPtr); if (Tk_IsTopLevel(contPtr)) { @@ -213,25 +230,44 @@ enum { } /* - * Use the toplevel coordinates to find the containing Tk window. Then - * convert local into the coordinates of that window. (The converted - * local coordinates are only needed for scrollwheel events.) + * Use the local coordinates to find the Tk window which should receive + * this event. Also convert local into the coordinates of that window. + * (The converted local coordinates are only needed for scrollwheel + * events.) */ - int win_x, win_y; - tkwin = Tk_TopCoordsToWindow(tkwin, local.x, local.y, &win_x, &win_y); - local.x = win_x; - local.y = win_y; + target = Tk_TopCoordsToWindow(tkwin, local.x, local.y, &win_x, &win_y); /* - * Generate an XEvent for this mouse event. + * Ignore the event if a local grab is in effect and the Tk window is + * not in the grabber's subtree. */ - unsigned int state = 0; - if (button > 0) { - state |= TkGetButtonMask(button); + grabWinPtr = winPtr->dispPtr->grabWinPtr; + if (grabWinPtr && /* There is a grab in effect ... */ + !winPtr->dispPtr->grabFlags && /* and it is a local grab ... */ + grabWinPtr->mainPtr == winPtr->mainPtr){ /* in the same application. */ + Tk_Window tkwin2; + if (!target) { + return theEvent; + } + for (tkwin2 = target; + !Tk_IsTopLevel(tkwin2); + tkwin2 = Tk_Parent(tkwin2)) { + if (tkwin2 == (Tk_Window)grabWinPtr) { + break; + } + } + if (tkwin2 != (Tk_Window)grabWinPtr) { + return theEvent; + } } + /* + * Generate an XEvent for this mouse event. + */ + + unsigned int state = buttonState; NSUInteger modifiers = [theEvent modifierFlags]; if (modifiers & NSAlphaShiftKeyMask) { @@ -259,48 +295,45 @@ enum { if (eventType != NSScrollWheel) { /* - * For normal mouse events, Tk_UpdatePointer will send the XEvent. + * For normal mouse events, Tk_UpdatePointer will send the appropriate + * XEvents using its cached state information. Unfortunately, it will + * also recompute the local coordinates. */ #ifdef TK_MAC_DEBUG_EVENTS - TKLog(@"UpdatePointer %p x %f.0 y %f.0 %d", - tkwin, global.x, global.y, state); + TKLog(@"UpdatePointer %p x %.1f y %.1f %d", + target, global.x, global.y, state); #endif - Tk_UpdatePointer(tkwin, global.x, global.y, state); + + Tk_UpdatePointer(target, global.x, global.y, state); } else { + CGFloat delta; + XEvent xEvent; /* * For scroll wheel events we need to send the XEvent here. */ - CGFloat delta; - int coarseDelta; - XEvent xEvent; - xEvent.type = MouseWheelEvent; - xEvent.xbutton.x = local.x; - xEvent.xbutton.y = local.y; + xEvent.xbutton.x = win_x; + xEvent.xbutton.y = win_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); + xEvent.xany.display = Tk_Display(target); + xEvent.xany.window = Tk_WindowId(target); - delta = [theEvent deltaY]; + delta = [theEvent deltaY] * 120; 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.xkey.keycode = (delta > 0) ? ceil(delta) : floor(delta); xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); } - delta = [theEvent deltaX]; + delta = [theEvent deltaX] * 120; 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.xkey.keycode = (delta > 0) ? ceil(delta) : floor(delta); xEvent.xany.serial = LastKnownRequestProcessed(Tk_Display(tkwin)); Tk_QueueWindowEvent(&xEvent, TCL_QUEUE_TAIL); } @@ -314,34 +347,6 @@ enum { /* *---------------------------------------------------------------------- * - * 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. @@ -398,8 +403,15 @@ ButtonModifiers2State( * Tk on OSX supports at most 9 buttons. */ - state = (buttonState & 0x7F) * Button1Mask; - /* Handle buttons 8/9 */ + state = (buttonState & 0x079) * Button1Mask; + /* Handle swapped buttons 2/3 */ + if (buttonState & 0x02) { + state |= Button3Mask; + } + if (buttonState & 0x04) { + state |= Button2Mask; + } + /* Handle buttons 8/9 */ state |= (buttonState & 0x180) * (Button8Mask >> 7); if (keyModifiers & alphaLock) { @@ -468,8 +480,8 @@ XQueryPointer( NSPoint global = [NSEvent mouseLocation]; if (getLocal) { - MacDrawable *macWin = (MacDrawable *) w; - NSWindow *win = TkMacOSXDrawableWindow(w); + MacDrawable *macWin = (MacDrawable *)w; + NSWindow *win = TkMacOSXGetNSWindowForDrawable(w); if (win) { NSPoint local; @@ -507,7 +519,7 @@ XQueryPointer( * True if event(s) are generated - false otherwise. * * Side effects: - * Additional events may be place on the Tk event queue. Grab state may + * Additional events may be placed on the Tk event queue. Grab state may * also change. * *---------------------------------------------------------------------- @@ -545,7 +557,7 @@ TkGenerateButtonEventForXPointer( * True if event(s) are generated, false otherwise. * * Side effects: - * Additional events may be place on the Tk event queue. Grab state may + * Additional events may be placed on the Tk event queue. Grab state may * also change. * *---------------------------------------------------------------------- @@ -558,8 +570,8 @@ TkGenerateButtonEvent( 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); + MacDrawable *macWin = (MacDrawable *)window; + NSWindow *win = TkMacOSXGetNSWindowForDrawable(window); MouseEventData med; bzero(&med, sizeof(MouseEventData)); @@ -597,7 +609,7 @@ TkGenerateButtonEvent( * True if event(s) are generated - false otherwise. * * Side effects: - * Additional events may be place on the Tk event queue. Grab state may + * Additional events may be placed on the Tk event queue. Grab state may * also change. * *---------------------------------------------------------------------- @@ -611,7 +623,7 @@ GenerateButtonEvent( int dummy; TkDisplay *dispPtr; -#if UNUSED +#ifdef UNUSED /* * ButtonDown events will always occur in the front window. ButtonUp @@ -640,51 +652,40 @@ GenerateButtonEvent( return true; } +/* + *---------------------------------------------------------------------- + * + * TkpWarpPointer -- + * + * Move the mouse cursor to the screen location specified by the warpX and + * warpY fields of a TkDisplay. + * + * Results: + * None + * + * Side effects: + * The mouse cursor is moved. + * + *---------------------------------------------------------------------- + */ + void TkpWarpPointer( TkDisplay *dispPtr) { CGPoint pt; - NSPoint loc; - int wNum; if (dispPtr->warpWindow) { int x, y; - TkWindow *winPtr = (TkWindow *) dispPtr->warpWindow; - TkWindow *topPtr = winPtr->privatePtr->toplevel->winPtr; - NSWindow *w = TkMacOSXDrawableWindow(winPtr->window); - wNum = [w windowNumber]; Tk_GetRootCoords(dispPtr->warpWindow, &x, &y); pt.x = x + dispPtr->warpX; pt.y = y + dispPtr->warpY; - loc.x = dispPtr->warpX; - loc.y = Tk_Height(topPtr) - dispPtr->warpY; } else { - wNum = 0; - pt.x = loc.x = dispPtr->warpX; + pt.x = dispPtr->warpX; pt.y = dispPtr->warpY; - loc.y = TkMacOSXZeroScreenHeight() - pt.y; } - /* - * Generate an NSEvent of type NSMouseMoved. - * - * It is not clear why this is necessary. For example, calling - * event generate $w <Motion> -warp 1 -x $X -y $Y - * will cause two <Motion> events to be added to the Tcl queue. - */ - CGWarpMouseCursorPosition(pt); - NSEvent *warpEvent = [NSEvent mouseEventWithType:NSMouseMoved - location:loc - modifierFlags:0 - timestamp:GetCurrentEventTime() - windowNumber:wNum - context:nil - eventNumber:0 - clickCount:1 - pressure:0.0]; - [NSApp postEvent:warpEvent atStart:NO]; } /* @@ -712,8 +713,7 @@ TkpSetCapture( while (winPtr && !Tk_IsTopLevel(winPtr)) { winPtr = winPtr->parentPtr; } - [NSEvent stopPeriodicEvents]; - captureWinPtr = (Tk_Window) winPtr; + captureWinPtr = (Tk_Window)winPtr; } /* |