KGlobalAccel

kglobalaccel_mac.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2001, 2002 Ellis Whitehead <[email protected]>
4  SPDX-FileCopyrightText: 2006 Marijn Kruisselbrink <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "kglobalaccel_mac.h"
10 
11 #include <QDebug>
12 
13 #ifdef Q_OS_OSX
14 
15 #include <QList>
16 #include <QMultiMap>
17 
18 #include "globalshortcutsregistry.h"
19 #include "logging_p.h"
20 #include <KKeyServer>
21 
22 OSStatus hotKeyEventHandler(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void *inUserData)
23 {
24  UInt32 eventKind = GetEventKind(inEvent);
25  if (eventKind == kEventRawKeyDown) {
26  UInt32 keycode;
27  if (GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32, NULL, sizeof(keycode), NULL, &keycode) != noErr) {
28  qCWarning(KGLOBALACCELD) << "Error retrieving keycode parameter from event";
29  }
30  qCDebug(KGLOBALACCELD) << " key down, keycode = " << keycode;
31  } else if (eventKind == kEventHotKeyPressed) {
32  KGlobalAccelImpl *impl = static_cast<KGlobalAccelImpl *>(inUserData);
33  EventHotKeyID hotkey;
34  if (GetEventParameter(inEvent, kEventParamDirectObject, typeEventHotKeyID, NULL, sizeof(hotkey), NULL, &hotkey) != noErr) {
35  qCWarning(KGLOBALACCELD) << "Error retrieving hotkey parameter from event";
36  return eventNotHandledErr;
37  }
38  // Typecasts necesary to prevent a warning from gcc
39  return (impl->keyPressed(hotkey.id) ? (OSStatus)noErr : (OSStatus)eventNotHandledErr);
40  }
41  return eventNotHandledErr;
42 }
43 
44 void layoutChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
45 {
46  static_cast<KGlobalAccelImpl *>(observer)->keyboardLayoutChanged();
47 }
48 
49 KGlobalAccelImpl::KGlobalAccelImpl(GlobalShortcutsRegistry *owner)
50  : m_owner(owner)
51  , m_eventTarget(GetApplicationEventTarget())
52  , m_eventHandler(NewEventHandlerUPP(hotKeyEventHandler))
53 {
54  m_eventType[0].eventClass = kEventClassKeyboard;
55  m_eventType[0].eventKind = kEventHotKeyPressed;
56  m_eventType[1].eventClass = kEventClassKeyboard; // only useful for testing, is not used because count passed in call to InstallEventHandler is 1
57  m_eventType[1].eventKind = kEventRawKeyDown;
58  refs = new QMap<int, QList<EventHotKeyRef>>();
59 
60  CFStringRef str = CFStringCreateWithCString(NULL, "AppleKeyboardPreferencesChangedNotification", kCFStringEncodingASCII);
61  if (str) {
62  CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), this, layoutChanged, str, NULL, CFNotificationSuspensionBehaviorHold);
63  CFRelease(str);
64  } else {
65  qCWarning(KGLOBALACCELD) << "Couldn't create CFString to register for keyboard notifications";
66  }
67 }
68 
69 KGlobalAccelImpl::~KGlobalAccelImpl()
70 {
71  DisposeEventHandlerUPP(hotKeyEventHandler);
72  CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), this, NULL, NULL);
73  delete refs;
74 }
75 
76 bool KGlobalAccelImpl::grabKey(int keyQt, bool grab)
77 {
78  if (grab) {
79  qCDebug(KGLOBALACCELD) << "Grabbing key " << keyQt;
80  QList<uint> keyCodes;
81  uint mod;
82  KKeyServer::keyQtToCodeMac(keyQt, keyCodes);
83  KKeyServer::keyQtToModMac(keyQt, mod);
84 
85  qCDebug(KGLOBALACCELD) << "keyQt: " << keyQt << " mod: " << mod;
86  for (uint keyCode : qAsConst(keyCodes)) {
87  qCDebug(KGLOBALACCELD) << " keyCode: " << keyCode;
88  }
89 
90  EventHotKeyID ehkid;
91  ehkid.signature = 'Kgai';
92  ehkid.id = keyQt;
93  QList<EventHotKeyRef> hotkeys;
94  for (uint keyCode : qAsConst(keyCodes)) {
95  EventHotKeyRef ref;
96  if (RegisterEventHotKey(keyCode, mod, ehkid, m_eventTarget, 0, &ref) != noErr) {
97  qCWarning(KGLOBALACCELD) << "RegisterEventHotKey failed!";
98  }
99  hotkeys.append(ref);
100  }
101  refs->insert(keyQt, hotkeys);
102  } else {
103  qCDebug(KGLOBALACCELD) << "Ungrabbing key " << keyQt;
104  if (refs->count(keyQt) == 0)
105  qCWarning(KGLOBALACCELD) << "Trying to ungrab a key thas is not grabbed";
106  const auto lstRef = refs->value(keyQt);
107  for (const EventHotKeyRef &ref : lstRef) {
108  if (UnregisterEventHotKey(ref) != noErr) {
109  qCWarning(KGLOBALACCELD) << "UnregisterEventHotKey should not fail!";
110  }
111  }
112  refs->remove(keyQt);
113  }
114  return true;
115 }
116 
117 void KGlobalAccelImpl::setEnabled(bool enable)
118 {
119  if (enable) {
120  if (InstallEventHandler(m_eventTarget, m_eventHandler, 1, m_eventType, this, &m_curHandler) != noErr)
121  qCWarning(KGLOBALACCELD) << "InstallEventHandler failed!";
122  } else {
123  if (RemoveEventHandler(m_curHandler) != noErr)
124  qCWarning(KGLOBALACCELD) << "RemoveEventHandler failed!";
125  }
126 }
127 
128 bool KGlobalAccelImpl::keyPressed(int key)
129 {
130  return m_owner->keyPressed(key);
131 }
132 
133 void KGlobalAccelImpl::keyboardLayoutChanged()
134 {
135  // Keyboard layout might have changed, first ungrab all keys
136  QList<int> keys; // Array to store all the keys that were grabbed
137  while (!refs->empty()) {
138  int key = refs->begin().key();
139  keys.append(key);
140  grabKey(key, false);
141  }
142  // Now re-grab all the keys
143  for (int key : qAsConst(keys)) {
144  grabKey(key, true);
145  }
146 }
147 
148 #endif // !Q_OS_OSX
void setEnabled(bool)
Enable/disable all shortcuts. There will not be any grabbed shortcuts at this point.
virtual bool grabKey(int key, bool grab)=0
This function registers or unregisters a certain key for global capture, depending on grab...
void ref()
Global Shortcut Registry.
void append(const T &value)
KWINDOWSYSTEM_EXPORT bool keyQtToCodeMac(int keyQt, QList< uint > &keyCodes)
bool grabKey(int key, bool grab)
This function registers or unregisters a certain key for global capture, depending on grab...
KWINDOWSYSTEM_EXPORT bool keyQtToModMac(int keyQt, uint &mod)
QList::iterator begin()
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun May 16 2021 22:53:45 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.