extension.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. import Clutter from 'gi://Clutter';
  2. import Gio from 'gi://Gio';
  3. import GLib from 'gi://GLib';
  4. import GObject from 'gi://GObject';
  5. import St from 'gi://St';
  6. import * as Main from 'resource:///org/gnome/shell/ui/main.js';
  7. import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
  8. import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
  9. import * as QuickSettings from 'resource:///org/gnome/shell/ui/quickSettings.js';
  10. import * as KeyboardManager from 'resource:///org/gnome/shell/misc/keyboardManager.js';
  11. import * as KeyboardUI from 'resource:///org/gnome/shell/ui/keyboard.js';
  12. import { Dialog } from 'resource:///org/gnome/shell/ui/dialog.js';
  13. import { Extension, gettext as _ } from 'resource:///org/gnome/shell/extensions/extension.js';
  14. class KeyboardMenuToggle extends QuickSettings.QuickMenuToggle {
  15. static {
  16. GObject.registerClass(this);
  17. }
  18. constructor(extensionObject) {
  19. super({
  20. title: _('Screen Keyboard'),
  21. iconName: 'input-keyboard-symbolic',
  22. toggleMode: true,
  23. });
  24. this.extensionObject = extensionObject;
  25. this.settings = extensionObject.getSettings();
  26. this.menu.setHeader('input-keyboard-symbolic', _('Screen Keyboard'), _('Opening Mode'));
  27. this._itemsSection = new PopupMenu.PopupMenuSection();
  28. this._itemsSection.addMenuItem(new PopupMenu.PopupImageMenuItem(_('Never'), this.settings.get_int("enable-tap-gesture") == 0 ? 'emblem-ok-symbolic' : null));
  29. this._itemsSection.addMenuItem(new PopupMenu.PopupImageMenuItem(_("Only on Touch"), this.settings.get_int("enable-tap-gesture") == 1 ? 'emblem-ok-symbolic' : null));
  30. this._itemsSection.addMenuItem(new PopupMenu.PopupImageMenuItem(_("Always"), this.settings.get_int("enable-tap-gesture") == 2 ? 'emblem-ok-symbolic' : null));
  31. for (var i in this._itemsSection._getMenuItems()) {
  32. const item = this._itemsSection._getMenuItems()[i]
  33. const num = i
  34. item.connect('activate', () => this.settings.set_int("enable-tap-gesture", num))
  35. }
  36. this.menu.addMenuItem(this._itemsSection);
  37. this.settings.bind('indicator-enabled',
  38. this, 'checked',
  39. Gio.SettingsBindFlags.DEFAULT);
  40. this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
  41. const settingsItem = this.menu.addAction(_('More Settings'),
  42. () => this.extensionObject.openPreferences());
  43. settingsItem.visible = Main.sessionMode.allowSettings;
  44. this.menu._settingsActions[this.extensionObject.uuid] = settingsItem;
  45. }
  46. _refresh() {
  47. for (var i in this._itemsSection._getMenuItems()) {
  48. this._itemsSection._getMenuItems()[i].setIcon(this.settings.get_int("enable-tap-gesture") == i ? 'emblem-ok-symbolic' : null)
  49. }
  50. }
  51. };
  52. let keycodes;
  53. export default class GjsOskExtension extends Extension {
  54. _openKeyboard() {
  55. if (this.Keyboard.state == "closed") {
  56. this.Keyboard.opened = true;
  57. this.Keyboard.open();
  58. }
  59. }
  60. _closeKeyboard() {
  61. if (this.Keyboard.state == "opened") {
  62. this.Keyboard.opened = false;
  63. this.Keyboard.close();
  64. }
  65. }
  66. _toggleKeyboard() {
  67. if (!this.Keyboard.opened) {
  68. this._openKeyboard();
  69. this.Keyboard.openedFromButton = true;
  70. this.Keyboard.closedFromButton = false
  71. } else {
  72. this._closeKeyboard();
  73. this.Keyboard.openedFromButton = false;
  74. this.Keyboard.closedFromButton = true;
  75. }
  76. }
  77. open_interval() {
  78. global.stage.disconnect(this.tapConnect)
  79. if (this.openInterval !== null) {
  80. clearInterval(this.openInterval);
  81. this.openInterval = null;
  82. }
  83. this.openInterval = setInterval(() => {
  84. this.Keyboard.get_parent().set_child_at_index(this.Keyboard, this.Keyboard.get_parent().get_n_children() - 1);
  85. this.Keyboard.set_child_at_index(this.Keyboard.box, this.Keyboard.get_n_children() - 1);
  86. if (!this.Keyboard.openedFromButton && this.lastInputMethod) {
  87. if (Main.inputMethod.currentFocus != null && Main.inputMethod.currentFocus.is_focused() && !this.Keyboard.closedFromButton) {
  88. this._openKeyboard();
  89. } else if (!this.Keyboard.closedFromButton) {
  90. this._closeKeyboard();
  91. this.Keyboard.closedFromButton = false
  92. } else if (Main.inputMethod.currentFocus == null) {
  93. this.Keyboard.closedFromButton = false
  94. }
  95. }
  96. }, 300);
  97. this.tapConnect = global.stage.connect("event", (_actor, event) => {
  98. if (event.type() !== 4 && event.type() !== 5) {
  99. this.lastInputMethod = [false, event.type() >= 9 && event.type() <= 12, true][this.settings.get_int("enable-tap-gesture")]
  100. }
  101. })
  102. }
  103. enable() {
  104. this.settings = this.getSettings();
  105. this.openBit = this.settings.get_child("indicator");
  106. let [ok, contents] = GLib.file_get_contents(this.path + '/keycodes.json');
  107. if (ok) {
  108. keycodes = JSON.parse(contents)[['qwerty', 'azerty', 'dvorak', "qwertz"][this.settings.get_int("lang")]];
  109. }
  110. this.Keyboard = new Keyboard(this.settings);
  111. this._originalLastDeviceIsTouchscreen = KeyboardUI.KeyboardManager.prototype._lastDeviceIsTouchscreen;
  112. KeyboardUI.KeyboardManager.prototype._lastDeviceIsTouchscreen = () => { return false };
  113. this._indicator = null;
  114. this.openInterval = null;
  115. if (this.settings.get_boolean("indicator-enabled")) {
  116. this._indicator = new PanelMenu.Button(0.0, "GJS OSK Indicator", false);
  117. let icon = new St.Icon({
  118. gicon: new Gio.ThemedIcon({
  119. name: 'input-keyboard-symbolic'
  120. }),
  121. style_class: 'system-status-icon'
  122. });
  123. this._indicator.add_child(icon);
  124. this._indicator.connect("button-press-event", () => this._toggleKeyboard());
  125. this._indicator.connect("touch-event", (_actor, event) => {
  126. if (event.type() == 11) this._toggleKeyboard()
  127. });
  128. Main.panel.addToStatusArea("GJS OSK Indicator", this._indicator);
  129. }
  130. this._toggle = new KeyboardMenuToggle(this);
  131. this._quick_settings_indicator = new QuickSettings.SystemIndicator();
  132. this._quick_settings_indicator.quickSettingsItems.push(this._toggle);
  133. Main.panel.statusArea.quickSettings.addExternalIndicator(this._quick_settings_indicator);
  134. if (this.settings.get_int("enable-tap-gesture") > 0) {
  135. this.open_interval();
  136. }
  137. this.openFromCommandHandler = this.openBit.connect("changed", () => {
  138. this.openBit.set_boolean("opened", false)
  139. this._toggleKeyboard();
  140. })
  141. this.settingsHandler = this.settings.connect("changed", key => {
  142. this.Keyboard.openedFromButton = false;
  143. let [ok, contents] = GLib.file_get_contents(this.path + '/keycodes.json');
  144. if (ok) {
  145. keycodes = JSON.parse(contents)[["qwerty", "azerty", "dvorak", "qwertz"][this.settings.get_int("lang")]];
  146. }
  147. this.Keyboard.refresh();
  148. this._toggle._refresh();
  149. if (this.settings.get_boolean("indicator-enabled")) {
  150. if (this._indicator != null) {
  151. this._indicator.destroy();
  152. this._indicator = null;
  153. }
  154. this._indicator = new PanelMenu.Button(0.0, "GJS OSK Indicator", false);
  155. let icon = new St.Icon({
  156. gicon: new Gio.ThemedIcon({
  157. name: 'input-keyboard-symbolic'
  158. }),
  159. style_class: 'system-status-icon'
  160. });
  161. this._indicator.add_child(icon);
  162. this._indicator.connect("button-press-event", () => this._toggleKeyboard());
  163. this._indicator.connect("touch-event", (_actor, event) => {
  164. if (event.type() == 11) this._toggleKeyboard()
  165. });
  166. Main.panel.addToStatusArea("GJS OSK Indicator", this._indicator);
  167. } else {
  168. if (this._indicator != null) {
  169. this._indicator.destroy();
  170. this._indicator = null;
  171. }
  172. }
  173. global.stage.disconnect(this.tapConnect)
  174. if (this.openInterval !== null) {
  175. clearInterval(this.openInterval);
  176. this.openInterval = null;
  177. }
  178. if (this.settings.get_int("enable-tap-gesture") > 0) {
  179. this.open_interval();
  180. }
  181. });
  182. }
  183. disable() {
  184. this._quick_settings_indicator.quickSettingsItems.forEach(item => item.destroy());
  185. this._quick_settings_indicator.destroy();
  186. this._quick_settings_indicator = null;
  187. if (this._indicator !== null) {
  188. this._indicator.destroy();
  189. this._indicator = null;
  190. }
  191. this.Keyboard.destroy();
  192. this.settings.disconnect(this.settingsHandler);
  193. this.settings = null;
  194. this.openBit.disconnect(this.openFromCommandHandler);
  195. this.openBit = null;
  196. global.stage.disconnect(this.tapConnect)
  197. if (this.openInterval !== null) {
  198. clearInterval(this.openInterval);
  199. this.openInterval = null;
  200. }
  201. this._toggle.destroy()
  202. this._toggle = null
  203. this.settings = null
  204. this.Keyboard = null
  205. keycodes = null
  206. if (this._originalLastDeviceIsTouchscreen !== null) {
  207. KeyboardUI.KeyboardManager.prototype._lastDeviceIsTouchscreen = this._originalLastDeviceIsTouchscreen;
  208. this._originalLastDeviceIsTouchscreen = null;
  209. }
  210. }
  211. }
  212. class Keyboard extends Dialog {
  213. static [GObject.signals] = {
  214. 'drag-begin': {},
  215. 'drag-end': {}
  216. };
  217. static {
  218. GObject.registerClass(this);
  219. }
  220. _init(settings) {
  221. this.inputDevice = Clutter.get_default_backend().get_default_seat().create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE);
  222. this.startupInterval = setInterval(() => {
  223. this.init = KeyboardManager.getKeyboardManager()._current.id;
  224. this.initLay = Object.keys(KeyboardManager.getKeyboardManager()._layoutInfos);
  225. if (this.initLay == undefined || this.init == undefined || this.inputDevice == undefined) {
  226. return;
  227. }
  228. this.settings = settings;
  229. let monitor = Main.layoutManager.primaryMonitor;
  230. super._init(Main.layoutManager.uiGroup, 'db-keyboard-content');
  231. this.box = new St.BoxLayout({
  232. vertical: true
  233. });
  234. this.widthPercent = (monitor.width > monitor.height) ? settings.get_int("landscape-width-percent") / 100 : settings.get_int("portrait-width-percent") / 100;
  235. this.heightPercent = (monitor.width > monitor.height) ? settings.get_int("landscape-height-percent") / 100 : settings.get_int("portrait-height-percent") / 100;
  236. this.buildUI();
  237. this.draggable = false;
  238. this.add_child(this.box);
  239. this.close();
  240. this.box.set_name("osk-gjs")
  241. this.mod = [];
  242. this.modBtns = [];
  243. this.capsL = false;
  244. this.shift = false;
  245. this.alt = false;
  246. this.box.add_style_class_name("boxLay");
  247. this.box.set_style("background-color: rgb(" + settings.get_double("background-r") + "," + settings.get_double("background-g") + "," + settings.get_double("background-b") + ");")
  248. this.opened = false;
  249. this.state = "closed";
  250. this.delta = [];
  251. this.checkMonitor();
  252. this._dragging = false;
  253. clearInterval(this.startupInterval);
  254. }, 200);
  255. }
  256. destroy() {
  257. if (this.startupInterval !== null) {
  258. clearInterval(this.startupInterval);
  259. this.startupInterval = null;
  260. }
  261. if (this.startupTimeout !== null) {
  262. clearInterval(this.startupTimeout);
  263. this.startupTimeout = null;
  264. }
  265. if (this.monitorChecker !== null) {
  266. clearInterval(this.monitorChecker);
  267. this.monitorChecker = null;
  268. }
  269. if (this.textboxChecker !== null) {
  270. clearInterval(this.textboxChecker);
  271. this.textboxChecker = null;
  272. }
  273. if (this.stateTimeout !== null) {
  274. clearTimeout(this.stateTimeout);
  275. this.stateTimeout = null;
  276. }
  277. if (this.keyTimeout !== null) {
  278. clearTimeout(this.keyTimeout);
  279. this.keyTimeout = null;
  280. }
  281. super.destroy();
  282. }
  283. vfunc_button_press_event() {
  284. this.delta = [Clutter.get_current_event().get_coords()[0] - this.translation_x, Clutter.get_current_event().get_coords()[1] - this.translation_y];
  285. return this.startDragging(Clutter.get_current_event(), this.delta)
  286. }
  287. startDragging(event, delta) {
  288. if (this.draggable) {
  289. if (this._dragging)
  290. return Clutter.EVENT_PROPAGATE;
  291. this._dragging = true;
  292. this.box.set_opacity(255);
  293. this.box.ease({
  294. opacity: 200,
  295. duration: 100,
  296. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  297. onComplete: () => { }
  298. });
  299. let device = event.get_device();
  300. let sequence = event.get_event_sequence();
  301. this._grab = global.stage.grab(this);
  302. this._grabbedDevice = device;
  303. this._grabbedSequence = sequence;
  304. this.emit('drag-begin');
  305. let [absX, absY] = event.get_coords();
  306. this.snapMovement(absX - delta[0], absY - delta[1]);
  307. return Clutter.EVENT_STOP;
  308. } else {
  309. return Clutter.EVENT_PROPAGATE;
  310. }
  311. }
  312. vfunc_button_release_event() {
  313. if (this._dragging && !this._grabbedSequence) {
  314. return this.endDragging();
  315. }
  316. return Clutter.EVENT_PROPAGATE;
  317. }
  318. endDragging() {
  319. if (this.draggable) {
  320. if (this._dragging) {
  321. if (this._releaseId) {
  322. this.disconnect(this._releaseId);
  323. this._releaseId = 0;
  324. }
  325. if (this._grab) {
  326. this._grab.dismiss();
  327. this._grab = null;
  328. }
  329. this.box.set_opacity(200);
  330. this.box.ease({
  331. opacity: 255,
  332. duration: 100,
  333. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  334. onComplete: () => { }
  335. });
  336. this._grabbedSequence = null;
  337. this._grabbedDevice = null;
  338. this._dragging = false;
  339. this.delta = [];
  340. this.emit('drag-end');
  341. this._dragging = false;
  342. }
  343. return Clutter.EVENT_STOP;
  344. } else {
  345. return Clutter.EVENT_STOP;
  346. }
  347. }
  348. vfunc_motion_event() {
  349. let event = Clutter.get_current_event();
  350. if (this._dragging && !this._grabbedSequence) {
  351. this.motionEvent(event);
  352. }
  353. return Clutter.EVENT_PROPAGATE;
  354. }
  355. motionEvent(event) {
  356. if (this.draggable) {
  357. let [absX, absY] = event.get_coords();
  358. this.snapMovement(absX - this.delta[0], absY - this.delta[1]);
  359. return Clutter.EVENT_STOP
  360. } else {
  361. return Clutter.EVENT_STOP
  362. }
  363. }
  364. vfunc_touch_event() {
  365. let event = Clutter.get_current_event();
  366. let sequence = event.get_event_sequence();
  367. if (!this._dragging && event.type() == Clutter.EventType.TOUCH_BEGIN) {
  368. this.delta = [event.get_coords()[0] - this.translation_x, event.get_coords()[1] - this.translation_y];
  369. this.startDragging(event);
  370. return Clutter.EVENT_STOP;
  371. } else if (this._grabbedSequence && sequence.get_slot() === this._grabbedSequence.get_slot()) {
  372. if (event.type() == Clutter.EventType.TOUCH_UPDATE) {
  373. return this.motionEvent(event);
  374. } else if (event.type() == Clutter.EventType.TOUCH_END) {
  375. return this.endDragging();
  376. }
  377. }
  378. return Clutter.EVENT_PROPAGATE;
  379. }
  380. snapMovement(xPos, yPos) {
  381. let monitor = Main.layoutManager.primaryMonitor
  382. let snap_px = this.settings.get_int("snap-spacing-px")
  383. if (Math.abs(xPos - ((monitor.width * .5) - ((this.width * .5)))) <= 50) {
  384. xPos = ((monitor.width * .5) - ((this.width * .5)));
  385. } else if (Math.abs(xPos - snap_px) <= 50) {
  386. xPos = snap_px;
  387. } else if (Math.abs(xPos - (monitor.width - this.width - snap_px)) <= 50) {
  388. xPos = monitor.width - this.width - snap_px
  389. }
  390. if (Math.abs(yPos - (monitor.height - this.height - snap_px)) <= 50) {
  391. yPos = monitor.height - this.height - snap_px;
  392. } else if (Math.abs(yPos - snap_px) <= 50) {
  393. yPos = snap_px;
  394. } else if (Math.abs(yPos - ((monitor.height * .5) - (this.height * .5))) <= 50) {
  395. yPos = (monitor.height * .5) - (this.height * .5);
  396. }
  397. this.set_translation(xPos, yPos, 0);
  398. }
  399. checkMonitor() {
  400. let monitor = Main.layoutManager.primaryMonitor;
  401. let oldMonitorDimensions = [monitor.width, monitor.height];
  402. this.monitorChecker = setInterval(() => {
  403. monitor = Main.layoutManager.primaryMonitor;
  404. if (oldMonitorDimensions[0] != monitor.width || oldMonitorDimensions[1] != monitor.height) {
  405. this.refresh()
  406. oldMonitorDimensions = [monitor.width, monitor.height];
  407. }
  408. }, 200);
  409. }
  410. refresh() {
  411. let monitor = Main.layoutManager.primaryMonitor;
  412. this.box.remove_all_children();
  413. this.box.set_style_class_name("boxLay")
  414. this.widthPercent = (monitor.width > monitor.height) ? this.settings.get_int("landscape-width-percent") / 100 : this.settings.get_int("portrait-width-percent") / 100;
  415. this.heightPercent = (monitor.width > monitor.height) ? this.settings.get_int("landscape-height-percent") / 100 : this.settings.get_int("portrait-height-percent") / 100;
  416. this.buildUI();
  417. this.draggable = false;
  418. this.keys.forEach(keyholder => {
  419. if (!keyholder.has_style_class_name("move_btn")) {
  420. keyholder.set_opacity(0);
  421. keyholder.ease({
  422. opacity: 255,
  423. duration: 100,
  424. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  425. onComplete: () => {
  426. keyholder.set_z_position(0);
  427. this.box.remove_style_pseudo_class("dragging");
  428. }
  429. });
  430. }
  431. });
  432. if (this.startupTimeout !== null) {
  433. clearInterval(this.startupTimeout);
  434. this.startupTimeout = null;
  435. }
  436. this.startupTimeout = setTimeout(() => {
  437. this.init = KeyboardManager.getKeyboardManager()._current.id;
  438. this.initLay = Object.keys(KeyboardManager.getKeyboardManager()._layoutInfos);
  439. if (this.initLay == undefined || this.init == undefined) {
  440. this.refresh();
  441. return;
  442. }
  443. this.close();
  444. }, 200);
  445. this.mod = [];
  446. this.modBtns = [];
  447. this.capsL = false;
  448. this.shift = false;
  449. this.alt = false;
  450. this.box.set_style("background-color: rgb(" + this.settings.get_double("background-r") + "," + this.settings.get_double("background-g") + "," + this.settings.get_double("background-b") + ");")
  451. this.opened = false;
  452. this.state = "closed";
  453. this.delta = [];
  454. this.dragging = false;
  455. }
  456. open() {
  457. this.inputDevice = Clutter.get_default_backend().get_default_seat().create_virtual_device(Clutter.InputDeviceType.KEYBOARD_DEVICE);
  458. if (this.startupTimeout !== null) {
  459. clearInterval(this.startupTimeout);
  460. this.startupTimeout = null;
  461. }
  462. this.startupTimeout = setTimeout(() => {
  463. this.init = KeyboardManager.getKeyboardManager()._current.id;
  464. this.initLay = Object.keys(KeyboardManager.getKeyboardManager()._layoutInfos);
  465. if (this.initLay == undefined || this.init == undefined) {
  466. this.open();
  467. return;
  468. }
  469. let newLay = this.initLay;
  470. if (!newLay.includes(["us", "fr+azerty", "us+dvorak", "de+dsb_qwertz"][this.settings.get_int("lang")])) {
  471. newLay.push(["us", "fr+azerty", "us+dvorak", "de+dsb_qwertz"][this.settings.get_int("lang")]);
  472. KeyboardManager.getKeyboardManager().setUserLayouts(newLay);
  473. }
  474. KeyboardManager.getKeyboardManager().apply(["us", "fr+azerty", "us+dvorak", "de+dsb_qwertz"][this.settings.get_int("lang")]);
  475. KeyboardManager.getKeyboardManager().reapply();
  476. this.state = "opening"
  477. this.box.opacity = 0;
  478. this.show();
  479. this.box.ease({
  480. opacity: 255,
  481. duration: 100,
  482. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  483. onComplete: () => {
  484. if (this.stateTimeout !== null) {
  485. clearTimeout(this.stateTimeout);
  486. this.stateTimeout = null;
  487. }
  488. this.stateTimeout = setTimeout(() => {
  489. this.state = "opened"
  490. }, 500);
  491. let monitor = Main.layoutManager.primaryMonitor;
  492. let posX = [this.settings.get_int("snap-spacing-px"), ((monitor.width * .5) - ((this.width * .5))), monitor.width - this.width - this.settings.get_int("snap-spacing-px")][(this.settings.get_int("default-snap") % 3)];
  493. let posY = [this.settings.get_int("snap-spacing-px"), ((monitor.height * .5) - ((this.height * .5))), monitor.height - this.height - this.settings.get_int("snap-spacing-px")][Math.floor((this.settings.get_int("default-snap") / 3))];
  494. this.set_translation(posX, posY, 0);
  495. }
  496. });
  497. this.opened = true;
  498. }, 200);
  499. }
  500. close() {
  501. if (this.initLay !== undefined && this.init !== undefined) {
  502. KeyboardManager.getKeyboardManager().setUserLayouts(this.initLay);
  503. KeyboardManager.getKeyboardManager().apply(this.init);
  504. }
  505. let monitor = Main.layoutManager.primaryMonitor;
  506. let posX = [this.settings.get_int("snap-spacing-px"), ((monitor.width * .5) - ((this.width * .5))), monitor.width - this.width - this.settings.get_int("snap-spacing-px")][(this.settings.get_int("default-snap") % 3)];
  507. let posY = [this.settings.get_int("snap-spacing-px"), ((monitor.height * .5) - ((this.height * .5))), monitor.height - this.height - this.settings.get_int("snap-spacing-px")][Math.floor((this.settings.get_int("default-snap") / 3))];
  508. this.state = "closing"
  509. this.box.ease({
  510. opacity: 0,
  511. duration: 100,
  512. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  513. onComplete: () => {
  514. this.set_translation(0, 0, 0);
  515. this.set_translation(posX, posY, 0);
  516. this.opened = false;
  517. this.hide();
  518. if (this.stateTimeout !== null) {
  519. clearTimeout(this.stateTimeout);
  520. this.stateTimeout = null;
  521. }
  522. this.stateTimeout = setTimeout(() => {
  523. this.state = "closed"
  524. }, 500);
  525. },
  526. });
  527. this.openedFromButton = false
  528. this.releaseAllKeys();
  529. }
  530. buildUI() {
  531. this.keys = [];
  532. let monitor = Main.layoutManager.primaryMonitor
  533. var topRowWidth = Math.round((monitor.width - 40 - this.settings.get_int("snap-spacing-px") * 2) * this.widthPercent / 15)
  534. var topRowHeight = Math.round((monitor.height - 140 - this.settings.get_int("snap-spacing-px") * 2) * this.heightPercent / 6)
  535. let row1 = new St.BoxLayout({
  536. pack_start: true
  537. });
  538. for (var num in keycodes.row1) {
  539. const i = keycodes.row1[num]
  540. if (i.lowerName == "") {
  541. i.lowerName = i._lowerName;
  542. i.upperName = i._upperName;
  543. }
  544. var w = topRowWidth;
  545. let params = {
  546. height: topRowHeight,
  547. width: w
  548. }
  549. let styleClass = ""
  550. switch (i.lowerName) {
  551. case "delete":
  552. case "backspace":
  553. case "tab":
  554. case "capslock":
  555. case "shift":
  556. case "enter":
  557. case "ctrl":
  558. case "super":
  559. case "alt":
  560. case "space":
  561. case "left":
  562. case "up":
  563. case "down":
  564. case "right":
  565. styleClass = i.lowerName;
  566. i._lowerName = i.lowerName;
  567. i._upperName = i.upperName;
  568. i.lowerName = "";
  569. i.upperName = "";
  570. break;
  571. default:
  572. params.label = i.lowerName;
  573. break;
  574. }
  575. row1.add_child(new St.Button(params));
  576. if (styleClass != "") {
  577. row1.get_children()[num].add_style_class_name(styleClass + "_btn");
  578. }
  579. i.isMod = false
  580. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  581. i.isMod = true;
  582. }
  583. row1.get_children()[num].char = i;
  584. }
  585. this.keys.push.apply(this.keys, row1.get_children());
  586. row1.add_style_class_name("keysHolder");
  587. let row2 = new St.BoxLayout({
  588. pack_start: true
  589. });
  590. for (var num in keycodes.row2) {
  591. const i = keycodes.row2[num]
  592. if (i.lowerName == "") {
  593. i.lowerName = i._lowerName;
  594. i.upperName = i._upperName;
  595. }
  596. var w;
  597. if (num == 0) {
  598. w = ((row1.width - ((keycodes.row2.length - 2) * ((topRowWidth) + 5))) / 2) * 0.5;
  599. } else if (num == keycodes.row2.length - 1) {
  600. w = ((row1.width - ((keycodes.row2.length - 2) * ((topRowWidth) + 5))) / 2) * 1.5;
  601. } else {
  602. w = (topRowWidth) + 5;
  603. }
  604. let params = {
  605. height: topRowHeight + 20,
  606. width: w
  607. }
  608. let styleClass = ""
  609. switch (i.lowerName) {
  610. case "delete":
  611. case "backspace":
  612. case "tab":
  613. case "capslock":
  614. case "shift":
  615. case "enter":
  616. case "ctrl":
  617. case "super":
  618. case "alt":
  619. case "space":
  620. case "left":
  621. case "up":
  622. case "down":
  623. case "right":
  624. styleClass = i.lowerName;
  625. i._lowerName = i.lowerName;
  626. i._upperName = i.upperName;
  627. i.lowerName = "";
  628. i.upperName = "";
  629. break;
  630. default:
  631. params.label = i.lowerName;
  632. break;
  633. }
  634. row2.add_child(new St.Button(params));
  635. if (styleClass != "") {
  636. row2.get_children()[num].add_style_class_name(styleClass + "_btn");
  637. }
  638. i.isMod = false
  639. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  640. i.isMod = true;
  641. }
  642. row2.get_children()[num].char = i;
  643. }
  644. this.keys.push.apply(this.keys, row2.get_children());
  645. row2.add_style_class_name("keysHolder");
  646. let row3 = new St.BoxLayout({
  647. pack_start: true
  648. });
  649. for (var num in keycodes.row3) {
  650. const i = keycodes.row3[num]
  651. if (i.lowerName == "") {
  652. i.lowerName = i._lowerName;
  653. i.upperName = i._upperName;
  654. }
  655. var w;
  656. if (num == 0) {
  657. w = ((row1.width - ((keycodes.row3.length - 2) * ((topRowWidth) + 5))) / 2) * 1.1;
  658. } else if (num == keycodes.row3.length - 1) {
  659. w = ((row1.width - ((keycodes.row3.length - 2) * ((topRowWidth) + 5))) / 2) * 0.9;
  660. } else {
  661. w = (topRowWidth) + 5;
  662. }
  663. let params = {
  664. height: topRowHeight + 20,
  665. width: w
  666. }
  667. let styleClass = ""
  668. switch (i.lowerName) {
  669. case "delete":
  670. case "backspace":
  671. case "tab":
  672. case "capslock":
  673. case "shift":
  674. case "enter":
  675. case "ctrl":
  676. case "super":
  677. case "alt":
  678. case "space":
  679. case "left":
  680. case "up":
  681. case "down":
  682. case "right":
  683. styleClass = i.lowerName;
  684. i._lowerName = i.lowerName;
  685. i._upperName = i.upperName;
  686. i.lowerName = "";
  687. i.upperName = "";
  688. break;
  689. default:
  690. params.label = i.lowerName;
  691. break;
  692. }
  693. row3.add_child(new St.Button(params));
  694. if (styleClass != "") {
  695. row3.get_children()[num].add_style_class_name(styleClass + "_btn");
  696. }
  697. i.isMod = false
  698. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  699. i.isMod = true;
  700. }
  701. row3.get_children()[num].char = i;
  702. }
  703. this.keys.push.apply(this.keys, row3.get_children());
  704. row3.add_style_class_name("keysHolder");
  705. let row4 = new St.BoxLayout({
  706. pack_start: true
  707. });
  708. for (var num in keycodes.row4) {
  709. const i = keycodes.row4[num]
  710. if (i.lowerName == "") {
  711. i.lowerName = i._lowerName;
  712. i.upperName = i._upperName;
  713. }
  714. var w;
  715. if (num == 0 || num == keycodes.row4.length - 1) {
  716. w = ((row1.width - ((keycodes.row4.length - 2) * ((topRowWidth) + 5))) / 2);
  717. } else {
  718. w = (topRowWidth) + 5;
  719. }
  720. let params = {
  721. height: topRowHeight + 20,
  722. width: w
  723. }
  724. let styleClass = ""
  725. switch (i.lowerName) {
  726. case "delete":
  727. case "backspace":
  728. case "tab":
  729. case "capslock":
  730. case "shift":
  731. case "enter":
  732. case "ctrl":
  733. case "super":
  734. case "alt":
  735. case "space":
  736. case "left":
  737. case "up":
  738. case "down":
  739. case "right":
  740. styleClass = i.lowerName;
  741. i._lowerName = i.lowerName;
  742. i._upperName = i.upperName;
  743. i.lowerName = "";
  744. i.upperName = "";
  745. break;
  746. default:
  747. params.label = i.lowerName;
  748. break;
  749. }
  750. row4.add_child(new St.Button(params));
  751. if (styleClass != "") {
  752. row4.get_children()[num].add_style_class_name(styleClass + "_btn");
  753. }
  754. i.isMod = false
  755. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  756. i.isMod = true;
  757. }
  758. row4.get_children()[num].char = i;
  759. }
  760. this.keys.push.apply(this.keys, row4.get_children());
  761. row4.add_style_class_name("keysHolder");
  762. let row5 = new St.BoxLayout({
  763. pack_start: true
  764. });
  765. for (var num in keycodes.row5) {
  766. const i = keycodes.row5[num]
  767. if (i.lowerName == "") {
  768. i.lowerName = i._lowerName;
  769. i.upperName = i._upperName;
  770. }
  771. var w;
  772. if (num == 0 || num == keycodes.row5.length - 1) {
  773. w = ((row1.width - ((keycodes.row5.length - 2) * ((topRowWidth) + 5))) / 2);
  774. } else {
  775. w = (topRowWidth) + 5;
  776. }
  777. let params = {
  778. height: topRowHeight + 20,
  779. width: w
  780. }
  781. let styleClass = ""
  782. switch (i.lowerName) {
  783. case "delete":
  784. case "backspace":
  785. case "tab":
  786. case "capslock":
  787. case "shift":
  788. case "enter":
  789. case "ctrl":
  790. case "super":
  791. case "alt":
  792. case "space":
  793. case "left":
  794. case "up":
  795. case "down":
  796. case "right":
  797. styleClass = i.lowerName;
  798. i._lowerName = i.lowerName;
  799. i._upperName = i.upperName;
  800. i.lowerName = "";
  801. i.upperName = "";
  802. break;
  803. default:
  804. params.label = i.lowerName;
  805. break;
  806. }
  807. row5.add_child(new St.Button(params));
  808. if (styleClass != "") {
  809. row5.get_children()[num].add_style_class_name(styleClass + "_btn");
  810. }
  811. i.isMod = false
  812. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  813. i.isMod = true;
  814. }
  815. row5.get_children()[num].char = i;
  816. }
  817. this.keys.push.apply(this.keys, row5.get_children());
  818. row5.add_style_class_name("keysHolder");
  819. let row6 = new St.BoxLayout({
  820. pack_start: true
  821. });
  822. for (var num in keycodes.row6) {
  823. const i = keycodes.row6[num]
  824. if (i.lowerName == "") {
  825. i.lowerName = i._lowerName;
  826. i.upperName = i._upperName;
  827. }
  828. var w;
  829. if (num == 3) {
  830. w = ((row1.width - ((keycodes.row6.length + 1) * ((topRowWidth) + 5))));
  831. let params = {
  832. height: topRowHeight + 20,
  833. width: w
  834. }
  835. let styleClass = ""
  836. switch (i.lowerName) {
  837. case "delete":
  838. case "backspace":
  839. case "tab":
  840. case "capslock":
  841. case "shift":
  842. case "enter":
  843. case "ctrl":
  844. case "super":
  845. case "alt":
  846. case "space":
  847. case "left":
  848. case "up":
  849. case "down":
  850. case "right":
  851. styleClass = i.lowerName;
  852. i._lowerName = i.lowerName;
  853. i._upperName = i.upperName;
  854. i.lowerName = "";
  855. i.upperName = "";
  856. break;
  857. default:
  858. params.label = i.lowerName;
  859. break;
  860. }
  861. row6.add_child(new St.Button(params));
  862. if (styleClass != "") {
  863. row6.get_children()[num].add_style_class_name(styleClass + "_btn");
  864. }
  865. i.isMod = false
  866. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  867. i.isMod = true;
  868. }
  869. row6.get_children()[num].char = i;
  870. this.keys.push(row6.get_children()[num]);
  871. } else if (num == keycodes.row6.length - 1) {
  872. var gbox = new St.BoxLayout({
  873. pack_start: true
  874. });
  875. var btn1 = new St.Button();
  876. btn1.add_style_class_name("left_btn");
  877. gbox.add_child(btn1);
  878. var vbox = new St.BoxLayout({
  879. vertical: true
  880. });
  881. var btn2 = new St.Button();
  882. btn2.add_style_class_name("up_btn");
  883. var btn3 = new St.Button();
  884. btn3.add_style_class_name("down_btn");
  885. vbox.add_child(btn2);
  886. vbox.add_child(btn3);
  887. gbox.add_child(vbox);
  888. var btn4 = new St.Button();
  889. btn4.add_style_class_name("right_btn");
  890. gbox.add_child(btn4);
  891. var btn5 = new St.Button();
  892. btn5.add_style_class_name("move_btn")
  893. var btn6 = new St.Button();
  894. btn6.add_style_class_name("close_btn")
  895. if (this.settings.get_boolean("enable-drag")) {
  896. gbox.add_child(btn5);
  897. }
  898. gbox.add_child(btn6);
  899. btn5.connect("clicked", () => {
  900. if (this.settings.get_boolean("enable-drag")) {
  901. this.draggable = !this.draggable;
  902. this.keys.forEach(keyholder => {
  903. if (!keyholder.has_style_class_name("move_btn")) {
  904. keyholder.set_opacity(this.draggable ? 255 : 0);
  905. keyholder.ease({
  906. opacity: this.draggable ? 0 : 255,
  907. duration: 100,
  908. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  909. onComplete: () => {
  910. keyholder.set_z_position(this.draggable ? -10000000000000000000000000000 : 0);
  911. if (this.draggable) {
  912. this.box.add_style_pseudo_class("dragging");
  913. } else {
  914. this.box.remove_style_pseudo_class("dragging");
  915. }
  916. }
  917. });
  918. }
  919. });
  920. btn5.ease({
  921. width: btn5.width * (this.draggable ? 2 : 0.5),
  922. duration: 100,
  923. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  924. onComplete: () => { }
  925. })
  926. btn6.ease({
  927. width: this.draggable ? 0 : btn5.width / 2,
  928. duration: 100,
  929. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  930. onComplete: () => { }
  931. })
  932. }
  933. })
  934. btn6.connect("clicked", () => { this.close(); this.closedFromButton = true; });
  935. btn1.char = (keycodes.row6[keycodes.row6.length - 1])[0]
  936. btn2.char = (keycodes.row6[keycodes.row6.length - 1])[1]
  937. btn3.char = (keycodes.row6[keycodes.row6.length - 1])[2]
  938. btn4.char = (keycodes.row6[keycodes.row6.length - 1])[3]
  939. btn1.char.lowerName = ""
  940. btn1.char.upperName = ""
  941. btn2.char.lowerName = ""
  942. btn2.char.upperName = ""
  943. btn3.char.lowerName = ""
  944. btn3.char.upperName = ""
  945. btn4.char.lowerName = ""
  946. btn4.char.upperName = ""
  947. btn1.width = Math.round((((topRowWidth) + 5)) * (2 / 3));
  948. btn1.height = topRowHeight + 20;
  949. btn2.width = Math.round((((topRowWidth) + 5)) * (2 / 3));
  950. btn3.width = Math.round((((topRowWidth) + 5)) * (2 / 3));
  951. btn4.width = Math.round((((topRowWidth) + 5)) * (2 / 3));
  952. btn4.height = topRowHeight + 20;
  953. btn2.height = (topRowHeight + 20) / 2;
  954. btn3.height = (topRowHeight + 20) / 2;
  955. btn5.width = Math.round((topRowWidth / 2) + 2);
  956. btn6.width = this.settings.get_boolean("enable-drag") ? Math.round((topRowWidth / 2) + 2) : Math.round((topRowWidth) + 4);
  957. btn5.height = topRowHeight + 20;
  958. btn6.height = topRowHeight + 20;
  959. btn1.add_style_class_name('dr-b');
  960. btn2.add_style_class_name('dr-b');
  961. btn3.add_style_class_name('dr-b');
  962. btn4.add_style_class_name('dr-b');
  963. btn5.add_style_class_name('dr-b');
  964. btn6.add_style_class_name('dr-b');
  965. this.keys.push.apply(this.keys, [btn1, btn2, btn3, btn4, btn5, btn6]);
  966. gbox.add_style_class_name('keyActionBtns');
  967. row6.add_child(gbox);
  968. } else {
  969. w = (topRowWidth) + 5;
  970. let params = {
  971. height: topRowHeight + 20,
  972. width: w
  973. }
  974. let styleClass = ""
  975. switch (i.lowerName) {
  976. case "delete":
  977. case "backspace":
  978. case "tab":
  979. case "capslock":
  980. case "shift":
  981. case "enter":
  982. case "ctrl":
  983. case "super":
  984. case "alt":
  985. case "space":
  986. case "left":
  987. case "up":
  988. case "down":
  989. case "right":
  990. styleClass = i.lowerName;
  991. i._lowerName = i.lowerName;
  992. i._upperName = i.upperName;
  993. i.lowerName = "";
  994. i.upperName = "";
  995. break;
  996. default:
  997. params.label = i.lowerName;
  998. break;
  999. }
  1000. row6.add_child(new St.Button(params));
  1001. if (styleClass != "") {
  1002. row6.get_children()[num].add_style_class_name(styleClass + "_btn");
  1003. }
  1004. i.isMod = false
  1005. if ([42, 54, 29, 125, 56, 100, 97, 58].some(j => { return i.code == j })) {
  1006. i.isMod = true;
  1007. }
  1008. row6.get_children()[num].char = i;
  1009. this.keys.push(row6.get_children()[num]);
  1010. }
  1011. }
  1012. row6.add_style_class_name("keysHolder");
  1013. this.box.add_child(row1);
  1014. this.box.add_child(row2);
  1015. this.box.add_child(row3);
  1016. this.box.add_child(row4);
  1017. this.box.add_child(row5);
  1018. this.box.add_child(row6);
  1019. if (this.lightOrDark(this.settings.get_double("background-r"), this.settings.get_double("background-g"), this.settings.get_double("background-b"))) {
  1020. this.box.add_style_class_name("inverted");
  1021. } else {
  1022. this.box.add_style_class_name("regular");
  1023. }
  1024. this.keys.forEach(item => {
  1025. item.set_scale((item.width - this.settings.get_int("border-spacing-px") * 2) / item.width, (item.height - this.settings.get_int("border-spacing-px") * 2) / item.height);
  1026. item.set_style("font-size: " + this.settings.get_int("font-size-px") + "px; border-radius: " + (this.settings.get_boolean("round-key-corners") ? "5px;" : "0;") + "background-size: " + this.settings.get_int("font-size-px") + "px;");
  1027. if (this.lightOrDark(this.settings.get_double("background-r"), this.settings.get_double("background-g"), this.settings.get_double("background-b"))) {
  1028. item.add_style_class_name("inverted");
  1029. } else {
  1030. item.add_style_class_name("regular");
  1031. }
  1032. item.set_pivot_point(0.5, 0.5)
  1033. item.connect("destroy", () => {
  1034. if (item.button_pressed !== null) {
  1035. clearTimeout(item.button_pressed)
  1036. item.button_pressed == null
  1037. }
  1038. if (item.button_repeat !== null) {
  1039. clearInterval(item.button_repeat)
  1040. item.button_repeat == null
  1041. }
  1042. if (item.tap_pressed !== null) {
  1043. clearTimeout(item.tap_pressed)
  1044. item.tap_pressed == null
  1045. }
  1046. if (item.tap_repeat !== null) {
  1047. clearInterval(item.tap_repeat)
  1048. item.tap_repeat == null
  1049. }
  1050. })
  1051. let pressEv = (evType) => {
  1052. item.space_motion_handler = null
  1053. item.set_scale(1.2, 1.2)
  1054. item.add_style_pseudo_class("pressed")
  1055. let player
  1056. if (this.settings.get_boolean("play-sound")) {
  1057. player = global.display.get_sound_player();
  1058. player.play_from_theme("dialog-information", "tap", null)
  1059. }
  1060. if (["delete_btn", "backspace_btn", "up_btn", "down_btn", "left_btn", "right_btn"].some(e => item.has_style_class_name(e))) {
  1061. item.button_pressed = setTimeout(() => {
  1062. const oldModBtns = this.modBtns
  1063. item.button_repeat = setInterval(() => {
  1064. if (this.settings.get_boolean("play-sound")) {
  1065. player.play_from_theme("dialog-information", "tap", null)
  1066. }
  1067. this.decideMod(item.char)
  1068. for (var i of oldModBtns) {
  1069. this.decideMod(i.char, i)
  1070. }
  1071. }, 100);
  1072. }, 750);
  1073. } else if (item.has_style_class_name("space_btn")) {
  1074. item.button_pressed = setTimeout(() => {
  1075. let lastPos = (item.get_transformed_position()[0] + item.get_transformed_size()[0] / 2)
  1076. if (evType == "mouse") {
  1077. item.space_motion_handler = item.connect("motion_event", (actor, event) => {
  1078. let absX = event.get_coords()[0];
  1079. if (Math.abs(absX - lastPos) > 20) {
  1080. if (absX > lastPos) {
  1081. this.sendKey([106])
  1082. } else {
  1083. this.sendKey([105])
  1084. }
  1085. lastPos = absX
  1086. }
  1087. })
  1088. } else {
  1089. item.space_motion_handler = item.connect("touch_event", (actor, event) => {
  1090. if (event.type() == Clutter.EventType.TOUCH_UPDATE) {
  1091. let absX = event.get_coords()[0];
  1092. if (Math.abs(absX - lastPos) > 20) {
  1093. if (absX > lastPos) {
  1094. this.sendKey([106])
  1095. } else {
  1096. this.sendKey([105])
  1097. }
  1098. lastPos = absX
  1099. }
  1100. }
  1101. })
  1102. }
  1103. }, 750)
  1104. } else {
  1105. item.key_pressed = true;
  1106. item.button_pressed = setTimeout(() => {
  1107. releaseEv()
  1108. }, 1000);
  1109. }
  1110. }
  1111. let releaseEv = () => {
  1112. item.remove_style_pseudo_class("pressed")
  1113. item.ease({
  1114. scale_x: (item.width - this.settings.get_int("border-spacing-px") * 2) / item.width,
  1115. scale_y: (item.height - this.settings.get_int("border-spacing-px") * 2) / item.height,
  1116. duration: 100,
  1117. mode: Clutter.AnimationMode.EASE_OUT_QUAD,
  1118. onComplete: () => { item.set_scale((item.width - this.settings.get_int("border-spacing-px") * 2) / item.width, (item.height - this.settings.get_int("border-spacing-px") * 2) / item.height); }
  1119. })
  1120. if (item.button_pressed !== null) {
  1121. clearTimeout(item.button_pressed)
  1122. item.button_pressed == null
  1123. }
  1124. if (item.button_repeat !== null) {
  1125. clearInterval(item.button_repeat)
  1126. item.button_repeat == null
  1127. }
  1128. if (item.space_motion_handler !== null) {
  1129. item.disconnect(item.space_motion_handler)
  1130. item.space_motion_handler = null;
  1131. } else if (item.key_pressed == true || item.space_motion_handler == null) {
  1132. try {
  1133. if (!item.char.isMod) {
  1134. this.decideMod(item.char)
  1135. } else {
  1136. const modButton = item;
  1137. this.decideMod(item.char, modButton)
  1138. }
  1139. } catch { }
  1140. }
  1141. item.key_pressed = false;
  1142. }
  1143. item.connect("button-press-event", () => pressEv("mouse"))
  1144. item.connect("button-release-event", releaseEv)
  1145. item.connect("touch-event", () => {
  1146. if (Clutter.get_current_event().type() == Clutter.EventType.TOUCH_BEGIN) {
  1147. pressEv("touch")
  1148. } else if (Clutter.get_current_event().type() == Clutter.EventType.TOUCH_END || Clutter.get_current_event().type() == Clutter.EventType.TOUCH_CANCEL) {
  1149. releaseEv()
  1150. }
  1151. })
  1152. });
  1153. }
  1154. lightOrDark(r, g, b) {
  1155. var hsp;
  1156. hsp = Math.sqrt(
  1157. 0.299 * (r * r) +
  1158. 0.587 * (g * g) +
  1159. 0.114 * (b * b)
  1160. );
  1161. if (hsp > 127.5) {
  1162. return true;
  1163. } else {
  1164. return false;
  1165. }
  1166. }
  1167. releaseAllKeys() {
  1168. let instances = [];
  1169. function traverse(obj) {
  1170. for (let key in obj) {
  1171. if (obj.hasOwnProperty(key)) {
  1172. if (key === "code") {
  1173. instances.push(obj[key]);
  1174. } else if (typeof obj[key] === 'object' && obj[key] !== null) {
  1175. traverse(obj[key]);
  1176. }
  1177. }
  1178. }
  1179. }
  1180. traverse(keycodes);
  1181. instances.forEach(i => {
  1182. this.inputDevice.notify_key(Clutter.get_current_event_time(), i, Clutter.KeyState.RELEASED);
  1183. })
  1184. this.keys.forEach(item => {
  1185. item.key_pressed = false;
  1186. if (item.button_pressed !== null) {
  1187. clearTimeout(item.button_pressed)
  1188. item.button_pressed == null
  1189. }
  1190. if (item.button_repeat !== null) {
  1191. clearInterval(item.button_repeat)
  1192. item.button_repeat == null
  1193. }
  1194. if (item.space_motion_handler !== null) {
  1195. item.disconnect(item.space_motion_handler)
  1196. item.space_motion_handler = null;
  1197. }
  1198. })
  1199. }
  1200. sendKey(keys) {
  1201. try {
  1202. for (var i = 0; i < keys.length; i++) {
  1203. this.inputDevice.notify_key(Clutter.get_current_event_time(), keys[i], Clutter.KeyState.PRESSED);
  1204. }
  1205. if (this.keyTimeout !== null) {
  1206. clearTimeout(this.keyTimeout);
  1207. this.keyTimeout = null;
  1208. }
  1209. this.keyTimeout = setTimeout(() => {
  1210. for (var j = keys.length - 1; j >= 0; j--) {
  1211. this.inputDevice.notify_key(Clutter.get_current_event_time(), keys[j], Clutter.KeyState.RELEASED);
  1212. }
  1213. }, 100);
  1214. } catch (err) {
  1215. let source = new imports.ui.messageTray.SystemNotificationSource();
  1216. source.connect('destroy', () => {
  1217. source = null;
  1218. })
  1219. Main.messageTray.add(source);
  1220. let notification = new imports.ui.messageTray.Notification(source, "GJS-OSK: An unknown error occured", "Please report this bug to the Issues page:\n\n" + err + "\n\nKeys Pressed: " + keys)
  1221. notification.setTransient(false);
  1222. notification.setResident(false);
  1223. source.showNotification(notification);
  1224. notification.connect("activated", () => {
  1225. sendCommand("xdg-open https://github.com/Vishram1123/gjs-osk/issues");
  1226. });
  1227. }
  1228. }
  1229. decideMod(i, mBtn) {
  1230. if (i.code == 29 || i.code == 97 || i.code == 125) {
  1231. this.setNormMod(mBtn);
  1232. } else if (i.code == 56 || i.code == 100) {
  1233. this.setAlt(mBtn);
  1234. } else if (i.code == 42 || i.code == 54) {
  1235. this.setShift(mBtn);
  1236. } else if (i.code == 58) {
  1237. this.setCapsLock(mBtn);
  1238. } else {
  1239. this.mod.push(i.code);
  1240. this.sendKey(this.mod);
  1241. this.mod = [];
  1242. this.modBtns.forEach(button => {
  1243. button.remove_style_class_name("selected");
  1244. });
  1245. this.resetAllMod();
  1246. this.modBtns = [];
  1247. }
  1248. }
  1249. setCapsLock(button) {
  1250. if (!this.capsL) {
  1251. button.add_style_class_name("selected");
  1252. this.capsL = true;
  1253. this.keys.forEach(key => {
  1254. if (this.shift && key.char != undefined) {
  1255. if (key.char.letter == "primary") {
  1256. key.label = key.label.toLowerCase();
  1257. } else if (key.char.letter == "pseudo") {
  1258. key.label = key.char.upperName;
  1259. } else if (key.char.letter == undefined) {
  1260. key.label = key.char.upperName;
  1261. }
  1262. } else if (key.char != undefined && key.char.letter != undefined) {
  1263. key.label = key.label.toUpperCase();
  1264. }
  1265. });
  1266. } else {
  1267. button.remove_style_class_name("selected");
  1268. this.capsL = false;
  1269. this.keys.forEach(key => {
  1270. if (this.shift && key.char != undefined) {
  1271. if (key.char.letter == "primary") {
  1272. key.label = key.label.toUpperCase();
  1273. } else if (key.char.letter == "pseudo") {
  1274. key.label = key.char.upperName;
  1275. } else if (key.char.letter == undefined) {
  1276. key.label = key.char.upperName;
  1277. }
  1278. } else if (key.char != undefined && key.char.letter != undefined) {
  1279. key.label = key.label.toLowerCase();
  1280. }
  1281. });
  1282. }
  1283. this.sendKey([button.char.code]);
  1284. }
  1285. setAlt(button) {
  1286. if (!this.alt) {
  1287. this.alt = true;
  1288. this.keys.forEach(key => {
  1289. if (!this.shift && key.char != undefined) {
  1290. if (key.char.altName != "") {
  1291. key.label = key.char.altName;
  1292. }
  1293. }
  1294. });
  1295. } else {
  1296. this.alt = false;
  1297. this.keys.forEach(key => {
  1298. if (!this.shift && key.char != undefined) {
  1299. if (key.char.altName != "" && this.capsL && (key.char.letter == "primary" || key.char.letter == "pseudo")) {
  1300. key.label = key.char.lowerName.toUpperCase();
  1301. } else if (key.char.altName != "" && this.capsL) {
  1302. key.label = key.char.lowerName;
  1303. } else if (key.char.altName != "" && !this.capsL) {
  1304. key.label = key.char.lowerName;
  1305. }
  1306. }
  1307. });
  1308. this.sendKey([button.char.code]);
  1309. }
  1310. this.setNormMod(button);
  1311. }
  1312. setShift(button) {
  1313. if (!this.shift) {
  1314. this.shift = true;
  1315. this.keys.forEach(key => {
  1316. if (this.capsL && key.char != undefined) {
  1317. if (key.char.letter == "primary") {
  1318. key.label = key.char.lowerName.toLowerCase();
  1319. } else if (key.char.letter == "pseudo" || key.char.letter == undefined) {
  1320. key.label = key.char.upperName;
  1321. }
  1322. } else if (key.char != undefined) {
  1323. key.label = key.char.upperName;
  1324. }
  1325. });
  1326. } else {
  1327. this.shift = false;
  1328. this.keys.forEach(key => {
  1329. if (this.capsL && key.char != undefined) {
  1330. if (this.alt && key.char.altName != "") {
  1331. key.label = key.char.altName;
  1332. } else if (key.char.letter != undefined) {
  1333. key.label = key.char.lowerName.toUpperCase();
  1334. } else if (key.char.letter == undefined) {
  1335. key.label = key.char.lowerName;
  1336. }
  1337. } else if (key.char != undefined) {
  1338. if (this.alt && key.char.altName != "") {
  1339. key.label = key.char.altName;
  1340. } else {
  1341. key.label = key.char.lowerName;
  1342. }
  1343. }
  1344. });
  1345. this.sendKey([button.char.code]);
  1346. }
  1347. this.setNormMod(button);
  1348. }
  1349. setNormMod(button) {
  1350. if (this.mod.includes(button.char.code)) {
  1351. this.mod.splice(this.mod.indexOf(button.char.code), this.mod.indexOf(button.char.code) + 1);
  1352. button.remove_style_class_name("selected");
  1353. this.modBtns.splice(this.modBtns.indexOf(button), this.modBtns.indexOf(button) + 1);
  1354. this.sendKey([button.char.code]);
  1355. } else {
  1356. button.add_style_class_name("selected");
  1357. this.mod.push(button.char.code);
  1358. this.modBtns.push(button);
  1359. }
  1360. }
  1361. resetAllMod() {
  1362. if (this.shift) {
  1363. this.shift = false;
  1364. this.keys.forEach(key => {
  1365. if (this.capsL && key.char != undefined) {
  1366. if (this.alt && key.char.altName != "") {
  1367. key.label = key.char.altName;
  1368. } else if (key.char.letter != undefined) {
  1369. key.label = key.char.lowerName.toUpperCase();
  1370. } else if (key.char.letter == undefined) {
  1371. key.label = key.char.lowerName;
  1372. }
  1373. } else if (key.char != undefined) {
  1374. if (this.alt && key.char.altName != "") {
  1375. key.label = key.char.altName;
  1376. } else {
  1377. key.label = key.char.lowerName;
  1378. }
  1379. }
  1380. });
  1381. }
  1382. if (this.alt) {
  1383. this.alt = false;
  1384. this.keys.forEach(key => {
  1385. if (!this.shift && key.char != undefined) {
  1386. if (key.char.altName != "" && this.capsL && (key.char.letter == "primary" || key.char.letter == "pseudo")) {
  1387. key.label = key.char.lowerName.toUpperCase();
  1388. } else if (key.char.altName != "" && this.capsL) {
  1389. key.label = key.char.lowerName;
  1390. } else if (key.char.altName != "" && !this.capsL) {
  1391. key.label = key.char.lowerName;
  1392. }
  1393. }
  1394. });
  1395. }
  1396. }
  1397. };