git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitb9c6f2d1b831e9fba86793a968b3726be17b7e13
parentbf6484ead6
authorIsaac Freund <[email protected]>
date2020-06-16 20:54
code: create voidCast() util function

 river/Control.zig           | 11 ++++++-----
 river/Cursor.zig            | 38 +++++++++-----------------------------
 river/DecorationManager.zig |  6 ++----
 river/InputManager.zig      |  3 ++-
 river/Keyboard.zig          |  6 ++----
 river/LayerSurface.zig      |  3 ++-
 river/OutputStatus.zig      |  5 +++--
 river/Root.zig              |  3 ++-
 river/Seat.zig              |  6 ++----
 river/SeatStatus.zig        |  3 ++-
 river/Server.zig            | 17 ++++++-----------
 river/StatusManager.zig     | 11 ++++++-----
 river/View.zig              |  6 ++----
 river/XdgPopup.zig          |  3 ++-
 river/XdgToplevel.zig       |  3 ++-
 river/XwaylandUnmanaged.zig |  6 ++----
 river/render.zig            |  3 ++-
 river/util.zig              | 26 ++++++++++++++++++++++++++
 18 files changed, 80 insertions(+), 79 deletions(-)

diff --git a/river/Control.zig b/river/Control.zig
index 3a4ffe1..3da1f28 100644
--- a/river/Control.zig
+++ b/river/Control.zig
@@ -21,6 +21,7 @@ const std = @import("std");
 
 const c = @import("c.zig");
 const command = @import("command.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Seat = @import("Seat.zig");
@@ -65,7 +66,7 @@ fn handleDisplayDestroy(wl_listener: ?*c.wl_listener, data: ?*c_void) callconv(.
 
 /// Called when a client binds our global
 fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), data));
+    const self = util.voidCast(Self, data.?);
     const wl_resource = c.wl_resource_create(
         wl_client,
         &c.zriver_control_v1_interface,
@@ -85,7 +86,7 @@ fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callcon
 
 /// Remove the resource from the hash map and free all stored args
 fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
     const id = c.wl_resource_get_id(wl_resource);
     const list = self.args_map.remove(id).?.value;
     for (list.items) |arg| list.allocator.free(arg);
@@ -97,7 +98,7 @@ fn destroy(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource) callconv(.C)
 }
 
 fn addArgument(wl_client: ?*c.wl_client, wl_resource: ?*c.wl_resource, arg: ?[*:0]const u8) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
     const id = c.wl_resource_get_id(wl_resource);
     const allocator = self.server.allocator;
 
@@ -119,10 +120,10 @@ fn runCommand(
     seat_wl_resource: ?*c.wl_resource,
     callback_id: u32,
 ) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
     // This can be null if the seat is inert, in which case we ignore the request
     const wlr_seat_client = c.wlr_seat_client_from_resource(seat_wl_resource) orelse return;
-    const seat = @ptrCast(*Seat, @alignCast(@alignOf(*Seat), wlr_seat_client.*.seat.*.data));
+    const seat = util.voidCast(Seat, wlr_seat_client.*.seat.*.data.?);
     const allocator = self.server.allocator;
 
     const callback_resource = c.wl_resource_create(
diff --git a/river/Cursor.zig b/river/Cursor.zig
index 9eede57..b716da7 100644
--- a/river/Cursor.zig
+++ b/river/Cursor.zig
@@ -21,6 +21,7 @@ const build_options = @import("build_options");
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const LayerSurface = @import("LayerSurface.zig");
 const Log = @import("log.zig").Log;
@@ -133,10 +134,7 @@ fn handleAxis(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     // This event is forwarded by the cursor when a pointer emits an axis event,
     // for example when you move the scroll wheel.
     const cursor = @fieldParentPtr(Self, "listen_axis", listener.?);
-    const event = @ptrCast(
-        *c.wlr_event_pointer_axis,
-        @alignCast(@alignOf(*c.wlr_event_pointer_axis), data),
-    );
+    const event = util.voidCast(c.wlr_event_pointer_axis, data.?);
 
     // Notify the client with pointer focus of the axis event.
     c.wlr_seat_pointer_notify_axis(
@@ -153,10 +151,7 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     // This event is forwarded by the cursor when a pointer emits a button
     // event.
     const self = @fieldParentPtr(Self, "listen_button", listener.?);
-    const event = @ptrCast(
-        *c.wlr_event_pointer_button,
-        @alignCast(@alignOf(*c.wlr_event_pointer_button), data),
-    );
+    const event = util.voidCast(c.wlr_event_pointer_button, data.?);
     var sx: f64 = undefined;
     var sy: f64 = undefined;
 
@@ -166,10 +161,7 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
         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),
-                );
+                const layer_surface = util.voidCast(LayerSurface, wlr_layer_surface.*.data.?);
                 self.seat.setFocusRaw(.{ .layer = layer_surface });
             }
         }
@@ -179,7 +171,7 @@ fn handleButton(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
         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));
+                const view = util.voidCast(View, wlr_xdg_surface.*.data.?);
                 self.seat.focus(view);
             }
         }
@@ -211,10 +203,7 @@ fn handleMotionAbsolute(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C)
     // 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),
-    );
+    const event = util.voidCast(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);
 }
@@ -223,10 +212,7 @@ 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),
-    );
+    const event = util.voidCast(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
@@ -239,10 +225,7 @@ fn handleMotion(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 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 event = util.voidCast(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
@@ -307,10 +290,7 @@ fn surfaceAt(self: Self, lx: f64, ly: f64, sx: *f64, sy: *f64) ?*c.wlr_surface {
     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;
-    const output = @ptrCast(
-        *Output,
-        @alignCast(@alignOf(*Output), wlr_output.*.data orelse return null),
-    );
+    const output = util.voidCast(Output, wlr_output.*.data orelse return null);
 
     // Get output-local coords from the layout coords
     var ox = lx;
diff --git a/river/DecorationManager.zig b/river/DecorationManager.zig
index 5a9abb4..0e280d1 100644
--- a/river/DecorationManager.zig
+++ b/river/DecorationManager.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Decoration = @import("Decoration.zig");
 const Server = @import("Server.zig");
@@ -46,10 +47,7 @@ pub fn init(self: *Self, server: *Server) !void {
 
 fn handleNewToplevelDecoration(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_toplevel_decoration", listener.?);
-    const wlr_xdg_toplevel_decoration = @ptrCast(
-        *c.wlr_xdg_toplevel_decoration_v1,
-        @alignCast(@alignOf(*c.wlr_xdg_toplevel_decoration_v1), data),
-    );
+    const wlr_xdg_toplevel_decoration = util.voidCast(c.wlr_xdg_toplevel_decoration_v1, data.?);
 
     const node = self.decorations.allocateNode(self.server.allocator) catch unreachable;
     node.data.init(self, wlr_xdg_toplevel_decoration);
diff --git a/river/InputManager.zig b/river/InputManager.zig
index 3a95346..62acc2d 100644
--- a/river/InputManager.zig
+++ b/river/InputManager.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Seat = @import("Seat.zig");
@@ -137,7 +138,7 @@ fn handleInhibitDeactivate(listener: ?*c.wl_listener, data: ?*c_void) callconv(.
 /// This event is raised by the backend when a new input device becomes available.
 fn handleNewInput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_input", listener.?);
-    const device = @ptrCast(*c.wlr_input_device, @alignCast(@alignOf(*c.wlr_input_device), data));
+    const device = util.voidCast(c.wlr_input_device, data.?);
 
     // TODO: suport multiple seats
     if (self.seats.first) |seat_node| {
diff --git a/river/Keyboard.zig b/river/Keyboard.zig
index cbfd6cd..8f57370 100644
--- a/river/Keyboard.zig
+++ b/river/Keyboard.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Seat = @import("Seat.zig");
@@ -72,10 +73,7 @@ pub fn init(self: *Self, seat: *Seat, wlr_input_device: *c.wlr_input_device) !vo
 fn handleKey(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     // This event is raised when a key is pressed or released.
     const self = @fieldParentPtr(Self, "listen_key", listener.?);
-    const event = @ptrCast(
-        *c.wlr_event_keyboard_key,
-        @alignCast(@alignOf(*c.wlr_event_keyboard_key), data),
-    );
+    const event = util.voidCast(c.wlr_event_keyboard_key, data.?);
 
     const wlr_keyboard = self.wlr_keyboard;
 
diff --git a/river/LayerSurface.zig b/river/LayerSurface.zig
index 088dd97..c33678f 100644
--- a/river/LayerSurface.zig
+++ b/river/LayerSurface.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Box = @import("Box.zig");
 const Log = @import("log.zig").Log;
@@ -187,7 +188,7 @@ fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 
 fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_popup", listener.?);
-    const wlr_xdg_popup = @ptrCast(*c.wlr_xdg_popup, @alignCast(@alignOf(*c.wlr_xdg_popup), data));
+    const wlr_xdg_popup = util.voidCast(c.wlr_xdg_popup, data.?);
     const allocator = self.output.root.server.allocator;
 
     // This will free itself on destroy
diff --git a/river/OutputStatus.zig b/river/OutputStatus.zig
index b08c195..563e42b 100644
--- a/river/OutputStatus.zig
+++ b/river/OutputStatus.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Output = @import("Output.zig");
@@ -45,7 +46,7 @@ pub fn init(self: *Self, output: *Output, wl_resource: *c.wl_resource) void {
 }
 
 fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, @ptrCast(*c_void, c.wl_resource_get_user_data(wl_resource)));
     const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
     self.output.status_trackers.remove(node);
 }
@@ -65,7 +66,7 @@ pub fn sendViewTags(self: Self) void {
             Log.Error.log("out of memory", .{});
             return;
         };
-        const ptr_u32 = @ptrCast(*u32, @alignCast(@alignOf(u32), ptr));
+        const ptr_u32 = util.voidCast(u32, ptr);
         ptr_u32.* = node.view.current_tags;
     }
     c.zriver_output_status_v1_send_view_tags(self.wl_resource, &view_tags);
diff --git a/river/Root.zig b/river/Root.zig
index 601b415..806d0a5 100644
--- a/river/Root.zig
+++ b/river/Root.zig
@@ -21,6 +21,7 @@ const std = @import("std");
 const build_options = @import("build_options");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Output = @import("Output.zig");
@@ -175,7 +176,7 @@ fn startTransaction(self: *Self) void {
 }
 
 fn handleTimeout(data: ?*c_void) callconv(.C) c_int {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), data));
+    const self = util.voidCast(Self, data.?);
 
     Log.Error.log("Transaction timed out. Some imperfect frames may be shown.", .{});
 
diff --git a/river/Seat.zig b/river/Seat.zig
index 5fc1320..40d64a9 100644
--- a/river/Seat.zig
+++ b/river/Seat.zig
@@ -21,6 +21,7 @@ const std = @import("std");
 
 const c = @import("c.zig");
 const command = @import("command.zig");
+const util = @import("util.zig");
 
 const Cursor = @import("Cursor.zig");
 const InputManager = @import("InputManager.zig");
@@ -328,9 +329,6 @@ fn addPointer(self: Self, device: *c.struct_wlr_input_device) void {
 
 fn handleRequestSetSelection(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_request_set_selection", listener.?);
-    const event = @ptrCast(
-        *c.wlr_seat_request_set_selection_event,
-        @alignCast(@alignOf(*c.wlr_seat_request_set_selection_event), data),
-    );
+    const event = util.voidCast(c.wlr_seat_request_set_selection_event, data.?);
     c.wlr_seat_set_selection(self.wlr_seat, event.source, event.serial);
 }
diff --git a/river/SeatStatus.zig b/river/SeatStatus.zig
index 3f50f90..1f6b408 100644
--- a/river/SeatStatus.zig
+++ b/river/SeatStatus.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Seat = @import("Seat.zig");
@@ -50,7 +51,7 @@ pub fn init(self: *Self, seat: *Seat, wl_resource: *c.wl_resource) void {
 }
 
 fn handleResourceDestroy(wl_resource: ?*c.wl_resource) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
     const node = @fieldParentPtr(std.SinglyLinkedList(Self).Node, "data", self);
     self.seat.status_trackers.remove(node);
 }
diff --git a/river/Server.zig b/river/Server.zig
index b10918a..27eebba 100644
--- a/river/Server.zig
+++ b/river/Server.zig
@@ -21,6 +21,7 @@ const build_options = @import("build_options");
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Config = @import("Config.zig");
 const Control = @import("Control.zig");
@@ -183,7 +184,7 @@ pub fn run(self: Self) void {
 
 fn handleNewOutput(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_output", listener.?);
-    const wlr_output = @ptrCast(*c.wlr_output, @alignCast(@alignOf(*c.wlr_output), data));
+    const wlr_output = util.voidCast(c.wlr_output, data.?);
     Log.Debug.log("New output {}", .{wlr_output.name});
     self.root.addOutput(wlr_output);
 }
@@ -192,7 +193,7 @@ fn handleNewXdgSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) v
     // This event is raised when wlr_xdg_shell receives a new xdg surface from a
     // client, either a toplevel (application window) or popup.
     const self = @fieldParentPtr(Self, "listen_new_xdg_surface", listener.?);
-    const wlr_xdg_surface = @ptrCast(*c.wlr_xdg_surface, @alignCast(@alignOf(*c.wlr_xdg_surface), data));
+    const wlr_xdg_surface = util.voidCast(c.wlr_xdg_surface, data.?);
 
     if (wlr_xdg_surface.role == .WLR_XDG_SURFACE_ROLE_POPUP) {
         Log.Debug.log("New xdg_popup", .{});
@@ -210,10 +211,7 @@ fn handleNewXdgSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) v
 /// This event is raised when the layer_shell recieves a new surface from a client.
 fn handleNewLayerSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_layer_surface", listener.?);
-    const wlr_layer_surface = @ptrCast(
-        *c.wlr_layer_surface_v1,
-        @alignCast(@alignOf(*c.wlr_layer_surface_v1), data),
-    );
+    const wlr_layer_surface = util.voidCast(c.wlr_layer_surface_v1, data.?);
 
     Log.Debug.log(
         "New layer surface: namespace {}, layer {}, anchor {}, size {}x{}, margin ({},{},{},{}), exclusive_zone {}",
@@ -252,17 +250,14 @@ fn handleNewLayerSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C)
     }
 
     // The layer surface will add itself to the proper list of the output on map
-    const output = @ptrCast(*Output, @alignCast(@alignOf(*Output), wlr_layer_surface.output.*.data));
+    const output = util.voidCast(Output, wlr_layer_surface.output.*.data.?);
     const node = self.allocator.create(std.TailQueue(LayerSurface).Node) catch unreachable;
     node.data.init(output, wlr_layer_surface);
 }
 
 fn handleNewXwaylandSurface(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_xwayland_surface", listener.?);
-    const wlr_xwayland_surface = @ptrCast(
-        *c.wlr_xwayland_surface,
-        @alignCast(@alignOf(*c.wlr_xwayland_surface), data),
-    );
+    const wlr_xwayland_surface = util.voidCast(c.wlr_xwayland_surface, data.?);
 
     if (wlr_xwayland_surface.override_redirect) {
         Log.Debug.log("New unmanaged xwayland surface", .{});
diff --git a/river/StatusManager.zig b/river/StatusManager.zig
index fb2630c..69f77fb 100644
--- a/river/StatusManager.zig
+++ b/river/StatusManager.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Log = @import("log.zig").Log;
 const Output = @import("Output.zig");
@@ -63,7 +64,7 @@ fn handleDisplayDestroy(wl_listener: ?*c.wl_listener, data: ?*c_void) callconv(.
 
 /// Called when a client binds our global
 fn bind(wl_client: ?*c.wl_client, data: ?*c_void, version: u32, id: u32) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), data));
+    const self = util.voidCast(Self, data.?);
     const wl_resource = c.wl_resource_create(
         wl_client,
         &c.zriver_status_manager_v1_interface,
@@ -87,10 +88,10 @@ fn getRiverOutputStatus(
     new_id: u32,
     output_wl_resource: ?*c.wl_resource,
 ) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
     // This can be null if the output is inert, in which case we ignore the request
     const wlr_output = c.wlr_output_from_resource(output_wl_resource) orelse return;
-    const output = @ptrCast(*Output, @alignCast(@alignOf(*Output), wlr_output.*.data));
+    const output = util.voidCast(Output, wlr_output.*.data.?);
     const allocator = self.server.allocator;
 
     const node = allocator.create(std.SinglyLinkedList(OutputStatus).Node) catch {
@@ -120,10 +121,10 @@ fn getRiverSeatStatus(
     new_id: u32,
     seat_wl_resource: ?*c.wl_resource,
 ) callconv(.C) void {
-    const self = @ptrCast(*Self, @alignCast(@alignOf(*Self), c.wl_resource_get_user_data(wl_resource)));
+    const self = util.voidCast(Self, c.wl_resource_get_user_data(wl_resource).?);
     // This can be null if the seat is inert, in which case we ignore the request
     const wlr_seat_client = c.wlr_seat_client_from_resource(seat_wl_resource) orelse return;
-    const seat = @ptrCast(*Seat, @alignCast(@alignOf(*Seat), wlr_seat_client.*.seat.*.data));
+    const seat = util.voidCast(Seat, wlr_seat_client.*.seat.*.data.?);
     const allocator = self.server.allocator;
 
     const node = allocator.create(std.SinglyLinkedList(SeatStatus).Node) catch {
diff --git a/river/View.zig b/river/View.zig
index 4f8f0bb..6d6e147 100644
--- a/river/View.zig
+++ b/river/View.zig
@@ -21,6 +21,7 @@ const build_options = @import("build_options");
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Box = @import("Box.zig");
 const Log = @import("log.zig").Log;
@@ -173,10 +174,7 @@ fn saveBuffersIterator(
     surface_y: c_int,
     data: ?*c_void,
 ) callconv(.C) void {
-    const saved_buffers = @ptrCast(
-        *std.ArrayList(SavedBuffer),
-        @alignCast(@alignOf(*std.ArrayList(SavedBuffer)), data),
-    );
+    const saved_buffers = util.voidCast(std.ArrayList(SavedBuffer), data.?);
     if (wlr_surface) |surface| {
         if (c.wlr_surface_has_buffer(surface)) {
             saved_buffers.append(.{
diff --git a/river/XdgPopup.zig b/river/XdgPopup.zig
index b4ee56c..d56b7a2 100644
--- a/river/XdgPopup.zig
+++ b/river/XdgPopup.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Box = @import("Box.zig");
 const Output = @import("Output.zig");
@@ -73,7 +74,7 @@ fn handleDestroy(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 /// Called when a new xdg popup is requested by the client
 fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_popup", listener.?);
-    const wlr_xdg_popup = @ptrCast(*c.wlr_xdg_popup, @alignCast(@alignOf(*c.wlr_xdg_popup), data));
+    const wlr_xdg_popup = util.voidCast(c.wlr_xdg_popup, data.?);
     const allocator = self.output.root.server.allocator;
 
     // This will free itself on destroy
diff --git a/river/XdgToplevel.zig b/river/XdgToplevel.zig
index c76312a..f329d9a 100644
--- a/river/XdgToplevel.zig
+++ b/river/XdgToplevel.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Box = @import("Box.zig");
 const Log = @import("log.zig").Log;
@@ -242,7 +243,7 @@ fn handleCommit(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
 /// Called when a new xdg popup is requested by the client
 fn handleNewPopup(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "listen_new_popup", listener.?);
-    const wlr_xdg_popup = @ptrCast(*c.wlr_xdg_popup, @alignCast(@alignOf(*c.wlr_xdg_popup), data));
+    const wlr_xdg_popup = util.voidCast(c.wlr_xdg_popup, data.?);
     const allocator = self.view.output.root.server.allocator;
 
     // This will free itself on destroy
diff --git a/river/XwaylandUnmanaged.zig b/river/XwaylandUnmanaged.zig
index fe33e73..ca70fa8 100644
--- a/river/XwaylandUnmanaged.zig
+++ b/river/XwaylandUnmanaged.zig
@@ -20,6 +20,7 @@ const Self = @This();
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Box = @import("Box.zig");
 const Log = @import("log.zig").Log;
@@ -71,10 +72,7 @@ pub fn surfaceAt(self: Self, ox: f64, oy: f64, sx: *f64, sy: *f64) ?*c.wlr_surfa
 
 fn handleRequestConfigure(listener: ?*c.wl_listener, data: ?*c_void) callconv(.C) void {
     const self = @fieldParentPtr(Self, "liseten_request_configure", listener.?);
-    const wlr_xwayland_surface_configure_event = @ptrCast(
-        *c.wlr_xwayland_surface_configure_event,
-        @alignCast(@alignOf(*c.wlr_xwayland_surface_configure_event), data),
-    );
+    const wlr_xwayland_surface_configure_event = util.voidCast(c.wlr_xwayland_surface_configure_event, data.?);
     c.wlr_xwayland_surface_configure(
         self.wlr_xwayland_surface,
         wlr_xwayland_surface_configure_event.x,
diff --git a/river/render.zig b/river/render.zig
index 14e8ae0..8fe41db 100644
--- a/river/render.zig
+++ b/river/render.zig
@@ -19,6 +19,7 @@ const build_options = @import("build_options");
 const std = @import("std");
 
 const c = @import("c.zig");
+const util = @import("util.zig");
 
 const Box = @import("Box.zig");
 const LayerSurface = @import("LayerSurface.zig");
@@ -193,7 +194,7 @@ fn renderSurfaceIterator(
     surface_y: c_int,
     data: ?*c_void,
 ) callconv(.C) void {
-    const rdata = @ptrCast(*SurfaceRenderData, @alignCast(@alignOf(SurfaceRenderData), data));
+    const rdata = util.voidCast(SurfaceRenderData, data.?);
 
     renderTexture(
         rdata.output.*,
diff --git a/river/util.zig b/river/util.zig
new file mode 100644
index 0000000..c1064cb
--- /dev/null
+++ b/river/util.zig
@@ -0,0 +1,26 @@
+// This file is part of river, a dynamic tiling wayland compositor.
+//
+// Copyright 2020 Isaac Freund
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+/// Take a pointer to c_void and cast it to a pointer to T. This function
+/// exists to avoid having the verbosity of the required alignment casts all
+/// over the code.
+pub fn voidCast(comptime T: type, ptr: var) *T {
+    // See https://github.com/ziglang/zig/issues/5618
+    if (@TypeOf(ptr) != *c_void)
+        @compileError("voidCast takes *c_void but " ++ @typeName(@TypeOf(ptr)) ++ " was provided");
+    return @ptrCast(*T, @alignCast(@alignOf(*T), ptr));
+}