From fda39b75c1cfccfb21f432abd3750da7b58e6f75 Mon Sep 17 00:00:00 2001 From: matt1432 Date: Tue, 24 Oct 2023 17:26:38 -0400 Subject: [PATCH] feat(ags pointers): move everything to service and add onClickUnfocus param to popups --- hosts/wim/config/ags/config.js | 28 ++--- hosts/wim/config/ags/js/misc/closer.js | 89 ++------------- hosts/wim/config/ags/js/misc/popup.js | 5 +- hosts/wim/config/ags/js/overview/main.js | 1 + hosts/wim/config/ags/services/pointers.js | 125 +++++++++++++++++----- 5 files changed, 128 insertions(+), 120 deletions(-) diff --git a/hosts/wim/config/ags/config.js b/hosts/wim/config/ags/config.js index dc97854..628070e 100644 --- a/hosts/wim/config/ags/config.js +++ b/hosts/wim/config/ags/config.js @@ -1,6 +1,8 @@ import { App, Utils } from './imports.js'; -import Closer from './js/misc/closer.js'; +import closeAll from './js/misc/closer.js'; +globalThis.closeAll = closeAll; + import Powermenu from './js/powermenu.js'; import * as Bar from './js/bar/main.js'; import NotifCenter from './js/notifications/center.js'; @@ -25,25 +27,25 @@ export default { notificationPopupTimeout: 5000, cacheNotificationActions: true, closeWindowDelay: { - 'quick-settings': 500, - 'notification-center': 500, - 'calendar': 500, - 'powermenu': 500, - 'overview': 500, 'applauncher': 500, + 'calendar': 500, + 'notification-center': 500, + 'overview': 500, + 'powermenu': 500, + 'quick-settings': 500, }, windows: [ + AppLauncher(), + Calendar(), + NotifCenter(), + Overview(), Powermenu(), + QuickSettings(), + Bar.Bar(), Bar.BgGradient(), - Closer(), - NotifCenter(), - NotifPopups(), - Calendar(), - QuickSettings(), - Overview(), - AppLauncher(), Corners.Bottomleft(), Corners.Bottomright(), + NotifPopups(), ], }; diff --git a/hosts/wim/config/ags/js/misc/closer.js b/hosts/wim/config/ags/js/misc/closer.js index 0623ed1..1d14436 100644 --- a/hosts/wim/config/ags/js/misc/closer.js +++ b/hosts/wim/config/ags/js/misc/closer.js @@ -1,86 +1,15 @@ -import { App, Utils, Widget } from '../../imports.js'; -const { Window, Revealer } = Widget; +import { App } from '../../imports.js'; +// TODO: find a way to not need this? import Pointers from '../../services/pointers.js'; -const ALWAYS_OPEN = [ - 'closer', - 'bar', - 'bg-gradient', - 'notifications', - 'cornertl', - 'cornertr', - 'cornerbl', - 'cornerbr', -]; - -const closeAll = () => { - App.windows.forEach(w => { - if (!ALWAYS_OPEN.some(window => window === w.name)) - App.closeWindow(w.name); +export default () => { + const closableWindows = Array.from(App.windows).filter(w => { + return w[1].closeOnUnfocus && + w[1].closeOnUnfocus !== 'none'; + }); + closableWindows.forEach(w => { + App.closeWindow(w[0]); }); - App.closeWindow('closer'); }; -globalThis.closeAll = closeAll; - -Pointers.connect('new-line', (_, out) => { - if (out) { - Utils.execAsync('hyprctl layers -j').then(layers => { - layers = JSON.parse(layers); - - Utils.execAsync('hyprctl cursorpos -j').then(pos => { - pos = JSON.parse(pos); - - Object.values(layers).forEach(key => { - const bar = key['levels']['3'] - .find(n => n.namespace === 'bar'); - - const widgets = key['levels']['3'] - .filter(n => !ALWAYS_OPEN.includes(n.namespace)); - - if (pos.x > bar.x && pos.x < bar.x + bar.w && - pos.y > bar.y && pos.y < bar.y + bar.h) { - - // don't handle clicks when on bar - } - else { - widgets.forEach(l => { - if (!(pos.x > l.x && pos.x < l.x + l.w && - pos.y > l.y && pos.y < l.y + l.h)) { - closeAll(); - return; - } - }); - } - }); - }).catch(print); - }).catch(print); - } -}); - -export default () => Window({ - name: 'closer', - popup: true, - layer: 'top', - - child: Revealer({ - connections: [[App, (_, windowName, visible) => { - const anyVisibleAndClosable = Array.from(App.windows).some(w => { - const isAlwaysOpen = ALWAYS_OPEN.some(window => window === w[0]); - - return w[1].visible && !isAlwaysOpen; - }); - - if (!anyVisibleAndClosable) - App.closeWindow('closer'); - - if (windowName === 'closer') { - if (visible) - Pointers.startProc(); - else - Pointers.killProc(); - } - }]], - }), -}); diff --git a/hosts/wim/config/ags/js/misc/popup.js b/hosts/wim/config/ags/js/misc/popup.js index 6df9782..5b15d3a 100644 --- a/hosts/wim/config/ags/js/misc/popup.js +++ b/hosts/wim/config/ags/js/misc/popup.js @@ -5,6 +5,7 @@ const { Revealer, Box, Window } = Widget; export default ({ name, child, + closeOnUnfocus = 'released', transition = 'slide_down', onOpen = () => {}, ...props @@ -27,9 +28,6 @@ export default ({ if (currentName === name) { rev.reveal_child = visible; onOpen(child); - - if (visible && name !== 'overview') - App.openWindow('closer'); } }]], child: child, @@ -37,5 +35,6 @@ export default ({ }), }); window.getChild = () => child; + window.closeOnUnfocus = closeOnUnfocus; return window; }; diff --git a/hosts/wim/config/ags/js/overview/main.js b/hosts/wim/config/ags/js/overview/main.js index 01f971d..462a9df 100644 --- a/hosts/wim/config/ags/js/overview/main.js +++ b/hosts/wim/config/ags/js/overview/main.js @@ -15,6 +15,7 @@ function update(box) { export default () => PopupWindow({ name: 'overview', transition: 'crossfade', + closeOnUnfocus: 'none', onOpen: child => update(child), child: Box({ diff --git a/hosts/wim/config/ags/services/pointers.js b/hosts/wim/config/ags/services/pointers.js index 626d6bb..e2c4c9b 100644 --- a/hosts/wim/config/ags/services/pointers.js +++ b/hosts/wim/config/ags/services/pointers.js @@ -1,4 +1,4 @@ -import { Service, Utils } from '../imports.js'; +import { App, Service, Utils } from '../imports.js'; import GUdev from 'gi://GUdev'; const UDEV_POINTERS = [ @@ -8,12 +8,15 @@ const UDEV_POINTERS = [ 'ID_INPUT_TOUCHSCREEN', 'ID_INPUT_TABLET', ]; -const LIB_POINTERS = [ - 'BTN', +const ON_RELEASE_TRIGGERS = [ 'released', 'TOUCH_UP', 'HOLD_END', ]; +const ON_CLICK_TRIGGERS = [ + 'pressed', + 'TOUCH_DOWN', +]; class Pointers extends Service { @@ -23,6 +26,8 @@ class Pointers extends Service { 'proc-destroyed': ['boolean'], 'device-fetched': ['boolean'], 'new-line': ['string'], + 'released': ['string'], + 'clicked': ['string'], }); } @@ -31,31 +36,17 @@ class Pointers extends Service { devices = []; udevClient = GUdev.Client.new(['input']); - get process() { return this.proc; } + get process() { return this.proc; } get lastLine() { return this.output; } get pointers() { return this.devices; } constructor() { super(); this.initUdevConnection(); + this.initAppConnection(); } // FIXME: logitech mouse screws everything up on disconnect - getDevices() { - this.devices = []; - this.udevClient.query_by_subsystem('input').forEach(dev => { - const isPointer = UDEV_POINTERS.some(p => dev.has_property(p)); - if (isPointer) { - const hasEventFile = dev.has_property('DEVNAME') && - dev.get_property('DEVNAME').includes('event'); - if (hasEventFile) - this.devices.push(dev.get_property('DEVNAME')); - } - }); - - this.emit('device-fetched', true); - } - initUdevConnection() { this.getDevices(); this.udevClient.connect('uevent', (_, action) => { @@ -69,6 +60,21 @@ class Pointers extends Service { }); } + getDevices() { + this.devices = []; + this.udevClient.query_by_subsystem('input').forEach(dev => { + const isPointer = UDEV_POINTERS.some(p => dev.has_property(p)); + if (isPointer) { + const hasEventFile = dev.has_property('DEVNAME') && + dev.get_property('DEVNAME').includes('event'); + if (hasEventFile) + this.devices.push(dev.get_property('DEVNAME')); + } + }); + + this.emit('device-fetched', true); + } + startProc() { if (this.proc) return; @@ -82,11 +88,21 @@ class Pointers extends Service { this.proc = Utils.subprocess( ['libinput', 'debug-events', ...args], output => { - if (!output.includes('cancelled')) { - if (LIB_POINTERS.some(p => output.includes(p))) { - this.output = output; - this.emit('new-line', output); - } + if (output.includes('cancelled')) + return; + + if (ON_RELEASE_TRIGGERS.some(p => output.includes(p))) { + this.output = output; + this.detectClickedOutside('released'); + this.emit('released', output); + this.emit('new-line', output); + } + + if (ON_CLICK_TRIGGERS.some(p => output.includes(p))) { + this.output = output; + this.detectClickedOutside('clicked'); + this.emit('clicked', output); + this.emit('new-line', output); } }, err => logError(err), @@ -101,6 +117,67 @@ class Pointers extends Service { this.emit('proc-destroyed', true); } } + + initAppConnection() { + App.connect('window-toggled', () => { + const anyVisibleAndClosable = Array.from(App.windows).some(w => { + const closable = (w[1].closeOnUnfocus && + w[1].closeOnUnfocus !== 'none'); + + return w[1].visible && closable; + }); + + if (anyVisibleAndClosable) + this.startProc(); + + else + this.killProc(); + }); + } + + detectClickedOutside(clickStage) { + const toClose = Array.from(App.windows).some(w => { + const closable = (w[1].closeOnUnfocus && + w[1].closeOnUnfocus === clickStage); + + return w[1].visible && closable; + }); + if (!toClose) + return; + + Utils.execAsync('hyprctl layers -j').then(layers => { + layers = JSON.parse(layers); + + Utils.execAsync('hyprctl cursorpos -j').then(pos => { + pos = JSON.parse(pos); + + Object.values(layers).forEach(key => { + const bar = key['levels']['3'] + .find(n => n.namespace === 'bar'); + + const widgets = key['levels']['3'].filter(n => { + const window = App.getWindow(n.namespace); + return window.closeOnUnfocus && + window.closeOnUnfocus === clickStage; + }); + + if (pos.x > bar.x && pos.x < bar.x + bar.w && + pos.y > bar.y && pos.y < bar.y + bar.h) { + + // don't handle clicks when on bar + // TODO: make this configurable + } + else { + widgets.forEach(w => { + if (!(pos.x > w.x && pos.x < w.x + w.w && + pos.y > w.y && pos.y < w.y + w.h)) + App.closeWindow(w.namespace); + }); + } + }); + }).catch(print); + }).catch(print); + } } const pointersService = new Pointers();