diff --git a/include/render.c b/include/render.c index 36e42c3..c757fa0 100644 --- a/include/render.c +++ b/include/render.c @@ -2,14 +2,14 @@ #include #include -struct wlr_backend *wlr_backend_autocreate(struct wl_display *display) { +struct wlr_backend *zag_wlr_backend_autocreate(struct wl_display *display) { return wlr_backend_autocreate(display, NULL); } -struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend) { +struct wlr_renderer *zag_wlr_backend_get_renderer(struct wlr_backend *backend) { return wlr_backend_get_renderer(backend); } -bool wlr_backend_start(struct wlr_backend *backend) { +bool zag_wlr_backend_start(struct wlr_backend *backend) { return wlr_backend_start(backend); } diff --git a/include/render.h b/include/render.h index 3ac3cf2..a30d6e6 100644 --- a/include/render.h +++ b/include/render.h @@ -18,9 +18,9 @@ struct wlr_backend { } events; }; -struct wlr_backend *wlr_backend_autocreate(struct wl_display *display); -struct wlr_renderer *wlr_backend_get_renderer(struct wlr_backend *backend); -bool wlr_backend_start(struct wlr_backend *backend); +struct wlr_backend *zag_wlr_backend_autocreate(struct wl_display *display); +struct wlr_renderer *zag_wlr_backend_get_renderer(struct wlr_backend *backend); +bool zag_wlr_backend_start(struct wlr_backend *backend); #endif diff --git a/src/c.zig b/src/c.zig index 5169a17..3073111 100644 --- a/src/c.zig +++ b/src/c.zig @@ -1,5 +1,3 @@ -// Functions that couldn't be automatically translated - pub const c = @cImport({ @cDefine("WLR_USE_UNSTABLE", {}); @cInclude("time.h"); @@ -22,11 +20,7 @@ pub const c = @cImport({ @cInclude("wlr/util/log.h"); @cInclude("xkbcommon/xkbcommon.h"); + // Contains a subset of functions from wlr/backend.h and wlr/render/wlr_renderer.h + // that can be automatically imported @cInclude("include/render.h"); }); - -pub const manual = struct { - pub inline fn xkb_map_new_from_names(context: var, names: var, flags: var) ?*c.struct_xkb_keymap { - return c.xkb_keymap_new_from_names(context, names, flags); - } -}; diff --git a/src/cursor.zig b/src/cursor.zig index 2ef9fab..a7e4c98 100644 --- a/src/cursor.zig +++ b/src/cursor.zig @@ -1,6 +1,9 @@ const std = @import("std"); const c = @import("c.zig").c; +const Seat = @import("seat.zig").Seat; +const View = @import("view.zig").View; + const CursorMode = enum { Passthrough, Move, @@ -19,9 +22,9 @@ pub const Cursor = struct { listen_axis: c.wl_listener, listen_frame: c.wl_listener, - listen_request_cursor: c.wl_listener, + listen_request_set_cursor: c.wl_listener, - cursor_mode: CursorMode, + mode: CursorMode, grabbed_view: ?*View, grab_x: f64, grab_y: f64, @@ -31,7 +34,6 @@ pub const Cursor = struct { pub fn init(seat: *Seat) !@This() { var cursor = @This(){ - .server = seat.server, .seat = seat, // Creates a wlroots utility for tracking the cursor image shown on screen. @@ -81,8 +83,8 @@ pub const Cursor = struct { .resize_edges = 0, }; - c.wlr_cursor_attach_output_layout(cursor.wlr_cursor, seat.*.server.*.output_layout); - _ = c.wlr_xcursor_manager_load(server.cursor_mgr, 1); + c.wlr_cursor_attach_output_layout(cursor.wlr_cursor, seat.server.wlr_output_layout); + _ = c.wlr_xcursor_manager_load(cursor.wlr_xcursor_manager, 1); // wlr_cursor *only* displays an image on screen. It does not move around // when the pointer moves. However, we can attach input devices to it, and @@ -97,18 +99,18 @@ pub const Cursor = struct { c.wl_signal_add(&cursor.wlr_cursor.*.events.frame, &cursor.listen_frame); // This listens for clients requesting a specific cursor image - c.wl_signal_add(&server.seat.*.events.request_set_cursor, &cursor.listen_request_set_cursor); + c.wl_signal_add(&seat.wlr_seat.events.request_set_cursor, &cursor.listen_request_set_cursor); return cursor; } - fn process_cursor_move(server: *Server, time: u32) void { + fn process_move(self: *@This(), time: u32) void { // Move the grabbed view to the new position. - server.*.grabbed_view.?.*.x = @floatToInt(c_int, server.*.cursor.*.x - server.*.grab_x); - server.*.grabbed_view.?.*.y = @floatToInt(c_int, server.*.cursor.*.y - server.*.grab_y); + self.grabbed_view.?.*.x = @floatToInt(c_int, self.wlr_cursor.x - self.grab_x); + self.grabbed_view.?.*.y = @floatToInt(c_int, self.wlr_cursor.y - self.grab_y); } - fn process_cursor_resize(server: *Server, time: u32) void { + fn process_resize(self: *@This(), 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 // on one or two axes, but can also move the view if you resize from the top @@ -117,61 +119,61 @@ pub const Cursor = struct { // Note that I took some shortcuts here. In a more fleshed-out compositor, // you'd wait for the client to prepare a buffer at the new size, then // commit any movement that was prepared. - var view = server.*.grabbed_view; - var dx: f64 = (server.*.cursor.*.x - server.*.grab_x); - var dy: f64 = (server.*.cursor.*.y - server.*.grab_y); - var x: f64 = @intToFloat(f64, view.?.*.x); - var y: f64 = @intToFloat(f64, view.?.*.y); + // TODO: Handle null view + var view = self.grabbed_view; - var width = @intToFloat(f64, server.*.grab_width); - var height = @intToFloat(f64, server.*.grab_height); - if (server.*.resize_edges & @intCast(u32, c.WLR_EDGE_TOP) != 0) { - y = server.*.grab_y + dy; + var dx: f64 = self.wlr_cursor.x - self.grab_x; + var dy: f64 = self.wlr_cursor.y - self.grab_y; + var x: f64 = @intToFloat(f64, view.?.x); + var y: f64 = @intToFloat(f64, view.?.y); + + var width = @intToFloat(f64, self.grab_width); + var height = @intToFloat(f64, self.grab_height); + if (self.resize_edges & @intCast(u32, c.WLR_EDGE_TOP) != 0) { + y = self.grab_y + dy; height -= dy; if (height < 1) { y += height; } - } else if (server.*.resize_edges & @intCast(u32, c.WLR_EDGE_BOTTOM) != 0) { + } else if (self.resize_edges & @intCast(u32, c.WLR_EDGE_BOTTOM) != 0) { height += dy; } - if (server.*.resize_edges & @intCast(u32, c.WLR_EDGE_LEFT) != 0) { - x = server.*.grab_x + dx; + if (self.resize_edges & @intCast(u32, c.WLR_EDGE_LEFT) != 0) { + x = self.grab_x + dx; width -= dx; if (width < 1) { x += width; } - } else if (server.*.resize_edges & @intCast(u32, c.WLR_EDGE_RIGHT) != 0) { + } else if (self.resize_edges & @intCast(u32, c.WLR_EDGE_RIGHT) != 0) { width += dx; } - view.?.*.x = @floatToInt(c_int, x); - view.?.*.y = @floatToInt(c_int, y); + view.?.x = @floatToInt(c_int, x); + view.?.y = @floatToInt(c_int, y); _ = c.wlr_xdg_toplevel_set_size( - view.?.*.xdg_surface, + view.?.wlr_xdg_surface, @floatToInt(u32, width), @floatToInt(u32, height), ); } - fn process_cursor_motion(server: *Server, time: u32) void { + fn process_motion(self: *@This(), time: u32) void { // If the mode is non-passthrough, delegate to those functions. - if (server.*.cursor_mode == CursorMode.Move) { - process_cursor_move(server, time); + if (self.mode == CursorMode.Move) { + self.process_move(time); return; - } else if (server.*.cursor_mode == CursorMode.Resize) { - process_cursor_resize(server, time); + } else if (self.mode == CursorMode.Resize) { + self.process_resize(time); return; } // Otherwise, find the view under the pointer and send the event along. var sx: f64 = undefined; var sy: f64 = undefined; - var seat = server.*.seat; var opt_surface: ?*c.wlr_surface = null; - var view = desktop_view_at( - server, - server.*.cursor.*.x, - server.*.cursor.*.y, + var view = self.seat.server.desktop_view_at( + self.wlr_cursor.x, + self.wlr_cursor.y, &opt_surface, &sx, &sy, @@ -182,37 +184,38 @@ pub const Cursor = struct { // default. This is what makes the cursor image appear when you move it // around the screen, not over any views. c.wlr_xcursor_manager_set_cursor_image( - server.*.cursor_mgr, + self.wlr_xcursor_manager, "left_ptr", - server.*.cursor, + self.wlr_cursor, ); } + var wlr_seat = self.seat.wlr_seat; if (opt_surface) |surface| { - const focus_changed = seat.*.pointer_state.focused_surface != surface; + const focus_changed = wlr_seat.pointer_state.focused_surface != 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. - c.wlr_seat_pointer_notify_enter(seat, surface, sx, sy); + c.wlr_seat_pointer_notify_enter(wlr_seat, surface, sx, sy); if (!focus_changed) { // 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(seat, time, sx, sy); + c.wlr_seat_pointer_notify_motion(wlr_seat, time, sx, sy); } } else { // 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(seat); + c.wlr_seat_pointer_clear_focus(wlr_seat); } } fn handle_motion(listener: [*c]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) - var server = @fieldParentPtr(Server, "cursor_motion", listener); + var cursor = @fieldParentPtr(Cursor, "listen_motion", listener); var event = @ptrCast( *c.wlr_event_pointer_motion, @alignCast(@alignOf(*c.wlr_event_pointer_motion), data), @@ -222,8 +225,8 @@ pub const Cursor = struct { // 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(server.*.cursor, event.*.device, event.*.delta_x, event.*.delta_y); - process_cursor_motion(server, event.*.time_msec); + c.wlr_cursor_move(cursor.wlr_cursor, event.device, event.delta_x, event.delta_y); + cursor.process_motion(event.time_msec); } fn handle_motion_absolute(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { @@ -233,39 +236,38 @@ pub const Cursor = struct { // 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. - var server = @fieldParentPtr(Server, "cursor_motion_absolute", listener); + var cursor = @fieldParentPtr(Cursor, "listen_motion_absolute", listener); var event = @ptrCast( *c.wlr_event_pointer_motion_absolute, @alignCast(@alignOf(*c.wlr_event_pointer_motion_absolute), data), ); - c.wlr_cursor_warp_absolute(server.*.cursor, event.*.device, event.*.x, event.*.y); - process_cursor_motion(server, event.*.time_msec); + c.wlr_cursor_warp_absolute(cursor.wlr_cursor, event.device, event.x, event.y); + cursor.process_motion(event.time_msec); } fn handle_button(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { // This event is forwarded by the cursor when a pointer emits a button // event. - var server = @fieldParentPtr(Server, "cursor_button", listener); + var cursor = @fieldParentPtr(Cursor, "listen_button", listener); var 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( - server.*.seat, - event.*.time_msec, - event.*.button, - event.*.state, + 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; - var view = desktop_view_at( - server, - server.*.cursor.*.x, - server.*.cursor.*.y, + var view = cursor.seat.server.desktop_view_at( + cursor.wlr_cursor.x, + cursor.wlr_cursor.y, &surface, &sx, &sy, @@ -273,11 +275,11 @@ pub const Cursor = struct { if (event.*.state == c.enum_wlr_button_state.WLR_BUTTON_RELEASED) { // If you released any buttons, we exit interactive move/resize mode. - server.*.cursor_mode = CursorMode.Passthrough; + cursor.mode = CursorMode.Passthrough; } else { // Focus that client if the button was _pressed_ if (view) |v| { - focus_view(v, surface.?); + v.focus(surface.?); } } } @@ -285,19 +287,20 @@ pub const Cursor = struct { fn handle_axis(listener: [*c]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. - var server = @fieldParentPtr(Server, "cursor_axis", listener); + var cursor = @fieldParentPtr(Cursor, "listen_axis", listener); var 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( - server.*.seat, - event.*.time_msec, - event.*.orientation, - event.*.delta, - event.*.delta_discrete, - event.*.source, + cursor.seat.wlr_seat, + event.time_msec, + event.orientation, + event.delta, + event.delta_discrete, + event.source, ); } @@ -306,19 +309,19 @@ pub const Cursor = struct { // 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. - var server = @fieldParentPtr(Server, "cursor_frame", listener); + var cursor = @fieldParentPtr(Cursor, "listen_frame", listener); // Notify the client with pointer focus of the frame event. - c.wlr_seat_pointer_notify_frame(server.*.seat); + c.wlr_seat_pointer_notify_frame(cursor.seat.wlr_seat); } fn handle_request_set_cursor(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { // This event is rasied by the seat when a client provides a cursor image - var server = @fieldParentPtr(Server, "request_cursor", listener); + var cursor = @fieldParentPtr(Cursor, "listen_request_set_cursor", listener); var event = @ptrCast( *c.wlr_seat_pointer_request_set_cursor_event, @alignCast(@alignOf(*c.wlr_seat_pointer_request_set_cursor_event), data), ); - var focused_client = server.*.seat.*.pointer_state.focused_client; + 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. @@ -328,10 +331,10 @@ pub const Cursor = struct { // on the output that it's currently on and continue to do so as the // cursor moves between outputs. c.wlr_cursor_set_surface( - server.*.cursor, - event.*.surface, - event.*.hotspot_x, - event.*.hotspot_y, + cursor.wlr_cursor, + event.surface, + event.hotspot_x, + event.hotspot_y, ); } } diff --git a/src/keyboard.zig b/src/keyboard.zig index e769ac0..7e201b4 100644 --- a/src/keyboard.zig +++ b/src/keyboard.zig @@ -1,7 +1,9 @@ const std = @import("std"); const c = @import("c.zig").c; -const Keyboard = struct { +const Seat = @import("seat.zig").Seat; + +pub const Keyboard = struct { seat: *Seat, device: *c.wlr_input_device, @@ -35,7 +37,7 @@ const Keyboard = struct { const context = c.xkb_context_new(c.enum_xkb_context_flags.XKB_CONTEXT_NO_FLAGS); defer c.xkb_context_unref(context); - const keymap = man_c.xkb_map_new_from_names( + const keymap = c.xkb_keymap_new_from_names( context, &rules, c.enum_xkb_keymap_compile_flags.XKB_KEYMAP_COMPILE_NO_FLAGS, @@ -47,8 +49,8 @@ const Keyboard = struct { c.wlr_keyboard_set_repeat_info(keyboard_device, 25, 600); // Setup listeners for keyboard events - c.wl_signal_add(&keyboard_device.*.events.modifiers, &keyboard.*.listen_modifiers); - c.wl_signal_add(&keyboard_device.*.events.key, &keyboard.*.listen_key); + c.wl_signal_add(&keyboard_device.*.events.modifiers, &keyboard.listen_modifiers); + c.wl_signal_add(&keyboard_device.*.events.key, &keyboard.listen_key); return keyboard; } @@ -62,10 +64,10 @@ const Keyboard = struct { // 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.*.server.*.seat, keyboard.*.device); + c.wlr_seat_set_keyboard(keyboard.seat.wlr_seat, keyboard.*.device); // Send modifiers to the client. - c.wlr_seat_keyboard_notify_modifiers(keyboard.*.server.*.seat, &keyboard.*.device.*.unnamed_37.keyboard.*.modifiers); + c.wlr_seat_keyboard_notify_modifiers(keyboard.seat.wlr_seat, &keyboard.*.device.*.unnamed_37.keyboard.*.modifiers); } fn handle_key(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { @@ -76,26 +78,24 @@ const Keyboard = struct { @alignCast(@alignOf(*c.wlr_event_keyboard_key), data), ); - const server = keyboard.*.server; - const seat = server.*.seat; - const keyboard_device = keyboard.*.device.*.unnamed_37.keyboard; + const keyboard_device = keyboard.device.unnamed_37.keyboard; // Translate libinput keycode -> xkbcommon - const keycode = event.*.keycode + 8; + const keycode = event.keycode + 8; // Get a list of keysyms based on the keymap for this keyboard - var syms: *c.xkb_keysym_t = undefined; + var syms: ?[*]c.xkb_keysym_t = undefined; const nsyms = c.xkb_state_key_get_syms(keyboard_device.*.xkb_state, keycode, &syms); var handled = false; const modifiers = c.wlr_keyboard_get_modifiers(keyboard_device); if (modifiers & @intCast(u32, c.WLR_MODIFIER_LOGO) != 0 and - event.*.state == c.enum_wlr_key_state.WLR_KEY_PRESSED) + event.state == c.enum_wlr_key_state.WLR_KEY_PRESSED) { // If mod is held down and this button was _pressed_, we attempt to // process it as a compositor keybinding. var i: usize = 0; while (i < nsyms) { - handled = keyboard.seat.server.handle_keybinding(syms[i]); + handled = keyboard.seat.server.handle_keybinding(syms.?[i]); if (handled) { break; } @@ -105,12 +105,13 @@ const Keyboard = struct { if (!handled) { // Otherwise, we pass it along to the client. - c.wlr_seat_set_keyboard(seat, keyboard.*.device); + const wlr_seat = keyboard.seat.wlr_seat; + c.wlr_seat_set_keyboard(wlr_seat, keyboard.device); c.wlr_seat_keyboard_notify_key( - seat, - event.*.time_msec, - event.*.keycode, - @intCast(u32, @enumToInt(event.*.state)), + wlr_seat, + event.time_msec, + event.keycode, + @intCast(u32, @enumToInt(event.state)), ); } } diff --git a/src/main.zig b/src/main.zig index 6da2ffe..6794dd6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,6 +1,8 @@ const std = @import("std"); const c = @import("c.zig").c; +const Server = @import("server.zig").Server; + pub fn main() !void { std.debug.warn("Starting up.\n", .{}); diff --git a/src/output.zig b/src/output.zig index fa61fe4..218197a 100644 --- a/src/output.zig +++ b/src/output.zig @@ -1,6 +1,9 @@ const std = @import("std"); const c = @import("c.zig").c; +const Server = @import("server.zig").Server; +const View = @import("view.zig").View; + const RenderData = struct { output: *c.wlr_output, renderer: *c.wlr_renderer, @@ -8,7 +11,7 @@ const RenderData = struct { when: *c.struct_timespec, }; -const Output = struct { +pub const Output = struct { server: *Server, wlr_output: *c.wlr_output, listen_frame: c.wl_listener, @@ -40,13 +43,13 @@ const Output = struct { }; // Sets up a listener for the frame notify event. - c.wl_signal_add(&wlr_output.*.events.frame, &output.*.listen_frame); + c.wl_signal_add(&wlr_output.events.frame, &output.listen_frame); // Add the new output to the layout. The add_auto function arranges outputs // from left-to-right in the order they appear. A more sophisticated // compositor would let the user configure the arrangement of outputs in the // layout. - c.wlr_output_layout_add_auto(server.output_layout, wlr_output); + c.wlr_output_layout_add_auto(server.wlr_output_layout, wlr_output); // Creating the global adds a wl_output global to the display, which Wayland // clients can see to find out information about the output (such as @@ -59,8 +62,8 @@ const Output = struct { fn handle_frame(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { // This function is called every time an output is ready to display a frame, // generally at the output's refresh rate (e.g. 60Hz). - var output = @fieldParentPtr(Output, "frame", listener); - var renderer = output.*.server.*.renderer; + var output = @fieldParentPtr(Output, "listen_frame", listener); + var renderer = output.server.wlr_renderer; var now: c.struct_timespec = undefined; _ = c.clock_gettime(c.CLOCK_MONOTONIC, &now); @@ -93,7 +96,7 @@ const Output = struct { }; // This calls our render_surface function for each surface among the // xdg_surface's toplevel and popups. - c.wlr_xdg_surface_for_each_surface(view.*.xdg_surface, render_surface, &rdata); + c.wlr_xdg_surface_for_each_surface(view.*.wlr_xdg_surface, render_surface, &rdata); } // Hardware cursors are rendered by the GPU on a separate plane, and can be @@ -133,7 +136,7 @@ const Output = struct { // output-local coordinates, or (2000 - 1920). var ox: f64 = 0.0; var oy: f64 = 0.0; - c.wlr_output_layout_output_coords(view.*.server.*.output_layout, output, &ox, &oy); + c.wlr_output_layout_output_coords(view.server.wlr_output_layout, output, &ox, &oy); ox += @intToFloat(f64, view.*.x + sx); oy += @intToFloat(f64, view.*.y + sy); diff --git a/src/seat.zig b/src/seat.zig index 7ec9b07..10e6ec2 100644 --- a/src/seat.zig +++ b/src/seat.zig @@ -1,6 +1,10 @@ const std = @import("std"); const c = @import("c.zig").c; +const Cursor = @import("cursor.zig").Cursor; +const Keyboard = @import("keyboard.zig").Keyboard; +const Server = @import("server.zig").Server; + // TODO: InputManager and multi-seat support pub const Seat = struct { server: *Server, @@ -13,7 +17,7 @@ pub const Seat = struct { // Mulitple keyboards are handled separately keyboards: std.ArrayList(Keyboard), - pub fn init(server: *Server, allocator: *std.mem.Allocator) @This() { + pub fn init(server: *Server, allocator: *std.mem.Allocator) !@This() { var seat = @This(){ .server = server, // This seems to be the default seat name used by compositors @@ -27,16 +31,18 @@ pub const Seat = struct { }, }; - seat.cursor = cursor.Cursor.init(server); + seat.cursor = try Cursor.init(&seat); // Set up handler for all new input devices made available. This // includes keyboards, pointers, touch, etc. - c.wl_signal_add(&server.*.backend.*.events.new_input, &seat.new_input); + c.wl_signal_add(&server.wlr_backend.events.new_input, &seat.listen_new_input); + + return seat; } - fn add_keyboard(self: *@This(), device: *c.wlr_input_device) void { - self.keyboards.append(Keyboard.init(self, device)); - c.wlr_seat_set_keyboard(self, device); + fn add_keyboard(self: *@This(), device: *c.wlr_input_device) !void { + try self.keyboards.append(Keyboard.init(self, device)); + c.wlr_seat_set_keyboard(self.wlr_seat, device); } fn add_pointer(self: *@This(), device: *c.struct_wlr_input_device) void { @@ -53,7 +59,7 @@ pub const Seat = struct { var device = @ptrCast(*c.wlr_input_device, @alignCast(@alignOf(*c.wlr_input_device), data)); switch (device.*.type) { - .WLR_INPUT_DEVICE_KEYBOARD => seat.add_keyboard(device), + .WLR_INPUT_DEVICE_KEYBOARD => seat.add_keyboard(device) catch unreachable, .WLR_INPUT_DEVICE_POINTER => seat.add_pointer(device), else => {}, } @@ -63,9 +69,9 @@ pub const Seat = struct { // there are no pointer devices, so we always include that capability. var caps: u32 = @intCast(u32, c.WL_SEAT_CAPABILITY_POINTER); // if list not empty - if (c.wl_list_empty(&server.*.keyboards) == 0) { + if (seat.keyboards.span().len > 0) { caps |= @intCast(u32, c.WL_SEAT_CAPABILITY_KEYBOARD); } - c.wlr_seat_set_capabilities(server.*.seat, caps); + c.wlr_seat_set_capabilities(seat.wlr_seat, caps); } }; diff --git a/src/server.zig b/src/server.zig index 7206341..6d114ad 100644 --- a/src/server.zig +++ b/src/server.zig @@ -1,6 +1,10 @@ const std = @import("std"); const c = @import("c.zig").c; +const Output = @import("output.zig").Output; +const Seat = @import("seat.zig").Seat; +const View = @import("view.zig").View; + pub const Server = struct { wl_display: *c.wl_display, wlr_backend: *c.wlr_backend, @@ -17,8 +21,10 @@ pub const Server = struct { // Must stay ordered bottom to top views: std.ArrayList(View), + seat: Seat, + pub fn init(allocator: *std.mem.Allocator) !@This() { - var server = undefined; + var server: @This() = undefined; // The Wayland display is managed by libwayland. It handles accepting // clients from the Unix socket, manging Wayland globals, and so on. @@ -31,19 +37,20 @@ pub const Server = struct { // a tty or wayland if WAYLAND_DISPLAY is set. // // This frees itself when the wl_display is destroyed. - server.wlr_backend = c.wlr_backend_autocreate(server.wl_display) orelse + server.wlr_backend = c.zag_wlr_backend_autocreate(server.wl_display) orelse return error.CantCreateWlrBackend; // If we don't provide a renderer, autocreate makes a GLES2 renderer for us. // The renderer is responsible for defining the various pixel formats it // supports for shared memory, this configures that for clients. - server.wlr_renderer = c.wlr_backend_get_renderer(server.backend) orelse + server.wlr_renderer = c.zag_wlr_backend_get_renderer(server.wlr_backend) orelse return error.CantGetWlrRenderer; - c.wlr_renderer_init_wl_display(server.wlr_renderer, server.wl_display) orelse - return error.CantInitWlDisplay; + // TODO: Handle failure after https://github.com/swaywm/wlroots/pull/2080 + c.wlr_renderer_init_wl_display(server.wlr_renderer, server.wl_display);// orelse + // return error.CantInitWlDisplay; // These both free themselves when the wl_display is destroyed - _ = c.wlr_compositor_create(server.wl_display, server.renderer) orelse + _ = c.wlr_compositor_create(server.wl_display, server.wlr_renderer) orelse return error.CantCreateWlrCompositor; _ = c.wlr_data_device_manager_create(server.wl_display) orelse return error.CantCreateWlrDataDeviceManager; @@ -57,7 +64,7 @@ pub const Server = struct { server.outputs = std.ArrayList(Output).init(std.heap.c_allocator); // Setup a listener for new outputs - server.listen_new_output = handle_new_output; + server.listen_new_output.notify = handle_new_output; c.wl_signal_add(&server.wlr_backend.*.events.new_output, &server.listen_new_output); // Set up our list of views and the xdg-shell. The xdg-shell is a Wayland @@ -67,7 +74,9 @@ pub const Server = struct { server.wlr_xdg_shell = c.wlr_xdg_shell_create(server.wl_display) orelse return error.CantCreateWlrXdgShell; server.listen_new_xdg_surface.notify = handle_new_xdg_surface; - c.wl_signal_add(&server.xdg_shell.*.events.new_surface, &server.listen_new_xdg_surface); + c.wl_signal_add(&server.wlr_xdg_shell.*.events.new_surface, &server.listen_new_xdg_surface); + + server.seat = try Seat.init(&server, std.heap.c_allocator); return server; } @@ -87,8 +96,7 @@ pub const Server = struct { // Start the backend. This will enumerate outputs and inputs, become the DRM // master, etc - if (!c.wlr_backend_start(self.wlr_backend)) { - c.wlr_backend_destroy(self.wlr_backend); + if (!c.zag_wlr_backend_start(self.wlr_backend)) { return error.CantStartBackend; } @@ -101,7 +109,7 @@ pub const Server = struct { /// Enter the wayland event loop and block until the compositor is exited pub fn run(self: @This()) void { - c.wl_display_run(server.wl_display); + c.wl_display_run(self.wl_display); } pub fn handle_keybinding(self: *@This(), sym: c.xkb_keysym_t) bool { @@ -111,7 +119,7 @@ pub const Server = struct { // // This function assumes the proper modifier is held down. switch (sym) { - c.XKB_KEY_Escape => c.wl_display_terminate(server.*.wl_display), + c.XKB_KEY_Escape => c.wl_display_terminate(self.wl_display), c.XKB_KEY_F1 => { // Cycle to the next view //if (c.wl_list_length(&server.*.views) > 1) { @@ -129,11 +137,11 @@ pub const Server = struct { } fn handle_new_output(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { - var server = @fieldParentPtr(Server, "new_output", listener); + var server = @fieldParentPtr(Server, "listen_new_output", listener); var wlr_output = @ptrCast(*c.wlr_output, @alignCast(@alignOf(*c.wlr_output), data)); // TODO: Handle failure - server.outputs.append(Output.init(server, wlr_output) catch unreachable); + server.outputs.append(Output.init(server, wlr_output) catch unreachable) catch unreachable; } fn handle_new_xdg_surface(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { @@ -153,7 +161,7 @@ pub const Server = struct { /// Finds the top most view under the output layout coordinates lx, ly /// returns the view if found, and a pointer to the wlr_surface as well as the surface coordinates pub fn desktop_view_at(self: *@This(), lx: f64, ly: f64, surface: *?*c.wlr_surface, sx: *f64, sy: *f64) ?*View { - for (server.*.views.span()) |*view| { + for (self.views.span()) |*view| { if (view.is_at(lx, ly, surface, sx, sy)) { return view; } diff --git a/src/view.zig b/src/view.zig index 96ac6eb..74a7cab 100644 --- a/src/view.zig +++ b/src/view.zig @@ -1,6 +1,8 @@ const std = @import("std"); const c = @import("c.zig").c; +const Server = @import("server.zig").Server; + pub const View = struct { server: *Server, wlr_xdg_surface: *c.wlr_xdg_surface, @@ -39,12 +41,15 @@ pub const View = struct { // .link = undefined, // .notify = handle_request_resize, // }, + .mapped = false, + .x = 0, + .y = 0, }; // Listen to the various events it can emit - c.wl_signal_add(&xdg_surface.*.events.map, &view.*.listen_map); - c.wl_signal_add(&xdg_surface.*.events.unmap, &view.*.listen_unmap); - c.wl_signal_add(&xdg_surface.*.events.destroy, &view.*.listen_destroy); + c.wl_signal_add(&view.wlr_xdg_surface.events.map, &view.listen_map); + c.wl_signal_add(&view.wlr_xdg_surface.events.unmap, &view.listen_unmap); + c.wl_signal_add(&view.wlr_xdg_surface.events.destroy, &view.listen_destroy); // var toplevel = xdg_surface.*.unnamed_160.toplevel; // c.wl_signal_add(&toplevel.*.events.request_move, &view.*.request_move); @@ -55,18 +60,18 @@ pub const View = struct { fn handle_map(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { // Called when the surface is mapped, or ready to display on-screen. - var view = @fieldParentPtr(View, "map", listener); - view.*.mapped = true; - focus_view(view, view.*.xdg_surface.*.surface); + var view = @fieldParentPtr(View, "listen_map", listener); + view.mapped = true; + view.focus(view.wlr_xdg_surface.surface); } fn handle_unmap(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { - var view = @fieldParentPtr(View, "unmap", listener); + var view = @fieldParentPtr(View, "listen_unmap", listener); view.*.mapped = false; } fn handle_destroy(listener: [*c]c.wl_listener, data: ?*c_void) callconv(.C) void { - var view = @fieldParentPtr(View, "destroy", listener); + var view = @fieldParentPtr(View, "listen_destroy", listener); var server = view.*.server; const idx = for (server.*.views.span()) |*v, i| { if (v == view) { @@ -84,10 +89,10 @@ pub const View = struct { // // ignore for now // } - fn focus_view(view: *View, surface: *c.wlr_surface) void { - const server = view.server; - const seat = server.*.seat; - const prev_surface = seat.*.keyboard_state.focused_surface; + fn focus(self: *@This(), surface: *c.wlr_surface) void { + const server = self.server; + const wlr_seat = server.seat.wlr_seat; + const prev_surface = wlr_seat.keyboard_state.focused_surface; if (prev_surface == surface) { // Don't re-focus an already focused surface. @@ -103,25 +108,31 @@ pub const View = struct { } // Find the index - const idx = for (server.*.views.span()) |*v, i| { - if (v == view) { + const idx = for (server.views.span()) |*v, i| { + if (self == v) { break i; } } else unreachable; // Move the view to the front - server.*.views.append(server.*.views.orderedRemove(idx)) catch unreachable; + server.views.append(server.views.orderedRemove(idx)) catch unreachable; - var moved_view = &server.*.views.span()[server.*.views.span().len - 1]; + var moved_self = &server.views.span()[server.views.span().len - 1]; // Activate the new surface - _ = c.wlr_xdg_toplevel_set_activated(moved_view.*.xdg_surface, true); + _ = c.wlr_xdg_toplevel_set_activated(moved_self.wlr_xdg_surface, true); // Tell the seat to have the keyboard enter this surface. wlroots will keep // track of this and automatically send key events to the appropriate // clients without additional work on your part. - var keyboard = c.wlr_seat_get_keyboard(seat); - c.wlr_seat_keyboard_notify_enter(seat, moved_view.*.xdg_surface.*.surface, &keyboard.*.keycodes, keyboard.*.num_keycodes, &keyboard.*.modifiers); + var keyboard = c.wlr_seat_get_keyboard(wlr_seat); + c.wlr_seat_keyboard_notify_enter( + wlr_seat, + moved_self.wlr_xdg_surface.surface, + &keyboard.*.keycodes, + keyboard.*.num_keycodes, + &keyboard.*.modifiers, + ); } fn is_at(self: *@This(), lx: f64, ly: f64, surface: *?*c.wlr_surface, sx: *f64, sy: *f64) bool { @@ -130,15 +141,15 @@ pub const View = struct { // coordinates lx and ly (in output Layout Coordinates). If so, it sets the // surface pointer to that wlr_surface and the sx and sy coordinates to the // coordinates relative to that surface's top-left corner. - var view_sx = lx - @intToFloat(f64, view.*.x); - var view_sy = ly - @intToFloat(f64, view.*.y); + var view_sx = lx - @intToFloat(f64, self.x); + var view_sy = ly - @intToFloat(f64, self.y); // This variable seems to have been unsued in TinyWL // struct wlr_surface_state *state = &view->xdg_surface->surface->current; var _sx: f64 = undefined; var _sy: f64 = undefined; - var _surface = c.wlr_xdg_surface_surface_at(view.*.xdg_surface, view_sx, view_sy, &_sx, &_sy); + var _surface = c.wlr_xdg_surface_surface_at(self.wlr_xdg_surface, view_sx, view_sy, &_sx, &_sy); if (_surface) |surface_at| { sx.* = _sx;