nixos-configs/nixosModules/ags/config/services/monitor-clicks.ts

194 lines
5.8 KiB
TypeScript
Raw Normal View History

2024-10-16 21:44:45 -04:00
import { subprocess } from 'astal';
import { App } from 'astal/gtk3';
import GObject, { register, signal } from 'astal/gobject';
2024-10-22 13:09:39 -04:00
import AstalIO from 'gi://AstalIO';
2024-10-16 21:44:45 -04:00
import { hyprMessage } from '../lib';
const ON_RELEASE_TRIGGERS = [
'released',
'TOUCH_UP',
'HOLD_END',
];
const ON_CLICK_TRIGGERS = [
'pressed',
'TOUCH_DOWN',
];
/* Types */
import PopupWindow from '../widgets/misc/popup-window';
2024-10-16 21:44:45 -04:00
import { CursorPos, Layer, LayerResult } from '../lib';
@register()
export default class MonitorClicks extends GObject.Object {
@signal(Boolean)
declare procStarted: (state: boolean) => void;
@signal(Boolean)
declare procDestroyed: (state: boolean) => void;
@signal(String)
declare released: (procLine: string) => void;
@signal(String)
declare clicked: (procLine: string) => void;
private process = null as AstalIO.Process | null;
constructor() {
super();
this.#initAppConnection();
}
startProc() {
if (this.process) {
return;
}
this.process = subprocess(
['libinput', 'debug-events'],
(output) => {
if (output.includes('cancelled')) {
return;
}
if (ON_RELEASE_TRIGGERS.some((p) => output.includes(p))) {
MonitorClicks.detectClickedOutside('released');
this.emit('released', output);
}
if (ON_CLICK_TRIGGERS.some((p) => output.includes(p))) {
MonitorClicks.detectClickedOutside('clicked');
this.emit('clicked', output);
}
},
);
this.emit('proc-started', true);
}
killProc() {
if (this.process) {
this.process.kill();
this.process = null;
this.emit('proc-destroyed', true);
}
}
#initAppConnection() {
App.connect('window-toggled', () => {
const anyVisibleAndClosable =
(App.get_windows() as PopupWindow[]).some((w) => {
const closable = w.close_on_unfocus &&
!(
w.close_on_unfocus === 'none' ||
w.close_on_unfocus === 'stay'
);
return w.visible && closable;
});
if (anyVisibleAndClosable) {
this.startProc();
}
else {
this.killProc();
}
});
}
static async detectClickedOutside(clickStage: string) {
const toClose = ((App.get_windows() as PopupWindow[])).some((w) => {
const closable = (
w.close_on_unfocus &&
w.close_on_unfocus === clickStage
);
return w.visible && closable;
});
if (!toClose) {
return;
}
2024-10-16 22:33:15 -04:00
try {
const layers = JSON.parse(await hyprMessage('j/layers')) as LayerResult;
const pos = JSON.parse(await hyprMessage('j/cursorpos')) as CursorPos;
Object.values(layers).forEach((key) => {
const overlayLayer = key.levels['3'];
if (overlayLayer) {
const noCloseWidgetsNames = [
'bar-',
'osk',
];
const getNoCloseWidgets = (names: string[]) => {
const arr = [] as Layer[];
names.forEach((name) => {
arr.push(
overlayLayer.find(
(n) => n.namespace.startsWith(name),
) ||
2024-10-28 14:45:10 -04:00
// Return an empty Layer if widget doesn't exist
{
address: '',
x: 0,
y: 0,
w: 0,
h: 0,
namespace: '',
},
2024-10-16 22:33:15 -04:00
);
});
2024-10-16 21:44:45 -04:00
2024-10-16 22:33:15 -04:00
return arr;
};
const clickIsOnWidget = (w: Layer) => {
2024-10-28 14:45:10 -04:00
return (
pos.x > w.x && pos.x < w.x + w.w &&
pos.y > w.y && pos.y < w.y + w.h
);
2024-10-16 22:33:15 -04:00
};
2024-10-16 21:44:45 -04:00
2024-10-16 22:33:15 -04:00
const noCloseWidgets = getNoCloseWidgets(noCloseWidgetsNames);
2024-10-16 21:44:45 -04:00
2024-10-16 22:33:15 -04:00
const widgets = overlayLayer.filter((n) => {
let window = null as null | PopupWindow;
2024-10-16 21:44:45 -04:00
2024-10-28 14:45:10 -04:00
if (App.get_windows().some((win) => win.name === n.namespace)) {
2024-10-16 22:33:15 -04:00
window = (App.get_window(n.namespace) as PopupWindow);
}
2024-10-16 21:44:45 -04:00
2024-10-16 22:33:15 -04:00
return window &&
2024-10-28 14:45:10 -04:00
window.close_on_unfocus &&
window.close_on_unfocus ===
clickStage;
2024-10-16 22:33:15 -04:00
});
2024-10-16 21:44:45 -04:00
2024-10-16 22:33:15 -04:00
if (noCloseWidgets.some(clickIsOnWidget)) {
2024-10-28 14:45:10 -04:00
// Don't handle clicks when on certain widgets
2024-10-16 22:33:15 -04:00
}
else {
widgets.forEach((w) => {
2024-10-28 14:45:10 -04:00
if (!(
pos.x > w.x && pos.x < w.x + w.w &&
pos.y > w.y && pos.y < w.y + w.h
)) {
2024-10-16 22:33:15 -04:00
App.get_window(w.namespace)?.set_visible(false);
}
});
}
2024-10-16 21:44:45 -04:00
}
2024-10-16 22:33:15 -04:00
});
}
catch (e) {
console.log(e);
}
2024-10-16 21:44:45 -04:00
}
}