-
-
Save tadeokondrak/03b83789b57febe4b5567f8e80102f9c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const std = @import("std"); | |
usingnamespace @cImport({ | |
@cDefine("_GNU_SOURCE", {}); | |
@cDefine("WLR_USE_UNSTABLE", {}); | |
@cDefine("EGL_NO_X11", {}); | |
@cInclude("wayland-server-core.h"); | |
@cInclude("wlr/util/log.h"); | |
@cInclude("wlr/backend.h"); | |
@cInclude("wlr/render/wlr_renderer.h"); | |
@cInclude("wlr/types/wlr_compositor.h"); | |
@cInclude("wlr/types/wlr_data_device.h"); | |
@cInclude("wlr/types/wlr_output_layout.h"); | |
@cInclude("wlr/types/wlr_xcursor_manager.h"); | |
@cInclude("wlr/types/wlr_xdg_shell.h"); | |
@cInclude("wlr/types/wlr_cursor.h"); | |
@cInclude("stdlib.h"); | |
@cInclude("stdio.h"); | |
}); | |
const Listener = extern struct { | |
inner: wl_listener, | |
self: ?*c_void, | |
fn connect(listener: *Listener, self: var, signal: *wl_signal, comptime notify: var) void { | |
listener.inner.notify = struct { | |
fn callback(listener_: ?*wl_listener, data: ?*c_void) callconv(.C) void { | |
const self_ = @ptrCast(*Listener, listener_).self; | |
const args = @typeInfo(@TypeOf(notify)).Fn.args; | |
const Self = args[0].arg_type.?; | |
const Data = if (args.len > 1) args[1].arg_type.? else null; | |
if (args.len > 1) wlrLog(.Debug, "listener: {}", .{@typeName(Data)}); | |
const ret = @call(.{ .modifier = .always_inline }, notify, if (args.len == 1) | |
.{ | |
@ptrCast(Self, @alignCast(@alignOf(Self), self_)), | |
} | |
else | |
.{ | |
@ptrCast(Self, @alignCast(@alignOf(Self), self_)), | |
@ptrCast(Data, @alignCast(@alignOf(Data), data)), | |
}); | |
if (@typeInfo(@TypeOf(notify)).Fn.return_type) |T| { | |
if (@typeId(T) == .ErrorUnion) { | |
ret catch |err| { | |
std.debug.warn("error in wl_listener handler: {}\n", .{err}); | |
std.process.exit(1); | |
}; | |
} | |
} | |
} | |
}.callback; | |
listener.self = self; | |
wl_signal_add(signal, &listener.inner); | |
} | |
fn disconnect(listener: *Listener) void { | |
wl_list_remove(&listener.inner.link); | |
} | |
}; | |
const Server = struct { | |
display: *wl_display, | |
event_loop: *wl_event_loop, | |
socket: [:0]const u8, | |
backend: *wlr_backend, | |
renderer: *wlr_renderer, | |
compositor: *wlr_compositor, | |
data_device_manager: *wlr_data_device_manager, | |
xdg_shell: *wlr_xdg_shell, | |
output_layout: *wlr_output_layout, | |
xcursor_manager: *wlr_xcursor_manager, | |
seat: *Seat, | |
outputs: std.TailQueue(Output), | |
new_input: Listener = undefined, | |
new_output: Listener = undefined, | |
new_xdg_surface: Listener = undefined, | |
fn init() !*Server { | |
const server = try std.heap.c_allocator.create(Server); | |
errdefer std.heap.c_allocator.destroy(server); | |
server.display = wl_display_create() orelse return error.Failed; | |
errdefer wl_display_destroy(server.display); | |
server.event_loop = wl_display_get_event_loop(server.display) orelse unreachable; | |
server.socket = std.mem.toSliceConst( | |
u8, | |
wl_display_add_socket_auto(server.display) orelse return error.Failed, | |
); | |
server.backend = wlr_backend_autocreate(server.display, null) orelse return error.Failed; | |
errdefer wlr_backend_destroy(server.backend); | |
server.renderer = wlr_backend_get_renderer(server.backend) orelse return error.Failed; | |
server.output_layout = wlr_output_layout_create(); | |
errdefer wlr_output_layout_destroy(server.output_layout); | |
server.xcursor_manager = wlr_xcursor_manager_create(null, 24); | |
errdefer wlr_xcursor_manager_destroy(server.xcursor_manager); | |
if (wlr_xcursor_manager_load(server.xcursor_manager, 1) != 0) return error.Failed; | |
wlr_renderer_init_wl_display(server.renderer, server.display); | |
server.compositor = | |
wlr_compositor_create(server.display, server.renderer) orelse return error.Failed; | |
server.data_device_manager = | |
wlr_data_device_manager_create(server.display) orelse return error.Failed; | |
server.xdg_shell = wlr_xdg_shell_create(server.display) orelse return error.Failed; | |
server.seat = try Seat.init(server, "seat0"); | |
errdefer server.seat.deinit(); | |
server.outputs = std.TailQueue(Output).init(); | |
errdefer server.outputs.deinit(); | |
server.new_input.connect(server, &server.backend.events.new_input, new_input); | |
errdefer server.new_input.disconnect(); | |
server.new_output.connect(server, &server.backend.events.new_output, new_output); | |
errdefer server.new_output.disconnect(); | |
server.new_xdg_surface.connect(server, &server.xdg_shell.events.new_surface, new_xdg_surface); | |
errdefer server.new_xdg_surface.disconnect(); | |
return server; | |
} | |
fn new_input(server: *Server, wlr: *wlr_input_device) !void { | |
wlrLog(.Info, "New input: {s}", .{wlr.name}); | |
switch (wlr.@"type") { | |
.WLR_INPUT_DEVICE_KEYBOARD => {}, | |
.WLR_INPUT_DEVICE_POINTER => { | |
try server.seat.new_pointer(wlr); | |
}, | |
.WLR_INPUT_DEVICE_TOUCH => {}, | |
.WLR_INPUT_DEVICE_TABLET_TOOL => {}, | |
.WLR_INPUT_DEVICE_TABLET_PAD => {}, | |
.WLR_INPUT_DEVICE_SWITCH => {}, | |
else => unreachable, | |
} | |
} | |
fn new_output(server: *Server, wlr: *wlr_output) !void { | |
wlrLog(.Info, "New output: {}", .{wlr.name}); | |
const node = try server.outputs.createNode(Output.init(server, wlr), std.heap.c_allocator); | |
node.data.connect(); | |
server.outputs.append(node); | |
} | |
fn new_xdg_surface(server: *Server, surface: *wlr_xdg_surface) void { | |
wlrLog(.Debug, "New xdg surface", .{}); | |
} | |
fn run(server: *Server) !void { | |
if (!wlr_backend_start(server.backend)) { | |
return error.BackendStartFailed; | |
} | |
if (setenv("WAYLAND_DISPLAY", server.socket, 1) != 0) { | |
return error.SetenvFailed; | |
} | |
wl_display_run(server.display); | |
} | |
fn deinit(server: *Server) void { | |
server.new_output.disconnect(); | |
server.new_xdg_surface.disconnect(); | |
while (server.outputs.pop()) |node| { | |
node.data.deinit(); | |
std.heap.c_allocator.destroy(node); | |
} | |
server.seat.deinit(); | |
wlr_output_layout_destroy(server.output_layout); | |
wlr_backend_destroy(server.backend); | |
wl_display_destroy_clients(server.display); | |
wl_display_destroy(server.display); | |
std.heap.c_allocator.destroy(server); | |
} | |
}; | |
const Pointer = struct { | |
seat: *Seat, | |
wlr: *wlr_input_device, | |
fn init(seat: *Seat, wlr: *wlr_input_device) Pointer { | |
return Pointer{ | |
.seat = seat, | |
.wlr = wlr, | |
}; | |
} | |
fn connect(pointer: *Pointer) void { | |
wlr_cursor_attach_input_device(pointer.seat.cursor.wlr, pointer.wlr); | |
} | |
}; | |
const Cursor = struct { | |
seat: *Seat, | |
wlr: *wlr_cursor, | |
motion: Listener = undefined, | |
motion_absolute: Listener = undefined, | |
button: Listener = undefined, | |
axis: Listener = undefined, | |
frame: Listener = undefined, | |
fn init(seat: *Seat) !*Cursor { | |
var cursor = try std.heap.c_allocator.create(Cursor); | |
cursor.seat = seat; | |
cursor.wlr = wlr_cursor_create() orelse return error.OutOfMemory; | |
return cursor; | |
} | |
fn connect(cursor: *Cursor) void { | |
wlr_cursor_attach_output_layout(cursor.wlr, cursor.seat.server.output_layout); | |
cursor.motion.connect(cursor, &cursor.wlr.events.motion, motion); | |
cursor.motion.connect(cursor, &cursor.wlr.events.motion, motion); | |
cursor.motion_absolute.connect(cursor, &cursor.wlr.events.motion_absolute, motion_absolute); | |
cursor.button.connect(cursor, &cursor.wlr.events.button, button); | |
cursor.axis.connect(cursor, &cursor.wlr.events.axis, axis); | |
cursor.frame.connect(cursor, &cursor.wlr.events.frame, frame); | |
} | |
fn deinit(cursor: *Cursor) void { | |
cursor.motion.disconnect(); | |
cursor.motion_absolute.disconnect(); | |
cursor.button.disconnect(); | |
cursor.axis.disconnect(); | |
cursor.frame.disconnect(); | |
} | |
fn motion(cursor: *Cursor, event: *wlr_event_pointer_motion) void { | |
wlr_xcursor_manager_set_cursor_image( | |
cursor.seat.server.xcursor_manager, | |
"left_ptr", | |
cursor.wlr, | |
); | |
} | |
fn motion_absolute(cursor: *Cursor, event: *wlr_event_pointer_motion_absolute) void {} | |
fn button(cursor: *Cursor, event: *wlr_event_pointer_button) void {} | |
fn axis(cursor: *Cursor, event: *wlr_event_pointer_axis) void {} | |
fn frame(cursor: *Cursor) void {} | |
}; | |
const Seat = struct { | |
server: *Server, | |
wlr: *wlr_seat, | |
cursor: *Cursor, | |
destroy: Listener = undefined, | |
pointers: std.TailQueue(Pointer), // TODO: destroy | |
fn init(server: *Server, name: [:0]const u8) !*Seat { | |
const seat = try std.heap.c_allocator.create(Seat); | |
seat.server = server; | |
seat.wlr = wlr_seat_create(server.display, name); | |
errdefer wlr_seat_destroy(seat.wlr); | |
seat.cursor = try Cursor.init(seat); | |
errdefer seat.cursor.deinit(); | |
seat.pointers = std.TailQueue(Pointer).init(); | |
errdefer seat.pointers.deinit(); | |
seat.destroy.connect(seat, &seat.wlr.events.destroy, destroy); | |
errdefer seat.destroy.disconnect(); | |
return seat; | |
} | |
fn new_pointer(seat: *Seat, wlr: *wlr_input_device) !void { | |
const node = try seat.pointers.createNode(Pointer.init(seat, wlr), std.heap.c_allocator); | |
node.data.connect(); | |
seat.pointers.append(node); | |
} | |
fn new_keyboard(seat: *Seat, wlr: *wlr_input_device) void {} | |
fn deinit(seat: *Seat) void { | |
seat.cursor.deinit(); | |
wlr_seat_destroy(seat.wlr); | |
} | |
fn destroy(seat: *Seat) void { | |
seat.destroy.disconnect(); | |
while (seat.pointers.pop()) |node| { | |
//node.data.deinit(); | |
std.heap.c_allocator.destroy(node); | |
} | |
} | |
}; | |
const Output = struct { | |
server: *Server, | |
wlr: *wlr_output, | |
frame: Listener = undefined, | |
destroy: Listener = undefined, | |
transform: Listener = undefined, | |
mode: Listener = undefined, | |
fn init(server: *Server, wlr: *wlr_output) Output { | |
const output = Output{ | |
.server = server, | |
.wlr = wlr, | |
}; | |
return output; | |
} | |
fn deinit(output: *Output) void { | |
output.frame.disconnect(); | |
output.destroy.disconnect(); | |
wlr_output_destroy_global(output.wlr); | |
wlr_output_destroy(output.wlr); | |
} | |
fn connect(output: *Output) void { | |
output.frame.connect(output, &output.wlr.events.frame, frame); | |
output.destroy.connect(output, &output.wlr.events.destroy, destroy); | |
output.transform.connect(output, &output.wlr.events.transform, transform); | |
output.mode.connect(output, &output.wlr.events.mode, mode); | |
if (wl_list_empty(&output.wlr.modes) == 0) { | |
wlr_output_set_mode(output.wlr, wlr_output_preferred_mode(output.wlr) orelse | |
@fieldParentPtr(wlr_output_mode, "link", output.wlr.modes.prev)); | |
} | |
wlr_output_layout_add_auto(output.server.output_layout, output.wlr); | |
wlr_output_create_global(output.wlr); | |
} | |
fn frame(output: *Output) !void { | |
if (!wlr_output_attach_render(output.wlr, null)) { | |
return error.AttachRender; | |
} | |
errdefer wlr_output_rollback(output.wlr); | |
var width: c_int = undefined; | |
var height: c_int = undefined; | |
wlr_output_effective_resolution(output.wlr, &width, &height); | |
{ | |
wlr_renderer_begin(output.server.renderer, width, height); | |
defer wlr_renderer_end(output.server.renderer); | |
wlr_renderer_clear(output.server.renderer, &[_]f32{ 0.0, 0.5, 0.5, 1.0 }); | |
wlr_output_render_software_cursors(output.wlr, null); | |
} | |
if (!wlr_output_commit(output.wlr)) { | |
return error.OutputCommit; | |
} | |
} | |
fn destroy(output: *Output) void { | |
const node = @fieldParentPtr(@TypeOf(output.server.outputs).Node, "data", output); | |
output.server.outputs.remove(node); | |
std.heap.c_allocator.destroy(node); | |
output.frame.disconnect(); | |
output.destroy.disconnect(); | |
wlr_output_destroy_global(output.wlr); | |
} | |
fn transform(output: *Output) void {} | |
fn mode(output: *Output) void {} | |
}; | |
const LogImportance = enum { | |
Error = WLR_ERROR, | |
Info = WLR_INFO, | |
Debug = WLR_DEBUG, | |
}; | |
fn wlrLog(importance: LogImportance, comptime fmt: []const u8, args: var) void { | |
const color_code: []const u8 = switch (importance) { | |
.Error => "31;1", | |
.Info => "34;1", | |
.Debug => "30;1", | |
}; | |
const msg = std.fmt.allocPrint(std.heap.c_allocator, fmt, args) catch return; | |
defer std.heap.c_allocator.free(msg); | |
if (@enumToInt(importance) - 1 < @enumToInt(wlr_log_get_verbosity())) { | |
std.debug.warn("\x1b[{}m[zig] {}\x1b[0m\n", .{ color_code, msg }); | |
} | |
} | |
fn wlrLogCallback(importance: wlr_log_importance, fmt: ?[*]const u8, args: ?*__va_list_tag) callconv(.C) void { | |
var msg: ?[*:0]u8 = undefined; | |
const length = vasprintf(&msg, fmt, args); | |
if (length == -1) { | |
return; | |
} | |
defer free(msg); | |
const color_code: []const u8 = switch (importance) { | |
.WLR_ERROR => "31;1", | |
.WLR_INFO => "34;1", | |
.WLR_DEBUG => "30;1", | |
else => unreachable, | |
}; | |
if (@enumToInt(importance) - 1 < @enumToInt(wlr_log_get_verbosity())) { | |
std.debug.warn("\x1b[{}m{s}\x1b[0m\n", .{ color_code, msg }); | |
} | |
} | |
pub fn main() !void { | |
wlr_log_init(.WLR_DEBUG, wlrLogCallback); | |
var server = try Server.init(); | |
defer server.deinit(); | |
try server.run(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment