Skip to content

Instantly share code, notes, and snippets.

@tadeokondrak
Created April 28, 2020 13:01
Show Gist options
  • Save tadeokondrak/03b83789b57febe4b5567f8e80102f9c to your computer and use it in GitHub Desktop.
Save tadeokondrak/03b83789b57febe4b5567f8e80102f9c to your computer and use it in GitHub Desktop.
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