Closed matt1432 closed 1 hour ago
I have the same problem.
could you provide a simple app.tsx
to repro?
import Hyprland from "gi://AstalHyprland"
App.start({
instanceName: "hyprland-segfault",
main() {
const hyprland = Hyprland.get_default()
return <window>
// make it crash
</window>
}
})
@Aylur I managed to break it down to about 200 lines. I figured out that I need both my current client widget and workspace indicator widget for the segfault to happen. The workspace indicator doesn't even need to be visible.
import { App, Astal, Gtk, Widget } from 'astal/gtk3';
import { bind, Variable } from 'astal';
import AstalApps from 'gi://AstalApps';
import AstalHyprland from 'gi://AstalHyprland';
const CurrentClient = () => {
const applications = AstalApps.Apps.new();
const hyprland = AstalHyprland.get_default();
const visibleIcon = Variable<boolean>(false);
const focusedIcon = Variable<string>('');
const focusedTitle = Variable<string>('');
let lastFocusedAddress: string | null;
const updateVars = () => setTimeout(() => {
const client = hyprland.get_focused_client();
lastFocusedAddress = client ? client.get_address() : null;
const app = applications.fuzzy_query(
client?.get_class() ?? '',
)[0];
const icon = app?.iconName;
if (icon) {
visibleIcon.set(true);
focusedIcon.set(icon);
}
else {
visibleIcon.set(false);
}
focusedTitle.set(client?.get_title() ?? '');
const id = client?.connect('notify::title', (c) => {
if (c.get_address() !== lastFocusedAddress) {
c.disconnect(id);
}
focusedTitle.set(c.get_title());
});
}, 5 * 10);
updateVars();
hyprland.connect('notify::focused-client', () => updateVars());
return (
<box
visible={bind(focusedTitle).as((title) => title !== '')}
>
<icon
visible={bind(visibleIcon)}
css="font-size: 32px;"
icon={bind(focusedIcon)}
/>
<label
label={bind(focusedTitle)}
truncate
/>
</box>
);
};
const Workspace = ({ id = 0 }) => {
const hyprland = AstalHyprland.get_default();
return (
<revealer name={id.toString()}>
<eventbox>
<box
setup={(self) => {
const update = (
_: Widget.Box,
_c?: AstalHyprland.Client,
) => {
const workspace = hyprland.get_workspace(id);
const occupied = workspace && workspace.get_clients().length > 0;
self.toggleClassName('occupied', occupied);
};
update(self);
self
.hook(hyprland, 'event', () => update(self))
// Deal with urgent windows
.hook(hyprland, 'urgent', update)
.hook(hyprland, 'notify::focused-workspace', () => {
if (hyprland.get_focused_workspace().get_id() === id) {
self.toggleClassName('urgent', false);
}
});
}}
/>
</eventbox>
</revealer>
);
};
const Workspaces = () => {
const Hyprland = AstalHyprland.get_default();
const L_PADDING = 2;
const WS_WIDTH = 30;
const updateHighlight = (self: Widget.Box) => {
const currentId = Hyprland.get_focused_workspace().get_id().toString();
const indicators = ((self.get_parent() as Widget.Overlay)
.child as Widget.Box)
.children as Widget.Revealer[];
const currentIndex = indicators.findIndex((w) => w.name === currentId);
if (currentIndex >= 0) {
self.css = `margin-left: ${L_PADDING + (currentIndex * WS_WIDTH)}px`;
}
};
const highlight = (
<box
className="button active"
valign={Gtk.Align.CENTER}
halign={Gtk.Align.START}
setup={(self) => {
self.hook(Hyprland, 'notify::focused-workspace', updateHighlight);
}}
/>
) as Widget.Box;
let workspaces: Widget.Revealer[] = [];
return (
<box>
<overlay overlay={highlight}>
<box
setup={(self) => {
const refresh = () => {
(self.children as Widget.Revealer[]).forEach((rev) => {
rev.reveal_child = false;
});
workspaces.forEach((ws) => {
ws.reveal_child = true;
});
};
const updateWorkspaces = () => {
Hyprland.get_workspaces().forEach((ws) => {
const currentWs = (self.children as Widget.Revealer[])
.find((ch) => ch.name === ws.id.toString());
if (!currentWs && ws.id > 0) {
self.add(Workspace({ id: ws.id }));
}
});
// Make sure the order is correct
workspaces.forEach((workspace, i) => {
(workspace.get_parent() as Widget.Box)
.reorder_child(workspace, i);
});
};
const updateAll = () => {
workspaces = (self.children as Widget.Revealer[])
.filter((ch) => {
return Hyprland.get_workspaces().find((ws) => {
return ws.id.toString() === ch.name;
});
})
.sort((a, b) => parseInt(a.name ?? '0') - parseInt(b.name ?? '0'));
updateWorkspaces();
refresh();
};
updateAll();
self.hook(Hyprland, 'event', updateAll);
}}
/>
</overlay>
</box>
);
};
App.start({
instanceName: 'hyprland-segfault',
main() {
return (
<window
exclusivity={Astal.Exclusivity.EXCLUSIVE}
anchor={
Astal.WindowAnchor.TOP |
Astal.WindowAnchor.LEFT |
Astal.WindowAnchor.RIGHT
}
>
<box hexpand halign={Gtk.Align.START}>
<Workspaces />
<CurrentClient />
</box>
</window>
);
},
});
It takes a few seconds or minutes for it to crash. I usually just spam close and open terminals, have a window change its title and change workspaces a few times and it'll crash
I think the error is in the get_client (at least for me). Because when I comented this line
self.toggleClassName("occupied", (hypr.get_workspace(i)?.get_clients().length || 0) > 0)
.
the program stopped crashing
Describe the bug AGS crashes after some time since f9a35261aeb8ea135ff0f279acbf2d12164e427a with the message:
error: signal: segmentation fault (core dumped)
.To Reproduce Here's my code for current Hyprland client: https://github.com/matt1432/nixos-configs/blob/master/nixosModules/ags/config/widgets/bar/items/current-client.tsx