feature: add subprocess util

this would be the equivalent of deflisten in eww
This commit is contained in:
Aylur
2023-08-12 23:46:45 +02:00
parent a3f717ed50
commit 9d7be06ea8
2 changed files with 82 additions and 92 deletions
+40 -88
View File
@@ -1,7 +1,6 @@
import GLib from 'gi://GLib'; import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import Service from './service.js'; import Service from './service.js';
import { error, execAsync, warning } from '../utils.js'; import { error, execAsync, subprocess } from '../utils.js';
const HIS = GLib.getenv('HYPRLAND_INSTANCE_SIGNATURE'); const HIS = GLib.getenv('HYPRLAND_INSTANCE_SIGNATURE');
@@ -44,13 +43,6 @@ type Active = {
workspace: ActiveWorkspace workspace: ActiveWorkspace
} }
type HyprlandState = {
active: Active
monitors: Map<string, Monitor>
workspaces: Map<number, Workspace>
clients: Map<string, Client>
}
class HyprlandService extends Service { class HyprlandService extends Service {
static { static {
Service.register(this, { Service.register(this, {
@@ -59,55 +51,59 @@ class HyprlandService extends Service {
}); });
} }
_state!: HyprlandState; _active: Active;
_monitors: Map<string, Monitor>;
_workspaces: Map<number, Workspace>;
_clients: Map<string, Client>;
constructor() { constructor() {
if (!HIS) if (!HIS)
error('Hyprland is not running'); error('Hyprland is not running');
super(); super();
this._state = { this._active = {
active: { client: {
client: { address: '',
address: '', title: '',
title: '', class: '',
class: '', },
}, monitor: '',
monitor: '', workspace: {
workspace: { id: 0,
id: 0, name: '',
name: '',
},
}, },
monitors: new Map(),
workspaces: new Map(),
clients: new Map(),
}; };
this._monitors = new Map();
this._workspaces = new Map();
this._clients = new Map();
this._sync(); this._sync();
this._startSocat();
const socat = `socat -U - UNIX-CONNECT:/tmp/hypr/${HIS}/.socket2.sock`;
subprocess(['bash', '-c', socat], line => {
this._onEvent(line);
});
} }
async _sync() { async _sync() {
try { try {
const monitors = await execAsync('hyprctl -j monitors'); const monitors = await execAsync('hyprctl -j monitors');
this._state.monitors = new Map(); this._monitors = new Map();
(JSON.parse(monitors as string) as Monitor[]).forEach(monitor => { (JSON.parse(monitors as string) as Monitor[]).forEach(monitor => {
this._state.monitors.set(monitor.name, monitor); this._monitors.set(monitor.name, monitor);
if (monitor.focused) { if (monitor.focused) {
this._state.active.monitor = monitor.name; this._active.monitor = monitor.name;
this._state.active.workspace = monitor.activeWorkspace; this._active.workspace = monitor.activeWorkspace;
} }
}); });
const workspaces = await execAsync('hyprctl -j workspaces'); const workspaces = await execAsync('hyprctl -j workspaces');
this._state.workspaces = new Map(); this._workspaces = new Map();
(JSON.parse(workspaces as string) as Workspace[]).forEach(ws => { (JSON.parse(workspaces as string) as Workspace[]).forEach(ws => {
this._state.workspaces.set(ws.id, ws); this._workspaces.set(ws.id, ws);
}); });
const clients = await execAsync('hyprctl -j clients'); const clients = await execAsync('hyprctl -j clients');
this._state.clients = new Map(); this._clients = new Map();
(JSON.parse(clients as string) as Client[]).forEach(c => { (JSON.parse(clients as string) as Client[]).forEach(c => {
const { const {
address, address,
@@ -119,7 +115,7 @@ class HyprlandService extends Service {
floating, floating,
} = c; } = c;
this._state.clients.set(address.substring(2), { this._clients.set(address.substring(2), {
address, address,
pid, pid,
workspace, workspace,
@@ -145,16 +141,16 @@ class HyprlandService extends Service {
switch (e) { switch (e) {
case 'activewindow': case 'activewindow':
this._state.active.client.class = argv[0]; this._active.client.class = argv[0];
this._state.active.client.title = argv[1]; this._active.client.title = argv[1];
break; break;
case 'activewindowv2': case 'activewindowv2':
this._state.active.client.address = argv[0]; this._active.client.address = argv[0];
break; break;
case 'closewindow': case 'closewindow':
this._state.active.client = { this._active.client = {
class: '', class: '',
title: '', title: '',
address: '', address: '',
@@ -172,7 +168,7 @@ class HyprlandService extends Service {
break; break;
} }
case 'changefloating': { case 'changefloating': {
const client = this._state.clients.get(argv[0]); const client = this._clients.get(argv[0]);
if (client) if (client)
client.floating = argv[1] === '1'; client.floating = argv[1] === '1';
break; break;
@@ -184,50 +180,6 @@ class HyprlandService extends Service {
this.emit('changed'); this.emit('changed');
} }
_startSocat() {
try {
const socat = `
socat -U - UNIX-CONNECT:/tmp/hypr/${HIS}/.socket2.sock | while read lines
do
echo $lines
done`;
const proc = Gio.Subprocess.new(
['bash', '-c', socat],
Gio.SubprocessFlags.STDOUT_PIPE,
);
const pipe = proc.get_stdout_pipe();
if (!pipe) {
warning('socat error');
return;
}
const stdout = new Gio.DataInputStream({
base_stream: pipe,
close_base_stream: true,
});
this._readSocat(stdout);
} catch (e) {
logError(e as Error);
}
}
_readSocat(stdout: Gio.DataInputStream) {
stdout.read_line_async(GLib.PRIORITY_LOW, null, (stdout, res) => {
try {
const line = stdout?.read_line_finish_utf8(res)[0];
if (line) {
this._onEvent(line);
this._readSocat(stdout);
}
} catch (e) {
logError(e as Error);
}
});
}
} }
export default class Hyprland { export default class Hyprland {
@@ -239,10 +191,10 @@ export default class Hyprland {
return Hyprland._instance; return Hyprland._instance;
} }
static get active() { return Hyprland.instance._state.active; } static get active() { return Hyprland.instance._active; }
static get monitors() { return Hyprland.instance._state.monitors; } static get monitors() { return Hyprland.instance._monitors; }
static get workspaces() { return Hyprland.instance._state.workspaces; } static get workspaces() { return Hyprland.instance._workspaces; }
static get clients() { return Hyprland.instance._state.clients; } static get clients() { return Hyprland.instance._clients; }
static HyprctlGet(cmd: string): unknown | object { static HyprctlGet(cmd: string): unknown | object {
const [success, out, err] = const [success, out, err] =
+42 -4
View File
@@ -222,11 +222,8 @@ export function isRunning(dbusName: string) {
} }
export function execAsync(cmd: string | string[]) { export function execAsync(cmd: string | string[]) {
if (typeof cmd === 'string')
cmd = cmd.split(' ');
const proc = Gio.Subprocess.new( const proc = Gio.Subprocess.new(
cmd, typeof cmd === 'string' ? cmd.split(' ') : cmd,
Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_PIPE, Gio.SubprocessFlags.STDERR_PIPE,
); );
@@ -258,3 +255,44 @@ export function exec(cmd: string) {
return decoder.decode(out).trim(); return decoder.decode(out).trim();
} }
export function subprocess(
cmd: string | string[],
callback: (out: string) => void,
onError = logError,
) {
try {
const read = (stdout: Gio.DataInputStream) => {
stdout.read_line_async(GLib.PRIORITY_LOW, null, (stdout, res) => {
try {
const output = stdout?.read_line_finish_utf8(res)[0];
if (output) {
callback(output);
read(stdout);
}
} catch (e) {
return onError(e as Error);
}
});
};
const proc = Gio.Subprocess.new(
typeof cmd === 'string' ? cmd.split(' ') : cmd,
Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_PIPE,
);
const pipe = proc.get_stdout_pipe();
if (!pipe)
return onError(new Error(`subprocess ${cmd} stdout pipe is null`));
const stdout = new Gio.DataInputStream({
base_stream: pipe,
close_base_stream: true,
});
read(stdout);
} catch (e) {
return onError(e as Error);
}
}