keybindings.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  1. /*
  2. * This file is part of the Forge extension for GNOME
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. */
  18. // Gnome imports
  19. import GObject from "gi://GObject";
  20. import Meta from "gi://Meta";
  21. import Shell from "gi://Shell";
  22. // Gnome Shell imports
  23. import * as Main from "resource:///org/gnome/shell/ui/main.js";
  24. // Shared state
  25. import { Logger } from "../shared/logger.js";
  26. export class Keybindings extends GObject.Object {
  27. static {
  28. GObject.registerClass(this);
  29. }
  30. /** @type {import('./extension.js').default} */
  31. ext;
  32. constructor(ext) {
  33. super();
  34. Logger.debug(`created keybindings`);
  35. this._grabbers = new Map();
  36. // this._bindSignals();
  37. this.ext = ext;
  38. this.extWm = ext.extWm;
  39. this.kbdSettings = ext.kbdSettings;
  40. this.settings = ext.settings;
  41. this.buildBindingDefinitions();
  42. }
  43. // @deprecated
  44. _acceleratorActivate(action) {
  45. let grabber = this._grabbers.get(action);
  46. if (grabber) {
  47. Logger.debug(`Firing accelerator ${grabber.accelerator} : ${grabber.name}`);
  48. grabber.callback();
  49. } else {
  50. Logger.error(`No listeners [action={${action}}]`);
  51. }
  52. }
  53. // @deprecated
  54. _bindSignals() {
  55. global.display.connect("accelerator-activated", (_display, action, _deviceId, _timestamp) => {
  56. this._acceleratorActivate(action);
  57. });
  58. }
  59. enable() {
  60. let keybindings = this._bindings;
  61. for (const key in keybindings) {
  62. Main.wm.addKeybinding(
  63. key,
  64. this.kbdSettings,
  65. Meta.KeyBindingFlags.NONE,
  66. Shell.ActionMode.NORMAL,
  67. keybindings[key]
  68. );
  69. }
  70. Logger.debug(`keybindings:enable`);
  71. }
  72. disable() {
  73. let keybindings = this._bindings;
  74. for (const key in keybindings) {
  75. Main.wm.removeKeybinding(key);
  76. }
  77. Logger.debug(`keybindings:disable`);
  78. }
  79. // @deprecated
  80. enableListenForBindings() {
  81. windowConfig.forEach((config) => {
  82. config.shortcut.forEach((shortcut) => {
  83. this.listenFor(shortcut, () => {
  84. config.actions.forEach((action) => {
  85. this.extWm.command(action);
  86. });
  87. });
  88. });
  89. });
  90. }
  91. // @deprecated
  92. disableListenForBindings() {
  93. // The existing grabber items are from the custom config by
  94. // this extension.
  95. this._grabbers.forEach((grabber) => {
  96. global.display.ungrab_accelerator(grabber.action);
  97. Main.wm.allowKeybinding(grabber.name, Shell.ActionMode.NONE);
  98. });
  99. this._grabbers.clear();
  100. }
  101. /**
  102. * API for quick binding of keys to function. This is going to be useful with SpaceMode
  103. *
  104. * @param {String} accelerator - keybinding combinations
  105. * @param {Function} callback - function to call when the accelerator is invoked
  106. *
  107. * Credits:
  108. * - https://superuser.com/a/1182899
  109. * - Adapted based on current Gnome-shell API or syntax
  110. */
  111. listenFor(accelerator, callback) {
  112. let grabFlags = Meta.KeyBindingFlags.NONE;
  113. let action = global.display.grab_accelerator(accelerator, grabFlags);
  114. if (action == Meta.KeyBindingAction.NONE) {
  115. Logger.error(`Unable to grab accelerator [binding={${accelerator}}]`);
  116. // TODO - check the gnome keybindings for conflicts and notify the user
  117. } else {
  118. let name = Meta.external_binding_name_for_action(action);
  119. Logger.debug(`Requesting WM to allow binding [name={${name}}]`);
  120. Main.wm.allowKeybinding(name, Shell.ActionMode.ALL);
  121. this._grabbers.set(action, {
  122. name: name,
  123. accelerator: accelerator,
  124. callback: callback,
  125. action: action,
  126. });
  127. }
  128. }
  129. get modifierState() {
  130. const [_x, _y, state] = this.extWm.getPointer();
  131. return state;
  132. }
  133. allowDragDropTile() {
  134. const tileModifier = this.kbdSettings.get_string("mod-mask-mouse-tile");
  135. const modState = this.modifierState;
  136. // Using Clutter.ModifierType values and also testing for pointer
  137. // being grabbed (256). E.g. grabbed + pressing Super = 256 + 64 = 320
  138. // See window.js#_handleMoving() - an overlay preview is shown.
  139. // See window.js#_handleGrabOpEnd() - when the drag has been dropped
  140. switch (tileModifier) {
  141. case "Super":
  142. return modState === 64 || modState === 320;
  143. case "Alt":
  144. return modState === 8 || modState === 264;
  145. case "Ctrl":
  146. return modState === 4 || modState === 260;
  147. case "None":
  148. return true;
  149. }
  150. return false;
  151. }
  152. buildBindingDefinitions() {
  153. this._bindings = {
  154. "window-toggle-float": () => {
  155. let actions = [
  156. {
  157. name: "FloatToggle",
  158. mode: "float",
  159. x: "center",
  160. y: "center",
  161. width: 0.65,
  162. height: 0.75,
  163. },
  164. ];
  165. actions.forEach((action) => {
  166. this.extWm.command(action);
  167. });
  168. },
  169. "window-toggle-always-float": () => {
  170. let action = {
  171. name: "FloatClassToggle",
  172. mode: "float",
  173. x: "center",
  174. y: "center",
  175. width: 0.65,
  176. height: 0.75,
  177. };
  178. this.extWm.command(action);
  179. },
  180. "window-focus-left": () => {
  181. let actions = [
  182. {
  183. name: "Focus",
  184. direction: "Left",
  185. },
  186. ];
  187. actions.forEach((action) => {
  188. this.extWm.command(action);
  189. });
  190. },
  191. "window-focus-down": () => {
  192. let actions = [
  193. {
  194. name: "Focus",
  195. direction: "Down",
  196. },
  197. ];
  198. actions.forEach((action) => {
  199. this.extWm.command(action);
  200. });
  201. },
  202. "window-focus-up": () => {
  203. let actions = [
  204. {
  205. name: "Focus",
  206. direction: "Up",
  207. },
  208. ];
  209. actions.forEach((action) => {
  210. this.extWm.command(action);
  211. });
  212. },
  213. "window-focus-right": () => {
  214. let actions = [
  215. {
  216. name: "Focus",
  217. direction: "Right",
  218. },
  219. ];
  220. actions.forEach((action) => {
  221. this.extWm.command(action);
  222. });
  223. },
  224. "window-swap-left": () => {
  225. let actions = [
  226. {
  227. name: "Swap",
  228. direction: "Left",
  229. },
  230. ];
  231. actions.forEach((action) => {
  232. this.extWm.command(action);
  233. });
  234. },
  235. "window-swap-down": () => {
  236. let actions = [
  237. {
  238. name: "Swap",
  239. direction: "Down",
  240. },
  241. ];
  242. actions.forEach((action) => {
  243. this.extWm.command(action);
  244. });
  245. },
  246. "window-swap-up": () => {
  247. let actions = [
  248. {
  249. name: "Swap",
  250. direction: "Up",
  251. },
  252. ];
  253. actions.forEach((action) => {
  254. this.extWm.command(action);
  255. });
  256. },
  257. "window-swap-right": () => {
  258. let actions = [
  259. {
  260. name: "Swap",
  261. direction: "Right",
  262. },
  263. ];
  264. actions.forEach((action) => {
  265. this.extWm.command(action);
  266. });
  267. },
  268. "window-move-left": () => {
  269. let actions = [
  270. {
  271. name: "Move",
  272. direction: "Left",
  273. },
  274. ];
  275. actions.forEach((action) => {
  276. this.extWm.command(action);
  277. });
  278. },
  279. "window-move-down": () => {
  280. let actions = [
  281. {
  282. name: "Move",
  283. direction: "Down",
  284. },
  285. ];
  286. actions.forEach((action) => {
  287. this.extWm.command(action);
  288. });
  289. },
  290. "window-move-up": () => {
  291. let actions = [
  292. {
  293. name: "Move",
  294. direction: "Up",
  295. },
  296. ];
  297. actions.forEach((action) => {
  298. this.extWm.command(action);
  299. });
  300. },
  301. "window-move-right": () => {
  302. let actions = [
  303. {
  304. name: "Move",
  305. direction: "Right",
  306. },
  307. ];
  308. actions.forEach((action) => {
  309. this.extWm.command(action);
  310. });
  311. },
  312. "con-split-layout-toggle": () => {
  313. let actions = [{ name: "LayoutToggle" }];
  314. actions.forEach((action) => {
  315. this.extWm.command(action);
  316. });
  317. },
  318. "con-split-vertical": () => {
  319. let actions = [{ name: "Split", orientation: "vertical" }];
  320. actions.forEach((action) => {
  321. this.extWm.command(action);
  322. });
  323. },
  324. "con-split-horizontal": () => {
  325. let actions = [{ name: "Split", orientation: "horizontal" }];
  326. actions.forEach((action) => {
  327. this.extWm.command(action);
  328. });
  329. },
  330. "con-stacked-layout-toggle": () => {
  331. let action = { name: "LayoutStackedToggle" };
  332. this.extWm.command(action);
  333. },
  334. "con-tabbed-layout-toggle": () => {
  335. let action = { name: "LayoutTabbedToggle" };
  336. this.extWm.command(action);
  337. },
  338. "con-tabbed-showtab-decoration-toggle": () => {
  339. let action = { name: "ShowTabDecorationToggle" };
  340. this.extWm.command(action);
  341. },
  342. "focus-border-toggle": () => {
  343. let action = { name: "FocusBorderToggle" };
  344. this.extWm.command(action);
  345. },
  346. "prefs-tiling-toggle": () => {
  347. let action = { name: "TilingModeToggle" };
  348. this.extWm.command(action);
  349. },
  350. "window-gap-size-increase": () => {
  351. let action = { name: "GapSize", amount: 1 };
  352. this.extWm.command(action);
  353. },
  354. "window-gap-size-decrease": () => {
  355. let action = { name: "GapSize", amount: -1 };
  356. this.extWm.command(action);
  357. },
  358. "workspace-active-tile-toggle": () => {
  359. let action = { name: "WorkspaceActiveTileToggle" };
  360. this.extWm.command(action);
  361. },
  362. "prefs-open": () => {
  363. let action = { name: "PrefsOpen" };
  364. this.extWm.command(action);
  365. },
  366. "window-swap-last-active": () => {
  367. let action = {
  368. name: "WindowSwapLastActive",
  369. };
  370. this.extWm.command(action);
  371. },
  372. "window-snap-one-third-right": () => {
  373. let action = {
  374. name: "SnapLayoutMove",
  375. direction: "Right",
  376. amount: 1 / 3,
  377. };
  378. this.extWm.command(action);
  379. },
  380. "window-snap-two-third-right": () => {
  381. let action = {
  382. name: "SnapLayoutMove",
  383. direction: "Right",
  384. amount: 2 / 3,
  385. };
  386. this.extWm.command(action);
  387. },
  388. "window-snap-one-third-left": () => {
  389. let action = {
  390. name: "SnapLayoutMove",
  391. direction: "Left",
  392. amount: 1 / 3,
  393. };
  394. this.extWm.command(action);
  395. },
  396. "window-snap-two-third-left": () => {
  397. let action = {
  398. name: "SnapLayoutMove",
  399. direction: "Left",
  400. amount: 2 / 3,
  401. };
  402. this.extWm.command(action);
  403. },
  404. "window-snap-center": () => {
  405. let action = {
  406. name: "SnapLayoutMove",
  407. direction: "Center",
  408. };
  409. this.extWm.command(action);
  410. },
  411. "window-resize-top-increase": () => {
  412. let action = {
  413. name: "WindowResizeTop",
  414. amount: this.settings.get_uint("resize-amount"),
  415. };
  416. this.extWm.command(action);
  417. },
  418. "window-resize-top-decrease": () => {
  419. let action = {
  420. name: "WindowResizeTop",
  421. amount: -1 * this.settings.get_uint("resize-amount"),
  422. };
  423. this.extWm.command(action);
  424. },
  425. "window-resize-bottom-increase": () => {
  426. let action = {
  427. name: "WindowResizeBottom",
  428. amount: this.settings.get_uint("resize-amount"),
  429. };
  430. this.extWm.command(action);
  431. },
  432. "window-resize-bottom-decrease": () => {
  433. let action = {
  434. name: "WindowResizeBottom",
  435. amount: -1 * this.settings.get_uint("resize-amount"),
  436. };
  437. this.extWm.command(action);
  438. },
  439. "window-resize-left-increase": () => {
  440. let action = {
  441. name: "WindowResizeLeft",
  442. amount: this.settings.get_uint("resize-amount"),
  443. };
  444. this.extWm.command(action);
  445. },
  446. "window-resize-left-decrease": () => {
  447. let action = {
  448. name: "WindowResizeLeft",
  449. amount: -1 * this.settings.get_uint("resize-amount"),
  450. };
  451. this.extWm.command(action);
  452. },
  453. "window-resize-right-increase": () => {
  454. let action = {
  455. name: "WindowResizeRight",
  456. amount: this.settings.get_uint("resize-amount"),
  457. };
  458. this.extWm.command(action);
  459. },
  460. "window-resize-right-decrease": () => {
  461. let action = {
  462. name: "WindowResizeRight",
  463. amount: -1 * this.settings.get_uint("resize-amount"),
  464. };
  465. this.extWm.command(action);
  466. },
  467. };
  468. }
  469. }