global_shortcuts_generic.cpp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // This file is part of Desktop App Toolkit,
  2. // a set of libraries for developing nice desktop applications.
  3. //
  4. // For license and copyright information please follow this link:
  5. // https://github.com/desktop-app/legal/blob/master/LEGAL
  6. //
  7. #include "base/global_shortcuts_generic.h"
  8. #include "base/platform/base_platform_global_shortcuts.h"
  9. #include "base/invoke_queued.h"
  10. namespace base {
  11. namespace {
  12. constexpr auto kShortcutLimit = 4;
  13. std::mutex GlobalMutex;
  14. std::vector<not_null<GlobalShortcutManagerGeneric*>> Managers;
  15. [[nodiscard]] GlobalShortcut MakeShortcut(
  16. std::vector<GlobalShortcutKeyGeneric> descriptors) {
  17. return std::make_shared<GlobalShortcutValueGeneric>(
  18. std::move(descriptors));
  19. }
  20. [[nodiscard]] bool Matches(
  21. const std::vector<GlobalShortcutKeyGeneric> &sorted,
  22. const flat_set<GlobalShortcutKeyGeneric> &down) {
  23. if (sorted.size() > down.size()) {
  24. return false;
  25. }
  26. auto j = begin(down);
  27. for (const auto descriptor : sorted) {
  28. while (true) {
  29. if (*j > descriptor) {
  30. return false;
  31. } else if (*j < descriptor) {
  32. ++j;
  33. if (j == end(down)) {
  34. return false;
  35. }
  36. } else {
  37. break;
  38. }
  39. }
  40. }
  41. return true;
  42. }
  43. void ScheduleForAll(GlobalShortcutKeyGeneric descriptor, bool down) {
  44. std::unique_lock lock{ GlobalMutex };
  45. for (const auto manager : Managers) {
  46. manager->schedule(descriptor, down);
  47. }
  48. }
  49. } // namespace
  50. std::unique_ptr<GlobalShortcutManager> CreateGlobalShortcutManager() {
  51. return std::make_unique<GlobalShortcutManagerGeneric>();
  52. }
  53. bool GlobalShortcutsAvailable() {
  54. return Platform::GlobalShortcuts::Available();
  55. }
  56. bool GlobalShortcutsAllowed() {
  57. return Platform::GlobalShortcuts::Allowed();
  58. }
  59. GlobalShortcutValueGeneric::GlobalShortcutValueGeneric(
  60. std::vector<GlobalShortcutKeyGeneric> descriptors)
  61. : _descriptors(std::move(descriptors)) {
  62. Expects(!_descriptors.empty());
  63. }
  64. QString GlobalShortcutValueGeneric::toDisplayString() {
  65. auto result = QStringList();
  66. result.reserve(_descriptors.size());
  67. for (const auto descriptor : _descriptors) {
  68. result.push_back(Platform::GlobalShortcuts::KeyName(descriptor));
  69. }
  70. return result.join(" + ");
  71. }
  72. QByteArray GlobalShortcutValueGeneric::serialize() {
  73. static_assert(sizeof(GlobalShortcutKeyGeneric) == sizeof(uint64));
  74. const auto size = sizeof(GlobalShortcutKeyGeneric) * _descriptors.size();
  75. auto result = QByteArray(size, Qt::Uninitialized);
  76. memcpy(result.data(), _descriptors.data(), size);
  77. return result;
  78. }
  79. GlobalShortcutManagerGeneric::GlobalShortcutManagerGeneric() {
  80. std::unique_lock lock{ GlobalMutex };
  81. const auto start = Managers.empty();
  82. Managers.push_back(this);
  83. lock.unlock();
  84. if (start) {
  85. Platform::GlobalShortcuts::Start(ScheduleForAll);
  86. }
  87. }
  88. GlobalShortcutManagerGeneric::~GlobalShortcutManagerGeneric() {
  89. std::unique_lock lock{ GlobalMutex };
  90. Managers.erase(ranges::remove(Managers, not_null{ this }), end(Managers));
  91. const auto stop = Managers.empty();
  92. lock.unlock();
  93. if (stop) {
  94. Platform::GlobalShortcuts::Stop();
  95. }
  96. }
  97. void GlobalShortcutManagerGeneric::startRecording(
  98. Fn<void(GlobalShortcut)> progress,
  99. Fn<void(GlobalShortcut)> done) {
  100. Expects(done != nullptr);
  101. _recordingDown.clear();
  102. _recordingUp.clear();
  103. _recording = true;
  104. _recordingProgress = std::move(progress);
  105. _recordingDone = std::move(done);
  106. }
  107. void GlobalShortcutManagerGeneric::stopRecording() {
  108. _recordingDown.clear();
  109. _recordingUp.clear();
  110. _recording = false;
  111. _recordingDone = nullptr;
  112. _recordingProgress = nullptr;
  113. }
  114. void GlobalShortcutManagerGeneric::startWatching(
  115. GlobalShortcut shortcut,
  116. Fn<void(bool pressed)> callback) {
  117. Expects(shortcut != nullptr);
  118. Expects(callback != nullptr);
  119. const auto i = ranges::find(_watchlist, shortcut, &Watch::shortcut);
  120. if (i != end(_watchlist)) {
  121. i->callback = std::move(callback);
  122. } else {
  123. auto sorted = static_cast<GlobalShortcutValueGeneric*>(
  124. shortcut.get())->descriptors();
  125. std::sort(begin(sorted), end(sorted));
  126. _watchlist.push_back(Watch{
  127. std::move(shortcut),
  128. std::move(sorted),
  129. std::move(callback)
  130. });
  131. }
  132. }
  133. void GlobalShortcutManagerGeneric::stopWatching(GlobalShortcut shortcut) {
  134. const auto i = ranges::find(_watchlist, shortcut, &Watch::shortcut);
  135. if (i != end(_watchlist)) {
  136. _watchlist.erase(i);
  137. }
  138. _pressed.erase(ranges::find(_pressed, shortcut), end(_pressed));
  139. }
  140. GlobalShortcut GlobalShortcutManagerGeneric::shortcutFromSerialized(
  141. QByteArray serialized) {
  142. const auto single = sizeof(GlobalShortcutKeyGeneric);
  143. if (serialized.isEmpty() || serialized.size() % single) {
  144. return nullptr;
  145. }
  146. auto count = serialized.size() / single;
  147. auto list = std::vector<GlobalShortcutKeyGeneric>(count);
  148. memcpy(list.data(), serialized.constData(), serialized.size());
  149. return MakeShortcut(std::move(list));
  150. }
  151. void GlobalShortcutManagerGeneric::schedule(
  152. GlobalShortcutKeyGeneric descriptor,
  153. bool down) {
  154. InvokeQueued(this, [=] { process(descriptor, down); });
  155. }
  156. void GlobalShortcutManagerGeneric::process(
  157. GlobalShortcutKeyGeneric descriptor,
  158. bool down) {
  159. if (!down) {
  160. _down.remove(descriptor);
  161. }
  162. if (_recording) {
  163. processRecording(descriptor, down);
  164. return;
  165. }
  166. auto scheduled = std::vector<Fn<void(bool pressed)>>();
  167. if (down) {
  168. _down.emplace(descriptor);
  169. for (const auto &watch : _watchlist) {
  170. if (watch.sorted.size() > _down.size()
  171. || ranges::contains(_pressed, watch.shortcut)) {
  172. continue;
  173. } else if (Matches(watch.sorted, _down)) {
  174. _pressed.push_back(watch.shortcut);
  175. scheduled.push_back(watch.callback);
  176. }
  177. }
  178. } else {
  179. _down.remove(descriptor);
  180. for (auto i = begin(_pressed); i != end(_pressed);) {
  181. const auto generic = static_cast<GlobalShortcutValueGeneric*>(
  182. i->get());
  183. if (!ranges::contains(generic->descriptors(), descriptor)) {
  184. ++i;
  185. } else {
  186. const auto j = ranges::find(
  187. _watchlist,
  188. *i,
  189. &Watch::shortcut);
  190. Assert(j != end(_watchlist));
  191. scheduled.push_back(j->callback);
  192. i = _pressed.erase(i);
  193. }
  194. }
  195. }
  196. for (const auto &callback : scheduled) {
  197. callback(down);
  198. }
  199. }
  200. void GlobalShortcutManagerGeneric::processRecording(
  201. GlobalShortcutKeyGeneric descriptor,
  202. bool down) {
  203. if (down) {
  204. processRecordingPress(descriptor);
  205. } else {
  206. processRecordingRelease(descriptor);
  207. }
  208. }
  209. void GlobalShortcutManagerGeneric::processRecordingPress(
  210. GlobalShortcutKeyGeneric descriptor) {
  211. auto changed = false;
  212. _recordingUp.remove(descriptor);
  213. for (const auto descriptor : _recordingUp) {
  214. const auto i = ranges::remove(_recordingDown, descriptor);
  215. if (i != end(_recordingDown)) {
  216. _recordingDown.erase(i, end(_recordingDown));
  217. changed = true;
  218. }
  219. }
  220. _recordingUp.clear();
  221. const auto i = std::find(
  222. begin(_recordingDown),
  223. end(_recordingDown),
  224. descriptor);
  225. if (i == _recordingDown.end()) {
  226. _recordingDown.push_back(descriptor);
  227. changed = true;
  228. }
  229. if (!changed) {
  230. return;
  231. } else if (_recordingDown.size() == kShortcutLimit) {
  232. finishRecording();
  233. } else if (const auto onstack = _recordingProgress) {
  234. onstack(MakeShortcut(_recordingDown));
  235. }
  236. }
  237. void GlobalShortcutManagerGeneric::processRecordingRelease(
  238. GlobalShortcutKeyGeneric descriptor) {
  239. const auto i = std::find(
  240. begin(_recordingDown),
  241. end(_recordingDown),
  242. descriptor);
  243. if (i == end(_recordingDown)) {
  244. return;
  245. }
  246. _recordingUp.emplace(descriptor);
  247. Assert(_recordingUp.size() <= _recordingDown.size());
  248. if (_recordingUp.size() == _recordingDown.size()) {
  249. // All keys are up, we got the shortcut.
  250. // Some down keys are not up yet.
  251. finishRecording();
  252. }
  253. }
  254. void GlobalShortcutManagerGeneric::finishRecording() {
  255. Expects(!_recordingDown.empty());
  256. auto result = MakeShortcut(std::move(_recordingDown));
  257. _recordingDown.clear();
  258. _recordingUp.clear();
  259. _recording = false;
  260. const auto done = _recordingDone;
  261. _recordingDone = nullptr;
  262. _recordingProgress = nullptr;
  263. done(std::move(result));
  264. }
  265. } // namespace base