💥 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:
+43
-44
@@ -22,7 +22,7 @@ import { createBinding, createComputed, createRoot, getScope, Scope } from "ags"
|
||||
import { OSDModes, triggerOSD } from "./window/osd";
|
||||
import { programArgs, programInvocationName } from "system";
|
||||
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 { NightLight } from "./modules/nightlight";
|
||||
import { Backlights } from "./modules/backlight";
|
||||
@@ -209,68 +209,67 @@ you should use the socket in the XDG_RUNTIME_DIR/colorshell.sock for a faster re
|
||||
);
|
||||
|
||||
// handle communication via socket
|
||||
this.#connections.set(this.#socketService,
|
||||
this.#socketService.connect("incoming", (_, conn) => {
|
||||
createScopedConnection(this.#socketService, "incoming", (conn) => {
|
||||
const inputStream = Gio.DataInputStream.new(conn.inputStream);
|
||||
inputStream.read_upto_async('\x00', -1, GLib.PRIORITY_DEFAULT, null, (_, res) => {
|
||||
const [args, len] = inputStream.read_upto_finish(res);
|
||||
inputStream.close(null);
|
||||
conn.inputStream.close(null);
|
||||
const [args, len] = inputStream.read_upto_finish(res);
|
||||
inputStream.close(null);
|
||||
conn.inputStream.close(null);
|
||||
|
||||
if(len < 1) {
|
||||
console.error(`Colorshell: No args provided via socket call`);
|
||||
return;
|
||||
}
|
||||
if(len < 1) {
|
||||
console.error(`Colorshell: No args provided via socket call`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const [success, parsedArgs] = GLib.shell_parse_argv(`colorshell ${args}`);
|
||||
parsedArgs?.splice(0, 1); // remove the unnecessary `colorshell` part
|
||||
try {
|
||||
const [success, parsedArgs] = GLib.shell_parse_argv(`colorshell ${args}`);
|
||||
parsedArgs?.splice(0, 1); // remove the unnecessary `colorshell` part
|
||||
|
||||
if(success) {
|
||||
handleArguments({
|
||||
print_literal: (msg) => conn.outputStream.write_bytes(
|
||||
encoder.encode(`${msg}\n`),
|
||||
null
|
||||
),
|
||||
// TODO: support writing to stderr(i don't know how to do that :sob:)
|
||||
printerr_literal: (msg) => conn.outputStream.write_bytes(
|
||||
encoder.encode(`${msg}\n`),
|
||||
null
|
||||
)
|
||||
}, parsedArgs!);
|
||||
|
||||
conn.outputStream.flush(null);
|
||||
conn.close(null);
|
||||
return;
|
||||
}
|
||||
|
||||
conn.outputStream.write_bytes(
|
||||
encoder.encode("Error: Unexpected error occurred on argument parsing!"),
|
||||
null
|
||||
);
|
||||
if(success) {
|
||||
handleArguments({
|
||||
print_literal: (msg) => conn.outputStream.write_bytes(
|
||||
encoder.encode(`${msg}\n`),
|
||||
null
|
||||
),
|
||||
// TODO: support writing to stderr(i don't know how to do that :sob:)
|
||||
printerr_literal: (msg) => conn.outputStream.write_bytes(
|
||||
encoder.encode(`${msg}\n`),
|
||||
null
|
||||
)
|
||||
}, parsedArgs!);
|
||||
|
||||
conn.outputStream.flush(null);
|
||||
conn.close(null);
|
||||
} catch(_e) {
|
||||
const e = _e as Error;
|
||||
console.error(`Colorshell: An error occurred while writing to socket output. Stderr:\n${
|
||||
e.message}\n${e.stack}`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
})
|
||||
);
|
||||
conn.outputStream.write_bytes(
|
||||
encoder.encode("Error: Unexpected error occurred on argument parsing!"),
|
||||
null
|
||||
);
|
||||
|
||||
conn.outputStream.flush(null);
|
||||
conn.close(null);
|
||||
} catch(_e) {
|
||||
const e = _e as Error;
|
||||
console.error(`Colorshell: An error occurred while writing to socket output. Stderr:\n${
|
||||
e.message}\n${e.stack}`);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private main(): void {
|
||||
Gtk.init();
|
||||
Adw.init();
|
||||
this.init();
|
||||
|
||||
createRoot((dispose) => {
|
||||
console.log(`Colorshell: Initializing things`);
|
||||
this.#connections.set(this, this.connect("shutdown", () => dispose()));
|
||||
|
||||
this.init();
|
||||
this.#scope = getScope();
|
||||
|
||||
NightLight.getDefault();
|
||||
|
||||
@@ -1,15 +1,23 @@
|
||||
import { register } from "ags/gobject";
|
||||
import { Compositors } from ".";
|
||||
import { createRoot } from "ags";
|
||||
import { register } from "ags/gobject";
|
||||
import { createRoot, getScope, Scope } from "ags";
|
||||
import { createScopedConnection } from "../utils";
|
||||
|
||||
|
||||
import AstalHyprland from "gi://AstalHyprland";
|
||||
|
||||
|
||||
type Event = "activewindow" | "activewindowv2"
|
||||
| "workspace" | "workspacev2"
|
||||
| "focusedmon" | "focusedmonv2";
|
||||
|
||||
@register({ GTypeName: "CompositorHyprland" })
|
||||
export class CompositorHyprland extends Compositors.Compositor {
|
||||
#scope: Scope;
|
||||
hyprland: AstalHyprland.Hyprland;
|
||||
|
||||
protected _focusedClient: Compositors.Client | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -19,16 +27,43 @@ export class CompositorHyprland extends Compositors.Compositor {
|
||||
throw new Error(`Couldn't initialize CompositorHyprland: ${e}`);
|
||||
}
|
||||
|
||||
createRoot(() => {
|
||||
this.#scope = createRoot(() => {
|
||||
createScopedConnection(
|
||||
this.hyprland, "workspace-added", (hws) => {
|
||||
// check workspace existance
|
||||
if(this._workspaces.filter(w => w.id === hws.id)[0])
|
||||
return;
|
||||
this.hyprland, "event", (e, args) => {
|
||||
switch(e as Event) {
|
||||
case "activewindowv2":
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import GObject, { getter, gtype, property, register } from "ags/gobject";
|
||||
import GLib from "gi://GLib?version=2.0";
|
||||
|
||||
|
||||
export default Compositors;
|
||||
|
||||
/** WIP modular implementation of a system that supports implementing
|
||||
* a variety of Wayland Compositors
|
||||
* @todo implement more general compositor info + a lot of stuff
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
import { CompositorHyprland } from "../../../modules/compositors/hyprland";
|
||||
import { Gtk } from "ags/gtk4";
|
||||
import { createBinding, With } from "ags";
|
||||
import { variableToBoolean } from "../../../modules/utils";
|
||||
import { getAppIcon, getSymbolicIcon } from "../../../modules/apps";
|
||||
|
||||
import Pango from "gi://Pango?version=1.0";
|
||||
import AstalHyprland from "gi://AstalHyprland";
|
||||
|
||||
|
||||
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"));
|
||||
const hyprland = new CompositorHyprland;
|
||||
|
||||
export const FocusedClient = () => {
|
||||
const focusedClient = createBinding(hyprland, "focusedClient");
|
||||
|
||||
Reference in New Issue
Block a user