-
Notifications
You must be signed in to change notification settings - Fork 1
[macOS] Fix global shortcuts: F-key keycodes and headless event processing #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,6 +1,9 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <algorithm> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <atomic> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <cctype> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <mutex> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <string> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <thread> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <unordered_map> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include <vector> | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -138,12 +141,26 @@ bool ParseAcceleratorMac(const std::string& accelerator, UInt32& modifiers, UInt | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (key_token.rfind("f", 0) == 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| int fnum = std::stoi(key_token.substr(1)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (fnum >= 1 && fnum <= 24) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| keycode = kVK_F1 + (fnum - 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (key_token.rfind("f", 0) == 0 && key_token.size() > 1) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Carbon virtual key codes for function keys are non-contiguous; use explicit table. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| static const UInt32 kFKeyCodes[] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| kVK_F1, kVK_F2, kVK_F3, kVK_F4, kVK_F5, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| kVK_F6, kVK_F7, kVK_F8, kVK_F9, kVK_F10, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| kVK_F11, kVK_F12, kVK_F13, kVK_F14, kVK_F15, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| kVK_F16, kVK_F17, kVK_F18, kVK_F19, kVK_F20, | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| int fnum = std::stoi(key_token.substr(1)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (fnum >= 1 && fnum <= static_cast<int>(std::size(kFKeyCodes))) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| keycode = kFKeyCodes[fnum - 1]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (const std::invalid_argument&) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (const std::out_of_range&) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (key_token == "space") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -189,11 +206,17 @@ bool ParseAcceleratorMac(const std::string& accelerator, UInt32& modifiers, UInt | |||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } // namespace | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Polling interval for the background event thread (seconds). | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| static constexpr EventTimeout kEventPollTimeout = kEventDurationSecond * 0.1; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| class ShortcutManagerImpl final : public ShortcutManager::Impl { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| public: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| explicit ShortcutManagerImpl(ShortcutManager* manager) : manager_(manager) {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| ~ShortcutManagerImpl() override { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| StopThread(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| JoinThread(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const auto& [id, hotkey] : hotkeys_) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| UnregisterEventHotKey(hotkey); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -208,7 +231,7 @@ explicit ShortcutManagerImpl(ShortcutManager* manager) : manager_(manager) {} | |||||||||||||||||||||||||||||||||||||||||||||||||||
| bool IsSupported() override { return true; } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| bool RegisterShortcut(const std::shared_ptr<Shortcut>& shortcut) override { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| EnsureHandler(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| EnsureThread(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| UInt32 modifiers = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| UInt32 keycode = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -242,10 +265,10 @@ bool UnregisterShortcut(const std::shared_ptr<Shortcut>& shortcut) override { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| return true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| void SetupEventMonitoring() override { EnsureHandler(); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| void SetupEventMonitoring() override { EnsureThread(); } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| void CleanupEventMonitoring() override { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Keep handler installed as long as shortcuts exist. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Keep thread running while shortcuts may still be registered. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| private: | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -279,6 +302,59 @@ void EnsureHandler() { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| &handler_); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| void EnsureThread() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::lock_guard<std::mutex> lock(thread_mutex_); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (running_.load()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| EnsureHandler(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| running_.store(true); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| thread_ = std::thread([this]() { ThreadMain(); }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| void StopThread() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::lock_guard<std::mutex> lock(thread_mutex_); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!running_.load()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| running_.store(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Unlock before joining so the thread can complete any in-progress work. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Note: the thread only touches running_ and Carbon APIs, so this is safe. | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+318
to
+325
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| std::lock_guard<std::mutex> lock(thread_mutex_); | |
| if (!running_.load()) { | |
| return; | |
| } | |
| running_.store(false); | |
| // Unlock before joining so the thread can complete any in-progress work. | |
| // Note: the thread only touches running_ and Carbon APIs, so this is safe. | |
| std::thread local_thread; | |
| { | |
| std::lock_guard<std::mutex> lock(thread_mutex_); | |
| if (!running_.load()) { | |
| return; | |
| } | |
| running_.store(false); | |
| // Move the thread object out while holding the lock so that thread_ | |
| // is no longer joinable when EnsureThread() next runs. | |
| local_thread = std::move(thread_); | |
| } | |
| // Join outside the lock so the worker can finish any in-progress work. | |
| if (local_thread.joinable()) { | |
| local_thread.join(); | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ParseAcceleratorMacusesstd::size(kFKeyCodes)but this file doesn't include a header that guaranteesstd::sizeis declared (C++17 places it in<iterator>). This can fail to compile on some toolchains; include the appropriate header (e.g.<iterator>/<array>) or avoidstd::sizehere (e.g.std::extent_v/sizeof-based length).