lib/sys/src/x11/display.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2
3 pub const ParseError = error{
4 MissingDisplay,
5 RemoteDisplayUnsupported,
6 InvalidDisplay,
7 };
8
9 pub const Target = struct {
10 display: u32,
11 screen: u32,
12
13 pub fn socketPath(self: Target, buffer: []u8) ![]const u8 {
14 return std.fmt.bufPrint(buffer, "/tmp/.X11-unix/X{d}", .{self.display});
15 }
16 };
17
18 pub fn parse(value: ?[]const u8) ParseError!Target {
19 const raw = value orelse return error.MissingDisplay;
20 if (raw.len == 0) return error.MissingDisplay;
21
22 var rest = raw;
23 if (std.mem.indexOfScalar(u8, rest, ':')) |colon| {
24 const host = rest[0..colon];
25 if (host.len != 0 and !std.mem.eql(u8, host, "unix")) {
26 return error.RemoteDisplayUnsupported;
27 }
28 rest = rest[colon + 1 ..];
29 } else {
30 return error.InvalidDisplay;
31 }
32
33 var display_part = rest;
34 var screen_part: []const u8 = "";
35 if (std.mem.indexOfScalar(u8, rest, '.')) |dot| {
36 display_part = rest[0..dot];
37 screen_part = rest[dot + 1 ..];
38 }
39
40 const display = std.fmt.parseInt(u32, display_part, 10) catch return error.InvalidDisplay;
41 const screen = if (screen_part.len == 0)
42 0
43 else
44 std.fmt.parseInt(u32, screen_part, 10) catch return error.InvalidDisplay;
45
46 return .{ .display = display, .screen = screen };
47 }
48
49 test "parse accepts local display forms" {
50 try std.testing.expectEqual(Target{ .display = 0, .screen = 0 }, try parse(":0"));
51 try std.testing.expectEqual(Target{ .display = 1, .screen = 0 }, try parse(":1"));
52 try std.testing.expectEqual(Target{ .display = 0, .screen = 2 }, try parse(":0.2"));
53 try std.testing.expectEqual(Target{ .display = 10, .screen = 0 }, try parse("unix:10"));
54 }
55
56 test "parse rejects remote and malformed displays" {
57 try std.testing.expectError(error.MissingDisplay, parse(null));
58 try std.testing.expectError(error.MissingDisplay, parse(""));
59 try std.testing.expectError(error.RemoteDisplayUnsupported, parse("remote.host:0"));
60 try std.testing.expectError(error.InvalidDisplay, parse("0"));
61 try std.testing.expectError(error.InvalidDisplay, parse(":zero"));
62 }
63
64 test "socket path names the abstract unix socket" {
65 var buffer: [64]u8 = undefined;
66 const target = try parse(":1");
67 try std.testing.expectEqualStrings("/tmp/.X11-unix/X1", try target.socketPath(&buffer));
68 }