/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qnativeevents.h" #include #include // ************************************************************ // Quartz // ************************************************************ static Qt::KeyboardModifiers getModifiersFromQuartzEvent(CGEventRef inEvent) { Qt::KeyboardModifiers m; CGEventFlags flags = CGEventGetFlags(inEvent); if (flags & kCGEventFlagMaskShift || flags & kCGEventFlagMaskAlphaShift) m |= Qt::ShiftModifier; if (flags & kCGEventFlagMaskControl) m |= Qt::ControlModifier; if (flags & kCGEventFlagMaskAlternate) m |= Qt::AltModifier; if (flags & kCGEventFlagMaskCommand) m |= Qt::MetaModifier; return m; } static void setModifiersFromQNativeEvent(CGEventRef inEvent, const QNativeEvent &event) { CGEventFlags flags = CGEventFlags(0); if (event.modifiers.testFlag(Qt::ShiftModifier)) flags = CGEventFlags(flags | kCGEventFlagMaskShift); if (event.modifiers.testFlag(Qt::ControlModifier)) flags = CGEventFlags(flags | kCGEventFlagMaskControl); if (event.modifiers.testFlag(Qt::AltModifier)) flags = CGEventFlags(flags | kCGEventFlagMaskAlternate); if (event.modifiers.testFlag(Qt::MetaModifier)) flags = CGEventFlags(flags | kCGEventFlagMaskCommand); CGEventSetFlags(inEvent, flags); } static QPoint getMouseLocationFromQuartzEvent(CGEventRef inEvent) { CGPoint pos = CGEventGetLocation(inEvent); QPoint tmp; tmp.setX(pos.x); tmp.setY(pos.y); return tmp; } static QChar getCharFromQuartzEvent(CGEventRef inEvent) { UniCharCount count = 0; UniChar c; CGEventKeyboardGetUnicodeString(inEvent, 1, &count, &c); return QChar(c); } static CGEventRef EventHandler_Quartz(CGEventTapProxy proxy, CGEventType type, CGEventRef inEvent, void *refCon) { Q_UNUSED(proxy); QNativeInput *nativeInput = static_cast(refCon); switch (type){ case kCGEventKeyDown:{ QNativeKeyEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.nativeKeyCode = CGEventGetIntegerValueField(inEvent, kCGKeyboardEventKeycode); e.character = getCharFromQuartzEvent(inEvent); e.press = true; nativeInput->notify(&e); break; } case kCGEventKeyUp:{ QNativeKeyEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.nativeKeyCode = CGEventGetIntegerValueField(inEvent, kCGKeyboardEventKeycode); e.character = getCharFromQuartzEvent(inEvent); e.press = false; nativeInput->notify(&e); break; } case kCGEventLeftMouseDown:{ QNativeMouseButtonEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); e.clickCount = CGEventGetIntegerValueField(inEvent, kCGMouseEventClickState); e.button = Qt::LeftButton; nativeInput->notify(&e); break; } case kCGEventLeftMouseUp:{ QNativeMouseButtonEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); e.clickCount = 0; e.button = Qt::LeftButton; nativeInput->notify(&e); break; } case kCGEventRightMouseDown:{ QNativeMouseButtonEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); e.clickCount = CGEventGetIntegerValueField(inEvent, kCGMouseEventClickState); e.button = Qt::RightButton; nativeInput->notify(&e); break; } case kCGEventRightMouseUp:{ QNativeMouseButtonEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); e.clickCount = 0; e.button = Qt::RightButton; nativeInput->notify(&e); break; } case kCGEventMouseMoved:{ QNativeMouseMoveEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); nativeInput->notify(&e); break; } case kCGEventLeftMouseDragged:{ QNativeMouseDragEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); e.clickCount = CGEventGetIntegerValueField(inEvent, kCGMouseEventClickState); e.button = Qt::LeftButton; nativeInput->notify(&e); break; } case kCGEventScrollWheel:{ QNativeMouseWheelEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.delta = CGEventGetIntegerValueField(inEvent, kCGScrollWheelEventDeltaAxis1); e.globalPos = getMouseLocationFromQuartzEvent(inEvent); nativeInput->notify(&e); break; } case kCGEventFlagsChanged:{ QNativeModifierEvent e; e.modifiers = getModifiersFromQuartzEvent(inEvent); e.nativeKeyCode = CGEventGetIntegerValueField(inEvent, kCGKeyboardEventKeycode); nativeInput->notify(&e); break; } } return inEvent; } Qt::Native::Status insertEventHandler_Quartz(QNativeInput *nativeInput) { uid_t uid = geteuid(); if (uid != 0) qWarning("MacNativeEvents: You must be root to listen for key events!"); CFMachPortRef port = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionListenOnly, kCGEventMaskForAllEvents, EventHandler_Quartz, nativeInput); CFRunLoopSourceRef eventSrc = CFMachPortCreateRunLoopSource(NULL, port, 0); CFRunLoopAddSource(CFRunLoopGetMain(), eventSrc, kCFRunLoopCommonModes); return Qt::Native::Success; } Qt::Native::Status removeEventHandler_Quartz() { return Qt::Native::Success; // ToDo: } Qt::Native::Status sendNativeKeyEvent_Quartz(const QNativeKeyEvent &event) { CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, event.press); setModifiersFromQNativeEvent(e, event); CGEventPost(kCGHIDEventTap, e); CFRelease(e); return Qt::Native::Success; } Qt::Native::Status sendNativeMouseMoveEvent_Quartz(const QNativeMouseMoveEvent &event) { CGPoint pos; pos.x = event.globalPos.x(); pos.y = event.globalPos.y(); CGEventRef e = CGEventCreateMouseEvent(0, kCGEventMouseMoved, pos, kCGMouseButtonLeft /* ignored */); setModifiersFromQNativeEvent(e, event); CGEventPost(kCGHIDEventTap, e); CFRelease(e); return Qt::Native::Success; } Qt::Native::Status sendNativeMouseButtonEvent_Quartz(const QNativeMouseButtonEvent &event) { CGPoint pos; pos.x = event.globalPos.x(); pos.y = event.globalPos.y(); CGEventType type = kCGEventNull; if (event.button == Qt::LeftButton) type = (event.clickCount > 0) ? kCGEventLeftMouseDown : kCGEventLeftMouseUp; else if (event.button == Qt::RightButton) type = (event.clickCount > 0) ? kCGEventRightMouseDown : kCGEventRightMouseUp; else type = (event.clickCount > 0) ? kCGEventOtherMouseDown : kCGEventOtherMouseUp; // The mouseButton argument to CGEventCreateMouseEvent() is ignored unless the type // is kCGEventOtherSomething, so defaulting to kCGMouseButtonLeft is fine. CGMouseButton mouseButton = (type == kCGEventOtherMouseDown || type == kCGEventOtherMouseUp) ? kCGMouseButtonCenter : kCGMouseButtonLeft; CGEventRef e = CGEventCreateMouseEvent(0, type, pos, mouseButton); setModifiersFromQNativeEvent(e, event); CGEventSetIntegerValueField(e, kCGMouseEventClickState, event.clickCount); CGEventPost(kCGHIDEventTap, e); CFRelease(e); return Qt::Native::Success; } Qt::Native::Status sendNativeMouseDragEvent_Quartz(const QNativeMouseDragEvent &event) { CGPoint pos; pos.x = event.globalPos.x(); pos.y = event.globalPos.y(); CGEventType type = kCGEventNull; if (event.button == Qt::LeftButton) type = kCGEventLeftMouseDragged; else if (event.button == Qt::RightButton) type = kCGEventRightMouseDragged; else type = kCGEventOtherMouseDragged; // The mouseButton argument to CGEventCreateMouseEvent() is ignored unless the type // is kCGEventOtherSomething, so defaulting to kCGMouseButtonLeft is fine. CGMouseButton mouseButton = type == kCGEventOtherMouseDragged ? kCGMouseButtonCenter : kCGMouseButtonLeft; CGEventRef e = CGEventCreateMouseEvent(0, type, pos, mouseButton); setModifiersFromQNativeEvent(e, event); CGEventPost(kCGHIDEventTap, e); CFRelease(e); return Qt::Native::Success; } Qt::Native::Status sendNativeMouseWheelEvent_Quartz(const QNativeMouseWheelEvent &event) { CGPoint pos; pos.x = event.globalPos.x(); pos.y = event.globalPos.y(); CGEventRef e = CGEventCreateScrollWheelEvent(0, kCGScrollEventUnitPixel, 1, 0); CGEventSetIntegerValueField(e, kCGScrollWheelEventDeltaAxis1, event.delta); CGEventSetLocation(e, pos); setModifiersFromQNativeEvent(e, event); CGEventPost(kCGHIDEventTap, e); CFRelease(e); return Qt::Native::Success; } Qt::Native::Status sendNativeModifierEvent_Quartz(const QNativeModifierEvent &event) { CGEventRef e = CGEventCreateKeyboardEvent(0, (uint)event.nativeKeyCode, 0); CGEventSetType(e, kCGEventFlagsChanged); setModifiersFromQNativeEvent(e, event); CGEventPost(kCGHIDEventTap, e); CFRelease(e); return Qt::Native::Success; } // ************************************************************ // QNativeInput methods: // ************************************************************ Qt::Native::Status QNativeInput::sendNativeMouseButtonEvent(const QNativeMouseButtonEvent &event) { return sendNativeMouseButtonEvent_Quartz(event); } Qt::Native::Status QNativeInput::sendNativeMouseMoveEvent(const QNativeMouseMoveEvent &event) { return sendNativeMouseMoveEvent_Quartz(event); } Qt::Native::Status QNativeInput::sendNativeMouseDragEvent(const QNativeMouseDragEvent &event) { return sendNativeMouseDragEvent_Quartz(event); } Qt::Native::Status QNativeInput::sendNativeMouseWheelEvent(const QNativeMouseWheelEvent &event) { return sendNativeMouseWheelEvent_Quartz(event); } Qt::Native::Status QNativeInput::sendNativeKeyEvent(const QNativeKeyEvent &event) { return sendNativeKeyEvent_Quartz(event); } Qt::Native::Status QNativeInput::sendNativeModifierEvent(const QNativeModifierEvent &event) { return sendNativeModifierEvent_Quartz(event); } Qt::Native::Status QNativeInput::subscribeForNativeEvents() { return insertEventHandler_Quartz(this); } Qt::Native::Status QNativeInput::unsubscribeForNativeEvents() { return removeEventHandler_Quartz(); } // Some Qt to Mac mappings: int QNativeKeyEvent::Key_A = 0; int QNativeKeyEvent::Key_B = 11; int QNativeKeyEvent::Key_C = 8; int QNativeKeyEvent::Key_1 = 18; int QNativeKeyEvent::Key_Backspace = 51; int QNativeKeyEvent::Key_Enter = 36; int QNativeKeyEvent::Key_Del = 117;