commit 8acfafa

shar  ·  2026-06-24 04:42:54 +0000 UTC
parent cdd869a
shm: create shared mem like protocol for buffers
3 files changed,  +750, -1
+3, -1
 1@@ -4,11 +4,13 @@
 2     .dependencies = .{
 3         .panthera = .{
 4             .url = "https://tangled.org/shar.tngl.sh/panthera/archive/main.tar.gz",
 5-            .hash = "panthera-0.0.0-GJ5Ew-zrBgAC_fcxweavj5y1jHvpGbWRMP7TjOF_Cezo",
 6+            .hash = "panthera-0.0.0-GJ5Ew7rrBgANMDHJLwaDlGSX2-Xl6tTLWemKLV03CQwA",
 7         },
 8     },
 9     .minimum_zig_version = "0.16.0",
10     .paths = .{
11+        "compositor/src/altaica_shm.zig",
12+        "compositor/src/protocol.zig",
13         "compositor/src/wire.zig",
14         "tools/scanner.zig",
15         "protocol",
+306, -0
  1@@ -0,0 +1,306 @@
  2+extern "env" fn log_error(ptr: [*]const u8, len: u32) void;
  3+
  4+const MAX_PIXELS = 2 * 1024 * 1024;
  5+const MAX_POOLS = 32;
  6+const MAX_BUFFERS = 64;
  7+const MAX_FREE = 64;
  8+
  9+const FreeBlock = struct { offset: usize, size: usize };
 10+
 11+pub const Pool = struct {
 12+    id: u32,
 13+    client_id: u32,
 14+    width: u32,
 15+    height: u32,
 16+    format: u32,
 17+    pixels: []u32,
 18+    pixel_offset: usize,
 19+};
 20+
 21+const BufferRef = struct {
 22+    pool_id: u32,
 23+    pool_client: u32,
 24+    x: u32, y: u32,
 25+    w: u32, h: u32,
 26+};
 27+
 28+var pixel_heap: [MAX_PIXELS]u32 align(4) = undefined;
 29+var pixel_used: usize = 0;
 30+var free_blocks: [MAX_FREE]FreeBlock = undefined;
 31+var free_count: u32 = 0;
 32+
 33+fn allocPixels(count: usize) ?[]u32 {
 34+    // Check free list first
 35+    for (0..free_count) |i| {
 36+        const fb = free_blocks[i];
 37+        if (fb.size >= count) {
 38+            const slice = pixel_heap[fb.offset..fb.offset + count];
 39+            if (fb.size == count) {
 40+                // Exact match — remove from free list
 41+                var j = i;
 42+                while (j + 1 < free_count) : (j += 1) free_blocks[j] = free_blocks[j + 1];
 43+                free_count -= 1;
 44+            } else {
 45+                free_blocks[i] = .{ .offset = fb.offset + count, .size = fb.size - count };
 46+            }
 47+            return slice;
 48+        }
 49+    }
 50+    // Bump allocate
 51+    if (pixel_used + count > MAX_PIXELS) {
 52+        const msg = "altaica_shm: pixel heap full\n";
 53+        log_error(msg, msg.len);
 54+        return null;
 55+    }
 56+    const slice = pixel_heap[pixel_used..pixel_used + count];
 57+    pixel_used += count;
 58+    return slice;
 59+}
 60+
 61+fn freePixels(offset: usize, size: usize) void {
 62+    if (size == 0) return;
 63+    // Coalesce with adjacent free blocks
 64+    for (0..free_count) |i| {
 65+        const fb = free_blocks[i];
 66+        const fb_end = fb.offset + fb.size;
 67+        if (fb_end == offset) {
 68+            free_blocks[i] = .{ .offset = fb.offset, .size = fb.size + size };
 69+            // Check if this now connects to another block
 70+            for (0..free_count) |j| {
 71+                if (j == i) continue;
 72+                const fj = free_blocks[j];
 73+                const fj_end = fj.offset + fj.size;
 74+                if (fj_end == fb.offset) {
 75+                    free_blocks[i].offset = fj.offset;
 76+                    free_blocks[i].size += fj.size;
 77+                    var k = j;
 78+                    while (k + 1 < free_count) : (k += 1) free_blocks[k] = free_blocks[k + 1];
 79+                    free_count -= 1;
 80+                    break;
 81+                }
 82+            }
 83+            return;
 84+        }
 85+        if (offset == fb_end) {
 86+            free_blocks[i] = .{ .offset = fb.offset, .size = fb.size + size };
 87+            // Check adjacency forward
 88+            const new_end = free_blocks[i].offset + free_blocks[i].size;
 89+            for (0..free_count) |j| {
 90+                if (j == i) continue;
 91+                const fj = free_blocks[j];
 92+                if (fj.offset == new_end) {
 93+                    free_blocks[i].size += fj.size;
 94+                    var k = j;
 95+                    while (k + 1 < free_count) : (k += 1) free_blocks[k] = free_blocks[k + 1];
 96+                    free_count -= 1;
 97+                    break;
 98+                }
 99+            }
100+            return;
101+        }
102+    }
103+    // Append new free block
104+    if (free_count < MAX_FREE) {
105+        free_blocks[free_count] = .{ .offset = offset, .size = size };
106+        free_count += 1;
107+    }
108+}
109+
110+fn pixelsOffset(pixels: []u32) usize {
111+    const heap_start = @intFromPtr(&pixel_heap);
112+    const pix_start = @intFromPtr(pixels.ptr);
113+    return pix_start - heap_start;
114+}
115+
116+pub const FORMAT_ARGB8888: u32 = 0;
117+pub const FORMAT_XRGB8888: u32 = 1;
118+
119+fn isValidFormat(fmt: u32) bool {
120+    return fmt == FORMAT_ARGB8888 or fmt == FORMAT_XRGB8888;
121+}
122+
123+fn formatHasAlpha(fmt: u32) bool {
124+    return fmt == FORMAT_ARGB8888;
125+}
126+
127+var pools: [MAX_POOLS]?Pool = [_]?Pool{null} ** MAX_POOLS;
128+var buffers: [MAX_BUFFERS]?BufferRef = [_]?BufferRef{null} ** MAX_BUFFERS;
129+
130+pub fn findPool(client_id: u32, pool_id: u32) ?*Pool {
131+    for (&pools) |*maybe| {
132+        const p = &(maybe.* orelse continue);
133+        if (p.client_id == client_id and p.id == pool_id) return p;
134+    }
135+    return null;
136+}
137+
138+pub fn createPool(client_id: u32, id: u32, width: u32, height: u32, format: u32) bool {
139+    if (!isValidFormat(format)) {
140+        const msg = "altaica_shm: invalid format\n";
141+        log_error(msg, msg.len);
142+        return false;
143+    }
144+    for (&pools) |*maybe| {
145+        if (maybe.* == null) {
146+            const pixels = allocPixels(width * height) orelse {
147+                const msg = "altaica_shm: pixel heap full\n";
148+                log_error(msg, msg.len);
149+                return false;
150+            };
151+            @memset(pixels, if (formatHasAlpha(format)) 0x00000000 else 0xFF000000);
152+            maybe.* = Pool{
153+                .id = id, .client_id = client_id,
154+                .width = width, .height = height,
155+                .format = format, .pixels = pixels,
156+                .pixel_offset = pixelsOffset(pixels),
157+            };
158+            return true;
159+        }
160+    }
161+    const msg = "altaica_shm: no free pool slots\n";
162+    log_error(msg, msg.len);
163+    return false;
164+}
165+
166+pub fn destroyPool(client_id: u32, pool_id: u32) void {
167+    for (&pools) |*maybe| {
168+        const p = &(maybe.* orelse continue);
169+        if (p.client_id == client_id and p.id == pool_id) {
170+            // Free all buffers referencing this pool
171+            for (&buffers) |*b| {
172+                const bref = b.* orelse continue;
173+                if (bref.pool_id == pool_id and bref.pool_client == client_id) {
174+                    b.* = null;
175+                }
176+            }
177+            // Free pixel memory
178+            freePixels(p.pixel_offset, p.width * p.height);
179+            maybe.* = null;
180+            return;
181+        }
182+    }
183+}
184+
185+pub fn resizePool(client_id: u32, pool_id: u32, new_w: u32, new_h: u32) bool {
186+    const pool = findPool(client_id, pool_id) orelse return false;
187+    const new_count = new_w * new_h;
188+    const old_count = pool.width * pool.height;
189+    if (new_count <= old_count) {
190+        pool.width = new_w;
191+        pool.height = new_h;
192+        return true;
193+    }
194+    const old_offset = pool.pixel_offset;
195+    const new_pixels = allocPixels(new_count) orelse return false;
196+    const copy_rows = @min(pool.height, new_h);
197+    const copy_cols = @min(pool.width, new_w);
198+    for (0..copy_rows) |row| {
199+        for (0..copy_cols) |col| {
200+            new_pixels[row * new_w + col] = pool.pixels[row * pool.width + col];
201+        }
202+    }
203+    for (old_count..new_count) |i| new_pixels[i] = 0xFF000000;
204+    freePixels(old_offset, old_count);
205+    pool.pixels = new_pixels;
206+    pool.pixel_offset = pixelsOffset(new_pixels);
207+    pool.width = new_w;
208+    pool.height = new_h;
209+    return true;
210+}
211+
212+pub fn uploadPixels(client_id: u32, pool_id: u32, x: u32, y: u32, w: u32, h: u32, data: []const u8) void {
213+    const pool = findPool(client_id, pool_id) orelse return;
214+    if (x + w > pool.width or y + h > pool.height) return;
215+    if (data.len < w * h * 4) return;
216+
217+    for (0..h) |row| {
218+        const dst_row = y + row;
219+        for (0..w) |col| {
220+            const dst_col = x + col;
221+            const si = (row * w + col) * 4;
222+            const a = data[si + 3];
223+            const r = data[si + 2];
224+            const g = data[si + 1];
225+            const b = data[si + 0];
226+            const di = dst_row * pool.width + dst_col;
227+            pool.pixels[di] = (@as(u32, a) << 24) | (@as(u32, r) << 16) | (@as(u32, g) << 8) | b;
228+        }
229+    }
230+}
231+
232+pub fn findBuffer(idx: u32) ?BufferRef {
233+    if (idx >= MAX_BUFFERS) return null;
234+    return buffers[idx];
235+}
236+
237+pub fn allocBufferIdx() ?u32 {
238+    for (&buffers, 0..) |*maybe, i| {
239+        if (maybe.* == null) {
240+            return @as(u32, @intCast(i));
241+        }
242+    }
243+    return null;
244+}
245+
246+pub fn setBuffer(idx: u32, ref: BufferRef) void {
247+    if (idx < MAX_BUFFERS) buffers[idx] = ref;
248+}
249+
250+pub fn freeBuffer(idx: u32) void {
251+    if (idx < MAX_BUFFERS) buffers[idx] = null;
252+}
253+
254+/// Copy a sub-region from pool to a destination surface with different strides
255+pub fn copyRegion(
256+    pool_id: u32,
257+    pool_client: u32,
258+    dst: []u32,
259+    dst_stride: u32,
260+    src_x: u32,
261+    src_y: u32,
262+    w: u32,
263+    h: u32,
264+    dst_x: u32,
265+    dst_y: u32,
266+) bool {
267+    const pool = findPool(pool_client, pool_id) orelse return false;
268+    if (src_x + w > pool.width or src_y + h > pool.height) return false;
269+    for (0..h) |row| {
270+        for (0..w) |col| {
271+            const si = (src_y + row) * pool.width + (src_x + col);
272+            const di = (dst_y + row) * dst_stride + (dst_x + col);
273+            if (di >= dst.len) return false;
274+            dst[di] = pool.pixels[si];
275+        }
276+    }
277+    return true;
278+}
279+
280+pub fn copyFromPool(pool_id: u32, pool_client: u32, dst: []u32, w: u32, h: u32, sx: u32, sy: u32) bool {
281+    const pool = findPool(pool_client, pool_id) orelse return false;
282+    if (sx + w > pool.width or sy + h > pool.height) return false;
283+    if (dst.len < w * h) return false;
284+    for (0..h) |row| {
285+        for (0..w) |col| {
286+            const si = (sy + row) * pool.width + (sx + col);
287+            dst[row * w + col] = pool.pixels[si];
288+        }
289+    }
290+    return true;
291+}
292+
293+pub fn copyFromPoolScaled(pool_id: u32, pool_client: u32, dst: []u32, dst_w: u32, dst_h: u32, src_x: u32, src_y: u32, src_w: u32, src_h: u32) bool {
294+    const pool = findPool(pool_client, pool_id) orelse return false;
295+    if (src_x + src_w > pool.width or src_y + src_h > pool.height) return false;
296+    if (dst.len < dst_w * dst_h) return false;
297+    if (src_w == 0 or src_h == 0 or dst_w == 0 or dst_h == 0) return false;
298+    for (0..dst_h) |dy| {
299+        const sy = (dy * src_h) / dst_h;
300+        for (0..dst_w) |dx| {
301+            const sx = (dx * src_w) / dst_w;
302+            const si = (src_y + sy) * pool.width + (src_x + sx);
303+            dst[dy * dst_w + dx] = pool.pixels[si];
304+        }
305+    }
306+    return true;
307+}
+441, -0
  1@@ -0,0 +1,441 @@
  2+const wire = @import("wire.zig");
  3+const ArgType = wire.ArgType;
  4+
  5+pub const Interface = enum(u8) {
  6+    altaica_shm,
  7+    altaica_shm_pool,
  8+    wl_buffer,
  9+    wl_callback,
 10+    wl_compositor,
 11+    wl_data_device,
 12+    wl_data_device_manager,
 13+    wl_data_offer,
 14+    wl_data_source,
 15+    wl_display,
 16+    wl_fixes,
 17+    wl_keyboard,
 18+    wl_output,
 19+    wl_pointer,
 20+    wl_region,
 21+    wl_registry,
 22+    wl_seat,
 23+    wl_shell,
 24+    wl_shell_surface,
 25+    wl_shm,
 26+    wl_shm_pool,
 27+    wl_subcompositor,
 28+    wl_subsurface,
 29+    wl_surface,
 30+    wl_touch,
 31+    wp_viewport,
 32+    wp_viewporter,
 33+    xdg_popup,
 34+    xdg_positioner,
 35+    xdg_surface,
 36+    xdg_toplevel,
 37+    xdg_wm_base,
 38+};
 39+
 40+pub const MessageSig = struct {
 41+    opcode: u16,
 42+    args: []const ArgType,
 43+};
 44+
 45+pub fn requestSig(iface: Interface, opcode: u16) ?[]const ArgType {
 46+    const sigs = requestSigs(iface);
 47+    for (sigs) |s| if (s.opcode == opcode) return s.args;
 48+    return null;
 49+}
 50+
 51+pub fn eventSig(iface: Interface, opcode: u16) ?[]const ArgType {
 52+    const sigs = eventSigs(iface);
 53+    for (sigs) |s| if (s.opcode == opcode) return s.args;
 54+    return null;
 55+}
 56+
 57+fn requestSigs(iface: Interface) []const MessageSig {
 58+    return switch (iface) {
 59+        .altaica_shm => &.{
 60+            .{ .opcode = 0, .args = &.{.new_id, .int, .int, .uint} },
 61+        },
 62+        .altaica_shm_pool => &.{
 63+            .{ .opcode = 0, .args = &.{.int, .int, .int, .int} },
 64+            .{ .opcode = 1, .args = &.{.new_id, .int, .int, .int, .int} },
 65+            .{ .opcode = 2, .args = &.{.int, .int} },
 66+            .{ .opcode = 3, .args = &.{} },
 67+        },
 68+        .wl_buffer => &.{
 69+            .{ .opcode = 0, .args = &.{} },
 70+        },
 71+        .wl_callback => &.{},
 72+        .wl_compositor => &.{
 73+            .{ .opcode = 0, .args = &.{.new_id} },
 74+            .{ .opcode = 1, .args = &.{.new_id} },
 75+            .{ .opcode = 2, .args = &.{} },
 76+        },
 77+        .wl_data_device => &.{
 78+            .{ .opcode = 0, .args = &.{.object, .object, .object, .uint} },
 79+            .{ .opcode = 1, .args = &.{.object, .uint} },
 80+            .{ .opcode = 2, .args = &.{} },
 81+        },
 82+        .wl_data_device_manager => &.{
 83+            .{ .opcode = 0, .args = &.{.new_id} },
 84+            .{ .opcode = 1, .args = &.{.new_id, .object} },
 85+            .{ .opcode = 2, .args = &.{} },
 86+        },
 87+        .wl_data_offer => &.{
 88+            .{ .opcode = 0, .args = &.{.uint, .string} },
 89+            .{ .opcode = 1, .args = &.{.string, .fd} },
 90+            .{ .opcode = 2, .args = &.{} },
 91+            .{ .opcode = 3, .args = &.{} },
 92+            .{ .opcode = 4, .args = &.{.uint, .uint} },
 93+        },
 94+        .wl_data_source => &.{
 95+            .{ .opcode = 0, .args = &.{.string} },
 96+            .{ .opcode = 1, .args = &.{} },
 97+            .{ .opcode = 2, .args = &.{.uint} },
 98+        },
 99+        .wl_display => &.{
100+            .{ .opcode = 0, .args = &.{.new_id} },
101+            .{ .opcode = 1, .args = &.{.new_id} },
102+        },
103+        .wl_fixes => &.{
104+            .{ .opcode = 0, .args = &.{} },
105+            .{ .opcode = 1, .args = &.{.object} },
106+            .{ .opcode = 2, .args = &.{.object, .uint} },
107+        },
108+        .wl_keyboard => &.{
109+            .{ .opcode = 0, .args = &.{} },
110+        },
111+        .wl_output => &.{
112+            .{ .opcode = 0, .args = &.{} },
113+        },
114+        .wl_pointer => &.{
115+            .{ .opcode = 0, .args = &.{.uint, .object, .int, .int} },
116+            .{ .opcode = 1, .args = &.{} },
117+        },
118+        .wl_region => &.{
119+            .{ .opcode = 0, .args = &.{} },
120+            .{ .opcode = 1, .args = &.{.int, .int, .int, .int} },
121+            .{ .opcode = 2, .args = &.{.int, .int, .int, .int} },
122+        },
123+        .wl_registry => &.{
124+            .{ .opcode = 0, .args = &.{.uint, .new_id} },
125+        },
126+        .wl_seat => &.{
127+            .{ .opcode = 0, .args = &.{.new_id} },
128+            .{ .opcode = 1, .args = &.{.new_id} },
129+            .{ .opcode = 2, .args = &.{.new_id} },
130+            .{ .opcode = 3, .args = &.{} },
131+        },
132+        .wl_shell => &.{
133+            .{ .opcode = 0, .args = &.{.new_id, .object} },
134+        },
135+        .wl_shell_surface => &.{
136+            .{ .opcode = 0, .args = &.{.uint} },
137+            .{ .opcode = 1, .args = &.{.object, .uint} },
138+            .{ .opcode = 2, .args = &.{.object, .uint, .uint} },
139+            .{ .opcode = 3, .args = &.{} },
140+            .{ .opcode = 4, .args = &.{.object, .int, .int, .uint} },
141+            .{ .opcode = 5, .args = &.{.uint, .uint, .object} },
142+            .{ .opcode = 6, .args = &.{.object, .uint, .object, .int, .int, .uint} },
143+            .{ .opcode = 7, .args = &.{.object} },
144+            .{ .opcode = 8, .args = &.{.string} },
145+            .{ .opcode = 9, .args = &.{.string} },
146+        },
147+        .wl_shm => &.{
148+            .{ .opcode = 0, .args = &.{.new_id, .fd, .int} },
149+            .{ .opcode = 1, .args = &.{} },
150+        },
151+        .wl_shm_pool => &.{
152+            .{ .opcode = 0, .args = &.{.new_id, .int, .int, .int, .int, .uint} },
153+            .{ .opcode = 1, .args = &.{} },
154+            .{ .opcode = 2, .args = &.{.int} },
155+        },
156+        .wl_subcompositor => &.{
157+            .{ .opcode = 0, .args = &.{} },
158+            .{ .opcode = 1, .args = &.{.new_id, .object, .object} },
159+        },
160+        .wl_subsurface => &.{
161+            .{ .opcode = 0, .args = &.{} },
162+            .{ .opcode = 1, .args = &.{.int, .int} },
163+            .{ .opcode = 2, .args = &.{.object} },
164+            .{ .opcode = 3, .args = &.{.object} },
165+            .{ .opcode = 4, .args = &.{} },
166+            .{ .opcode = 5, .args = &.{} },
167+        },
168+        .wl_surface => &.{
169+            .{ .opcode = 0, .args = &.{} },
170+            .{ .opcode = 1, .args = &.{.object, .int, .int} },
171+            .{ .opcode = 2, .args = &.{.int, .int, .int, .int} },
172+            .{ .opcode = 3, .args = &.{.new_id} },
173+            .{ .opcode = 4, .args = &.{.object} },
174+            .{ .opcode = 5, .args = &.{.object} },
175+            .{ .opcode = 6, .args = &.{} },
176+            .{ .opcode = 7, .args = &.{.int} },
177+            .{ .opcode = 8, .args = &.{.int} },
178+            .{ .opcode = 9, .args = &.{.int, .int, .int, .int} },
179+            .{ .opcode = 10, .args = &.{.int, .int} },
180+            .{ .opcode = 11, .args = &.{.new_id} },
181+        },
182+        .wl_touch => &.{
183+            .{ .opcode = 0, .args = &.{} },
184+        },
185+        .wp_viewport => &.{
186+            .{ .opcode = 0, .args = &.{} },
187+            .{ .opcode = 1, .args = &.{.fixed, .fixed, .fixed, .fixed} },
188+            .{ .opcode = 2, .args = &.{.int, .int} },
189+        },
190+        .wp_viewporter => &.{
191+            .{ .opcode = 0, .args = &.{} },
192+            .{ .opcode = 1, .args = &.{.new_id, .object} },
193+        },
194+        .xdg_popup => &.{
195+            .{ .opcode = 0, .args = &.{} },
196+            .{ .opcode = 1, .args = &.{.object, .uint} },
197+            .{ .opcode = 2, .args = &.{.object, .uint} },
198+        },
199+        .xdg_positioner => &.{
200+            .{ .opcode = 0, .args = &.{} },
201+            .{ .opcode = 1, .args = &.{.int, .int} },
202+            .{ .opcode = 2, .args = &.{.int, .int, .int, .int} },
203+            .{ .opcode = 3, .args = &.{.uint} },
204+            .{ .opcode = 4, .args = &.{.uint} },
205+            .{ .opcode = 5, .args = &.{.uint} },
206+            .{ .opcode = 6, .args = &.{.int, .int} },
207+            .{ .opcode = 7, .args = &.{} },
208+            .{ .opcode = 8, .args = &.{.int, .int} },
209+            .{ .opcode = 9, .args = &.{.uint} },
210+        },
211+        .xdg_surface => &.{
212+            .{ .opcode = 0, .args = &.{} },
213+            .{ .opcode = 1, .args = &.{.new_id} },
214+            .{ .opcode = 2, .args = &.{.new_id, .object, .object} },
215+            .{ .opcode = 3, .args = &.{.int, .int, .int, .int} },
216+            .{ .opcode = 4, .args = &.{.uint} },
217+        },
218+        .xdg_toplevel => &.{
219+            .{ .opcode = 0, .args = &.{} },
220+            .{ .opcode = 1, .args = &.{.object} },
221+            .{ .opcode = 2, .args = &.{.string} },
222+            .{ .opcode = 3, .args = &.{.string} },
223+            .{ .opcode = 4, .args = &.{.object, .uint, .int, .int} },
224+            .{ .opcode = 5, .args = &.{.object, .uint} },
225+            .{ .opcode = 6, .args = &.{.object, .uint, .uint} },
226+            .{ .opcode = 7, .args = &.{.int, .int} },
227+            .{ .opcode = 8, .args = &.{.int, .int} },
228+            .{ .opcode = 9, .args = &.{} },
229+            .{ .opcode = 10, .args = &.{} },
230+            .{ .opcode = 11, .args = &.{.object} },
231+            .{ .opcode = 12, .args = &.{} },
232+            .{ .opcode = 13, .args = &.{} },
233+        },
234+        .xdg_wm_base => &.{
235+            .{ .opcode = 0, .args = &.{} },
236+            .{ .opcode = 1, .args = &.{.new_id} },
237+            .{ .opcode = 2, .args = &.{.new_id, .object} },
238+            .{ .opcode = 3, .args = &.{.uint} },
239+        },
240+    };
241+}
242+
243+fn eventSigs(iface: Interface) []const MessageSig {
244+    return switch (iface) {
245+        .altaica_shm => &.{},
246+        .altaica_shm_pool => &.{},
247+        .wl_buffer => &.{
248+            .{ .opcode = 0, .args = &.{} },
249+        },
250+        .wl_callback => &.{
251+            .{ .opcode = 0, .args = &.{.uint} },
252+        },
253+        .wl_compositor => &.{},
254+        .wl_data_device => &.{
255+            .{ .opcode = 0, .args = &.{.new_id} },
256+            .{ .opcode = 1, .args = &.{.uint, .object, .fixed, .fixed, .object} },
257+            .{ .opcode = 2, .args = &.{} },
258+            .{ .opcode = 3, .args = &.{.uint, .fixed, .fixed} },
259+            .{ .opcode = 4, .args = &.{} },
260+            .{ .opcode = 5, .args = &.{.object} },
261+        },
262+        .wl_data_device_manager => &.{},
263+        .wl_data_offer => &.{
264+            .{ .opcode = 0, .args = &.{.string} },
265+            .{ .opcode = 1, .args = &.{.uint} },
266+            .{ .opcode = 2, .args = &.{.uint} },
267+        },
268+        .wl_data_source => &.{
269+            .{ .opcode = 0, .args = &.{.string} },
270+            .{ .opcode = 1, .args = &.{.string, .fd} },
271+            .{ .opcode = 2, .args = &.{} },
272+            .{ .opcode = 3, .args = &.{} },
273+            .{ .opcode = 4, .args = &.{} },
274+            .{ .opcode = 5, .args = &.{.uint} },
275+        },
276+        .wl_display => &.{
277+            .{ .opcode = 0, .args = &.{.object, .uint, .string} },
278+            .{ .opcode = 1, .args = &.{.uint} },
279+        },
280+        .wl_fixes => &.{},
281+        .wl_keyboard => &.{
282+            .{ .opcode = 0, .args = &.{.uint, .fd, .uint} },
283+            .{ .opcode = 1, .args = &.{.uint, .object, .array} },
284+            .{ .opcode = 2, .args = &.{.uint, .object} },
285+            .{ .opcode = 3, .args = &.{.uint, .uint, .uint, .uint} },
286+            .{ .opcode = 4, .args = &.{.uint, .uint, .uint, .uint, .uint} },
287+            .{ .opcode = 5, .args = &.{.int, .int} },
288+        },
289+        .wl_output => &.{
290+            .{ .opcode = 0, .args = &.{.int, .int, .int, .int, .int, .string, .string, .int} },
291+            .{ .opcode = 1, .args = &.{.uint, .int, .int, .int} },
292+            .{ .opcode = 2, .args = &.{} },
293+            .{ .opcode = 3, .args = &.{.int} },
294+            .{ .opcode = 4, .args = &.{.string} },
295+            .{ .opcode = 5, .args = &.{.string} },
296+        },
297+        .wl_pointer => &.{
298+            .{ .opcode = 0, .args = &.{.uint, .object, .fixed, .fixed} },
299+            .{ .opcode = 1, .args = &.{.uint, .object} },
300+            .{ .opcode = 2, .args = &.{.uint, .fixed, .fixed} },
301+            .{ .opcode = 3, .args = &.{.uint, .uint, .uint, .uint} },
302+            .{ .opcode = 4, .args = &.{.uint, .uint, .fixed} },
303+            .{ .opcode = 5, .args = &.{} },
304+            .{ .opcode = 6, .args = &.{.uint} },
305+            .{ .opcode = 7, .args = &.{.uint, .uint} },
306+            .{ .opcode = 8, .args = &.{.uint, .int} },
307+            .{ .opcode = 9, .args = &.{.uint, .int} },
308+            .{ .opcode = 10, .args = &.{.uint, .uint} },
309+            .{ .opcode = 11, .args = &.{.fixed, .fixed} },
310+        },
311+        .wl_region => &.{},
312+        .wl_registry => &.{
313+            .{ .opcode = 0, .args = &.{.uint, .string, .uint} },
314+            .{ .opcode = 1, .args = &.{.uint} },
315+        },
316+        .wl_seat => &.{
317+            .{ .opcode = 0, .args = &.{.uint} },
318+            .{ .opcode = 1, .args = &.{.string} },
319+        },
320+        .wl_shell => &.{},
321+        .wl_shell_surface => &.{
322+            .{ .opcode = 0, .args = &.{.uint} },
323+            .{ .opcode = 1, .args = &.{.uint, .int, .int} },
324+            .{ .opcode = 2, .args = &.{} },
325+        },
326+        .wl_shm => &.{
327+            .{ .opcode = 0, .args = &.{.uint} },
328+        },
329+        .wl_shm_pool => &.{},
330+        .wl_subcompositor => &.{},
331+        .wl_subsurface => &.{},
332+        .wl_surface => &.{
333+            .{ .opcode = 0, .args = &.{.object} },
334+            .{ .opcode = 1, .args = &.{.object} },
335+            .{ .opcode = 2, .args = &.{.int} },
336+            .{ .opcode = 3, .args = &.{.uint} },
337+        },
338+        .wl_touch => &.{
339+            .{ .opcode = 0, .args = &.{.uint, .uint, .object, .int, .fixed, .fixed} },
340+            .{ .opcode = 1, .args = &.{.uint, .uint, .int} },
341+            .{ .opcode = 2, .args = &.{.uint, .int, .fixed, .fixed} },
342+            .{ .opcode = 3, .args = &.{} },
343+            .{ .opcode = 4, .args = &.{} },
344+            .{ .opcode = 5, .args = &.{.int, .fixed, .fixed} },
345+            .{ .opcode = 6, .args = &.{.int, .fixed} },
346+        },
347+        .wp_viewport => &.{},
348+        .wp_viewporter => &.{},
349+        .xdg_popup => &.{
350+            .{ .opcode = 0, .args = &.{.int, .int, .int, .int} },
351+            .{ .opcode = 1, .args = &.{} },
352+            .{ .opcode = 2, .args = &.{.uint} },
353+        },
354+        .xdg_positioner => &.{},
355+        .xdg_surface => &.{
356+            .{ .opcode = 0, .args = &.{.uint} },
357+        },
358+        .xdg_toplevel => &.{
359+            .{ .opcode = 0, .args = &.{.int, .int, .array} },
360+            .{ .opcode = 1, .args = &.{} },
361+            .{ .opcode = 2, .args = &.{.int, .int} },
362+            .{ .opcode = 3, .args = &.{.array} },
363+        },
364+        .xdg_wm_base => &.{
365+            .{ .opcode = 0, .args = &.{.uint} },
366+        },
367+    };
368+}
369+
370+pub fn globalName(iface: Interface) []const u8 {
371+    return switch (iface) {
372+        .altaica_shm => "altaica_shm",
373+        .altaica_shm_pool => "altaica_shm_pool",
374+        .wl_buffer => "wl_buffer",
375+        .wl_callback => "wl_callback",
376+        .wl_compositor => "wl_compositor",
377+        .wl_data_device => "wl_data_device",
378+        .wl_data_device_manager => "wl_data_device_manager",
379+        .wl_data_offer => "wl_data_offer",
380+        .wl_data_source => "wl_data_source",
381+        .wl_display => "wl_display",
382+        .wl_fixes => "wl_fixes",
383+        .wl_keyboard => "wl_keyboard",
384+        .wl_output => "wl_output",
385+        .wl_pointer => "wl_pointer",
386+        .wl_region => "wl_region",
387+        .wl_registry => "wl_registry",
388+        .wl_seat => "wl_seat",
389+        .wl_shell => "wl_shell",
390+        .wl_shell_surface => "wl_shell_surface",
391+        .wl_shm => "wl_shm",
392+        .wl_shm_pool => "wl_shm_pool",
393+        .wl_subcompositor => "wl_subcompositor",
394+        .wl_subsurface => "wl_subsurface",
395+        .wl_surface => "wl_surface",
396+        .wl_touch => "wl_touch",
397+        .wp_viewport => "wp_viewport",
398+        .wp_viewporter => "wp_viewporter",
399+        .xdg_popup => "xdg_popup",
400+        .xdg_positioner => "xdg_positioner",
401+        .xdg_surface => "xdg_surface",
402+        .xdg_toplevel => "xdg_toplevel",
403+        .xdg_wm_base => "xdg_wm_base",
404+    };
405+}
406+
407+pub fn globalVersion(iface: Interface) u32 {
408+    return switch (iface) {
409+        .altaica_shm => 1,
410+        .altaica_shm_pool => 1,
411+        .wl_buffer => 1,
412+        .wl_callback => 1,
413+        .wl_compositor => 7,
414+        .wl_data_device => 4,
415+        .wl_data_device_manager => 4,
416+        .wl_data_offer => 4,
417+        .wl_data_source => 4,
418+        .wl_display => 1,
419+        .wl_fixes => 2,
420+        .wl_keyboard => 11,
421+        .wl_output => 4,
422+        .wl_pointer => 11,
423+        .wl_region => 7,
424+        .wl_registry => 1,
425+        .wl_seat => 11,
426+        .wl_shell => 1,
427+        .wl_shell_surface => 1,
428+        .wl_shm => 2,
429+        .wl_shm_pool => 2,
430+        .wl_subcompositor => 1,
431+        .wl_subsurface => 1,
432+        .wl_surface => 7,
433+        .wl_touch => 11,
434+        .wp_viewport => 1,
435+        .wp_viewporter => 1,
436+        .xdg_popup => 7,
437+        .xdg_positioner => 7,
438+        .xdg_surface => 7,
439+        .xdg_toplevel => 7,
440+        .xdg_wm_base => 7,
441+    };
442+}