feat(ags): make a service to monitor clicks instead of closer window

This commit is contained in:
matt1432 2023-10-16 17:06:19 -04:00
parent 9b7fd772f0
commit 0733e83964
3 changed files with 138 additions and 113 deletions

View file

@ -1,5 +1,7 @@
import { App, Widget } from '../../imports.js';
const { Window, EventBox } = Widget;
import { App, Utils, Widget } from '../../imports.js';
const { Window, Revealer } = Widget;
import Pointers from '../../services/pointers.js';
const ALWAYS_OPEN = [
'closer',
@ -12,7 +14,6 @@ const ALWAYS_OPEN = [
];
// TODO: close on scroll event too?
export const closeAll = () => {
App.windows.forEach(w => {
if (!ALWAYS_OPEN.some(window => window === w.name))
@ -21,19 +22,61 @@ export const closeAll = () => {
App.closeWindow('closer');
};
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 => {
let bar = key['levels']['3']
.find(n => n.namespace === "bar");
let 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 const Closer = Window({
name: 'closer',
popup: true,
layer: 'top',
anchor: [ 'top', 'bottom', 'left', 'right' ],
child: EventBox({
onPrimaryClickRelease: () => closeAll(),
connections: [[App, (_b, _w, _v) => {
child: Revealer({
connections: [[App, (_, windowName, visible) => {
if (!Array.from(App.windows).some(w => w[1].visible &&
!ALWAYS_OPEN.some(window => window === w[0]))) {
App.closeWindow('closer');
}
if (windowName === 'closer') {
if (visible)
Pointers.startProc();
else
Pointers.killProc();
}
}]],
}),
});

View file

@ -0,0 +1,88 @@
// TODO: read /dev to recalculate devices and remake subprocess
import { Service, Utils } from '../imports.js';
class Pointers extends Service {
static {
Service.register(this, {
'proc-started': ['boolean'],
'proc-destroyed': ['boolean'],
'device-fetched': ['boolean'],
'new-line': ['string'],
});
}
proc;
output = "";
devices = new Map();
get proc() { return this.proc; }
get output() { return this.output; }
get devices() { return this.devices; }
constructor() {
super();
this.parseDevices();
}
parseDevices() {
Utils.execAsync(['libinput', 'list-devices']).then(out => {
let lines = out.split('\n');
let device = null;
let devices = new Map();
lines.forEach(line => {
let parts = line.split(':');
if (parts[0] === 'Device') {
device = {};
devices.set(parts[1].trim(), device);
}
else if (device && parts[1]) {
let key = parts[0].trim();
let value = parts[1].trim();
device[key] = value;
}
});
this.devices = Array.from(devices).filter(dev => dev.Capabilities &&
dev.Capabilities.includes('pointer'));
this.emit('device-fetched', true);
}).catch(print);
}
startProc() {
let args = [];
this.devices.forEach(dev => {
if (dev.Kernel) {
args.push('--device');
args.push(dev.Kernel);
}
});
this.proc = Utils.subprocess(
['libinput', 'debug-events', ...args],
(output) => {
if (output.includes('BTN') && output.includes('released') ||
output.includes('TOUCH_UP') ||
output.includes('HOLD_END') && !output.includes('cancelled')) {
this.output = output;
this.emit('new-line', output);
}
},
(err) => logError(err)
);
this.emit('proc-started', true);
}
killProc() {
if (this.proc) {
this.proc.force_exit();
this.emit('proc-destroyed', true);
}
}
}
const pointersService = new Pointers();
export default pointersService;

View file

@ -1,106 +0,0 @@
/*
import Libinput from './libinput.js';
Libinput.instance.connect('device-init', () => {
let pointers = [];
Libinput.devices.forEach(dev => {
if (dev.Capabilities.includes('pointer')) {
pointers.push(dev);
}
})
Libinput.addDebugInstance('pointers', pointers)
.connect('new-line', e => print(e.lastLine))
});
*/
/*
// WIP
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 => {
key['levels']['3'].forEach(l => {
print(l.namespace);
if (pos.x > l.x && pos.x < l.x + l.w &&
pos.y > l.y && pos.y < l.y + l.h)
{
print('inside window');
}
else {
print('outside window');
}
});
});
}).catch(print);
}).catch(print);
*/
// TODO: use hyprctl layers to determine if clicks were on a widget
// read /dev to recalculate devices and remake subprocess
const { Service, Utils } = '../imports.js';
class PointersService extends Service {
static {
Service.register(this, {
'log-started': ['boolean'],
'device-fetched': ['boolean'],
});
}
log;
output = "";
devices = new Map();
get log() {return this.log;}
get output() {return this.output;}
get devices() {return this.devices;}
parseDevices() {
Utils.execAsync(['libinput', 'list-devices']).then(out => {
let lines = out.split('\n');
let device = null;
let devices = new Map();
lines.forEach(line => {
let parts = line.split(':');
if (parts[0] === 'Device') {
device = {};
devices.set(parts[1].trim(), device);
}
else if (device && parts[1]) {
let key = parts[0].trim();
let value = parts[1].trim();
device[key] = value;
}
});
this.devices = devices.filter(dev => dev.Capabilities && dev.Capabilities.includes('pointer'));
this.emit('device-fetched', true);
});
}
startLog() {
let args = [];
this.devices.forEach(dev => {
if (dev.Kernel) {
args.push('--device');
args.push(dev.Kernel);
}
});
this.log = Utils.subprocess(
['libinput', 'debug-events', ...args],
(output) => {
this.output = output;
},
(err) => logError(err)
);
this.emit('log-started', true);
}
}