💥 fix(bar/focused-client): focused client is null after opening a new window on an empty workspace

AstalHyprland reports the focused client in a wrong way, so I decided to use a different way to get the focused client using colorshell's `Compositor` abstraction
This commit is contained in:
retrozinndev
2025-10-25 15:35:00 -03:00
parent 1478558bb6
commit 8e8ca30974
4 changed files with 88 additions and 60 deletions
+5 -6
View File
@@ -22,7 +22,7 @@ import { createBinding, createComputed, createRoot, getScope, Scope } from "ags"
import { OSDModes, triggerOSD } from "./window/osd"; import { OSDModes, triggerOSD } from "./window/osd";
import { programArgs, programInvocationName } from "system"; import { programArgs, programInvocationName } from "system";
import { setConsoleLogDomain } from "console"; import { setConsoleLogDomain } from "console";
import { createSubscription, encoder, secureBaseBinding } from "./modules/utils"; import { createScopedConnection, createSubscription, encoder, secureBaseBinding } from "./modules/utils";
import { exec } from "ags/process"; import { exec } from "ags/process";
import { NightLight } from "./modules/nightlight"; import { NightLight } from "./modules/nightlight";
import { Backlights } from "./modules/backlight"; import { Backlights } from "./modules/backlight";
@@ -209,8 +209,7 @@ you should use the socket in the XDG_RUNTIME_DIR/colorshell.sock for a faster re
); );
// handle communication via socket // handle communication via socket
this.#connections.set(this.#socketService, createScopedConnection(this.#socketService, "incoming", (conn) => {
this.#socketService.connect("incoming", (_, conn) => {
const inputStream = Gio.DataInputStream.new(conn.inputStream); const inputStream = Gio.DataInputStream.new(conn.inputStream);
inputStream.read_upto_async('\x00', -1, GLib.PRIORITY_DEFAULT, null, (_, res) => { inputStream.read_upto_async('\x00', -1, GLib.PRIORITY_DEFAULT, null, (_, res) => {
const [args, len] = inputStream.read_upto_finish(res); const [args, len] = inputStream.read_upto_finish(res);
@@ -259,18 +258,18 @@ you should use the socket in the XDG_RUNTIME_DIR/colorshell.sock for a faster re
}); });
return false; return false;
}) });
);
} }
private main(): void { private main(): void {
Gtk.init(); Gtk.init();
Adw.init(); Adw.init();
this.init();
createRoot((dispose) => { createRoot((dispose) => {
console.log(`Colorshell: Initializing things`); console.log(`Colorshell: Initializing things`);
this.#connections.set(this, this.connect("shutdown", () => dispose())); this.#connections.set(this, this.connect("shutdown", () => dispose()));
this.init();
this.#scope = getScope(); this.#scope = getScope();
NightLight.getDefault(); NightLight.getDefault();
+43 -8
View File
@@ -1,15 +1,23 @@
import { register } from "ags/gobject";
import { Compositors } from "."; import { Compositors } from ".";
import { createRoot } from "ags"; import { register } from "ags/gobject";
import { createRoot, getScope, Scope } from "ags";
import { createScopedConnection } from "../utils"; import { createScopedConnection } from "../utils";
import AstalHyprland from "gi://AstalHyprland"; import AstalHyprland from "gi://AstalHyprland";
type Event = "activewindow" | "activewindowv2"
| "workspace" | "workspacev2"
| "focusedmon" | "focusedmonv2";
@register({ GTypeName: "CompositorHyprland" }) @register({ GTypeName: "CompositorHyprland" })
export class CompositorHyprland extends Compositors.Compositor { export class CompositorHyprland extends Compositors.Compositor {
#scope: Scope;
hyprland: AstalHyprland.Hyprland; hyprland: AstalHyprland.Hyprland;
protected _focusedClient: Compositors.Client | null = null;
constructor() { constructor() {
super(); super();
@@ -19,16 +27,43 @@ export class CompositorHyprland extends Compositors.Compositor {
throw new Error(`Couldn't initialize CompositorHyprland: ${e}`); throw new Error(`Couldn't initialize CompositorHyprland: ${e}`);
} }
createRoot(() => { this.#scope = createRoot(() => {
createScopedConnection( createScopedConnection(
this.hyprland, "workspace-added", (hws) => { this.hyprland, "event", (e, args) => {
// check workspace existance switch(e as Event) {
if(this._workspaces.filter(w => w.id === hws.id)[0]) case "activewindowv2":
return; const address = args;
const clients = AstalHyprland.get_default().clients;
const focusedClient = clients.filter(c =>
c.address === address
)[0];
// TODO if(focusedClient) {
this._focusedClient = new Compositors.Client({
address: address,
class: focusedClient.class ?? "",
initialClass: focusedClient.initialClass ?? "",
mapped: focusedClient.mapped,
position: [focusedClient.x, focusedClient.y],
title: focusedClient.title ?? ""
});
this.notify("focused-client");
return;
}
this._focusedClient = null;
this.notify("focused-client");
break;
}
} }
); );
return getScope();
}); });
} }
vfunc_dispose(): void {
this.#scope.dispose();
}
} }
-2
View File
@@ -4,8 +4,6 @@ import GObject, { getter, gtype, property, register } from "ags/gobject";
import GLib from "gi://GLib?version=2.0"; import GLib from "gi://GLib?version=2.0";
export default Compositors;
/** WIP modular implementation of a system that supports implementing /** WIP modular implementation of a system that supports implementing
* a variety of Wayland Compositors * a variety of Wayland Compositors
* @todo implement more general compositor info + a lot of stuff * @todo implement more general compositor info + a lot of stuff
+2 -6
View File
@@ -1,16 +1,12 @@
import { CompositorHyprland } from "../../../modules/compositors/hyprland";
import { Gtk } from "ags/gtk4"; import { Gtk } from "ags/gtk4";
import { createBinding, With } from "ags"; import { createBinding, With } from "ags";
import { variableToBoolean } from "../../../modules/utils"; import { variableToBoolean } from "../../../modules/utils";
import { getAppIcon, getSymbolicIcon } from "../../../modules/apps"; import { getAppIcon, getSymbolicIcon } from "../../../modules/apps";
import Pango from "gi://Pango?version=1.0"; import Pango from "gi://Pango?version=1.0";
import AstalHyprland from "gi://AstalHyprland";
const hyprland = new CompositorHyprland;
const hyprland = AstalHyprland.get_default();
// Fix empty focused-client on opening a window on an empty workspace
hyprland.connect("notify::clients", () => hyprland.notify("focused-client"));
export const FocusedClient = () => { export const FocusedClient = () => {
const focusedClient = createBinding(hyprland, "focusedClient"); const focusedClient = createBinding(hyprland, "focusedClient");