Make Cursor a toplevel struct
This commit is contained in:
		
							
								
								
									
										667
									
								
								src/cursor.zig
									
									
									
									
									
								
							
							
						
						
									
										667
									
								
								src/cursor.zig
									
									
									
									
									
								
							@ -1,4 +1,7 @@
 | 
				
			|||||||
 | 
					const Self = @This();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const std = @import("std");
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const c = @import("c.zig");
 | 
					const c = @import("c.zig");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LayerSurface = @import("layer_surface.zig");
 | 
					const LayerSurface = @import("layer_surface.zig");
 | 
				
			||||||
@ -14,374 +17,370 @@ const CursorMode = enum {
 | 
				
			|||||||
    Resize,
 | 
					    Resize,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
pub const Cursor = struct {
 | 
					seat: *Seat,
 | 
				
			||||||
    const Self = @This();
 | 
					wlr_cursor: *c.wlr_cursor,
 | 
				
			||||||
 | 
					wlr_xcursor_manager: *c.wlr_xcursor_manager,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    seat: *Seat,
 | 
					mode: CursorMode,
 | 
				
			||||||
    wlr_cursor: *c.wlr_cursor,
 | 
					grabbed_view: ?*View,
 | 
				
			||||||
    wlr_xcursor_manager: *c.wlr_xcursor_manager,
 | 
					grab_x: f64,
 | 
				
			||||||
 | 
					grab_y: f64,
 | 
				
			||||||
 | 
					grab_width: c_int,
 | 
				
			||||||
 | 
					grab_height: c_int,
 | 
				
			||||||
 | 
					resize_edges: u32,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mode: CursorMode,
 | 
					listen_axis: c.wl_listener,
 | 
				
			||||||
    grabbed_view: ?*View,
 | 
					listen_button: c.wl_listener,
 | 
				
			||||||
    grab_x: f64,
 | 
					listen_frame: c.wl_listener,
 | 
				
			||||||
    grab_y: f64,
 | 
					listen_motion_absolute: c.wl_listener,
 | 
				
			||||||
    grab_width: c_int,
 | 
					listen_motion: c.wl_listener,
 | 
				
			||||||
    grab_height: c_int,
 | 
					listen_request_set_cursor: c.wl_listener,
 | 
				
			||||||
    resize_edges: u32,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    listen_axis: c.wl_listener,
 | 
					pub fn init(self: *Self, seat: *Seat) !void {
 | 
				
			||||||
    listen_button: c.wl_listener,
 | 
					    self.seat = seat;
 | 
				
			||||||
    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 {
 | 
					    // Creates a wlroots utility for tracking the cursor image shown on screen.
 | 
				
			||||||
        self.seat = seat;
 | 
					    //
 | 
				
			||||||
 | 
					    // TODO: free this, it allocates!
 | 
				
			||||||
 | 
					    self.wlr_cursor = c.wlr_cursor_create() orelse
 | 
				
			||||||
 | 
					        return error.CantCreateWlrCursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Creates a wlroots utility for tracking the cursor image shown on screen.
 | 
					    // Creates an xcursor manager, another wlroots utility which loads up
 | 
				
			||||||
        //
 | 
					    // Xcursor themes to source cursor images from and makes sure that cursor
 | 
				
			||||||
        // TODO: free this, it allocates!
 | 
					    // images are available at all scale factors on the screen (necessary for
 | 
				
			||||||
        self.wlr_cursor = c.wlr_cursor_create() orelse
 | 
					    // HiDPI support). We add a cursor theme at scale factor 1 to begin with.
 | 
				
			||||||
            return error.CantCreateWlrCursor;
 | 
					    //
 | 
				
			||||||
 | 
					    // TODO: free this, it allocates!
 | 
				
			||||||
 | 
					    self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, 24) orelse
 | 
				
			||||||
 | 
					        return error.CantCreateWlrXCursorManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Creates an xcursor manager, another wlroots utility which loads up
 | 
					    c.wlr_cursor_attach_output_layout(self.wlr_cursor, seat.input_manager.server.root.wlr_output_layout);
 | 
				
			||||||
        // Xcursor themes to source cursor images from and makes sure that cursor
 | 
					    _ = c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, 1);
 | 
				
			||||||
        // images are available at all scale factors on the screen (necessary for
 | 
					 | 
				
			||||||
        // HiDPI support). We add a cursor theme at scale factor 1 to begin with.
 | 
					 | 
				
			||||||
        //
 | 
					 | 
				
			||||||
        // TODO: free this, it allocates!
 | 
					 | 
				
			||||||
        self.wlr_xcursor_manager = c.wlr_xcursor_manager_create(null, 24) orelse
 | 
					 | 
				
			||||||
            return error.CantCreateWlrXCursorManager;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        c.wlr_cursor_attach_output_layout(self.wlr_cursor, seat.input_manager.server.root.wlr_output_layout);
 | 
					    self.mode = CursorMode.Passthrough;
 | 
				
			||||||
        _ = c.wlr_xcursor_manager_load(self.wlr_xcursor_manager, 1);
 | 
					    self.grabbed_view = null;
 | 
				
			||||||
 | 
					    self.grab_x = 0.0;
 | 
				
			||||||
 | 
					    self.grab_y = 0.0;
 | 
				
			||||||
 | 
					    self.grab_width = 0;
 | 
				
			||||||
 | 
					    self.grab_height = 0;
 | 
				
			||||||
 | 
					    self.resize_edges = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.mode = CursorMode.Passthrough;
 | 
					    // wlr_cursor *only* displays an image on screen. It does not move around
 | 
				
			||||||
        self.grabbed_view = null;
 | 
					    // when the pointer moves. However, we can attach input devices to it, and
 | 
				
			||||||
        self.grab_x = 0.0;
 | 
					    // it will generate aggregate events for all of them. In these events, we
 | 
				
			||||||
        self.grab_y = 0.0;
 | 
					    // can choose how we want to process them, forwarding them to clients and
 | 
				
			||||||
        self.grab_width = 0;
 | 
					    // moving the cursor around. See following post for more detail:
 | 
				
			||||||
        self.grab_height = 0;
 | 
					    // https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
 | 
				
			||||||
        self.resize_edges = 0;
 | 
					    self.listen_axis.notify = handleAxis;
 | 
				
			||||||
 | 
					    c.wl_signal_add(&self.wlr_cursor.events.axis, &self.listen_axis);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // wlr_cursor *only* displays an image on screen. It does not move around
 | 
					    self.listen_button.notify = handleButton;
 | 
				
			||||||
        // when the pointer moves. However, we can attach input devices to it, and
 | 
					    c.wl_signal_add(&self.wlr_cursor.events.button, &self.listen_button);
 | 
				
			||||||
        // it will generate aggregate events for all of them. In these events, we
 | 
					 | 
				
			||||||
        // 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_axis.notify = handleAxis;
 | 
					 | 
				
			||||||
        c.wl_signal_add(&self.wlr_cursor.events.axis, &self.listen_axis);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.listen_button.notify = handleButton;
 | 
					    self.listen_frame.notify = handleFrame;
 | 
				
			||||||
        c.wl_signal_add(&self.wlr_cursor.events.button, &self.listen_button);
 | 
					    c.wl_signal_add(&self.wlr_cursor.events.frame, &self.listen_frame);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.listen_frame.notify = handleFrame;
 | 
					    self.listen_motion_absolute.notify = handleMotionAbsolute;
 | 
				
			||||||
        c.wl_signal_add(&self.wlr_cursor.events.frame, &self.listen_frame);
 | 
					    c.wl_signal_add(&self.wlr_cursor.events.motion_absolute, &self.listen_motion_absolute);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.listen_motion_absolute.notify = handleMotionAbsolute;
 | 
					    self.listen_motion.notify = handleMotion;
 | 
				
			||||||
        c.wl_signal_add(&self.wlr_cursor.events.motion_absolute, &self.listen_motion_absolute);
 | 
					    c.wl_signal_add(&self.wlr_cursor.events.motion, &self.listen_motion);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.listen_motion.notify = handleMotion;
 | 
					    self.listen_request_set_cursor.notify = handleRequestSetCursor;
 | 
				
			||||||
        c.wl_signal_add(&self.wlr_cursor.events.motion, &self.listen_motion);
 | 
					    c.wl_signal_add(&self.seat.wlr_seat.events.request_set_cursor, &self.listen_request_set_cursor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.listen_request_set_cursor.notify = handleRequestSetCursor;
 | 
					pub fn deinit(self: *Self) void {
 | 
				
			||||||
        c.wl_signal_add(&self.seat.wlr_seat.events.request_set_cursor, &self.listen_request_set_cursor);
 | 
					    c.wlr_xcursor_manager_destroy(self.wlr_xcursor_manager);
 | 
				
			||||||
    }
 | 
					    c.wlr_cursor_destroy(self.wlr_cursor);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn deinit(self: *Self) void {
 | 
					fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
				
			||||||
        c.wlr_xcursor_manager_destroy(self.wlr_xcursor_manager);
 | 
					    // This event is forwarded by the cursor when a pointer emits an axis event,
 | 
				
			||||||
        c.wlr_cursor_destroy(self.wlr_cursor);
 | 
					    // for example when you move the scroll wheel.
 | 
				
			||||||
    }
 | 
					    const cursor = @fieldParentPtr(Self, "listen_axis", listener.?);
 | 
				
			||||||
 | 
					    const event = @ptrCast(
 | 
				
			||||||
 | 
					        *c.wlr_event_pointer_axis,
 | 
				
			||||||
 | 
					        @alignCast(@alignOf(*c.wlr_event_pointer_axis), data),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
					    // Notify the client with pointer focus of the axis event.
 | 
				
			||||||
        // This event is forwarded by the cursor when a pointer emits an axis event,
 | 
					    c.wlr_seat_pointer_notify_axis(
 | 
				
			||||||
        // for example when you move the scroll wheel.
 | 
					        cursor.seat.wlr_seat,
 | 
				
			||||||
        const cursor = @fieldParentPtr(Self, "listen_axis", listener.?);
 | 
					        event.time_msec,
 | 
				
			||||||
        const event = @ptrCast(
 | 
					        event.orientation,
 | 
				
			||||||
            *c.wlr_event_pointer_axis,
 | 
					        event.delta,
 | 
				
			||||||
            @alignCast(@alignOf(*c.wlr_event_pointer_axis), data),
 | 
					        event.delta_discrete,
 | 
				
			||||||
        );
 | 
					        event.source,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Notify the client with pointer focus of the axis event.
 | 
					fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
				
			||||||
        c.wlr_seat_pointer_notify_axis(
 | 
					    // This event is forwarded by the cursor when a pointer emits a button
 | 
				
			||||||
            cursor.seat.wlr_seat,
 | 
					    // event.
 | 
				
			||||||
 | 
					    const self = @fieldParentPtr(Self, "listen_button", listener.?);
 | 
				
			||||||
 | 
					    const event = @ptrCast(
 | 
				
			||||||
 | 
					        *c.wlr_event_pointer_button,
 | 
				
			||||||
 | 
					        @alignCast(@alignOf(*c.wlr_event_pointer_button), data),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					    var sx: f64 = undefined;
 | 
				
			||||||
 | 
					    var sy: f64 = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
 | 
				
			||||||
 | 
					        // If the found surface is a keyboard inteactive layer surface,
 | 
				
			||||||
 | 
					        // give it keyboard focus.
 | 
				
			||||||
 | 
					        if (c.wlr_surface_is_layer_surface(wlr_surface)) {
 | 
				
			||||||
 | 
					            const wlr_layer_surface = c.wlr_layer_surface_v1_from_wlr_surface(wlr_surface);
 | 
				
			||||||
 | 
					            if (wlr_layer_surface.*.current.keyboard_interactive) {
 | 
				
			||||||
 | 
					                const layer_surface = @ptrCast(
 | 
				
			||||||
 | 
					                    *LayerSurface,
 | 
				
			||||||
 | 
					                    @alignCast(@alignOf(*LayerSurface), wlr_layer_surface.*.data),
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					                self.seat.setFocusRaw(.{ .layer = layer_surface });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If the found surface is an xdg toplevel surface, send keyboard
 | 
				
			||||||
 | 
					        // focus to the view.
 | 
				
			||||||
 | 
					        if (c.wlr_surface_is_xdg_surface(wlr_surface)) {
 | 
				
			||||||
 | 
					            const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(wlr_surface);
 | 
				
			||||||
 | 
					            if (wlr_xdg_surface.*.role == .WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
				
			||||||
 | 
					                const view = @ptrCast(*View, @alignCast(@alignOf(*View), wlr_xdg_surface.*.data));
 | 
				
			||||||
 | 
					                self.seat.focus(view);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        _ = c.wlr_seat_pointer_notify_button(
 | 
				
			||||||
 | 
					            self.seat.wlr_seat,
 | 
				
			||||||
            event.time_msec,
 | 
					            event.time_msec,
 | 
				
			||||||
            event.orientation,
 | 
					            event.button,
 | 
				
			||||||
            event.delta,
 | 
					            event.state,
 | 
				
			||||||
            event.delta_discrete,
 | 
					 | 
				
			||||||
            event.source,
 | 
					 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
					fn handleFrame(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
				
			||||||
        // This event is forwarded by the cursor when a pointer emits a button
 | 
					    // This event is forwarded by the cursor when a pointer emits an frame
 | 
				
			||||||
        // event.
 | 
					    // event. Frame events are sent after regular pointer events to group
 | 
				
			||||||
        const self = @fieldParentPtr(Self, "listen_button", listener.?);
 | 
					    // multiple events together. For instance, two axis events may happen at the
 | 
				
			||||||
        const event = @ptrCast(
 | 
					    // same time, in which case a frame event won't be sent in between.
 | 
				
			||||||
            *c.wlr_event_pointer_button,
 | 
					    const self = @fieldParentPtr(Self, "listen_frame", listener.?);
 | 
				
			||||||
            @alignCast(@alignOf(*c.wlr_event_pointer_button), data),
 | 
					    // Notify the client with pointer focus of the frame event.
 | 
				
			||||||
        );
 | 
					    c.wlr_seat_pointer_notify_frame(self.seat.wlr_seat);
 | 
				
			||||||
        var sx: f64 = undefined;
 | 
					}
 | 
				
			||||||
        var sy: f64 = undefined;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
 | 
					fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
				
			||||||
            // If the found surface is a keyboard inteactive layer surface,
 | 
					    // This event is forwarded by the cursor when a pointer emits an _absolute_
 | 
				
			||||||
            // give it keyboard focus.
 | 
					    // motion event, from 0..1 on each axis. This happens, for example, when
 | 
				
			||||||
            if (c.wlr_surface_is_layer_surface(wlr_surface)) {
 | 
					    // wlroots is running under a Wayland window rather than KMS+DRM, and you
 | 
				
			||||||
                const wlr_layer_surface = c.wlr_layer_surface_v1_from_wlr_surface(wlr_surface);
 | 
					    // move the mouse over the window. You could enter the window from any edge,
 | 
				
			||||||
                if (wlr_layer_surface.*.current.keyboard_interactive) {
 | 
					    // so we have to warp the mouse there. There is also some hardware which
 | 
				
			||||||
                    const layer_surface = @ptrCast(
 | 
					    // emits these events.
 | 
				
			||||||
                        *LayerSurface,
 | 
					    const self = @fieldParentPtr(Self, "listen_motion_absolute", listener.?);
 | 
				
			||||||
                        @alignCast(@alignOf(*LayerSurface), wlr_layer_surface.*.data),
 | 
					    const event = @ptrCast(
 | 
				
			||||||
                    );
 | 
					        *c.wlr_event_pointer_motion_absolute,
 | 
				
			||||||
                    self.seat.setFocusRaw(.{ .layer = layer_surface });
 | 
					        @alignCast(@alignOf(*c.wlr_event_pointer_motion_absolute), data),
 | 
				
			||||||
                }
 | 
					    );
 | 
				
			||||||
            }
 | 
					    c.wlr_cursor_warp_absolute(self.wlr_cursor, event.device, event.x, event.y);
 | 
				
			||||||
 | 
					    self.processMotion(event.time_msec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // If the found surface is an xdg toplevel surface, send keyboard
 | 
					fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
				
			||||||
            // focus to the view.
 | 
					    // This event is forwarded by the cursor when a pointer emits a _relative_
 | 
				
			||||||
            if (c.wlr_surface_is_xdg_surface(wlr_surface)) {
 | 
					    // pointer motion event (i.e. a delta)
 | 
				
			||||||
                const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(wlr_surface);
 | 
					    const self = @fieldParentPtr(Self, "listen_motion", listener.?);
 | 
				
			||||||
                if (wlr_xdg_surface.*.role == .WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
 | 
					    const event = @ptrCast(
 | 
				
			||||||
                    const view = @ptrCast(*View, @alignCast(@alignOf(*View), wlr_xdg_surface.*.data));
 | 
					        *c.wlr_event_pointer_motion,
 | 
				
			||||||
                    self.seat.focus(view);
 | 
					        @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(self.wlr_cursor, event.device, event.delta_x, event.delta_y);
 | 
				
			||||||
 | 
					    self.processMotion(event.time_msec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _ = c.wlr_seat_pointer_notify_button(
 | 
					fn handleRequestSetCursor(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
				
			||||||
                self.seat.wlr_seat,
 | 
					    // This event is rasied by the seat when a client provides a cursor image
 | 
				
			||||||
                event.time_msec,
 | 
					    const self = @fieldParentPtr(Self, "listen_request_set_cursor", listener.?);
 | 
				
			||||||
                event.button,
 | 
					    const event = @ptrCast(
 | 
				
			||||||
                event.state,
 | 
					        *c.wlr_seat_pointer_request_set_cursor_event,
 | 
				
			||||||
            );
 | 
					        @alignCast(@alignOf(*c.wlr_seat_pointer_request_set_cursor_event), data),
 | 
				
			||||||
        }
 | 
					    );
 | 
				
			||||||
    }
 | 
					    const focused_client = self.seat.wlr_seat.pointer_state.focused_client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fn handleFrame(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 | 
					    // This can be sent by any client, so we check to make sure this one is
 | 
				
			||||||
        // This event is forwarded by the cursor when a pointer emits an frame
 | 
					    // actually has pointer focus first.
 | 
				
			||||||
        // event. Frame events are sent after regular pointer events to group
 | 
					    if (focused_client == event.seat_client) {
 | 
				
			||||||
        // multiple events together. For instance, two axis events may happen at the
 | 
					        // Once we've vetted the client, we can tell the cursor to use the
 | 
				
			||||||
        // same time, in which case a frame event won't be sent in between.
 | 
					        // provided surface as the cursor image. It will set the hardware cursor
 | 
				
			||||||
        const self = @fieldParentPtr(Self, "listen_frame", listener.?);
 | 
					        // on the output that it's currently on and continue to do so as the
 | 
				
			||||||
        // Notify the client with pointer focus of the frame event.
 | 
					        // cursor moves between outputs.
 | 
				
			||||||
        c.wlr_seat_pointer_notify_frame(self.seat.wlr_seat);
 | 
					        c.wlr_cursor_set_surface(
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    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 self = @fieldParentPtr(Self, "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(self.wlr_cursor, event.device, event.x, event.y);
 | 
					 | 
				
			||||||
        self.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 self = @fieldParentPtr(Self, "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(self.wlr_cursor, event.device, event.delta_x, event.delta_y);
 | 
					 | 
				
			||||||
        self.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 self = @fieldParentPtr(Self, "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 = self.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(
 | 
					 | 
				
			||||||
                self.wlr_cursor,
 | 
					 | 
				
			||||||
                event.surface,
 | 
					 | 
				
			||||||
                event.hotspot_x,
 | 
					 | 
				
			||||||
                event.hotspot_y,
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fn processMotion(self: Self, time: u32) void {
 | 
					 | 
				
			||||||
        var sx: f64 = undefined;
 | 
					 | 
				
			||||||
        var sy: f64 = undefined;
 | 
					 | 
				
			||||||
        if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
 | 
					 | 
				
			||||||
            // "Enter" the surface if necessary. This lets the client know that the
 | 
					 | 
				
			||||||
            // cursor has entered one of its surfaces.
 | 
					 | 
				
			||||||
            //
 | 
					 | 
				
			||||||
            // Note that this gives the surface "pointer focus", which is distinct
 | 
					 | 
				
			||||||
            // from keyboard focus. You get pointer focus by moving the pointer over
 | 
					 | 
				
			||||||
            // a window.
 | 
					 | 
				
			||||||
            if (self.seat.input_manager.inputAllowed(wlr_surface)) {
 | 
					 | 
				
			||||||
                const wlr_seat = self.seat.wlr_seat;
 | 
					 | 
				
			||||||
                const focus_change = wlr_seat.pointer_state.focused_surface != wlr_surface;
 | 
					 | 
				
			||||||
                if (focus_change) {
 | 
					 | 
				
			||||||
                    Log.Debug.log("Pointer notify enter at ({},{})", .{ sx, sy });
 | 
					 | 
				
			||||||
                    c.wlr_seat_pointer_notify_enter(wlr_seat, wlr_surface, sx, sy);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    // The enter event contains coordinates, so we only need to notify
 | 
					 | 
				
			||||||
                    // on motion if the focus did not change.
 | 
					 | 
				
			||||||
                    c.wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // There is either no surface under the cursor or input is disallowed
 | 
					 | 
				
			||||||
        // Reset the cursor image to the default
 | 
					 | 
				
			||||||
        c.wlr_xcursor_manager_set_cursor_image(
 | 
					 | 
				
			||||||
            self.wlr_xcursor_manager,
 | 
					 | 
				
			||||||
            "left_ptr",
 | 
					 | 
				
			||||||
            self.wlr_cursor,
 | 
					            self.wlr_cursor,
 | 
				
			||||||
 | 
					            event.surface,
 | 
				
			||||||
 | 
					            event.hotspot_x,
 | 
				
			||||||
 | 
					            event.hotspot_y,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        // Clear pointer focus so future button events and such are not sent to
 | 
					    }
 | 
				
			||||||
        // the last client to have the cursor over it.
 | 
					}
 | 
				
			||||||
        c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
 | 
					
 | 
				
			||||||
 | 
					fn processMotion(self: Self, time: u32) void {
 | 
				
			||||||
 | 
					    var sx: f64 = undefined;
 | 
				
			||||||
 | 
					    var sy: f64 = undefined;
 | 
				
			||||||
 | 
					    if (self.surfaceAt(self.wlr_cursor.x, self.wlr_cursor.y, &sx, &sy)) |wlr_surface| {
 | 
				
			||||||
 | 
					        // "Enter" the surface if necessary. This lets the client know that the
 | 
				
			||||||
 | 
					        // cursor has entered one of its surfaces.
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // Note that this gives the surface "pointer focus", which is distinct
 | 
				
			||||||
 | 
					        // from keyboard focus. You get pointer focus by moving the pointer over
 | 
				
			||||||
 | 
					        // a window.
 | 
				
			||||||
 | 
					        if (self.seat.input_manager.inputAllowed(wlr_surface)) {
 | 
				
			||||||
 | 
					            const wlr_seat = self.seat.wlr_seat;
 | 
				
			||||||
 | 
					            const focus_change = wlr_seat.pointer_state.focused_surface != wlr_surface;
 | 
				
			||||||
 | 
					            if (focus_change) {
 | 
				
			||||||
 | 
					                Log.Debug.log("Pointer notify enter at ({},{})", .{ sx, sy });
 | 
				
			||||||
 | 
					                c.wlr_seat_pointer_notify_enter(wlr_seat, wlr_surface, sx, sy);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // The enter event contains coordinates, so we only need to notify
 | 
				
			||||||
 | 
					                // on motion if the focus did not change.
 | 
				
			||||||
 | 
					                c.wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Find the topmost surface under the output layout coordinates lx/ly
 | 
					    // There is either no surface under the cursor or input is disallowed
 | 
				
			||||||
    /// returns the surface if found and sets the sx/sy parametes to the
 | 
					    // Reset the cursor image to the default
 | 
				
			||||||
    /// surface coordinates.
 | 
					    c.wlr_xcursor_manager_set_cursor_image(
 | 
				
			||||||
    fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
 | 
					        self.wlr_xcursor_manager,
 | 
				
			||||||
        // Find the output to check
 | 
					        "left_ptr",
 | 
				
			||||||
        const root = self.seat.input_manager.server.root;
 | 
					        self.wlr_cursor,
 | 
				
			||||||
        const wlr_output = c.wlr_output_layout_output_at(root.wlr_output_layout, lx, ly) orelse
 | 
					    );
 | 
				
			||||||
            return null;
 | 
					    // Clear pointer focus so future button events and such are not sent to
 | 
				
			||||||
        const output = @ptrCast(
 | 
					    // the last client to have the cursor over it.
 | 
				
			||||||
            *Output,
 | 
					    c.wlr_seat_pointer_clear_focus(self.seat.wlr_seat);
 | 
				
			||||||
            @alignCast(@alignOf(*Output), wlr_output.*.data orelse return null),
 | 
					}
 | 
				
			||||||
        );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Get output-local coords from the layout coords
 | 
					 | 
				
			||||||
        var ox = lx;
 | 
					 | 
				
			||||||
        var oy = ly;
 | 
					 | 
				
			||||||
        c.wlr_output_layout_output_coords(root.wlr_output_layout, wlr_output, &ox, &oy);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check layers and views from top to bottom
 | 
					 | 
				
			||||||
        const layer_idxs = [_]usize{
 | 
					 | 
				
			||||||
            c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
 | 
					 | 
				
			||||||
            c.ZWLR_LAYER_SHELL_V1_LAYER_TOP,
 | 
					 | 
				
			||||||
            c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM,
 | 
					 | 
				
			||||||
            c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check overlay layer incl. popups
 | 
					 | 
				
			||||||
        if (layerSurfaceAt(output.*, output.layers[layer_idxs[0]], ox, oy, sx, sy, false)) |surface| {
 | 
					 | 
				
			||||||
            return surface;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check top-background popups only
 | 
					 | 
				
			||||||
        for (layer_idxs[1..4]) |layer_idx| {
 | 
					 | 
				
			||||||
            if (layerSurfaceAt(output.*, output.layers[layer_idx], ox, oy, sx, sy, true)) |surface| {
 | 
					 | 
				
			||||||
                return surface;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check top layer
 | 
					 | 
				
			||||||
        if (layerSurfaceAt(output.*, output.layers[layer_idxs[1]], ox, oy, sx, sy, false)) |surface| {
 | 
					 | 
				
			||||||
            return surface;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check floating views then normal views
 | 
					 | 
				
			||||||
        if (viewSurfaceAt(output.*, ox, oy, sx, sy, true)) |surface| {
 | 
					 | 
				
			||||||
            return surface;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (viewSurfaceAt(output.*, ox, oy, sx, sy, false)) |surface| {
 | 
					 | 
				
			||||||
            return surface;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Check the bottom-background layers
 | 
					 | 
				
			||||||
        for (layer_idxs[2..4]) |layer_idx| {
 | 
					 | 
				
			||||||
            if (layerSurfaceAt(output.*, output.layers[layer_idx], ox, oy, sx, sy, false)) |surface| {
 | 
					 | 
				
			||||||
                return surface;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Find the topmost surface under the output layout coordinates lx/ly
 | 
				
			||||||
 | 
					/// returns the surface if found and sets the sx/sy parametes to the
 | 
				
			||||||
 | 
					/// surface coordinates.
 | 
				
			||||||
 | 
					fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
 | 
				
			||||||
 | 
					    // Find the output to check
 | 
				
			||||||
 | 
					    const root = self.seat.input_manager.server.root;
 | 
				
			||||||
 | 
					    const wlr_output = c.wlr_output_layout_output_at(root.wlr_output_layout, lx, ly) orelse
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
 | 
					    const output = @ptrCast(
 | 
				
			||||||
 | 
					        *Output,
 | 
				
			||||||
 | 
					        @alignCast(@alignOf(*Output), wlr_output.*.data orelse return null),
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Get output-local coords from the layout coords
 | 
				
			||||||
 | 
					    var ox = lx;
 | 
				
			||||||
 | 
					    var oy = ly;
 | 
				
			||||||
 | 
					    c.wlr_output_layout_output_coords(root.wlr_output_layout, wlr_output, &ox, &oy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check layers and views from top to bottom
 | 
				
			||||||
 | 
					    const layer_idxs = [_]usize{
 | 
				
			||||||
 | 
					        c.ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
 | 
				
			||||||
 | 
					        c.ZWLR_LAYER_SHELL_V1_LAYER_TOP,
 | 
				
			||||||
 | 
					        c.ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM,
 | 
				
			||||||
 | 
					        c.ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND,
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check overlay layer incl. popups
 | 
				
			||||||
 | 
					    if (layerSurfaceAt(output.*, output.layers[layer_idxs[0]], ox, oy, sx, sy, false)) |surface| {
 | 
				
			||||||
 | 
					        return surface;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /// Find the topmost surface on the given layer at ox,oy. Will only check
 | 
					    // Check top-background popups only
 | 
				
			||||||
    /// popups if popups_only is true.
 | 
					    for (layer_idxs[1..4]) |layer_idx| {
 | 
				
			||||||
    fn layerSurfaceAt(
 | 
					        if (layerSurfaceAt(output.*, output.layers[layer_idx], ox, oy, sx, sy, true)) |surface| {
 | 
				
			||||||
        output: Output,
 | 
					            return surface;
 | 
				
			||||||
        layer: std.TailQueue(LayerSurface),
 | 
					        }
 | 
				
			||||||
        ox: f64,
 | 
					    }
 | 
				
			||||||
        oy: f64,
 | 
					
 | 
				
			||||||
        sx: *f64,
 | 
					    // Check top layer
 | 
				
			||||||
        sy: *f64,
 | 
					    if (layerSurfaceAt(output.*, output.layers[layer_idxs[1]], ox, oy, sx, sy, false)) |surface| {
 | 
				
			||||||
        popups_only: bool,
 | 
					        return surface;
 | 
				
			||||||
    ) ?*c.wlr_surface {
 | 
					    }
 | 
				
			||||||
        var it = layer.first;
 | 
					
 | 
				
			||||||
        while (it) |node| : (it = node.next) {
 | 
					    // Check floating views then normal views
 | 
				
			||||||
            const layer_surface = &node.data;
 | 
					    if (viewSurfaceAt(output.*, ox, oy, sx, sy, true)) |surface| {
 | 
				
			||||||
            const surface = c.wlr_layer_surface_v1_surface_at(
 | 
					        return surface;
 | 
				
			||||||
                layer_surface.wlr_layer_surface,
 | 
					    }
 | 
				
			||||||
                ox - @intToFloat(f64, layer_surface.box.x),
 | 
					    if (viewSurfaceAt(output.*, ox, oy, sx, sy, false)) |surface| {
 | 
				
			||||||
                oy - @intToFloat(f64, layer_surface.box.y),
 | 
					        return surface;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check the bottom-background layers
 | 
				
			||||||
 | 
					    for (layer_idxs[2..4]) |layer_idx| {
 | 
				
			||||||
 | 
					        if (layerSurfaceAt(output.*, output.layers[layer_idx], ox, oy, sx, sy, false)) |surface| {
 | 
				
			||||||
 | 
					            return surface;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Find the topmost surface on the given layer at ox,oy. Will only check
 | 
				
			||||||
 | 
					/// popups if popups_only is true.
 | 
				
			||||||
 | 
					fn layerSurfaceAt(
 | 
				
			||||||
 | 
					    output: Output,
 | 
				
			||||||
 | 
					    layer: std.TailQueue(LayerSurface),
 | 
				
			||||||
 | 
					    ox: f64,
 | 
				
			||||||
 | 
					    oy: f64,
 | 
				
			||||||
 | 
					    sx: *f64,
 | 
				
			||||||
 | 
					    sy: *f64,
 | 
				
			||||||
 | 
					    popups_only: bool,
 | 
				
			||||||
 | 
					) ?*c.wlr_surface {
 | 
				
			||||||
 | 
					    var it = layer.first;
 | 
				
			||||||
 | 
					    while (it) |node| : (it = node.next) {
 | 
				
			||||||
 | 
					        const layer_surface = &node.data;
 | 
				
			||||||
 | 
					        const surface = c.wlr_layer_surface_v1_surface_at(
 | 
				
			||||||
 | 
					            layer_surface.wlr_layer_surface,
 | 
				
			||||||
 | 
					            ox - @intToFloat(f64, layer_surface.box.x),
 | 
				
			||||||
 | 
					            oy - @intToFloat(f64, layer_surface.box.y),
 | 
				
			||||||
 | 
					            sx,
 | 
				
			||||||
 | 
					            sy,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        if (surface) |found| {
 | 
				
			||||||
 | 
					            if (!popups_only) {
 | 
				
			||||||
 | 
					                return found;
 | 
				
			||||||
 | 
					            } else if (c.wlr_surface_is_xdg_surface(found)) {
 | 
				
			||||||
 | 
					                const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(found);
 | 
				
			||||||
 | 
					                if (wlr_xdg_surface.*.role == .WLR_XDG_SURFACE_ROLE_POPUP) {
 | 
				
			||||||
 | 
					                    return found;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Find the topmost visible view surface (incl. popups) at ox,oy. Will
 | 
				
			||||||
 | 
					/// check only floating views if floating is true.
 | 
				
			||||||
 | 
					fn viewSurfaceAt(output: Output, ox: f64, oy: f64, sx: *f64, sy: *f64, floating: bool) ?*c.wlr_surface {
 | 
				
			||||||
 | 
					    var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags);
 | 
				
			||||||
 | 
					    while (it.next()) |node| {
 | 
				
			||||||
 | 
					        const view = &node.view;
 | 
				
			||||||
 | 
					        if (view.floating != floating) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        const surface = switch (view.impl) {
 | 
				
			||||||
 | 
					            .xdg_toplevel => |xdg_toplevel| c.wlr_xdg_surface_surface_at(
 | 
				
			||||||
 | 
					                xdg_toplevel.wlr_xdg_surface,
 | 
				
			||||||
 | 
					                ox - @intToFloat(f64, view.current_box.x),
 | 
				
			||||||
 | 
					                oy - @intToFloat(f64, view.current_box.y),
 | 
				
			||||||
                sx,
 | 
					                sx,
 | 
				
			||||||
                sy,
 | 
					                sy,
 | 
				
			||||||
            );
 | 
					            ),
 | 
				
			||||||
            if (surface) |found| {
 | 
					        };
 | 
				
			||||||
                if (!popups_only) {
 | 
					        if (surface) |found| {
 | 
				
			||||||
                    return found;
 | 
					            return found;
 | 
				
			||||||
                } else if (c.wlr_surface_is_xdg_surface(found)) {
 | 
					 | 
				
			||||||
                    const wlr_xdg_surface = c.wlr_xdg_surface_from_wlr_surface(found);
 | 
					 | 
				
			||||||
                    if (wlr_xdg_surface.*.role == .WLR_XDG_SURFACE_ROLE_POPUP) {
 | 
					 | 
				
			||||||
                        return found;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
    /// Find the topmost visible view surface (incl. popups) at ox,oy. Will
 | 
					}
 | 
				
			||||||
    /// check only floating views if floating is true.
 | 
					 | 
				
			||||||
    fn viewSurfaceAt(output: Output, ox: f64, oy: f64, sx: *f64, sy: *f64, floating: bool) ?*c.wlr_surface {
 | 
					 | 
				
			||||||
        var it = ViewStack(View).iterator(output.views.first, output.current_focused_tags);
 | 
					 | 
				
			||||||
        while (it.next()) |node| {
 | 
					 | 
				
			||||||
            const view = &node.view;
 | 
					 | 
				
			||||||
            if (view.floating != floating) {
 | 
					 | 
				
			||||||
                continue;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            const surface = switch (view.impl) {
 | 
					 | 
				
			||||||
                .xdg_toplevel => |xdg_toplevel| c.wlr_xdg_surface_surface_at(
 | 
					 | 
				
			||||||
                    xdg_toplevel.wlr_xdg_surface,
 | 
					 | 
				
			||||||
                    ox - @intToFloat(f64, view.current_box.x),
 | 
					 | 
				
			||||||
                    oy - @intToFloat(f64, view.current_box.y),
 | 
					 | 
				
			||||||
                    sx,
 | 
					 | 
				
			||||||
                    sy,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            };
 | 
					 | 
				
			||||||
            if (surface) |found| {
 | 
					 | 
				
			||||||
                return found;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -3,7 +3,7 @@ const Self = @This();
 | 
				
			|||||||
const c = @import("c.zig");
 | 
					const c = @import("c.zig");
 | 
				
			||||||
const std = @import("std");
 | 
					const std = @import("std");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Cursor = @import("cursor.zig").Cursor;
 | 
					const Cursor = @import("cursor.zig");
 | 
				
			||||||
const InputManager = @import("input_manager.zig");
 | 
					const InputManager = @import("input_manager.zig");
 | 
				
			||||||
const Keyboard = @import("keyboard.zig").Keyboard;
 | 
					const Keyboard = @import("keyboard.zig").Keyboard;
 | 
				
			||||||
const LayerSurface = @import("layer_surface.zig");
 | 
					const LayerSurface = @import("layer_surface.zig");
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user