| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- // This file is part of Desktop App Toolkit,
- // a set of libraries for developing nice desktop applications.
- //
- // For license and copyright information please follow this link:
- // https://github.com/desktop-app/legal/blob/master/LEGAL
- //
- #include "base/global_shortcuts_generic.h"
- #include "base/platform/base_platform_global_shortcuts.h"
- #include "base/invoke_queued.h"
- namespace base {
- namespace {
- constexpr auto kShortcutLimit = 4;
- std::mutex GlobalMutex;
- std::vector<not_null<GlobalShortcutManagerGeneric*>> Managers;
- [[nodiscard]] GlobalShortcut MakeShortcut(
- std::vector<GlobalShortcutKeyGeneric> descriptors) {
- return std::make_shared<GlobalShortcutValueGeneric>(
- std::move(descriptors));
- }
- [[nodiscard]] bool Matches(
- const std::vector<GlobalShortcutKeyGeneric> &sorted,
- const flat_set<GlobalShortcutKeyGeneric> &down) {
- if (sorted.size() > down.size()) {
- return false;
- }
- auto j = begin(down);
- for (const auto descriptor : sorted) {
- while (true) {
- if (*j > descriptor) {
- return false;
- } else if (*j < descriptor) {
- ++j;
- if (j == end(down)) {
- return false;
- }
- } else {
- break;
- }
- }
- }
- return true;
- }
- void ScheduleForAll(GlobalShortcutKeyGeneric descriptor, bool down) {
- std::unique_lock lock{ GlobalMutex };
- for (const auto manager : Managers) {
- manager->schedule(descriptor, down);
- }
- }
- } // namespace
- std::unique_ptr<GlobalShortcutManager> CreateGlobalShortcutManager() {
- return std::make_unique<GlobalShortcutManagerGeneric>();
- }
- bool GlobalShortcutsAvailable() {
- return Platform::GlobalShortcuts::Available();
- }
- bool GlobalShortcutsAllowed() {
- return Platform::GlobalShortcuts::Allowed();
- }
- GlobalShortcutValueGeneric::GlobalShortcutValueGeneric(
- std::vector<GlobalShortcutKeyGeneric> descriptors)
- : _descriptors(std::move(descriptors)) {
- Expects(!_descriptors.empty());
- }
- QString GlobalShortcutValueGeneric::toDisplayString() {
- auto result = QStringList();
- result.reserve(_descriptors.size());
- for (const auto descriptor : _descriptors) {
- result.push_back(Platform::GlobalShortcuts::KeyName(descriptor));
- }
- return result.join(" + ");
- }
- QByteArray GlobalShortcutValueGeneric::serialize() {
- static_assert(sizeof(GlobalShortcutKeyGeneric) == sizeof(uint64));
- const auto size = sizeof(GlobalShortcutKeyGeneric) * _descriptors.size();
- auto result = QByteArray(size, Qt::Uninitialized);
- memcpy(result.data(), _descriptors.data(), size);
- return result;
- }
- GlobalShortcutManagerGeneric::GlobalShortcutManagerGeneric() {
- std::unique_lock lock{ GlobalMutex };
- const auto start = Managers.empty();
- Managers.push_back(this);
- lock.unlock();
- if (start) {
- Platform::GlobalShortcuts::Start(ScheduleForAll);
- }
- }
- GlobalShortcutManagerGeneric::~GlobalShortcutManagerGeneric() {
- std::unique_lock lock{ GlobalMutex };
- Managers.erase(ranges::remove(Managers, not_null{ this }), end(Managers));
- const auto stop = Managers.empty();
- lock.unlock();
- if (stop) {
- Platform::GlobalShortcuts::Stop();
- }
- }
- void GlobalShortcutManagerGeneric::startRecording(
- Fn<void(GlobalShortcut)> progress,
- Fn<void(GlobalShortcut)> done) {
- Expects(done != nullptr);
- _recordingDown.clear();
- _recordingUp.clear();
- _recording = true;
- _recordingProgress = std::move(progress);
- _recordingDone = std::move(done);
- }
- void GlobalShortcutManagerGeneric::stopRecording() {
- _recordingDown.clear();
- _recordingUp.clear();
- _recording = false;
- _recordingDone = nullptr;
- _recordingProgress = nullptr;
- }
- void GlobalShortcutManagerGeneric::startWatching(
- GlobalShortcut shortcut,
- Fn<void(bool pressed)> callback) {
- Expects(shortcut != nullptr);
- Expects(callback != nullptr);
- const auto i = ranges::find(_watchlist, shortcut, &Watch::shortcut);
- if (i != end(_watchlist)) {
- i->callback = std::move(callback);
- } else {
- auto sorted = static_cast<GlobalShortcutValueGeneric*>(
- shortcut.get())->descriptors();
- std::sort(begin(sorted), end(sorted));
- _watchlist.push_back(Watch{
- std::move(shortcut),
- std::move(sorted),
- std::move(callback)
- });
- }
- }
- void GlobalShortcutManagerGeneric::stopWatching(GlobalShortcut shortcut) {
- const auto i = ranges::find(_watchlist, shortcut, &Watch::shortcut);
- if (i != end(_watchlist)) {
- _watchlist.erase(i);
- }
- _pressed.erase(ranges::find(_pressed, shortcut), end(_pressed));
- }
- GlobalShortcut GlobalShortcutManagerGeneric::shortcutFromSerialized(
- QByteArray serialized) {
- const auto single = sizeof(GlobalShortcutKeyGeneric);
- if (serialized.isEmpty() || serialized.size() % single) {
- return nullptr;
- }
- auto count = serialized.size() / single;
- auto list = std::vector<GlobalShortcutKeyGeneric>(count);
- memcpy(list.data(), serialized.constData(), serialized.size());
- return MakeShortcut(std::move(list));
- }
- void GlobalShortcutManagerGeneric::schedule(
- GlobalShortcutKeyGeneric descriptor,
- bool down) {
- InvokeQueued(this, [=] { process(descriptor, down); });
- }
- void GlobalShortcutManagerGeneric::process(
- GlobalShortcutKeyGeneric descriptor,
- bool down) {
- if (!down) {
- _down.remove(descriptor);
- }
- if (_recording) {
- processRecording(descriptor, down);
- return;
- }
- auto scheduled = std::vector<Fn<void(bool pressed)>>();
- if (down) {
- _down.emplace(descriptor);
- for (const auto &watch : _watchlist) {
- if (watch.sorted.size() > _down.size()
- || ranges::contains(_pressed, watch.shortcut)) {
- continue;
- } else if (Matches(watch.sorted, _down)) {
- _pressed.push_back(watch.shortcut);
- scheduled.push_back(watch.callback);
- }
- }
- } else {
- _down.remove(descriptor);
- for (auto i = begin(_pressed); i != end(_pressed);) {
- const auto generic = static_cast<GlobalShortcutValueGeneric*>(
- i->get());
- if (!ranges::contains(generic->descriptors(), descriptor)) {
- ++i;
- } else {
- const auto j = ranges::find(
- _watchlist,
- *i,
- &Watch::shortcut);
- Assert(j != end(_watchlist));
- scheduled.push_back(j->callback);
- i = _pressed.erase(i);
- }
- }
- }
- for (const auto &callback : scheduled) {
- callback(down);
- }
- }
- void GlobalShortcutManagerGeneric::processRecording(
- GlobalShortcutKeyGeneric descriptor,
- bool down) {
- if (down) {
- processRecordingPress(descriptor);
- } else {
- processRecordingRelease(descriptor);
- }
- }
- void GlobalShortcutManagerGeneric::processRecordingPress(
- GlobalShortcutKeyGeneric descriptor) {
- auto changed = false;
- _recordingUp.remove(descriptor);
- for (const auto descriptor : _recordingUp) {
- const auto i = ranges::remove(_recordingDown, descriptor);
- if (i != end(_recordingDown)) {
- _recordingDown.erase(i, end(_recordingDown));
- changed = true;
- }
- }
- _recordingUp.clear();
- const auto i = std::find(
- begin(_recordingDown),
- end(_recordingDown),
- descriptor);
- if (i == _recordingDown.end()) {
- _recordingDown.push_back(descriptor);
- changed = true;
- }
- if (!changed) {
- return;
- } else if (_recordingDown.size() == kShortcutLimit) {
- finishRecording();
- } else if (const auto onstack = _recordingProgress) {
- onstack(MakeShortcut(_recordingDown));
- }
- }
- void GlobalShortcutManagerGeneric::processRecordingRelease(
- GlobalShortcutKeyGeneric descriptor) {
- const auto i = std::find(
- begin(_recordingDown),
- end(_recordingDown),
- descriptor);
- if (i == end(_recordingDown)) {
- return;
- }
- _recordingUp.emplace(descriptor);
- Assert(_recordingUp.size() <= _recordingDown.size());
- if (_recordingUp.size() == _recordingDown.size()) {
- // All keys are up, we got the shortcut.
- // Some down keys are not up yet.
- finishRecording();
- }
- }
- void GlobalShortcutManagerGeneric::finishRecording() {
- Expects(!_recordingDown.empty());
- auto result = MakeShortcut(std::move(_recordingDown));
- _recordingDown.clear();
- _recordingUp.clear();
- _recording = false;
- const auto done = _recordingDone;
- _recordingDone = nullptr;
- _recordingProgress = nullptr;
- done(std::move(result));
- }
- } // namespace base
|