135 lines
3.6 KiB
Zig
135 lines
3.6 KiB
Zig
const std = @import("std");
|
|
|
|
const Action = @import("action.zig").Action;
|
|
const c = ffi.c;
|
|
const ffi = @import("ffi.zig");
|
|
|
|
pub const GuiState = struct {
|
|
child: ?*std.ChildProcess = null,
|
|
alloc: std.mem.Allocator,
|
|
/// Allocator used to allocate userdata that will be cleared at the
|
|
/// end of the application lifespan
|
|
user_data_arena: std.mem.Allocator,
|
|
};
|
|
|
|
pub fn activate(app: *c.GtkApplication, state: *GuiState) callconv(.C) void {
|
|
const win = c.gtk_application_window_new(app);
|
|
c.gtk_window_set_title(@ptrCast(*c.GtkWindow, win), "gpower2");
|
|
c.gtk_window_set_modal(@ptrCast(*c.GtkWindow, win), 1);
|
|
c.gtk_window_set_resizable(@ptrCast(*c.GtkWindow, win), 0);
|
|
c.gtk_window_set_icon_name(@ptrCast(*c.GtkWindow, win), "system-shutdown");
|
|
|
|
const eck = c.gtk_event_controller_key_new();
|
|
c.gtk_widget_add_controller(win, eck);
|
|
ffi.connectSignal(
|
|
eck,
|
|
"key-pressed",
|
|
@ptrCast(c.GCallback, handleKeypress),
|
|
@ptrCast(*c.GtkWindow, win),
|
|
);
|
|
|
|
const content = c.gtk_box_new(c.GTK_ORIENTATION_HORIZONTAL, 20);
|
|
c.gtk_window_set_child(@ptrCast(*c.GtkWindow, win), content);
|
|
inline for (.{ .top, .bottom, .start, .end }) |fun| {
|
|
@field(c, "gtk_widget_set_margin_" ++ @tagName(fun))(content, 20);
|
|
}
|
|
|
|
for ([_]Action{
|
|
.shutdown,
|
|
.reboot,
|
|
.@"suspend",
|
|
.hibernate,
|
|
}) |action| {
|
|
c.gtk_box_append(
|
|
@ptrCast(*c.GtkBox, content),
|
|
powerButton(state, @ptrCast(*c.GtkWindow, win), action),
|
|
);
|
|
}
|
|
|
|
c.gtk_widget_show(win);
|
|
}
|
|
|
|
const ButtonHandlerData = struct {
|
|
state: *GuiState,
|
|
action: Action,
|
|
win: *c.GtkWindow,
|
|
};
|
|
|
|
fn powerButton(
|
|
state: *GuiState,
|
|
win: *c.GtkWindow,
|
|
action: Action,
|
|
) *c.GtkWidget {
|
|
const text = switch (action) {
|
|
.shutdown => "Shutdown",
|
|
.reboot => "Reboot",
|
|
.@"suspend" => "Suspend",
|
|
.hibernate => "Hibernate",
|
|
};
|
|
|
|
const icon = switch (action) {
|
|
.shutdown => "system-shutdown",
|
|
.reboot => "system-reboot",
|
|
.@"suspend" => "system-suspend",
|
|
.hibernate => "system-hibernate",
|
|
};
|
|
|
|
var udata = state.user_data_arena.create(ButtonHandlerData) catch @panic("Failed to allocate button handler data!!");
|
|
udata.* = ButtonHandlerData{
|
|
.state = state,
|
|
.win = win,
|
|
.action = action,
|
|
};
|
|
|
|
const container = c.gtk_box_new(c.GTK_ORIENTATION_VERTICAL, 2);
|
|
|
|
const button = c.gtk_button_new();
|
|
c.gtk_box_append(@ptrCast(*c.GtkBox, container), button);
|
|
|
|
ffi.connectSignal(button, "clicked", @ptrCast(c.GCallback, handleClick), udata);
|
|
|
|
const image = c.gtk_image_new_from_icon_name(icon);
|
|
c.gtk_button_set_child(@ptrCast(*c.GtkButton, button), image);
|
|
|
|
c.gtk_image_set_pixel_size(@ptrCast(*c.GtkImage, image), 60);
|
|
|
|
const label = c.gtk_label_new(text);
|
|
c.gtk_box_append(@ptrCast(*c.GtkBox, container), label);
|
|
|
|
return container;
|
|
}
|
|
|
|
fn handleClick(
|
|
btn: *c.GtkButton,
|
|
udata: *ButtonHandlerData,
|
|
) void {
|
|
_ = btn;
|
|
_ = udata;
|
|
|
|
udata.action.execute(&udata.state.child, udata.state.alloc) catch |e| {
|
|
// TODO: error dialog
|
|
std.log.err("Error spawning child: {}", .{e});
|
|
};
|
|
|
|
c.gtk_window_close(udata.win);
|
|
}
|
|
|
|
fn handleKeypress(
|
|
eck: *c.GtkEventControllerKey,
|
|
keyval: c.guint,
|
|
keycode: c.guint,
|
|
state: c.GdkModifierType,
|
|
win: *c.GtkWindow,
|
|
) c.gboolean {
|
|
_ = eck;
|
|
_ = keycode;
|
|
_ = state;
|
|
|
|
if (keyval == c.GDK_KEY_Escape) {
|
|
c.gtk_window_close(win);
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|