Properly clean up resources on exit
This commit is contained in:
288
src/cursor.zig
288
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user