// @(#)root/graf2d:$Id$ // Author: Timur Pocheptsov 16/02/2012 /************************************************************************* * Copyright (C) 1995-2012, Rene Brun and Fons Rademakers. * * All rights reserved. * * * * For the licensing terms see $ROOTSYS/LICENSE. * * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ //#define NDEBUG #include #include #include #include "CocoaConstants.h" #include "ROOTOpenGLView.h" #include "QuartzWindow.h" #include "CocoaUtils.h" #include "KeySymbols.h" #include "X11Events.h" #include "TGClient.h" #include "TGWindow.h" #include "TList.h" @interface FakeCrossingEvent : NSEvent { NSWindow *fQuartzWindow; NSPoint fLocationInWindow; } @end @implementation FakeCrossingEvent //______________________________________________________________________________ - (id) initWithWindow : (NSWindow *) window location : (NSPoint) location { //Window should be always non-nil: we either enter some window, or exit some window. assert(window && "initWithWindow:location:, parameter 'window' is nil"); if (self = [super init]) { fQuartzWindow = window; fLocationInWindow = location; } return self; } //______________________________________________________________________________ - (NSWindow *) window { assert(fQuartzWindow && "window, fQuartzWindow is nil"); return fQuartzWindow; } //______________________________________________________________________________ - (NSPoint) locationInWindow { assert(fQuartzWindow != nil && "locationInWindow, fQuartzWindow is nil"); return fLocationInWindow; } //______________________________________________________________________________ - (NSTimeInterval) timestamp { //Hehe. return 0.; } @end namespace ROOT { namespace MacOSX { namespace X11 { namespace { //Convert unichar (from [NSEvent characters]) into //ROOT's key symbol (from KeySymbols.h). template struct KeySymPair { T1 fFirst; T2 fSecond; bool operator < (const KeySymPair &rhs)const { return fFirst < rhs.fFirst; } }; } //______________________________________________________________________________ void MapUnicharToKeySym(unichar key, char *buf, Int_t /*len*/, UInt_t &rootKeySym) { assert(buf != 0 && "MapUnicharToKeySym, parameter 'buf' is null"); static const KeySymPair keyMap[] = { {NSEnterCharacter, kKey_Enter}, {NSTabCharacter, kKey_Tab}, {NSCarriageReturnCharacter, kKey_Return}, {NSBackTabCharacter, kKey_Backtab}, //WHYWHYWHY apple does not have a constant for escape???? {27, kKey_Escape}, {NSDeleteCharacter, kKey_Backspace}, {NSUpArrowFunctionKey, kKey_Up}, {NSDownArrowFunctionKey, kKey_Down}, {NSLeftArrowFunctionKey, kKey_Left}, {NSRightArrowFunctionKey, kKey_Right}, {NSF1FunctionKey, kKey_F1}, {NSF2FunctionKey, kKey_F2}, {NSF3FunctionKey, kKey_F3}, {NSF4FunctionKey, kKey_F4}, {NSF5FunctionKey, kKey_F5}, {NSF6FunctionKey, kKey_F6}, {NSF7FunctionKey, kKey_F7}, {NSF8FunctionKey, kKey_F8}, {NSF9FunctionKey, kKey_F8}, {NSF10FunctionKey, kKey_F10}, {NSF11FunctionKey, kKey_F11}, {NSF12FunctionKey, kKey_F12}, {NSF13FunctionKey, kKey_F13}, {NSF14FunctionKey, kKey_F14}, {NSF15FunctionKey, kKey_F15}, {NSF16FunctionKey, kKey_F16}, {NSF17FunctionKey, kKey_F17}, {NSF18FunctionKey, kKey_F18}, {NSF19FunctionKey, kKey_F19}, {NSF20FunctionKey, kKey_F20}, {NSF21FunctionKey, kKey_F21}, {NSF22FunctionKey, kKey_F22}, {NSF23FunctionKey, kKey_F23}, {NSF24FunctionKey, kKey_F24}, {NSF25FunctionKey, kKey_F25}, {NSF26FunctionKey, kKey_F26}, {NSF27FunctionKey, kKey_F27}, {NSF28FunctionKey, kKey_F28}, {NSF29FunctionKey, kKey_F29}, {NSF30FunctionKey, kKey_F30}, {NSF31FunctionKey, kKey_F31}, {NSF32FunctionKey, kKey_F32}, {NSF33FunctionKey, kKey_F33}, {NSF34FunctionKey, kKey_F34}, {NSF35FunctionKey, kKey_F35}, {NSInsertFunctionKey, kKey_Insert}, {NSDeleteFunctionKey, kKey_Delete}, {NSHomeFunctionKey, kKey_Home}, {NSEndFunctionKey, kKey_End}, {NSPageUpFunctionKey, kKey_PageUp}, {NSPageDownFunctionKey, kKey_PageDown}, {NSPrintScreenFunctionKey, kKey_Print}, {NSScrollLockFunctionKey, kKey_ScrollLock}, {NSPauseFunctionKey, kKey_Pause}, {NSSysReqFunctionKey, kKey_SysReq}}; const unsigned nEntries = sizeof keyMap / sizeof keyMap[0]; buf[1] = 0; KeySymPair valueToFind = {}; valueToFind.fFirst = key; const KeySymPair *iter = std::lower_bound(keyMap, keyMap + nEntries, valueToFind); if (iter != keyMap + nEntries && iter->fFirst == key) { buf[0] = key <= 0x7e ? key : 0; rootKeySym = iter->fSecond; } else { buf[0] = key;//???? rootKeySym = key; } } //______________________________________________________________________________ Int_t MapKeySymToKeyCode(Int_t keySym) { //Apart from special keys, ROOT has also ASCII symbols, they map directly to themselves. if (keySym >= 0x20 && keySym <= 0x7e) return keySym; static const KeySymPair keyMap[] = { {kKey_Escape, 27}, {kKey_Tab, NSTabCharacter}, {kKey_Backtab, NSBackTabCharacter}, {kKey_Backspace, NSDeleteCharacter}, {kKey_Return, NSCarriageReturnCharacter}, {kKey_Enter, NSEnterCharacter}, {kKey_Insert, NSInsertFunctionKey}, {kKey_Delete, NSDeleteFunctionKey}, {kKey_Pause, NSPauseFunctionKey}, {kKey_Print, NSPrintScreenFunctionKey}, {kKey_SysReq, NSSysReqFunctionKey}, {kKey_Home, NSHomeFunctionKey}, {kKey_End, NSEndFunctionKey}, {kKey_Left, NSLeftArrowFunctionKey}, {kKey_Up, NSUpArrowFunctionKey}, {kKey_Right, NSRightArrowFunctionKey}, {kKey_Down, NSDownArrowFunctionKey}, {kKey_PageUp, NSPageUpFunctionKey}, {kKey_PageDown, NSPageDownFunctionKey}, //This part is bad. {kKey_Shift, 0}, {kKey_Control, 0}, {kKey_Alt, 0}, {kKey_CapsLock, 0}, {kKey_NumLock, 0}, // {kKey_ScrollLock, NSScrollLockFunctionKey}, {kKey_F1, NSF1FunctionKey}, {kKey_F2, NSF2FunctionKey}, {kKey_F3, NSF3FunctionKey}, {kKey_F4, NSF4FunctionKey}, {kKey_F5, NSF5FunctionKey}, {kKey_F6, NSF6FunctionKey}, {kKey_F7, NSF7FunctionKey}, {kKey_F8, NSF8FunctionKey}, {kKey_F8, NSF9FunctionKey}, {kKey_F10, NSF10FunctionKey}, {kKey_F11, NSF11FunctionKey}, {kKey_F12, NSF12FunctionKey}, {kKey_F13, NSF13FunctionKey}, {kKey_F14, NSF14FunctionKey}, {kKey_F15, NSF15FunctionKey}, {kKey_F16, NSF16FunctionKey}, {kKey_F17, NSF17FunctionKey}, {kKey_F18, NSF18FunctionKey}, {kKey_F19, NSF19FunctionKey}, {kKey_F20, NSF20FunctionKey}, {kKey_F21, NSF21FunctionKey}, {kKey_F22, NSF22FunctionKey}, {kKey_F23, NSF23FunctionKey}, {kKey_F24, NSF24FunctionKey}, {kKey_F25, NSF25FunctionKey}, {kKey_F26, NSF26FunctionKey}, {kKey_F27, NSF27FunctionKey}, {kKey_F28, NSF28FunctionKey}, {kKey_F29, NSF29FunctionKey}, {kKey_F30, NSF30FunctionKey}, {kKey_F31, NSF31FunctionKey}, {kKey_F32, NSF32FunctionKey}, {kKey_F33, NSF33FunctionKey}, {kKey_F34, NSF34FunctionKey}, {kKey_F35, NSF35FunctionKey} }; const unsigned nEntries = sizeof keyMap / sizeof keyMap[0]; KeySymPair valueToFind = {}; valueToFind.fFirst = static_cast(keySym); const KeySymPair *iter = std::lower_bound(keyMap, keyMap + nEntries, valueToFind); if (iter != keyMap + nEntries && iter->fFirst == keySym) return iter->fSecond; return 0; } //______________________________________________________________________________ NSUInteger GetCocoaKeyModifiersFromROOTKeyModifiers(UInt_t rootModifiers) { NSUInteger cocoaModifiers = 0; if (rootModifiers & kKeyLockMask) cocoaModifiers |= Details::kAlphaShiftKeyMask; if (rootModifiers & kKeyShiftMask) cocoaModifiers |= Details::kShiftKeyMask; if (rootModifiers & kKeyControlMask) cocoaModifiers |= Details::kControlKeyMask; if (rootModifiers & kKeyMod1Mask) cocoaModifiers |= Details::kAlternateKeyMask; if (rootModifiers & kKeyMod2Mask) cocoaModifiers |= Details::kCommandKeyMask; return cocoaModifiers; } //______________________________________________________________________________ UInt_t GetKeyboardModifiers() { const NSUInteger modifiers = [NSEvent modifierFlags]; UInt_t rootModifiers = 0; if (modifiers & Details::kAlphaShiftKeyMask) rootModifiers |= kKeyLockMask; if (modifiers & Details::kShiftKeyMask) rootModifiers |= kKeyShiftMask; if (modifiers & Details::kControlKeyMask) rootModifiers |= kKeyControlMask; if (modifiers & Details::kAlternateKeyMask) rootModifiers |= kKeyMod1Mask; if (modifiers & Details::kCommandKeyMask) rootModifiers |= kKeyMod2Mask; return rootModifiers; } //______________________________________________________________________________ UInt_t GetModifiers() { UInt_t rootModifiers = GetKeyboardModifiers(); const NSUInteger buttons = [NSEvent pressedMouseButtons]; if (buttons & 1) rootModifiers |= kButton1Mask; if (buttons & 2) rootModifiers |= kButton3Mask; if (buttons & (1 << 2)) rootModifiers |= kButton2Mask; return rootModifiers; } namespace Detail { #pragma mark - Several aux. functions to extract parameters from Cocoa events. //______________________________________________________________________________ Time_t TimeForCocoaEvent(NSEvent *theEvent) { //1. Event is not nil. assert(theEvent != nil && "TimeForCocoaEvent, parameter 'theEvent' is nil"); return [theEvent timestamp] * 1000; } //______________________________________________________________________________ Event_t NewX11EventFromCocoaEvent(unsigned windowID, NSEvent *theEvent) { //1. Event is not nil. assert(theEvent != nil && "NewX11EventFromCocoaEvent, parameter 'theEvent' is nil"); Event_t newEvent = {}; newEvent.fWindow = windowID; newEvent.fTime = TimeForCocoaEvent(theEvent); return newEvent; } //______________________________________________________________________________ void ConvertEventLocationToROOTXY(NSEvent *cocoaEvent, NSView *eventView, Event_t *rootEvent) { //1. All parameters are valid. //Both event and view must be in the same window, I do not check this here. assert(cocoaEvent != nil && "ConvertEventLocationToROOTXY, parameter 'cocoaEvent' is nil"); assert(eventView != nil && "ConvertEventLocationToROOTXY, parameter 'eventView' is nil"); assert(rootEvent != 0 && "ConvertEventLocationToROOTXY, parameter 'rootEvent' is null"); if (![cocoaEvent window]) NSLog(@"Error in ConvertEventLocationToROOTXY, window property" " of event is nil, can not convert coordinates correctly"); //Due to some reason, Apple has deprectated point conversion and requires to convert ... a rect. //Even more, on HiDPI point conversion produces wrong results and rect conversion works. const NSPoint screenPoint = ConvertPointFromBaseToScreen([cocoaEvent window], [cocoaEvent locationInWindow]); const NSPoint winPoint = ConvertPointFromScreenToBase(screenPoint, [eventView window]); const NSPoint viewPoint = [eventView convertPoint : winPoint fromView : nil]; rootEvent->fX = viewPoint.x; rootEvent->fY = viewPoint.y; rootEvent->fXRoot = GlobalXCocoaToROOT(screenPoint.x); rootEvent->fYRoot = GlobalYCocoaToROOT(screenPoint.y); } //______________________________________________________________________________ unsigned GetKeyboardModifiersFromCocoaEvent(NSEvent *theEvent) { assert(theEvent != nil && "GetKeyboardModifiersFromCocoaEvent, parameter 'event' is nil"); const NSUInteger modifiers = [theEvent modifierFlags]; unsigned rootModifiers = 0; if (modifiers & Details::kAlphaShiftKeyMask) rootModifiers |= kKeyLockMask; if (modifiers & Details::kShiftKeyMask) rootModifiers |= kKeyShiftMask; if (modifiers & Details::kControlKeyMask) rootModifiers |= kKeyControlMask; if (modifiers & Details::kAlternateKeyMask) rootModifiers |= kKeyMod1Mask; if (modifiers & Details::kCommandKeyMask) rootModifiers |= kKeyMod2Mask; return rootModifiers; } //______________________________________________________________________________ unsigned GetModifiersFromCocoaEvent(NSEvent *theEvent) { assert(theEvent != nil && "GetModifiersFromCocoaEvent, parameter 'event' is nil"); unsigned rootModifiers = GetKeyboardModifiersFromCocoaEvent(theEvent); const NSUInteger buttons = [NSEvent pressedMouseButtons]; if (buttons & 1) rootModifiers |= kButton1Mask; if (buttons & 2) rootModifiers |= kButton3Mask; if (buttons & (1 << 2)) rootModifiers |= kButton2Mask; return rootModifiers; } #pragma mark - Misc. aux. functions. //______________________________________________________________________________ bool IsParent(NSView *testParent, NSView *testChild) { assert(testParent != nil && "IsParent, parameter 'testParent' is nil"); assert(testChild != nil && "IsParent, parameter 'testChild' is nil"); if (testChild.fParentView) { NSView *parent = testChild.fParentView; while (parent) { if(parent == testParent) return true; parent = parent.fParentView; } } return false; } //______________________________________________________________________________ bool IsInBranch(NSView *parent, NSView *child, NSView *testView) { assert(child != nil && "IsInBranch, parameter 'child' is nil"); assert(testView != nil && "IsInBranch, parameter 'testView' is nil"); if (testView == child || testView == parent) return true; for (NSView *current = child.fParentView; current != parent; current = current.fParentView) { if (current == testView) return true; } return false; } //Relation between two views. enum Ancestry { kAView1IsParent, kAView2IsParent, kAHaveNonRootAncestor, kAAncestorIsRoot }; //______________________________________________________________________________ Ancestry FindLowestCommonAncestor(NSView *view1, NSView *view2, NSView **lca) { //Search for the lowest common ancestor. //View1 can not be parent of view2, view2 can not be parent of view1, //I do not check this condition here. assert(view1 != nil && "FindLowestCommonAncestor, parameter 'view1' is nil"); assert(view2 != nil && "findLowestCommonAncestor, parameter 'view2' is nil"); assert(lca != 0 && "FindLowestCommonAncestor, parameter 'lca' is null"); if (!view1.fParentView) return kAAncestorIsRoot; if (!view2.fParentView) return kAAncestorIsRoot; NSView * const ancestor = (NSView *)[view1 ancestorSharedWithView : view2]; if (ancestor) { *lca = ancestor; return kAHaveNonRootAncestor; } return kAAncestorIsRoot; } //______________________________________________________________________________ Ancestry FindRelation(NSView *view1, NSView *view2, NSView **lca) { assert(view1 != nil && "FindRelation, view1 parameter is nil"); assert(view2 != nil && "FindRelation, view2 parameter is nil"); assert(lca != 0 && "FindRelation, lca parameter is nil"); if (IsParent(view1, view2)) return kAView1IsParent; if (IsParent(view2, view1)) return kAView2IsParent; return FindLowestCommonAncestor(view1, view2, lca); } //______________________________________________________________________________ NSView *FindViewToPropagateEvent(NSView *viewFrom, Mask_t checkMask) { //This function does not check passive grabs. assert(viewFrom != nil && "FindViewToPropagateEvent, parameter 'view' is nil"); if (viewFrom.fEventMask & checkMask) return viewFrom; for (viewFrom = viewFrom.fParentView; viewFrom; viewFrom = viewFrom.fParentView) { if (viewFrom.fEventMask & checkMask) return viewFrom; } return nil; } //______________________________________________________________________________ NSView *FindViewToPropagateEvent(NSView *viewFrom, Mask_t checkMask, NSView *grabView, Mask_t grabMask) { //This function is called when we have a grab and owner_events == true, //in this case the grab view itself (and its grab mask) is checked //at the end (if no view was found before). Grab view can be in a hierarchy //for a 'viewFrom' view and can have matching fEventMask. assert(viewFrom != nil && "FindViewToPropagateEvent, parameter 'view' is nil"); if (viewFrom.fEventMask & checkMask) return viewFrom; for (viewFrom = viewFrom.fParentView; viewFrom; viewFrom = viewFrom.fParentView) { if (viewFrom.fEventMask & checkMask) return viewFrom; } if (grabView && (grabMask & checkMask)) return grabView; return nil; } #pragma mark - Aux. 'low-level' functions to generate events and call HandleEvent for a root window. //______________________________________________________________________________ void SendEnterEvent(EventQueue_t &queue, NSView *view, NSEvent *theEvent, EXMagic detail) { //1. Parameters are valid. //2. view.fID is valid. //3. A window for view.fID exists. //This view must receive enter notify, I do not check it here. assert(view != nil && "SendEnterEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendEnterEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendEnterEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendEnterEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t enterEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); enterEvent.fType = kEnterNotify; enterEvent.fCode = detail; enterEvent.fState = GetModifiersFromCocoaEvent(theEvent); //Coordinates. Event possible happend not in a view, //but window should be the same. Also, coordinates are always //inside a view. ConvertEventLocationToROOTXY(theEvent, view, &enterEvent); //Enqueue event again. queue.push_back(enterEvent); } //______________________________________________________________________________ void SendLeaveEvent(EventQueue_t &queue, NSView *view, NSEvent *theEvent, EXMagic detail) { //1. Parameters are valid. //2. view.fID is valid. //3. A window for view.fID exists. //This window should receive leave event, I do not check it here. assert(view != nil && "SendLeaveEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendLeaveEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendLeaveEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendLeaveEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t leaveEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); leaveEvent.fType = kLeaveNotify; leaveEvent.fCode = detail; leaveEvent.fState = GetModifiersFromCocoaEvent(theEvent); //Coordinates. Event possibly happend not in a view, also, coordinates are out of //the view. ConvertEventLocationToROOTXY(theEvent, view, &leaveEvent); //Enqueue event for ROOT. queue.push_back(leaveEvent); } //______________________________________________________________________________ void SendPointerMotionEvent(EventQueue_t &queue, NSView *view, NSEvent *theEvent) { //1. Parameters are valid. //2. view.fID is valid. //3. A window for view.fID exists. //View receives pointer motion events, I do not check this condition here. assert(view != nil && "SendPointerMotionEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendPointerMotionEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendPointerMotionEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendPointerMotionEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t motionEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); motionEvent.fType = kMotionNotify; motionEvent.fState = GetModifiersFromCocoaEvent(theEvent); ConvertEventLocationToROOTXY(theEvent, view, &motionEvent); //Enqueue event for ROOT. queue.push_back(motionEvent); } //______________________________________________________________________________ void SendButtonPressEvent(EventQueue_t &queue, NSView *view, NSEvent *theEvent, EMouseButton btn) { //1. Parameters are valid. //2. view.fID is valid. //3. A window for view.fID exists. //View receives this event (either grab or select input) // - I do not check this condition here. assert(view != nil && "SendButtonPressEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendButtonPressEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendButtonPressEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendButtonpressEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t pressEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); pressEvent.fType = kButtonPress; pressEvent.fCode = btn; pressEvent.fState = GetKeyboardModifiersFromCocoaEvent(theEvent); // ConvertEventLocationToROOTXY(theEvent, view, &pressEvent); // // //ROOT uses "subwindow" parameter for button press event also, for example, //scroll bar has several children windows - "buttons", they are not selecting //button press events (and not grabbing pointer). //This will work wrong, if we have overlapping views - we'll find a wrong subwindow. // NSPoint viewPoint = {}; viewPoint.x = pressEvent.fX; viewPoint.y = pressEvent.fY; for (NSView *child in [view subviews]) { if (!child.fIsOverlapped && [child hitTest : viewPoint]) {//Hit test goes down along the tree. pressEvent.fUser[0] = child.fID; break; } } //Enqueue event for ROOT. queue.push_back(pressEvent); } //______________________________________________________________________________ void SendButtonReleaseEvent(EventQueue_t &queue, NSView *view, NSEvent *theEvent, EMouseButton btn) { //1. Parameters are valid. //2. view.fID is valid. //3. A window for view.fID exists. //View must button release events, I do not check this here. assert(view != nil && "SendButtonReleaseEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendButtonReleaseEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendButtonReleaseEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendButtonReleaseEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t releaseEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); releaseEvent.fType = kButtonRelease; releaseEvent.fCode = btn; releaseEvent.fState = GetKeyboardModifiersFromCocoaEvent(theEvent); // ConvertEventLocationToROOTXY(theEvent, view, &releaseEvent); //Enqueue for ROOT. queue.push_back(releaseEvent); } //______________________________________________________________________________ void SendKeyPressEvent(EventQueue_t &queue, NSView *view, NSView *childView, NSEvent *theEvent, NSPoint windowPoint) { assert(view != nil && "SendKeyPressEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendKeyPressEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendKeyPressEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendKeyPressEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t keyPressEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); keyPressEvent.fType = kGKeyPress; keyPressEvent.fState = GetKeyboardModifiersFromCocoaEvent(theEvent); NSString * const characters = [theEvent charactersIgnoringModifiers]; assert(characters != nil && "SendKeyPressEvent, [theEvent characters] returned nil"); assert([characters length] > 0 && "SendKeyPressEvent, characters is an empty string"); keyPressEvent.fCode = [characters characterAtIndex : 0]; //convertPointFromBase is deprecated. //const NSPoint viewPoint = [view convertPointFromBase : windowPoint]; const NSPoint viewPoint = [view convertPoint : windowPoint fromView : nil]; //Coords. keyPressEvent.fX = viewPoint.x; keyPressEvent.fY = viewPoint.y; const NSPoint screenPoint = TranslateToScreen(view, viewPoint); keyPressEvent.fXRoot = screenPoint.x; keyPressEvent.fYRoot = screenPoint.y; //Subwindow. if (childView) keyPressEvent.fUser[0] = childView.fID; //Enqueue for ROOT. queue.push_back(keyPressEvent); } //______________________________________________________________________________ void SendKeyReleaseEvent(EventQueue_t &queue, NSView *view, NSView *childView, NSEvent *theEvent, NSPoint windowPoint) { assert(view != nil && "SendKeyReleaseEvent, parameter 'view' is nil"); assert(theEvent != nil && "SendKeyReleaseEvent, parameter 'event' is nil"); assert(view.fID != 0 && "SendKeyReleaseEvent, view.fID is 0"); TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendKeyPressEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t keyReleaseEvent = NewX11EventFromCocoaEvent(view.fID, theEvent); keyReleaseEvent.fType = kKeyRelease; keyReleaseEvent.fState = GetKeyboardModifiersFromCocoaEvent(theEvent); NSString * const characters = [theEvent charactersIgnoringModifiers]; assert(characters != nil && "SendKeyReleaseEvent, [theEvent characters] returned nil"); assert([characters length] > 0 && "SendKeyReleaseEvent, characters is an empty string"); keyReleaseEvent.fCode = [characters characterAtIndex : 0]; //Coords. const NSPoint viewPoint = [view convertPoint : windowPoint fromView : nil]; keyReleaseEvent.fX = viewPoint.x; keyReleaseEvent.fY = viewPoint.y; const NSPoint screenPoint = TranslateToScreen(view, viewPoint); keyReleaseEvent.fXRoot = screenPoint.x; keyReleaseEvent.fYRoot = screenPoint.y; //Subwindow. if (childView) keyReleaseEvent.fUser[0] = childView.fID; //Enqueue for ROOT. queue.push_back(keyReleaseEvent); } //______________________________________________________________________________ void SendFocusInEvent(EventQueue_t &queue, NSView *view, EXMagic mode) { assert(view != nil && "SendFocusInEvent, parameter 'view' is nil"); // TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendFocusInEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t focusInEvent = {}; focusInEvent.fWindow = view.fID; focusInEvent.fType = kFocusIn; focusInEvent.fCode = mode; queue.push_back(focusInEvent); } //______________________________________________________________________________ void SendFocusOutEvent(EventQueue_t &queue, NSView *view, EXMagic mode) { assert(view != nil && "SendFocusOutEvent, parameter 'view' is nil"); // TGWindow * const window = gClient->GetWindowById(view.fID); if (!window) { #ifdef DEBUG_ROOT_COCOA NSLog(@"SendFocusOutEvent, ROOT's widget %u was not found", view.fID); #endif return; } Event_t focusOutEvent = {}; focusOutEvent.fWindow = view.fID; focusOutEvent.fType = kFocusOut; focusOutEvent.fCode = mode;//code mode :) queue.push_back(focusOutEvent); } #pragma mark - Aux. functions to send events to view's branch. //______________________________________________________________________________ void SendEnterEventRange(EventQueue_t &queue, NSView *from, NSView *to, NSEvent *theEvent, EXMagic mode) { //[from, to) - legal range, 'to' must be ancestor for 'from'. assert(from != nil && "SendEnterEventRange, 'from' parameter is nil"); assert(to != nil && "SendEnterEventRange, 'to' parameter is nil"); assert(theEvent != nil && "SendEnterEventRange, event parameter is nil"); while (from != to) { if ([from acceptsCrossingEvents : kEnterWindowMask]) SendEnterEvent(queue, from, theEvent, mode); from = from.fParentView; } } //______________________________________________________________________________ void SendEnterEventClosedRange(EventQueue_t &queue, NSView *from, NSView *to, NSEvent *theEvent, EXMagic mode) { //[from, to] - inclusive, legal range, 'to' must be ancestor for 'from'. assert(from != nil && "SendEnterEventClosedRange, 'from' parameter is nil"); assert(to != nil && "SendEnterEventClosedRange, 'to' parameter is nil"); assert(theEvent != nil && "SendEnterEventClosedRange, event parameter is nil"); SendEnterEventRange(queue, from, to, theEvent, mode); if ([to acceptsCrossingEvents : kEnterWindowMask]) SendEnterEvent(queue, to, theEvent, mode); } //______________________________________________________________________________ void SendLeaveEventRange(EventQueue_t &queue, NSView *from, NSView *to, NSEvent *theEvent, EXMagic mode) { //[from, to) - legal range, 'to' must be ancestor for 'from'. assert(from != nil && "SendLeaveEventRange, 'from' parameter is nil"); assert(to != nil && "SendLeaveEventRange, 'to' parameter is nil"); assert(theEvent != nil && "SendLeaveEventRange, event parameter is nil"); while (from != to) { if ([from acceptsCrossingEvents : kLeaveWindowMask]) SendLeaveEvent(queue, from, theEvent, mode); from = from.fParentView; } } //______________________________________________________________________________ void SendLeaveEventClosedRange(EventQueue_t &queue, NSView *from, NSView *to, NSEvent *theEvent, EXMagic mode) { //[from, to] - inclusive, legal range, 'to' must be ancestor for 'from'. assert(from != nil && "SendLeaveEventClosedRange, 'from' parameter is nil"); assert(to != nil && "SendLeaveEventClosedRange, 'to' parameter is nil"); assert(theEvent != nil && "SendLeaveEventClosedRange, event parameter is nil"); SendLeaveEventRange(queue, from, to, theEvent, mode); if ([to acceptsCrossingEvents : kLeaveWindowMask]) SendLeaveEvent(queue, to, theEvent, mode); } #pragma mark - Top-level crossing event generators. //When passing parent and child view, parent view always //precedes the child, even if function's name is GenerateCrossingEventChildToParent. //______________________________________________________________________________ void GenerateCrossingEventChildToParent(EventQueue_t &queue, NSView *parent, NSView *child, NSEvent *theEvent, EXMagic detail) { //Pointer moves from window A to window B and A is an inferior of B. //Generate LeaveNotify on A (with detail NotifyAncestor). //Generate LeaveNotify for every window between A and B, exclusive (with detail NotifyVirtual) //Generate EnterNotify for B with detail NotifyInferior. //ROOT does not have NotifyAncestor/NotifyInferior. assert(parent != nil && "GenerateCrossingEventChildToParent, parameter 'parent' is nil"); assert(child != nil && "GenerateCrossingEventChildToParent, parameter 'child' is nil"); assert(theEvent != nil && "GenerateCrossingEventChildToParent, parameter 'event' is nil"); assert(child.fParentView != nil && "GenerateCrossingEventChildToParent, parameter 'child' must have QuartzView* parent"); //acceptsCrossingEvents will check grab event mask also, if view is a grab and if //owner_events == true. if ([child acceptsCrossingEvents : kLeaveWindowMask]) SendLeaveEvent(queue, child, theEvent, detail); //Leave event to a branch [child.fParentView, parent) SendLeaveEventRange(queue, child.fParentView, parent, theEvent, detail); //Enter event for the parent view. if ([parent acceptsCrossingEvents : kEnterWindowMask]) SendEnterEvent(queue, parent, theEvent, detail); } //______________________________________________________________________________ void GenerateCrossingEventParentToChild(EventQueue_t &queue, NSView *parent, NSView *child, NSEvent *theEvent, EXMagic detail) { //Pointer moves from window A to window B and B is an inferior of A. //Generate LeaveNotify event for A, detail == NotifyInferior. //Generate EnterNotify for each window between window A and window B, exclusive, // detail == NotifyVirtual (no such entity in ROOT). //Generate EnterNotify on window B, detail == NotifyAncestor. //ROOT does not have NotifyInferior/NotifyAncestor. assert(parent != nil && "GenerateCrossingEventParentToChild, parameter 'parent' is nil"); assert(child != nil && "GenerateCrossingEventParentToChild, parameter 'child' is nil"); assert(theEvent != nil && "GenerateCrossingEventParentToChild, parameter 'event' is nil"); assert(child.fParentView != nil && "GenerateCrossingEventParentToChild, parameter 'child' must have QuartzView* parent"); //If view is a grab and owner_events == true, //acceptsCrossingEvents will check the grab event mask also. if ([parent acceptsCrossingEvents : kLeaveWindowMask]) SendLeaveEvent(queue, parent, theEvent, detail); //Enter event for [child.fParentView, parent) - order is reversed, but it does not really matter. SendEnterEventRange(queue, child.fParentView, parent, theEvent, detail); //Enter event for the child view. if ([child acceptsCrossingEvents : kEnterWindowMask]) SendEnterEvent(queue, child, theEvent, detail); } //______________________________________________________________________________ void GenerateCrossingEventFromChild1ToChild2(EventQueue_t &queue, NSView *child1, NSView *child2, NSView *ancestor, NSEvent *theEvent, EXMagic detail) { //Pointer moves from window A to window B and window C is their lowest common ancestor. //Generate LeaveNotify for window A with detail == NotifyNonlinear. //Generate LeaveNotify for each window between A and C, exclusive, with detail == NotifyNonlinearVirtual //Generate EnterNotify (detail == NotifyNonlinearVirtual) for each window between C and B, exclusive //Generate EnterNotify for window B, with detail == NotifyNonlinear. assert(child1 != nil && "GenerateCrossingEventFromChild1ToChild2, parameter 'child1' is nil"); assert(child2 != nil && "GenerateCrossingEventFromChild1ToChild2, child2 parameter is nil"); assert(theEvent != nil && "GenerateCrossingEventFromChild1ToChild2, theEvent parameter is nil"); //ROOT does not have NotifyNonlinear/NotifyNonlinearVirtual. //acceptsCrossingEvents also checks grab event mask, if this view has a grab //and owner_events == true. if ([child1 acceptsCrossingEvents : kLeaveWindowMask]) SendLeaveEvent(queue, child1, theEvent, detail); if (!ancestor) { if (child1.fParentView)//Leave [child1.fParentView contentView] SendLeaveEventClosedRange(queue, child1.fParentView, (NSView *)[[child1 window] contentView], theEvent, detail); if (child2.fParentView)//Enter [child2.fParentView contentView] - order is reversed. SendEnterEventClosedRange(queue, child2.fParentView, (NSView *)[[child2 window] contentView], theEvent, detail); } else { if (child1.fParentView)//Leave [child1.fParentView ancestor) SendLeaveEventRange(queue, child1.fParentView, ancestor, theEvent, detail); if (child2.fParentView)//Enter [child2.fParentView, ancestor) - order reversed. SendEnterEventRange(queue, child2.fParentView, ancestor, theEvent, detail); } if ([child2 acceptsCrossingEvents : kEnterWindowMask]) SendEnterEvent(queue, child2, theEvent, detail); } //______________________________________________________________________________ void GenerateCrossingEvents(EventQueue_t &queue, NSView *fromView, NSView *toView, NSEvent *theEvent, EXMagic detail) { //Pointer moved from 'fromView' to 'toView'. //Check their relationship and generate leave/enter notify events. assert(theEvent != nil && "GenerateCrossingEvent, event parameter is nil"); if (fromView == toView) { //This can happen: tracking areas for stacked windows call //mouseExited even for overlapped views (so you have a bunch of mouseExited/mouseEntered //for one cursor move). In mouseEntered/mouseExited //I'm looking for the top level view under cursor and try to generate cross event //for this view only. return; } if (!fromView) { //We enter window "from the screen" - do not leave any window. //Send EnterNotify event. //Send enter notify event to a branch [toView contentView], order of //views is reversed, but no GUI actually depends on this. if (toView) SendEnterEventClosedRange(queue, toView, (NSView *)[[toView window] contentView], theEvent, detail); } else if (!toView) { //We exit all views. Order is correct here. SendLeaveEventClosedRange(queue, fromView, (NSView *)[[fromView window] contentView], theEvent, detail); } else { NSView *ancestor = 0; const Ancestry rel = FindRelation(fromView, toView, &ancestor); if (rel == kAView1IsParent) { //Case 1. //From A to B. //_________________ //| A | //| |---------| | //| | B | | //| | | | //| |---------| | //| | //|________________| GenerateCrossingEventParentToChild(queue, fromView, toView, theEvent, detail); } else if (rel == kAView2IsParent) { //Case 2. //From A to B. //_________________ //| B | //| |---------| | //| | A | | //| | | | //| |---------| | //| | //|________________| GenerateCrossingEventChildToParent(queue, toView, fromView, theEvent, detail); } else { //Case 3. //|--------------------------------| //| C |------| |-------| | //| | A | | B | | //| |______| |_______| | //|________________________________| //Ancestor is either some view, or 'root' window. //The fourth case (different screens) is not implemented (and I do not know, if I want to implement it). GenerateCrossingEventFromChild1ToChild2(queue, fromView, toView, ancestor, theEvent, detail); } } } //______________________________________________________________________________ void GenerateCrossingEventForGrabView(EventQueue_t &queue, NSView *fromView, NSView *toView, NSView *grabView, Mask_t grabEventMask, NSEvent *theEvent) { //When owner events == false, only grab view receives enter/leave notify events. //Send enter/leave event to a grab view. assert(theEvent != nil && "GenerateCrossingEventForGrabView, parameter 'event' is nil"); assert(grabView != nil && "GenerateCrossingEventForGrabView, parameter 'grabView' is nil"); assert((fromView != nil || toView != nil) && "GenerateCrossingEventForGrabView, both 'toView' and 'fromView' parameters are nil"); if (fromView == toView)//No crossing at all? return; const bool wantsEnter = grabEventMask & kEnterWindowMask; const bool wantsLeave = grabEventMask & kLeaveWindowMask; if (fromView == grabView && wantsLeave) return SendLeaveEvent(queue, grabView, theEvent, kNotifyNormal); if (toView == grabView && wantsEnter) return SendEnterEvent(queue, grabView, theEvent, kNotifyNormal); if (!fromView) { //We enter window "from the screen" - do not leave any window. //Send EnterNotify event to the grab view, if it's "in the branch". if (wantsEnter && IsParent(grabView, toView)) SendEnterEvent(queue, grabView, theEvent, kNotifyNormal); } else if (!toView) { //We exit all views.. if (wantsLeave && IsParent(grabView, fromView)) SendLeaveEvent(queue, grabView, theEvent, kNotifyNormal); } else { NSView *ancestor = 0; FindRelation(fromView, toView, &ancestor); if (IsInBranch(nil, fromView, grabView)) { if (wantsLeave) SendLeaveEvent(queue, grabView, theEvent, kNotifyNormal); } else if (IsInBranch(nil, toView, grabView)) { if (wantsEnter) SendEnterEvent(queue, grabView, theEvent, kNotifyNormal); } } } }//Detail //______________________________________________________________________________ EventTranslator::EventTranslator() : fViewUnderPointer(nil), fPointerGrabType(kPGNoGrab), fGrabEventMask(0), fOwnerEvents(true), fButtonGrabView(nil), fKeyGrabView(nil), fFocusView(nil), fImplicitGrabButton(kAnyButton) { } //______________________________________________________________________________ void EventTranslator::GenerateConfigureNotifyEvent(NSView *view, const NSRect &newFrame) { assert(view != nil && "GenerateConfigureNotifyEvent, parameter 'view' is nil"); Event_t newEvent = {}; newEvent.fWindow = view.fID; newEvent.fType = kConfigureNotify; newEvent.fX = newFrame.origin.x; newEvent.fY = newFrame.origin.y; newEvent.fWidth = newFrame.size.width; newEvent.fHeight = newFrame.size.height; TGWindow * const window = gClient->GetWindowById(view.fID); if (window) window->HandleEvent(&newEvent); } //______________________________________________________________________________ void EventTranslator::GenerateDestroyNotify(unsigned /*winID*/) { //Noop. } //______________________________________________________________________________ void EventTranslator::GenerateExposeEvent(NSView *view, const NSRect &exposedRect) { assert(view != nil && "GenerateExposeEvent, parameter 'view' is nil"); Event_t exposeEvent = {}; exposeEvent.fWindow = view.fID; exposeEvent.fType = kExpose; exposeEvent.fX = exposedRect.origin.x; exposeEvent.fY = exposedRect.origin.y; exposeEvent.fWidth = exposedRect.size.width; exposeEvent.fHeight = exposedRect.size.height; TGWindow * const window = gClient->GetWindowById(view.fID); assert(window != 0 && "GenerateExposeEvent, window was not found"); window->HandleEvent(&exposeEvent); } //______________________________________________________________________________ void EventTranslator::GenerateCrossingEvent(NSEvent *theEvent) { //View parameter can be nil. assert(theEvent != nil && "GenerateCrossingEvent, parameter 'event' is nil"); fPointerGrabType == kPGNoGrab ? GenerateCrossingEventNoGrab(theEvent) : GenerateCrossingEventActiveGrab(theEvent); } //______________________________________________________________________________ void EventTranslator::GenerateCrossingEventNoGrab(NSEvent *theEvent) { assert(theEvent && "GenerateCrossingEventNoGrab, parameter 'theEvent' is nil"); NSView * const candidateView = FindViewForPointerEvent(theEvent); //We moved from fViewUnderPointer (leave event) to candidateView (enter event). Detail::GenerateCrossingEvents(fEventQueue, fViewUnderPointer, candidateView, theEvent, kNotifyNormal); fViewUnderPointer = candidateView; } //______________________________________________________________________________ void EventTranslator::GenerateCrossingEventActiveGrab(NSEvent *theEvent) { assert(theEvent != nil && "GenerateCrossingEventActiveGrab, parameter 'theEvent' is nil"); NSView * const candidateView = FindViewForPointerEvent(theEvent); if (fOwnerEvents) { //Either passive grab (which was activated) or active grab set by TGCocoa::GrabPointer with //owner_events == true. This works the same way as nograb case, except not only fEventMask //is checked, but for grab view (if it's boundary was crossed) either it's passive grab mask //or active is also checked. Detail::GenerateCrossingEvents(fEventQueue, fViewUnderPointer, candidateView, theEvent, kNotifyNormal); } else if (fButtonGrabView && (fViewUnderPointer || candidateView)) { //Either implicit grab or GrabPointer with owner_events == false, //only grab view can receive enter/leave notify events. Only //grab event mask is checked, not view's own event mask. Detail::GenerateCrossingEventForGrabView(fEventQueue, fViewUnderPointer, candidateView, fButtonGrabView, fGrabEventMask, theEvent); } fViewUnderPointer = candidateView; } //______________________________________________________________________________ bool EventTranslator::HasPointerGrab()const { return fPointerGrabType != kPGNoGrab; } //______________________________________________________________________________ void EventTranslator::GeneratePointerMotionEvent(NSEvent *theEvent) { assert(theEvent != nil && "GeneratePointerMotionEvent, parameter 'theEvent' is nil"); if (fPointerGrabType == kPGNoGrab) return GeneratePointerMotionEventNoGrab(theEvent); else return GeneratePointerMotionEventActiveGrab(theEvent); } //______________________________________________________________________________ void EventTranslator::GenerateButtonPressEvent(NSView *eventView, NSEvent *theEvent, EMouseButton btn) { assert(eventView != nil && "GenerateButtonPressEvent, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateButtonpressEvent, parameter 'theEvent' is nil"); if (fPointerGrabType == kPGNoGrab) return GenerateButtonPressEventNoGrab(eventView, theEvent, btn); else return GenerateButtonPressEventActiveGrab(eventView, theEvent, btn); } //______________________________________________________________________________ void EventTranslator::GenerateButtonReleaseEvent(NSView *eventView, NSEvent *theEvent, EMouseButton btn) { assert(eventView != nil && "GenerateButtonReleaseEvent, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateButtonReleaseEvent, parameter 'theEvent' is nil"); if (fPointerGrabType == kPGNoGrab) return GenerateButtonReleaseEventNoGrab(eventView, theEvent, btn); else return GenerateButtonReleaseEventActiveGrab(eventView, theEvent, btn); } //______________________________________________________________________________ void EventTranslator::GenerateKeyPressEvent(NSView *eventView, NSEvent *theEvent) { assert(eventView != nil && "GenerateKeyPressEvent, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateKeyPressEvent, parameter 'theEvent' is nil"); if (![[theEvent charactersIgnoringModifiers] length]) return; if (!fFocusView) return; !fKeyGrabView ? GenerateKeyPressEventNoGrab(eventView, theEvent) : GenerateKeyEventActiveGrab(eventView, theEvent); } //______________________________________________________________________________ void EventTranslator::GenerateKeyReleaseEvent(NSView *eventView, NSEvent *theEvent) { assert(eventView != nil && "GenerateKeyReleaseEvent, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateKeyReleaseEvent, parameter 'theEvent' is nil"); if (![[theEvent charactersIgnoringModifiers] length]) return; if (!fFocusView) return; !fKeyGrabView ? GenerateKeyReleaseEventNoGrab(eventView, theEvent) : //GenerateKeyEventActiveGrab(eventView, theEvent); GenerateKeyEventForView(fKeyGrabView, theEvent); //Oh, only God forgives. fKeyGrabView = nil; } //______________________________________________________________________________ void EventTranslator::GenerateFocusChangeEvent(NSView *eventView) { if (eventView == fFocusView) return; if (fFocusView && (fFocusView.fEventMask & kFocusChangeMask)) Detail::SendFocusOutEvent(fEventQueue, fFocusView, kNotifyNormal); if (eventView) { if (eventView.fEventMask & kFocusChangeMask) Detail::SendFocusInEvent(fEventQueue, eventView, kNotifyNormal); fFocusView = eventView; } else fFocusView = nil; } //______________________________________________________________________________ void EventTranslator::SetPointerGrab(NSView *grabView, unsigned eventMask, bool ownerEvents) { assert(grabView != nil && "SetPointerGrab, parameter 'grabView' is nil"); if (fButtonGrabView) { //This can happen with X11, does this happen with ROOT's GUI? //Hm, should I send leave notify to the previous grab??? [fButtonGrabView cancelGrab]; } //There is no kNoButton, unfortunately (but there is additional check on //grab type). fImplicitGrabButton = kAnyButton; // fButtonGrabView = grabView; fPointerGrabType = kPGActiveGrab; fGrabEventMask = eventMask; fOwnerEvents = ownerEvents; //Generate sequence of crossing events - as if pointer //"jumps" to the grab view. if (grabView != fViewUnderPointer) { const NSPoint location = [[grabView window] mouseLocationOutsideOfEventStream]; const Util::NSScopeGuard event([[FakeCrossingEvent alloc] initWithWindow : [grabView window] location : location]); if (!event.Get()) { //Hehe, if this happend, is it still possible to log???? NSLog(@"EventTranslator::SetPointerGrab, crossing event initialization failed"); return; } Detail::GenerateCrossingEvents(fEventQueue, fViewUnderPointer, grabView, event.Get(), kNotifyGrab);//Uffffff, done! } //Activate the current grab now. [fButtonGrabView activateGrab : eventMask ownerEvents : fOwnerEvents]; } //______________________________________________________________________________ void EventTranslator::CancelPointerGrab() { if (fButtonGrabView) //Cancel grab (active, passive, implicit). [fButtonGrabView cancelGrab]; //We generate sequence of leave/enter notify events (if any) as if we jumped from the grab view to the pointer view. if (NSView * const candidateView = FindViewUnderPointer()) { const NSPoint location = [[candidateView window] mouseLocationOutsideOfEventStream]; const Util::NSScopeGuard event([[FakeCrossingEvent alloc] initWithWindow : [candidateView window] location : location ]); if (!event.Get()) { //Hehe, if this happend, is it still possible to log???? NSLog(@"EventTranslator::CancelPointerGrab, crossing event initialization failed"); return; } Detail::GenerateCrossingEvents(fEventQueue, fButtonGrabView, candidateView, event.Get(), kNotifyUngrab); // fViewUnderPointer = candidateView; } else if (fButtonGrabView) { //convertScreenToBase is deprecated. //const NSPoint location = [[fButtonGrabView window] convertScreenToBase : [NSEvent mouseLocation]]; const NSPoint location = ConvertPointFromScreenToBase([NSEvent mouseLocation], [fButtonGrabView window]); const Util::NSScopeGuard event([[FakeCrossingEvent alloc] initWithWindow : [fButtonGrabView window] location : location ]); if (!event.Get()) { //Hehe, if this happend, is it still possible to log???? NSLog(@"EventTranslator::CancelPointerGrab, crossing event initialization failed"); fViewUnderPointer = nil; return; } Detail::GenerateCrossingEvents(fEventQueue, fButtonGrabView, nil, event.Get(), kNotifyUngrab);//Ufff, done!!! // fViewUnderPointer = nil; } fImplicitGrabButton = kAnyButton; fButtonGrabView = nil; fPointerGrabType = kPGNoGrab; fGrabEventMask = 0; fOwnerEvents = true; } //______________________________________________________________________________ void EventTranslator::SetInputFocus(NSView *newFocusView) { if (fFocusView && (fFocusView.fEventMask & kFocusChangeMask)) Detail::SendFocusOutEvent(fEventQueue, fFocusView, kNotifyNormal); if (newFocusView) { if (newFocusView.fEventMask & kFocusChangeMask) Detail::SendFocusInEvent(fEventQueue, newFocusView, kNotifyNormal); fFocusView = newFocusView; } else fFocusView = nil; } //______________________________________________________________________________ unsigned EventTranslator::GetInputFocus()const { if (fFocusView) return fFocusView.fID; return 0; } namespace { //______________________________________________________________________________ void ClearPointerIfViewIsRelated(NSView *&view, Window_t winID) { NSView *v = view; if (v) { for (; v; v = v.fParentView) { if (v.fID == winID) { view = nil; break; } } } } }//unnamed namespace. //______________________________________________________________________________ void EventTranslator::CheckUnmappedView(Window_t winID) { //Window was unmapped, check, if it's the same window as the current grab, //or focus window, or key grabbing window and if so - do cleanup. if (fButtonGrabView) { for (NSView *view = fButtonGrabView; view; view = view.fParentView) { if (view.fID == winID) { CancelPointerGrab(); break; } } } if (fViewUnderPointer) { for (NSView *view = fViewUnderPointer; view; view = view.fParentView) { if (view.fID == winID) { NSPoint location = {}; location.x = fViewUnderPointer.fWidth / 2; location.y = fViewUnderPointer.fHeight / 2; location = [fViewUnderPointer convertPoint : location toView : nil]; const Util::NSScopeGuard event([[FakeCrossingEvent alloc] initWithWindow : [fViewUnderPointer window] location : location]); if (!event.Get()) { //Hehe, if this happend, is it still possible to log???? NSLog(@"EventTranslator::CheckUnmappedView, crossing event initialization failed"); return; } Detail::SendLeaveEvent(fEventQueue, fViewUnderPointer, event.Get(), kNotifyNormal); fViewUnderPointer = nil; break; } } } ClearPointerIfViewIsRelated(fFocusView, winID); ClearPointerIfViewIsRelated(fKeyGrabView, winID); } //______________________________________________________________________________ void EventTranslator::GeneratePointerMotionEventNoGrab(NSEvent *theEvent) { //Without grab, things are simple: find a view which accepts pointer motion event. assert(theEvent != nil && "GeneratePointerMotionEventNoGrab, parameter 'theEvent' is nil"); const Mask_t maskToTest = [NSEvent pressedMouseButtons] ? (kPointerMotionMask | kButtonMotionMask) : kPointerMotionMask; //Event without any emulated grab, receiver view can be "wrong" (result of Cocoa's "dragging"). if (NSView *candidateView = FindViewForPointerEvent(theEvent)) { //Do propagation. candidateView = Detail::FindViewToPropagateEvent(candidateView, maskToTest); if (candidateView)//We have such a view, send event to a corresponding ROOT's window. Detail::SendPointerMotionEvent(fEventQueue, candidateView, theEvent); } } //______________________________________________________________________________ void EventTranslator::GeneratePointerMotionEventActiveGrab(NSEvent *theEvent) { //More complex case. Grab can be result of button press and set by SetPointerGrab. //In case of button press (this is either passive->active or implicit grab), //Cocoa has it's own grab, so view (and window) can be not under cursor (but still //it receives events). So I can not simple use eventView here. if (!fButtonGrabView)//Implicit grab when nobody has PressButtonMask return; //assert(eventView != nil && "GeneratePointerMotionEventActiveGrab, view parameter is nil"); assert(theEvent != nil && "GeneratePointerMotionEventActiveGrab, parameter 'theEvent' is nil"); const Mask_t maskToTest = [NSEvent pressedMouseButtons] ? (kPointerMotionMask | kButtonMotionMask) : kPointerMotionMask; if (fOwnerEvents) { //Complex case, we have to correctly report event. if (NSView *candidateView = FindViewForPointerEvent(theEvent)) { candidateView = Detail::FindViewToPropagateEvent(candidateView, maskToTest, fButtonGrabView, fGrabEventMask); if (candidateView)//We have such a view, send event to a corresponding ROOT's window. Detail::SendPointerMotionEvent(fEventQueue, candidateView, theEvent); } else { //No such window - dispatch to the grab view. //Else: either implicit grab, or user requested grab with owner_grab == False. if (fGrabEventMask & maskToTest) Detail::SendPointerMotionEvent(fEventQueue, fButtonGrabView, theEvent); } } else { //Else: either implicit grab, or user requested grab with owner_grab == False. if (fGrabEventMask & maskToTest) Detail::SendPointerMotionEvent(fEventQueue, fButtonGrabView, theEvent); } } //______________________________________________________________________________ void EventTranslator::GenerateButtonPressEventNoGrab(NSView *view, NSEvent *theEvent, EMouseButton btn) { //Generate button press event when no pointer grab is active: //either find a window with a passive grab, or create an implicit //grab (to emulate X11's behavior). assert(view != nil && "GenerateButtonPressEventNoGrab, parameter 'view' is nil"); assert(theEvent != nil && "GenerateButtonPressEventNoGrab, parameter 'theEvent' is nil"); FindButtonGrab(view, theEvent, btn); fImplicitGrabButton = btn;//This info is useless for any grab type except the implicit one. //Now we have to generate a sequence of enter/leave notify events, //like we "jump" from the previous view under the pointer to a grab view. Detail::GenerateCrossingEvents(fEventQueue, fViewUnderPointer, fButtonGrabView, theEvent, kNotifyGrab); //"Activate" a grab now, depending on type. if (fButtonGrabView) { if (fPointerGrabType == kPGPassiveGrab) [fButtonGrabView activatePassiveGrab]; else if (fPointerGrabType == kPGImplicitGrab) [fButtonGrabView activateImplicitGrab]; } //Send press event to a grab view (either passive grab or implicit, //but it has the required event bitmask). if (fButtonGrabView) Detail::SendButtonPressEvent(fEventQueue, fButtonGrabView, theEvent, btn); } //______________________________________________________________________________ void EventTranslator::GenerateButtonPressEventActiveGrab(NSView * /*view*/, NSEvent *theEvent, EMouseButton btn) { //Generate button press event in the presence of activated pointer grab. //assert(view != nil && "GenerateButtonPressEventActiveGrab, view parameter is nil"); assert(theEvent != nil && "GenerateButtonPressEventActiveGrab, parameter 'theEvent' is nil"); //I did not find in X11 spec. the case when I have two passive grabs on window A and window B, //say left button on A and right button on B. What should happen if I press left button in A, move to //B and press the right button? In my test programm on X11 (Ubuntu) I can see, that now they BOTH //are active grabs. I'm not going to implement this mess, unless I have a correct formal description. if (!fButtonGrabView) return; if (fOwnerEvents) { if (NSView *candidateView = FindViewForPointerEvent(theEvent)) { //Do propagation. candidateView = Detail::FindViewToPropagateEvent(candidateView, kButtonPressMask, fButtonGrabView, fGrabEventMask); //We have such a view, send an event to a corresponding ROOT's window. if (candidateView) Detail::SendButtonPressEvent(fEventQueue, candidateView, theEvent, btn); } else { if (fGrabEventMask & kButtonPressMask) Detail::SendButtonPressEvent(fEventQueue, fButtonGrabView, theEvent, btn); } } else { if (fGrabEventMask & kButtonPressMask) Detail::SendButtonPressEvent(fEventQueue, fButtonGrabView, theEvent, btn); } } //______________________________________________________________________________ void EventTranslator::GenerateButtonReleaseEventNoGrab(NSView *eventView, NSEvent *theEvent, EMouseButton btn) { //Generate button release event when there is no active pointer grab. Can this even happen?? assert(eventView != nil && "GenerateButtonReleaseEventNoGrab, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateButtonReleaseEventNoGrabm parameter 'theEvent' is nil"); if (NSView *candidateView = Detail::FindViewToPropagateEvent(eventView, kButtonReleaseMask)) Detail::SendButtonReleaseEvent(fEventQueue, candidateView, theEvent, btn); } //______________________________________________________________________________ void EventTranslator::GenerateButtonReleaseEventActiveGrab(NSView *eventView, NSEvent *theEvent, EMouseButton btn) { //Generate button release event in the presence of active grab (explicit pointer grab, activated passive grab or implicit grab). assert(eventView != nil && "GenerateButtonReleaseEventActiveGrab, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateButtonReleaseEventActiveGrab, parameter 'theEvent' is nil"); if (!fButtonGrabView) { //Still we have to cancel this grab (it's implicit grab on a root window). CancelPointerGrab(); return; } //What if view is deleted in the middle of this function? const Util::NSStrongReference *> eventViewGuard(eventView); if (fButtonGrabView) { if (fOwnerEvents) {//X11: Either XGrabPointer with owner_events == True or passive grab (owner_events is always true) if (NSView *candidateView = FindViewForPointerEvent(theEvent)) { candidateView = Detail::FindViewToPropagateEvent(candidateView, kButtonReleaseMask, fButtonGrabView, fGrabEventMask); //candidateView is either some view, or grab view, if its mask is ok. if (candidateView) Detail::SendButtonReleaseEvent(fEventQueue, candidateView, theEvent, btn); } else if (fGrabEventMask & kButtonReleaseMask) Detail::SendButtonReleaseEvent(fEventQueue, fButtonGrabView, theEvent, btn); } else {//Either implicit grab or GrabPointer with owner_events == False. if (fGrabEventMask & kButtonReleaseMask) Detail::SendButtonReleaseEvent(fEventQueue, fButtonGrabView, theEvent, btn); } } else { CancelPointerGrab();//root window had a grab, cancel it now. } if (fPointerGrabType == kPGPassiveGrab && (btn == fButtonGrabView.fPassiveGrabButton || fButtonGrabView.fPassiveGrabButton == kAnyButton)) CancelPointerGrab(); if (fPointerGrabType == kPGImplicitGrab && btn == fImplicitGrabButton) CancelPointerGrab(); } //______________________________________________________________________________ void EventTranslator::GenerateKeyPressEventNoGrab(NSView *eventView, NSEvent *theEvent) { assert(eventView != nil && "GenerateKeyPressEventNoGrab, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateKeyPressEventNoGrab, parameter 'theEvent' is nil"); assert(fFocusView != nil && "GenerateKeyPressEventNoGrab, fFocusView is nil"); FindKeyGrabView(eventView, theEvent); if (!fKeyGrabView) { NSView *candidateView = fFocusView; if (Detail::IsParent(fFocusView, eventView)) { NSView * const testView = Detail::FindViewToPropagateEvent(eventView, kKeyPressMask); if (testView && (testView == fFocusView || Detail::IsParent(fFocusView, testView))) candidateView = testView; } GenerateKeyEventForView(candidateView, theEvent); } else GenerateKeyEventForView(fKeyGrabView, theEvent); } //______________________________________________________________________________ void EventTranslator::GenerateKeyEventActiveGrab(NSView *eventView, NSEvent *theEvent) { assert(eventView != nil && "GenerateKeyEventActiveGrab, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateKeyEventActiveGrab, parameter 'theEvent' is nil"); assert(fFocusView != nil && "GenerateKeyEventActiveGrab, fFocusView is nil"); const Mask_t eventMask = theEvent.type == Details::kKeyDown ? kKeyPressMask : kKeyReleaseMask; if (Detail::IsParent(fFocusView, eventView) || fFocusView == eventView) { NSView * const testView = Detail::FindViewToPropagateEvent(eventView, eventMask); if (testView && (testView == fFocusView || Detail::IsParent(fFocusView, testView))) GenerateKeyEventForView(testView, theEvent); } else GenerateKeyEventForView(fFocusView, theEvent);//Should I check the mask??? if (theEvent.type == Details::kKeyUp && fKeyGrabView) { //Cancel grab? //NSString *characters = [theEvent charactersIgnoringModifiers]; //assert(characters != nil && "GenerateKeyEventActiveGrab, [theEvent characters] returned nil"); //assert([characters length] > 0 && "GenerateKeyEventActiveGrab, characters is an empty string"); //Here I have a real trouble: on a key press GUI removes ... passive key grabs ... //this "does not affect any active grab", but later on a key release ... I'm not //able to find a grab to remove and can not ... cancel the grab. //I do it the same way it's done on Windows after all. //So, the condition was commented :( //if ([fKeyGrabView findPassiveKeyGrab : [characters characterAtIndex : 0]]) fKeyGrabView = nil;//Cancel grab. } } //______________________________________________________________________________ void EventTranslator::GenerateKeyReleaseEventNoGrab(NSView *eventView, NSEvent *theEvent) { assert(eventView != nil && "GenerateKeyReleaseEventNoGrab, parameter 'eventView' is nil"); assert(theEvent != nil && "GenerateKeyReleaseEventNoGrab, parameter 'theEvent' is nil"); NSView *candidateView = fFocusView; if (eventView == fFocusView || Detail::IsParent(fFocusView, eventView)) { NSView * const testView = Detail::FindViewToPropagateEvent(eventView, kKeyReleaseMask); if (testView && (testView == fFocusView || Detail::IsParent(fFocusView, testView))) candidateView = testView; } GenerateKeyEventForView(candidateView, theEvent); } //______________________________________________________________________________ void EventTranslator::GenerateKeyEventForView(NSView *view, NSEvent *theEvent) { //Generate key press event for a view without grab. assert(view != nil && "GenerateKeyEventForView, parameter 'view' is nil"); assert(theEvent != nil && "GenerateKeyEventForView, parameter 'theEvent' is nil"); assert((theEvent.type == Details::kKeyDown || theEvent.type == Details::kKeyUp) && "GenerateKeyEvenForView, event's type must be keydown or keyup"); const Mask_t eventType = theEvent.type == Details::kKeyDown ? kKeyPressMask : kKeyReleaseMask; NSView *childView = nil; NSPoint mousePosition = {}; if (QuartzWindow * const topLevel = FindWindowUnderPointer()) mousePosition = [topLevel mouseLocationOutsideOfEventStream]; if (eventType == kKeyPressMask) Detail::SendKeyPressEvent(fEventQueue, view, childView, theEvent, mousePosition); else Detail::SendKeyReleaseEvent(fEventQueue, view, childView, theEvent, mousePosition); } //______________________________________________________________________________ void EventTranslator::FindButtonGrab(NSView *fromView, NSEvent *theEvent, EMouseButton btn) { //Find a view to become a grab view - either passive or implicit. assert(fromView != nil && "FindButtonGrabView, parameter 'fromView' is nil"); assert(theEvent != nil && "FindButtonGrabView, parameter 'theEvent' is nil"); assert(fPointerGrabType == kPGNoGrab && "FindButtonGrabView, grab is already activated"); const unsigned keyModifiers = Detail::GetKeyboardModifiersFromCocoaEvent(theEvent); NSView *grabView = 0; NSView *buttonPressView = 0; for (NSView *view = fromView; view != nil; view = view.fParentView) { //Top-first view to receive button press event. if (!buttonPressView && (view.fEventMask & kButtonPressMask)) buttonPressView = view; //Bottom-first view with passive grab. if (view.fPassiveGrabButton == kAnyButton || view.fPassiveGrabButton == btn) { //Check modifiers. if (view.fPassiveGrabKeyModifiers == kAnyModifier || (view.fPassiveGrabKeyModifiers & keyModifiers)) grabView = view; } } if (grabView) { fButtonGrabView = grabView; fPointerGrabType = kPGPassiveGrab; fGrabEventMask = grabView.fPassiveGrabEventMask; fOwnerEvents = grabView.fPassiveGrabOwnerEvents; } else if (buttonPressView) { //This is an implicit grab. fButtonGrabView = buttonPressView; fPointerGrabType = kPGImplicitGrab; fGrabEventMask = buttonPressView.fEventMask; fOwnerEvents = false; } else { //Implicit grab with 'root' window? fButtonGrabView = nil; fPointerGrabType = kPGImplicitGrab; fGrabEventMask = 0; fOwnerEvents = false; } } //______________________________________________________________________________ void EventTranslator::FindKeyGrabView(NSView *eventView, NSEvent *theEvent) { assert(eventView != nil && "FindKeyGrabView, parameter 'eventView' is nil"); assert(theEvent != nil && "FindKeyGrabView, parameter 'theEvent' is nil"); NSString * const characters = [theEvent charactersIgnoringModifiers]; assert(characters != nil && "FindKeyGrabView, [theEvent characters] returned nil"); assert([characters length] > 0 && "FindKeyGrabView, characters is an empty string"); const unichar keyCode = [characters characterAtIndex : 0]; const NSUInteger modifiers = [theEvent modifierFlags] & Details::kDeviceIndependentModifierFlagsMask; NSView *currentView = fFocusView; if (eventView != fFocusView && Detail::IsParent(fFocusView, eventView)) currentView = eventView; for (; currentView; currentView = currentView.fParentView) { if ([currentView findPassiveKeyGrab : keyCode modifiers : modifiers]) fKeyGrabView = currentView; } } }//X11 }//MacOSX }//ROOT