Compare commits

..

3 commits

8 changed files with 101 additions and 80 deletions

View file

@ -21,7 +21,6 @@ exec(`sassc ${scss} ${css}`);
Setup(); Setup();
// TODO: get rid of 'properties' and 'binds' prop
export default { export default {
style: css, style: css,
notificationPopupTimeout: 5000, notificationPopupTimeout: 5000,

View file

@ -80,6 +80,7 @@ const ModKey = (key) => {
const button = EventBox({ const button = EventBox({
cursor: 'pointer', cursor: 'pointer',
class_name: 'key', class_name: 'key',
on_primary_click_release: (self) => { on_primary_click_release: (self) => {
console.log('mod toggled'); console.log('mod toggled');
@ -89,6 +90,7 @@ const ModKey = (key) => {
self.child.toggleClassName('active', !Mod.value); self.child.toggleClassName('active', !Mod.value);
Mod.value = !Mod.value; Mod.value = !Mod.value;
}, },
setup: (self) => { setup: (self) => {
self.hook(NormalClick, () => { self.hook(NormalClick, () => {
Mod.value = false; Mod.value = false;

View file

@ -7,14 +7,13 @@ import PopupWindow from './misc/popup.js';
import CursorBox from './misc/cursorbox.js'; import CursorBox from './misc/cursorbox.js';
// FIXME: eventboxes are the wrong size
const PowermenuWidget = () => CenterBox({ const PowermenuWidget = () => CenterBox({
class_name: 'powermenu', class_name: 'powermenu',
// @ts-expect-error // @ts-expect-error
vertical: false, vertical: false,
start_widget: CursorBox({ start_widget: CursorBox({
class_name: 'shutdown', class_name: 'shutdown button',
on_primary_click_release: () => execAsync(['systemctl', 'poweroff']) on_primary_click_release: () => execAsync(['systemctl', 'poweroff'])
.catch(print), .catch(print),
@ -24,7 +23,7 @@ const PowermenuWidget = () => CenterBox({
}), }),
center_widget: CursorBox({ center_widget: CursorBox({
class_name: 'reboot', class_name: 'reboot button',
on_primary_click_release: () => execAsync(['systemctl', 'reboot']) on_primary_click_release: () => execAsync(['systemctl', 'reboot'])
.catch(print), .catch(print),
@ -34,7 +33,7 @@ const PowermenuWidget = () => CenterBox({
}), }),
end_widget: CursorBox({ end_widget: CursorBox({
class_name: 'logout', class_name: 'logout button',
on_primary_click_release: () => Hyprland.sendMessage('dispatch exit') on_primary_click_release: () => Hyprland.sendMessage('dispatch exit')
.catch(print), .catch(print),

View file

@ -21,7 +21,7 @@ export default () => Box({
Icon({ Icon({
size: 26, size: 26,
class_name: 'slider-label', class_name: 'slider-label',
binds: [['icon', SpeakerIcon, 'value']], icon: SpeakerIcon.bind(),
}), }),
Slider({ Slider({
@ -62,7 +62,7 @@ export default () => Box({
children: [ children: [
Icon({ Icon({
class_name: 'slider-label', class_name: 'slider-label',
binds: [['icon', Brightness, 'screen-icon']], icon: Brightness.bind('screenIcon'),
}), }),
Slider({ Slider({

View file

@ -14,7 +14,7 @@
min-height: 130px; min-height: 130px;
} }
button { .button {
margin: 5px 10px; margin: 5px 10px;
border-radius: 12px; border-radius: 12px;
min-width: 80px; min-width: 80px;

View file

@ -1,8 +1,10 @@
import App from 'resource:///com/github/Aylur/ags/app.js'; import App from 'resource:///com/github/Aylur/ags/app.js';
import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js'; import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import Service from 'resource:///com/github/Aylur/ags/service.js'; import Service from 'resource:///com/github/Aylur/ags/service.js';
import { subprocess } from 'resource:///com/github/Aylur/ags/utils.js'; import { subprocess } from 'resource:///com/github/Aylur/ags/utils.js';
import GUdev from 'gi://GUdev';
const { GUdev } = imports.gi;
const UDEV_POINTERS = [ const UDEV_POINTERS = [
'ID_INPUT_MOUSE', 'ID_INPUT_MOUSE',
@ -34,8 +36,10 @@ class Pointers extends Service {
}); });
} }
/** @type typeof imports.gi.Gio.Subprocess */
#process; #process;
#lastLine = ''; #lastLine = '';
/** @type Array<string> */
#pointers = []; #pointers = [];
#udevClient = GUdev.Client.new(['input']); #udevClient = GUdev.Client.new(['input']);
@ -60,7 +64,12 @@ class Pointers extends Service {
// FIXME: logitech mouse screws everything up on disconnect // FIXME: logitech mouse screws everything up on disconnect
#initUdevConnection() { #initUdevConnection() {
this.#getDevices(); this.#getDevices();
this.#udevClient.connect('uevent', (_, action) => { this.#udevClient.connect('uevent',
/**
* @param {typeof imports.gi.GUdev.Client} _
* @param {string} action
*/
(_, action) => {
if (action === 'add' || action === 'remove') { if (action === 'add' || action === 'remove') {
this.#getDevices(); this.#getDevices();
if (this.#process) { if (this.#process) {
@ -73,8 +82,12 @@ class Pointers extends Service {
#getDevices() { #getDevices() {
this.#pointers = []; this.#pointers = [];
this.#udevClient.query_by_subsystem('input').forEach((dev) => { this.#udevClient.query_by_subsystem('input').forEach(
const isPointer = UDEV_POINTERS.some((p) => dev.has_property(p)); /** @param {typeof imports.gi.GUdev.Device} dev */
(dev) => {
const isPointer = UDEV_POINTERS.some(
(p) => dev.has_property(p),
);
if (isPointer) { if (isPointer) {
const hasEventFile = dev.has_property('DEVNAME') && const hasEventFile = dev.has_property('DEVNAME') &&
@ -85,7 +98,8 @@ class Pointers extends Service {
this.#pointers.push(dev.get_property('DEVNAME')); this.#pointers.push(dev.get_property('DEVNAME'));
} }
} }
}); },
);
this.emit('device-fetched', true); this.emit('device-fetched', true);
} }
@ -123,7 +137,6 @@ class Pointers extends Service {
this.emit('new-line', output); this.emit('new-line', output);
} }
}, },
(err) => logError(err),
); );
this.emit('proc-started', true); this.emit('proc-started', true);
} }
@ -139,8 +152,11 @@ class Pointers extends Service {
#initAppConnection() { #initAppConnection() {
App.connect('window-toggled', () => { App.connect('window-toggled', () => {
const anyVisibleAndClosable = Array.from(App.windows).some((w) => { const anyVisibleAndClosable = Array.from(App.windows).some((w) => {
// @ts-expect-error
const closable = w[1].attribute?.close_on_unfocus && const closable = w[1].attribute?.close_on_unfocus &&
// @ts-expect-error
!(w[1].attribute?.close_on_unfocus === 'none' || !(w[1].attribute?.close_on_unfocus === 'none' ||
// @ts-expect-error
w[1].attribute?.close_on_unfocus === 'stay'); w[1].attribute?.close_on_unfocus === 'stay');
return w[1].visible && closable; return w[1].visible && closable;
@ -156,9 +172,12 @@ class Pointers extends Service {
}); });
} }
/** @param {string} clickStage */
static detectClickedOutside(clickStage) { static detectClickedOutside(clickStage) {
const toClose = Array.from(App.windows).some((w) => { const toClose = Array.from(App.windows).some((w) => {
// @ts-expect-error
const closable = (w[1].attribute?.close_on_unfocus && const closable = (w[1].attribute?.close_on_unfocus &&
// @ts-expect-error
w[1].attribute?.close_on_unfocus === clickStage); w[1].attribute?.close_on_unfocus === clickStage);
return w[1].visible && closable; return w[1].visible && closable;
@ -168,22 +187,31 @@ class Pointers extends Service {
return; return;
} }
Hyprland.sendMessage('j/layers').then((layers) => { Hyprland.sendMessage('j/layers').then((response) => {
layers = JSON.parse(layers); // /** @type import('types/service/hyprland').Layer */
const layers = JSON.parse(response);
Hyprland.sendMessage('j/cursorpos').then((pos) => { Hyprland.sendMessage('j/cursorpos').then((res) => {
pos = JSON.parse(pos); const pos = JSON.parse(res);
Object.values(layers).forEach((key) => { Object.values(layers).forEach((key) => {
const bar = key.levels['3'] const bar = key.levels['3'].find(
.find((n) => n.namespace === 'bar'); /** @param {{ namespace: string }} n */
(n) => n.namespace === 'bar',
);
const widgets = key.levels['3'].filter((n) => { const widgets = key.levels['3'].filter(
/** @param {{ namespace: string }} n */
(n) => {
const window = App.getWindow(n.namespace); const window = App.getWindow(n.namespace);
// @ts-expect-error
return window?.attribute?.close_on_unfocus && return window?.attribute?.close_on_unfocus &&
window?.attribute?.close_on_unfocus === clickStage; // @ts-expect-error
}); window?.attribute
?.close_on_unfocus === clickStage;
},
);
if (pos.x > bar?.x && pos.x < bar?.x + bar?.w && if (pos.x > bar?.x && pos.x < bar?.x + bar?.w &&
pos.y > bar?.y && pos.y < bar?.y + bar?.h) { pos.y > bar?.y && pos.y < bar?.y + bar?.h) {
@ -192,12 +220,22 @@ class Pointers extends Service {
// TODO: make this configurable // TODO: make this configurable
} }
else { else {
widgets.forEach((w) => { widgets.forEach(
/** @param {{
* namespace: string
* x: number
* y: number
* h: number
* w: number
* }} w
*/
(w) => {
if (!(pos.x > w.x && pos.x < w.x + w.w && if (!(pos.x > w.x && pos.x < w.x + w.w &&
pos.y > w.y && pos.y < w.y + w.h)) { pos.y > w.y && pos.y < w.y + w.h)) {
App.closeWindow(w.namespace); App.closeWindow(w.namespace);
} }
}); },
);
} }
}); });
}).catch(print); }).catch(print);

View file

@ -2,7 +2,6 @@ import Hyprland from 'resource:///com/github/Aylur/ags/service/hyprland.js';
import Service from 'resource:///com/github/Aylur/ags/service.js'; import Service from 'resource:///com/github/Aylur/ags/service.js';
import TouchGestures from './touch-gestures.js'; import TouchGestures from './touch-gestures.js';
import { execAsync, subprocess } from 'resource:///com/github/Aylur/ags/utils.js'; import { execAsync, subprocess } from 'resource:///com/github/Aylur/ags/utils.js';
import GUdev from 'gi://GUdev';
const ROTATION_MAP = { const ROTATION_MAP = {
'normal': 0, 'normal': 0,
@ -11,6 +10,10 @@ const ROTATION_MAP = {
'left-up': 1, 'left-up': 1,
}; };
const SCREEN = 'desc:BOE 0x0964'; const SCREEN = 'desc:BOE 0x0964';
const DEVICES = [
'wacom-hid-52eb-finger',
'wacom-hid-52eb-pen',
];
class Tablet extends Service { class Tablet extends Service {
@ -30,9 +33,10 @@ class Tablet extends Service {
#tabletMode = false; #tabletMode = false;
#oskState = false; #oskState = false;
/** @type typeof imports.gi.Gio.Subprocess */
#autorotate; #autorotate;
/** @type typeof imports.gi.Gio.Subprocess */
#blockedInputs; #blockedInputs;
#udevClient = GUdev.Client.new(['input']);
get tabletMode() { get tabletMode() {
return this.#tabletMode; return this.#tabletMode;
@ -44,7 +48,6 @@ class Tablet extends Service {
constructor() { constructor() {
super(); super();
this.#initUdevConnection();
this.#listenOskState(); this.#listenOskState();
} }
@ -59,8 +62,7 @@ class Tablet extends Service {
'--device', '/dev/input/by-path/platform-AMDI0010:02-event-mouse', '--device', '/dev/input/by-path/platform-AMDI0010:02-event-mouse',
'--device', '/dev/input/by-path/platform-thinkpad_acpi-event', '--device', '/dev/input/by-path/platform-thinkpad_acpi-event',
'--device', '/dev/video-bus'], '--device', '/dev/video-bus'],
() => { /**/ }, () => { /**/ });
(err) => logError(err));
this.emit('inputs-blocked', true); this.emit('inputs-blocked', true);
} }
@ -113,31 +115,6 @@ class Tablet extends Service {
this.emit('mode-toggled', true); this.emit('mode-toggled', true);
} }
#initUdevConnection() {
this.#getDevices();
this.#udevClient.connect('uevent', (_, action) => {
if (action === 'add' || action === 'remove') {
this.#getDevices();
}
});
}
#getDevices() {
this.devices = [];
Hyprland.sendMessage('j/devices').then((out) => {
const devices = JSON.parse(out);
devices.touch.forEach((dev) => {
this.devices.push(dev.name);
});
devices.tablets.forEach((dev) => {
this.devices.push(dev.name);
});
}).catch(print);
this.emit('device-fetched', true);
}
startAutorotate() { startAutorotate() {
if (this.#autorotate) { if (this.#autorotate) {
return; return;
@ -153,7 +130,7 @@ class Tablet extends Service {
`keyword monitor ${SCREEN},transform,${orientation}`, `keyword monitor ${SCREEN},transform,${orientation}`,
).catch(print); ).catch(print);
const batchRotate = this.devices.map((dev) => const batchRotate = DEVICES.map((dev) =>
`keyword device:${dev}:transform ${orientation}; `); `keyword device:${dev}:transform ${orientation}; `);
Hyprland.sendMessage(`[[BATCH]] ${batchRotate.flat()}`); Hyprland.sendMessage(`[[BATCH]] ${batchRotate.flat()}`);
@ -164,7 +141,6 @@ class Tablet extends Service {
} }
} }
}, },
(err) => logError(err),
); );
this.emit('autorotate-started', true); this.emit('autorotate-started', true);
} }
@ -182,11 +158,14 @@ class Tablet extends Service {
['bash', '-c', 'busctl monitor --user sm.puri.OSK0'], ['bash', '-c', 'busctl monitor --user sm.puri.OSK0'],
(output) => { (output) => {
if (output.includes('BOOLEAN')) { if (output.includes('BOOLEAN')) {
this.#oskState = output.match('true|false')[0] === 'true'; const match = output.match('true|false');
if (match) {
this.#oskState = match[0] === 'true';
this.emit('osk-toggled', this.#oskState); this.emit('osk-toggled', this.#oskState);
} }
}
}, },
(err) => logError(err),
); );
} }

View file

@ -44,12 +44,17 @@ class TouchGestures extends Service {
} }
#gestures = new Map(); #gestures = new Map();
/** @type typeof imports.gi.Gio.Subprocess */
#gestureDaemon; #gestureDaemon;
get gestures() { get gestures() {
return this.#gestures; return this.#gestures;
} }
get gestureDaemon() {
return this.#gestureDaemon;
}
addGesture({ addGesture({
name, name,
nFingers = '1', nFingers = '1',
@ -57,7 +62,7 @@ class TouchGestures extends Service {
edge = '*', edge = '*',
distance = '*', distance = '*',
command, command,
} = {}) { }) {
gesture = String(gesture).toUpperCase(); gesture = String(gesture).toUpperCase();
if (!GESTURE_VERIF.includes(gesture)) { if (!GESTURE_VERIF.includes(gesture)) {
logError('Wrong gesture id'); logError('Wrong gesture id');
@ -119,7 +124,6 @@ class TouchGestures extends Service {
this.#gestureDaemon = subprocess( this.#gestureDaemon = subprocess(
command, command,
() => { /**/ }, () => { /**/ },
(err) => logError(err),
); );
this.emit('daemon-started', true); this.emit('daemon-started', true);
} }