2023-11-21 01:29:46 -05:00
|
|
|
import App from 'resource:///com/github/Aylur/ags/app.js';
|
2023-11-13 13:19:14 -05:00
|
|
|
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
|
2023-10-31 08:32:40 -04:00
|
|
|
import Service from 'resource:///com/github/Aylur/ags/service.js';
|
2023-11-13 13:19:14 -05:00
|
|
|
import { subprocess } from 'resource:///com/github/Aylur/ags/utils.js';
|
2023-10-24 14:21:33 -04:00
|
|
|
import GUdev from 'gi://GUdev';
|
|
|
|
|
|
|
|
const UDEV_POINTERS = [
|
|
|
|
'ID_INPUT_MOUSE',
|
|
|
|
'ID_INPUT_POINTINGSTICK',
|
|
|
|
'ID_INPUT_TOUCHPAD',
|
|
|
|
'ID_INPUT_TOUCHSCREEN',
|
|
|
|
'ID_INPUT_TABLET',
|
|
|
|
];
|
2023-10-24 17:26:38 -04:00
|
|
|
const ON_RELEASE_TRIGGERS = [
|
2023-10-24 14:21:33 -04:00
|
|
|
'released',
|
|
|
|
'TOUCH_UP',
|
|
|
|
'HOLD_END',
|
|
|
|
];
|
2023-10-24 17:26:38 -04:00
|
|
|
const ON_CLICK_TRIGGERS = [
|
|
|
|
'pressed',
|
|
|
|
'TOUCH_DOWN',
|
|
|
|
];
|
2023-10-16 17:06:19 -04:00
|
|
|
|
|
|
|
|
|
|
|
class Pointers extends Service {
|
2023-10-20 23:11:21 -04:00
|
|
|
static {
|
|
|
|
Service.register(this, {
|
|
|
|
'proc-started': ['boolean'],
|
|
|
|
'proc-destroyed': ['boolean'],
|
|
|
|
'device-fetched': ['boolean'],
|
|
|
|
'new-line': ['string'],
|
2023-10-24 17:26:38 -04:00
|
|
|
'released': ['string'],
|
|
|
|
'clicked': ['string'],
|
2023-10-20 23:11:21 -04:00
|
|
|
});
|
|
|
|
}
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
#process;
|
|
|
|
#lastLine = '';
|
|
|
|
#pointers = [];
|
|
|
|
#udevClient = GUdev.Client.new(['input']);
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
get process() {
|
|
|
|
return this.#process;
|
|
|
|
}
|
|
|
|
|
|
|
|
get lastLine() {
|
|
|
|
return this.#lastLine;
|
|
|
|
}
|
|
|
|
|
|
|
|
get pointers() {
|
|
|
|
return this.#pointers;
|
|
|
|
}
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-10-20 23:11:21 -04:00
|
|
|
constructor() {
|
|
|
|
super();
|
2023-11-21 01:29:46 -05:00
|
|
|
this.#initUdevConnection();
|
|
|
|
this.#initAppConnection();
|
2023-10-20 23:11:21 -04:00
|
|
|
}
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-10-24 14:21:33 -04:00
|
|
|
// FIXME: logitech mouse screws everything up on disconnect
|
2023-11-21 01:29:46 -05:00
|
|
|
#initUdevConnection() {
|
|
|
|
this.#getDevices();
|
|
|
|
this.#udevClient.connect('uevent', (_, action) => {
|
2023-10-24 17:26:38 -04:00
|
|
|
if (action === 'add' || action === 'remove') {
|
2023-12-18 23:20:32 -05:00
|
|
|
this.#getDevices();
|
2023-11-21 01:29:46 -05:00
|
|
|
if (this.#process) {
|
2023-10-24 17:26:38 -04:00
|
|
|
this.killProc();
|
|
|
|
this.startProc();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
#getDevices() {
|
|
|
|
this.#pointers = [];
|
|
|
|
this.#udevClient.query_by_subsystem('input').forEach((dev) => {
|
|
|
|
const isPointer = UDEV_POINTERS.some((p) => dev.has_property(p));
|
|
|
|
|
2023-10-24 14:21:33 -04:00
|
|
|
if (isPointer) {
|
|
|
|
const hasEventFile = dev.has_property('DEVNAME') &&
|
2023-11-21 01:29:46 -05:00
|
|
|
dev.get_property('DEVNAME')
|
|
|
|
.includes('event');
|
|
|
|
|
|
|
|
if (hasEventFile) {
|
|
|
|
this.#pointers.push(dev.get_property('DEVNAME'));
|
|
|
|
}
|
2023-10-24 14:21:33 -04:00
|
|
|
}
|
|
|
|
});
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-10-24 14:21:33 -04:00
|
|
|
this.emit('device-fetched', true);
|
|
|
|
}
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-10-20 23:11:21 -04:00
|
|
|
startProc() {
|
2023-11-21 01:29:46 -05:00
|
|
|
if (this.#process) {
|
2023-10-20 23:11:21 -04:00
|
|
|
return;
|
2023-11-21 01:29:46 -05:00
|
|
|
}
|
2023-10-16 18:43:12 -04:00
|
|
|
|
2023-10-20 23:11:21 -04:00
|
|
|
const args = [];
|
2023-11-21 01:29:46 -05:00
|
|
|
|
|
|
|
this.#pointers.forEach((dev) => {
|
2023-10-24 14:21:33 -04:00
|
|
|
args.push('--device');
|
|
|
|
args.push(dev);
|
2023-10-20 23:11:21 -04:00
|
|
|
});
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
this.#process = subprocess(
|
2023-10-20 23:11:21 -04:00
|
|
|
['libinput', 'debug-events', ...args],
|
2023-11-21 01:29:46 -05:00
|
|
|
(output) => {
|
|
|
|
if (output.includes('cancelled')) {
|
2023-10-24 17:26:38 -04:00
|
|
|
return;
|
2023-11-21 01:29:46 -05:00
|
|
|
}
|
2023-10-24 17:26:38 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
if (ON_RELEASE_TRIGGERS.some((p) => output.includes(p))) {
|
|
|
|
this.#lastLine = output;
|
|
|
|
Pointers.detectClickedOutside('released');
|
2023-10-24 17:26:38 -04:00
|
|
|
this.emit('released', output);
|
|
|
|
this.emit('new-line', output);
|
|
|
|
}
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
if (ON_CLICK_TRIGGERS.some((p) => output.includes(p))) {
|
|
|
|
this.#lastLine = output;
|
|
|
|
Pointers.detectClickedOutside('clicked');
|
2023-10-24 17:26:38 -04:00
|
|
|
this.emit('clicked', output);
|
|
|
|
this.emit('new-line', output);
|
2023-10-20 23:11:21 -04:00
|
|
|
}
|
|
|
|
},
|
2023-11-21 01:29:46 -05:00
|
|
|
(err) => logError(err),
|
2023-10-20 23:11:21 -04:00
|
|
|
);
|
|
|
|
this.emit('proc-started', true);
|
|
|
|
}
|
2023-10-16 17:06:19 -04:00
|
|
|
|
2023-10-20 23:11:21 -04:00
|
|
|
killProc() {
|
2023-11-21 01:29:46 -05:00
|
|
|
if (this.#process) {
|
|
|
|
this.#process.force_exit();
|
|
|
|
this.#process = null;
|
2023-10-20 23:11:21 -04:00
|
|
|
this.emit('proc-destroyed', true);
|
2023-10-16 17:06:19 -04:00
|
|
|
}
|
|
|
|
}
|
2023-10-24 17:26:38 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
#initAppConnection() {
|
2023-10-24 17:26:38 -04:00
|
|
|
App.connect('window-toggled', () => {
|
2023-11-21 01:29:46 -05:00
|
|
|
const anyVisibleAndClosable = Array.from(App.windows).some((w) => {
|
2023-12-18 23:20:32 -05:00
|
|
|
const closable = w[1].attribute?.close_on_unfocus &&
|
|
|
|
!(w[1].attribute?.close_on_unfocus === 'none' ||
|
|
|
|
w[1].attribute?.close_on_unfocus === 'stay');
|
2023-10-24 17:26:38 -04:00
|
|
|
|
|
|
|
return w[1].visible && closable;
|
|
|
|
});
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
if (anyVisibleAndClosable) {
|
2023-10-24 17:26:38 -04:00
|
|
|
this.startProc();
|
2023-11-21 01:29:46 -05:00
|
|
|
}
|
2023-10-24 17:26:38 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
else {
|
2023-10-24 17:26:38 -04:00
|
|
|
this.killProc();
|
2023-11-21 01:29:46 -05:00
|
|
|
}
|
2023-10-24 17:26:38 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
static detectClickedOutside(clickStage) {
|
|
|
|
const toClose = Array.from(App.windows).some((w) => {
|
2023-12-18 23:20:32 -05:00
|
|
|
const closable = (w[1].attribute?.close_on_unfocus &&
|
|
|
|
w[1].attribute?.close_on_unfocus === clickStage);
|
2023-10-24 17:26:38 -04:00
|
|
|
|
|
|
|
return w[1].visible && closable;
|
|
|
|
});
|
2023-11-21 01:29:46 -05:00
|
|
|
|
|
|
|
if (!toClose) {
|
2023-10-24 17:26:38 -04:00
|
|
|
return;
|
2023-11-21 01:29:46 -05:00
|
|
|
}
|
2023-10-24 17:26:38 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
Hyprland.sendMessage('j/layers').then((layers) => {
|
2023-10-24 17:26:38 -04:00
|
|
|
layers = JSON.parse(layers);
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
Hyprland.sendMessage('j/cursorpos').then((pos) => {
|
2023-10-24 17:26:38 -04:00
|
|
|
pos = JSON.parse(pos);
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
Object.values(layers).forEach((key) => {
|
|
|
|
const bar = key.levels['3']
|
|
|
|
.find((n) => n.namespace === 'bar');
|
2023-10-24 17:26:38 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
const widgets = key.levels['3'].filter((n) => {
|
2023-10-24 17:26:38 -04:00
|
|
|
const window = App.getWindow(n.namespace);
|
2023-11-21 01:29:46 -05:00
|
|
|
|
2023-12-18 23:20:32 -05:00
|
|
|
return window?.attribute?.close_on_unfocus &&
|
|
|
|
window?.attribute?.close_on_unfocus === clickStage;
|
2023-10-24 17:26:38 -04:00
|
|
|
});
|
|
|
|
|
2023-11-27 15:39:55 -05:00
|
|
|
if (pos.x > bar?.x && pos.x < bar?.x + bar?.w &&
|
|
|
|
pos.y > bar?.y && pos.y < bar?.y + bar?.h) {
|
2023-10-24 17:26:38 -04:00
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
// Don't handle clicks when on bar
|
2023-10-24 17:26:38 -04:00
|
|
|
// TODO: make this configurable
|
|
|
|
}
|
|
|
|
else {
|
2023-11-21 01:29:46 -05:00
|
|
|
widgets.forEach((w) => {
|
2023-10-24 17:26:38 -04:00
|
|
|
if (!(pos.x > w.x && pos.x < w.x + w.w &&
|
2023-11-21 01:29:46 -05:00
|
|
|
pos.y > w.y && pos.y < w.y + w.h)) {
|
2023-10-24 17:26:38 -04:00
|
|
|
App.closeWindow(w.namespace);
|
2023-11-21 01:29:46 -05:00
|
|
|
}
|
2023-10-24 17:26:38 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}).catch(print);
|
|
|
|
}).catch(print);
|
|
|
|
}
|
2023-10-16 17:06:19 -04:00
|
|
|
}
|
|
|
|
|
2023-11-21 01:29:46 -05:00
|
|
|
export default new Pointers();
|