summaryrefslogtreecommitdiffstats
path: root/macosx/tkMacOSXMouseEvent.c
diff options
context:
space:
mode:
Diffstat (limited to 'macosx/tkMacOSXMouseEvent.c')
-rw-r--r--macosx/tkMacOSXMouseEvent.c402
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;
}
/*