aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIsaac Freund <ifreund@ifreund.xyz>2020-04-18 12:21:43 +0200
committerIsaac Freund <ifreund@ifreund.xyz>2020-04-18 12:21:43 +0200
commitcd19a4615b822ab85ebe1430ec956f6b22f3d008 (patch)
treec3b9cd94870ecb8e37c5ca9d29cd1077c7820bef
parented7aca261bc51790334a0adc9e00df5eda6dbc83 (diff)
downloadriver-cd19a4615b822ab85ebe1430ec956f6b22f3d008.tar.gz
river-cd19a4615b822ab85ebe1430ec956f6b22f3d008.tar.xz
Properly clean up resources on exit
-rw-r--r--src/cursor.zig288
-rw-r--r--src/input_manager.zig7
-rw-r--r--src/keyboard.zig42
-rw-r--r--src/main.zig4
-rw-r--r--src/output.zig14
-rw-r--r--src/root.zig6
-rw-r--r--src/seat.zig13
-rw-r--r--src/server.zig6
-rw-r--r--src/view.zig6
9 files changed, 214 insertions, 172 deletions
diff --git a/src/cursor.zig b/src/cursor.zig
index 839276a..4ca999d 100644
--- a/src/cursor.zig
+++ b/src/cursor.zig
@@ -17,14 +17,6 @@ pub const Cursor = struct {
wlr_cursor: *c.wlr_cursor,
wlr_xcursor_manager: *c.wlr_xcursor_manager,
- listen_motion: c.wl_listener,
- listen_motion_absolute: c.wl_listener,
- listen_button: c.wl_listener,
- listen_axis: c.wl_listener,
- listen_frame: c.wl_listener,
-
- listen_request_set_cursor: c.wl_listener,
-
mode: CursorMode,
grabbed_view: ?*View,
grab_x: f64,
@@ -33,6 +25,13 @@ pub const Cursor = struct {
grab_height: c_int,
resize_edges: u32,
+ listen_axis: c.wl_listener,
+ listen_button: c.wl_listener,
+ listen_frame: c.wl_listener,
+ listen_motion_absolute: c.wl_listener,
+ listen_motion: c.wl_listener,
+ listen_request_set_cursor: c.wl_listener,
+
pub fn init(self: *Self, seat: *Seat) !void {
self.seat = seat;
@@ -68,31 +67,99 @@ pub const Cursor = struct {
// can choose how we want to process them, forwarding them to clients and
// moving the cursor around. See following post for more detail:
// https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
- self.listen_motion.notify = handleMotion;
- c.wl_signal_add(&self.wlr_cursor.events.motion, &self.listen_motion);
-
- self.listen_motion_absolute.notify = handleMotionAbsolute;
- c.wl_signal_add(&self.wlr_cursor.events.motion_absolute, &self.listen_motion_absolute);
+ self.listen_axis.notify = handleAxis;
+ c.wl_signal_add(&self.wlr_cursor.events.axis, &self.listen_axis);
self.listen_button.notify = handleButton;
c.wl_signal_add(&self.wlr_cursor.events.button, &self.listen_button);
- self.listen_axis.notify = handleAxis;
- c.wl_signal_add(&self.wlr_cursor.events.axis, &self.listen_axis);
-
self.listen_frame.notify = handleFrame;
c.wl_signal_add(&self.wlr_cursor.events.frame, &self.listen_frame);
- // This listens for clients requesting a specific cursor image
+ self.listen_motion_absolute.notify = handleMotionAbsolute;
+ c.wl_signal_add(&self.wlr_cursor.events.motion_absolute, &self.listen_motion_absolute);
+
+ self.listen_motion.notify = handleMotion;
+ c.wl_signal_add(&self.wlr_cursor.events.motion, &self.listen_motion);
+
self.listen_request_set_cursor.notify = handleRequestSetCursor;
c.wl_signal_add(&self.seat.wlr_seat.events.request_set_cursor, &self.listen_request_set_cursor);
}
- pub fn destroy(self: Self) void {
+ pub fn deinit(self: *Self) void {
c.wlr_xcursor_manager_destroy(self.wlr_xcursor_manager);
c.wlr_cursor_destroy(self.wlr_cursor);
}
+ fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is forwarded by the cursor when a pointer emits an axis event,
+ // for example when you move the scroll wheel.
+ const cursor = @fieldParentPtr(Cursor, "listen_axis", listener.?);
+ const event = @ptrCast(
+ *c.wlr_event_pointer_axis,
+ @alignCast(@alignOf(*c.wlr_event_pointer_axis), data),
+ );
+
+ // Notify the client with pointer focus of the axis event.
+ c.wlr_seat_pointer_notify_axis(
+ cursor.seat.wlr_seat,
+ event.time_msec,
+ event.orientation,
+ event.delta,
+ event.delta_discrete,
+ event.source,
+ );
+ }
+
+ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is forwarded by the cursor when a pointer emits a button
+ // event.
+ const cursor = @fieldParentPtr(Cursor, "listen_button", listener.?);
+ const event = @ptrCast(
+ *c.wlr_event_pointer_button,
+ @alignCast(@alignOf(*c.wlr_event_pointer_button), data),
+ );
+ // Notify the client with pointer focus that a button press has occurred
+ _ = c.wlr_seat_pointer_notify_button(
+ cursor.seat.wlr_seat,
+ event.time_msec,
+ event.button,
+ event.state,
+ );
+
+ var sx: f64 = undefined;
+ var sy: f64 = undefined;
+
+ var surface: ?*c.wlr_surface = null;
+ const view = cursor.seat.input_manager.server.root.viewAt(
+ cursor.wlr_cursor.x,
+ cursor.wlr_cursor.y,
+ &surface,
+ &sx,
+ &sy,
+ );
+
+ if (event.state == c.enum_wlr_button_state.WLR_BUTTON_RELEASED) {
+ // If you released any buttons, we exit interactive move/resize mode.
+ cursor.mode = CursorMode.Passthrough;
+ } else {
+ // Focus that client if the button was _pressed_
+ if (view) |v| {
+ cursor.seat.focus(v);
+ }
+ }
+ }
+
+ fn handleFrame(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is forwarded by the cursor when a pointer emits an frame
+ // event. Frame events are sent after regular pointer events to group
+ // multiple events together. For instance, two axis events may happen at the
+ // same time, in which case a frame event won't be sent in between.
+ const cursor = @fieldParentPtr(Cursor, "listen_frame", listener.?);
+ // Notify the client with pointer focus of the frame event.
+ c.wlr_seat_pointer_notify_frame(cursor.seat.wlr_seat);
+ }
+
fn processMove(self: Self, time: u32) void {
// Move the grabbed view to the new position.
// TODO: log on null
@@ -102,6 +169,64 @@ pub const Cursor = struct {
}
}
+ fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is forwarded by the cursor when a pointer emits an _absolute_
+ // motion event, from 0..1 on each axis. This happens, for example, when
+ // wlroots is running under a Wayland window rather than KMS+DRM, and you
+ // move the mouse over the window. You could enter the window from any edge,
+ // so we have to warp the mouse there. There is also some hardware which
+ // emits these events.
+ const cursor = @fieldParentPtr(Cursor, "listen_motion_absolute", listener.?);
+ const event = @ptrCast(
+ *c.wlr_event_pointer_motion_absolute,
+ @alignCast(@alignOf(*c.wlr_event_pointer_motion_absolute), data),
+ );
+ c.wlr_cursor_warp_absolute(cursor.wlr_cursor, event.device, event.x, event.y);
+ cursor.processMotion(event.time_msec);
+ }
+
+ fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is forwarded by the cursor when a pointer emits a _relative_
+ // pointer motion event (i.e. a delta)
+ const cursor = @fieldParentPtr(Cursor, "listen_motion", listener.?);
+ const event = @ptrCast(
+ *c.wlr_event_pointer_motion,
+ @alignCast(@alignOf(*c.wlr_event_pointer_motion), data),
+ );
+ // The cursor doesn't move unless we tell it to. The cursor automatically
+ // handles constraining the motion to the output layout, as well as any
+ // special configuration applied for the specific input device which
+ // generated the event. You can pass NULL for the device if you want to move
+ // the cursor around without any input.
+ c.wlr_cursor_move(cursor.wlr_cursor, event.device, event.delta_x, event.delta_y);
+ cursor.processMotion(event.time_msec);
+ }
+
+ fn handleRequestSetCursor(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is rasied by the seat when a client provides a cursor image
+ const cursor = @fieldParentPtr(Cursor, "listen_request_set_cursor", listener.?);
+ const event = @ptrCast(
+ *c.wlr_seat_pointer_request_set_cursor_event,
+ @alignCast(@alignOf(*c.wlr_seat_pointer_request_set_cursor_event), data),
+ );
+ const focused_client = cursor.seat.wlr_seat.pointer_state.focused_client;
+
+ // This can be sent by any client, so we check to make sure this one is
+ // actually has pointer focus first.
+ if (focused_client == event.seat_client) {
+ // Once we've vetted the client, we can tell the cursor to use the
+ // provided surface as the cursor image. It will set the hardware cursor
+ // on the output that it's currently on and continue to do so as the
+ // cursor moves between outputs.
+ c.wlr_cursor_set_surface(
+ cursor.wlr_cursor,
+ event.surface,
+ event.hotspot_x,
+ event.hotspot_y,
+ );
+ }
+ }
+
fn processsResize(self: Self, time: u32) void {
// Resizing the grabbed view can be a little bit complicated, because we
// could be resizing from any corner or edge. This not only resizes the view
@@ -205,131 +330,4 @@ pub const Cursor = struct {
c.wlr_seat_pointer_clear_focus(wlr_seat);
}
}
-
- fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits a _relative_
- // pointer motion event (i.e. a delta)
- const cursor = @fieldParentPtr(Cursor, "listen_motion", listener.?);
- const event = @ptrCast(
- *c.wlr_event_pointer_motion,
- @alignCast(@alignOf(*c.wlr_event_pointer_motion), data),
- );
- // The cursor doesn't move unless we tell it to. The cursor automatically
- // handles constraining the motion to the output layout, as well as any
- // special configuration applied for the specific input device which
- // generated the event. You can pass NULL for the device if you want to move
- // the cursor around without any input.
- c.wlr_cursor_move(cursor.wlr_cursor, event.device, event.delta_x, event.delta_y);
- cursor.processMotion(event.time_msec);
- }
-
- fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits an _absolute_
- // motion event, from 0..1 on each axis. This happens, for example, when
- // wlroots is running under a Wayland window rather than KMS+DRM, and you
- // move the mouse over the window. You could enter the window from any edge,
- // so we have to warp the mouse there. There is also some hardware which
- // emits these events.
- const cursor = @fieldParentPtr(Cursor, "listen_motion_absolute", listener.?);
- const event = @ptrCast(
- *c.wlr_event_pointer_motion_absolute,
- @alignCast(@alignOf(*c.wlr_event_pointer_motion_absolute), data),
- );
- c.wlr_cursor_warp_absolute(cursor.wlr_cursor, event.device, event.x, event.y);
- cursor.processMotion(event.time_msec);
- }
-
- fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits a button
- // event.
- const cursor = @fieldParentPtr(Cursor, "listen_button", listener.?);
- const event = @ptrCast(
- *c.wlr_event_pointer_button,
- @alignCast(@alignOf(*c.wlr_event_pointer_button), data),
- );
- // Notify the client with pointer focus that a button press has occurred
- _ = c.wlr_seat_pointer_notify_button(
- cursor.seat.wlr_seat,
- event.time_msec,
- event.button,
- event.state,
- );
-
- var sx: f64 = undefined;
- var sy: f64 = undefined;
-
- var surface: ?*c.wlr_surface = null;
- const view = cursor.seat.input_manager.server.root.viewAt(
- cursor.wlr_cursor.x,
- cursor.wlr_cursor.y,
- &surface,
- &sx,
- &sy,
- );
-
- if (event.state == c.enum_wlr_button_state.WLR_BUTTON_RELEASED) {
- // If you released any buttons, we exit interactive move/resize mode.
- cursor.mode = CursorMode.Passthrough;
- } else {
- // Focus that client if the button was _pressed_
- if (view) |v| {
- cursor.seat.focus(v);
- }
- }
- }
-
- fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits an axis event,
- // for example when you move the scroll wheel.
- const cursor = @fieldParentPtr(Cursor, "listen_axis", listener.?);
- const event = @ptrCast(
- *c.wlr_event_pointer_axis,
- @alignCast(@alignOf(*c.wlr_event_pointer_axis), data),
- );
-
- // Notify the client with pointer focus of the axis event.
- c.wlr_seat_pointer_notify_axis(
- cursor.seat.wlr_seat,
- event.time_msec,
- event.orientation,
- event.delta,
- event.delta_discrete,
- event.source,
- );
- }
-
- fn handleFrame(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is forwarded by the cursor when a pointer emits an frame
- // event. Frame events are sent after regular pointer events to group
- // multiple events together. For instance, two axis events may happen at the
- // same time, in which case a frame event won't be sent in between.
- const cursor = @fieldParentPtr(Cursor, "listen_frame", listener.?);
- // Notify the client with pointer focus of the frame event.
- c.wlr_seat_pointer_notify_frame(cursor.seat.wlr_seat);
- }
-
- fn handleRequestSetCursor(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is rasied by the seat when a client provides a cursor image
- const cursor = @fieldParentPtr(Cursor, "listen_request_set_cursor", listener.?);
- const event = @ptrCast(
- *c.wlr_seat_pointer_request_set_cursor_event,
- @alignCast(@alignOf(*c.wlr_seat_pointer_request_set_cursor_event), data),
- );
- const focused_client = cursor.seat.wlr_seat.pointer_state.focused_client;
-
- // This can be sent by any client, so we check to make sure this one is
- // actually has pointer focus first.
- if (focused_client == event.seat_client) {
- // Once we've vetted the client, we can tell the cursor to use the
- // provided surface as the cursor image. It will set the hardware cursor
- // on the output that it's currently on and continue to do so as the
- // cursor moves between outputs.
- c.wlr_cursor_set_surface(
- cursor.wlr_cursor,
- event.surface,
- event.hotspot_x,
- event.hotspot_y,
- );
- }
- }
};
diff --git a/src/input_manager.zig b/src/input_manager.zig
index 6f4d7ae..d2c11db 100644
--- a/src/input_manager.zig
+++ b/src/input_manager.zig
@@ -32,6 +32,13 @@ pub const InputManager = struct {
c.wl_signal_add(&self.server.wlr_backend.events.new_input, &self.listen_new_input);
}
+ pub fn deinit(self: *Self) void {
+ while (self.seats.pop()) |seat_node| {
+ seat_node.data.deinit();
+ self.server.allocator.destroy(seat_node);
+ }
+ }
+
/// Must be called whenever a view is unmapped.
pub fn handleViewUnmap(self: Self, view: *View) void {
var it = self.seats.first;
diff --git a/src/keyboard.zig b/src/keyboard.zig
index 7542a29..f270a22 100644
--- a/src/keyboard.zig
+++ b/src/keyboard.zig
@@ -11,8 +11,8 @@ pub const Keyboard = struct {
device: *c.wlr_input_device,
wlr_keyboard: *c.wlr_keyboard,
- listen_modifiers: c.wl_listener,
listen_key: c.wl_listener,
+ listen_modifiers: c.wl_listener,
pub fn init(self: *Self, seat: *Seat, device: *c.wlr_input_device) !void {
self.seat = seat;
@@ -45,29 +45,11 @@ pub const Keyboard = struct {
c.wlr_keyboard_set_repeat_info(self.wlr_keyboard, 25, 600);
// Setup listeners for keyboard events
- self.listen_modifiers.notify = handleModifiers;
- c.wl_signal_add(&self.wlr_keyboard.events.modifiers, &self.listen_modifiers);
-
self.listen_key.notify = handleKey;
c.wl_signal_add(&self.wlr_keyboard.events.key, &self.listen_key);
- }
-
- fn handleModifiers(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
- // This event is raised when a modifier key, such as shift or alt, is
- // pressed. We simply communicate this to the client. */
- const keyboard = @fieldParentPtr(Keyboard, "listen_modifiers", listener.?);
- // A seat can only have one keyboard, but this is a limitation of the
- // Wayland protocol - not wlroots. We assign all connected keyboards to the
- // same seat. You can swap out the underlying wlr_keyboard like this and
- // wlr_seat handles this transparently.
- c.wlr_seat_set_keyboard(keyboard.seat.wlr_seat, keyboard.device);
-
- // Send modifiers to the client.
- c.wlr_seat_keyboard_notify_modifiers(
- keyboard.seat.wlr_seat,
- &keyboard.wlr_keyboard.modifiers,
- );
+ self.listen_modifiers.notify = handleModifiers;
+ c.wl_signal_add(&self.wlr_keyboard.events.modifiers, &self.listen_modifiers);
}
fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
@@ -144,6 +126,24 @@ pub const Keyboard = struct {
}
}
+ fn handleModifiers(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
+ // This event is raised when a modifier key, such as shift or alt, is
+ // pressed. We simply communicate this to the client. */
+ const keyboard = @fieldParentPtr(Keyboard, "listen_modifiers", listener.?);
+
+ // A seat can only have one keyboard, but this is a limitation of the
+ // Wayland protocol - not wlroots. We assign all connected keyboards to the
+ // same seat. You can swap out the underlying wlr_keyboard like this and
+ // wlr_seat handles this transparently.
+ c.wlr_seat_set_keyboard(keyboard.seat.wlr_seat, keyboard.device);
+
+ // Send modifiers to the client.
+ c.wlr_seat_keyboard_notify_modifiers(
+ keyboard.seat.wlr_seat,
+ &keyboard.wlr_keyboard.modifiers,
+ );
+ }
+
/// Handle any builtin, harcoded compsitor bindings such as VT switching.
/// Returns true if the keysym was handled.
fn handleBuiltinKeybind(self: Self, keysym: c.xkb_keysym_t) bool {
diff --git a/src/main.zig b/src/main.zig
index ec9d5ca..9b581b2 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -12,11 +12,13 @@ pub fn main() !void {
var server: Server = undefined;
try server.init(std.heap.c_allocator);
- defer server.destroy();
+ defer server.deinit();
try server.start();
Log.Info.log("Running server...", .{});
server.run();
+
+ Log.Info.log("Shutting down server", .{});
}
diff --git a/src/output.zig b/src/output.zig
index e951fe3..bb88156 100644
--- a/src/output.zig
+++ b/src/output.zig
@@ -101,6 +101,20 @@ pub const Output = struct {
}
}
+ pub fn deinit(self: *Self) void {
+ for (self.layers) |*layer| {
+ while (layer.pop()) |layer_surface_node| {
+ self.root.server.allocator.destroy(layer_surface_node);
+ }
+ }
+
+ while (self.views.first) |node| {
+ node.view.deinit();
+ self.views.remove(node);
+ self.root.server.allocator.destroy(node);
+ }
+ }
+
/// Add a new view to the output. arrangeViews() will be called by the view
/// when it is mapped.
pub fn addView(self: *Self, wlr_xdg_surface: *c.wlr_xdg_surface) void {
diff --git a/src/root.zig b/src/root.zig
index eb7b0f3..7246577 100644
--- a/src/root.zig
+++ b/src/root.zig
@@ -50,7 +50,11 @@ pub const Root = struct {
self.transaction_timer = null;
}
- pub fn destroy(self: Self) void {
+ pub fn deinit(self: *Self) void {
+ while (self.outputs.pop()) |output_node| {
+ output_node.data.deinit();
+ self.server.allocator.destroy(output_node);
+ }
c.wlr_output_layout_destroy(self.wlr_output_layout);
}
diff --git a/src/seat.zig b/src/seat.zig
index dc25802..e011579 100644
--- a/src/seat.zig
+++ b/src/seat.zig
@@ -50,8 +50,17 @@ pub const Seat = struct {
self.focus_stack.init();
}
- pub fn destroy(self: Self) void {
- self.cursor.destroy();
+ pub fn deinit(self: *Self) void {
+ self.cursor.deinit();
+
+ while (self.keyboards.pop()) |node| {
+ self.input_manager.server.allocator.destroy(node);
+ }
+
+ while (self.focus_stack.first) |node| {
+ self.focus_stack.remove(node);
+ self.input_manager.server.allocator.destroy(node);
+ }
}
/// Set the current focus. If a visible view is passed it will be focused.
diff --git a/src/server.zig b/src/server.zig
index df1beb3..8371860 100644
--- a/src/server.zig
+++ b/src/server.zig
@@ -99,10 +99,12 @@ pub const Server = struct {
}
/// Free allocated memory and clean up
- pub fn destroy(self: Self) void {
+ pub fn deinit(self: *Self) void {
+ // Note: order is important here
c.wl_display_destroy_clients(self.wl_display);
c.wl_display_destroy(self.wl_display);
- self.root.destroy();
+ self.input_manager.deinit();
+ self.root.deinit();
}
/// Create the socket, set WAYLAND_DISPLAY, and start the backend
diff --git a/src/view.zig b/src/view.zig
index 6af7fe2..ea6fdc0 100644
--- a/src/view.zig
+++ b/src/view.zig
@@ -70,6 +70,12 @@ pub const View = struct {
c.wl_signal_add(&self.wlr_xdg_surface.events.unmap, &self.listen_unmap);
}
+ pub fn deinit(self: *Self) void {
+ if (self.stashed_buffer) |buffer| {
+ c.wlr_buffer_unref(buffer);
+ }
+ }
+
pub fn needsConfigure(self: Self) bool {
if (self.pending_box) |pending_box| {
return pending_box.width != self.current_box.width or