commit 926f524

shar  ·  2026-06-24 05:55:36 +0000 UTC
parent df9a5b4
wire protocol: scaffold viewporter protocol; shm: fixes
2 files changed,  +240, -41
+34, -41
 1@@ -1,6 +1,6 @@
 2 extern "env" fn log_error(ptr: [*]const u8, len: u32) void;
 3 
 4-const MAX_PIXELS = 2 * 1024 * 1024;
 5+const MAX_PIXELS = 4096 * 1440;
 6 const MAX_POOLS = 32;
 7 const MAX_BUFFERS = 64;
 8 const MAX_FREE = 64;
 9@@ -60,56 +60,49 @@ fn allocPixels(count: usize) ?[]u32 {
10 fn freePixels(offset: usize, size: usize) void {
11     if (size == 0) return;
12     // Coalesce with adjacent free blocks
13-    for (0..free_count) |i| {
14+    var coalesced_offset = offset;
15+    var coalesced_size = size;
16+    var added = false;
17+    var i: usize = 0;
18+    while (i < free_count) {
19         const fb = free_blocks[i];
20         const fb_end = fb.offset + fb.size;
21-        if (fb_end == offset) {
22-            free_blocks[i] = .{ .offset = fb.offset, .size = fb.size + size };
23-            // Check if this now connects to another block
24-            for (0..free_count) |j| {
25-                if (j == i) continue;
26-                const fj = free_blocks[j];
27-                const fj_end = fj.offset + fj.size;
28-                if (fj_end == fb.offset) {
29-                    free_blocks[i].offset = fj.offset;
30-                    free_blocks[i].size += fj.size;
31-                    var k = j;
32-                    while (k + 1 < free_count) : (k += 1) free_blocks[k] = free_blocks[k + 1];
33-                    free_count -= 1;
34-                    break;
35-                }
36-            }
37-            return;
38-        }
39-        if (offset == fb_end) {
40-            free_blocks[i] = .{ .offset = fb.offset, .size = fb.size + size };
41-            // Check adjacency forward
42-            const new_end = free_blocks[i].offset + free_blocks[i].size;
43-            for (0..free_count) |j| {
44-                if (j == i) continue;
45-                const fj = free_blocks[j];
46-                if (fj.offset == new_end) {
47-                    free_blocks[i].size += fj.size;
48-                    var k = j;
49-                    while (k + 1 < free_count) : (k += 1) free_blocks[k] = free_blocks[k + 1];
50-                    free_count -= 1;
51-                    break;
52-                }
53-            }
54-            return;
55+        if (fb_end == coalesced_offset) {
56+            coalesced_offset = fb.offset;
57+            coalesced_size += fb.size;
58+            // Remove this block
59+            var j = i;
60+            while (j + 1 < free_count) : (j += 1) free_blocks[j] = free_blocks[j + 1];
61+            free_count -= 1;
62+            added = true;
63+        } else if (coalesced_offset + coalesced_size == fb.offset) {
64+            coalesced_size += fb.size;
65+            var j = i;
66+            while (j + 1 < free_count) : (j += 1) free_blocks[j] = free_blocks[j + 1];
67+            free_count -= 1;
68+            added = true;
69+        } else {
70+            i += 1;
71         }
72     }
73-    // Append new free block
74-    if (free_count < MAX_FREE) {
75-        free_blocks[free_count] = .{ .offset = offset, .size = size };
76-        free_count += 1;
77+    // If the coalesced block is at the end of the used heap, pop it.
78+    if (coalesced_offset + coalesced_size == pixel_used) {
79+        pixel_used = coalesced_offset;
80+        return;
81+    }
82+    // Store the coalesced block
83+    if (!added) {
84+        if (free_count < MAX_FREE) {
85+            free_blocks[free_count] = .{ .offset = coalesced_offset, .size = coalesced_size };
86+            free_count += 1;
87+        }
88     }
89 }
90 
91 fn pixelsOffset(pixels: []u32) usize {
92     const heap_start = @intFromPtr(&pixel_heap);
93     const pix_start = @intFromPtr(pixels.ptr);
94-    return pix_start - heap_start;
95+    return (pix_start - heap_start) / @sizeOf(u32);
96 }
97 
98 pub const FORMAT_ARGB8888: u32 = 0;
+206, -0
  1@@ -0,0 +1,206 @@
  2+const wire = @import("wire.zig");
  3+const proto = @import("protocol.zig");
  4+const objects = @import("objects.zig");
  5+
  6+const MAX_VIEWPORTS = 16;
  7+
  8+pub const ViewportState = struct {
  9+    source_set: bool,
 10+    src_x: i32,
 11+    src_y: i32,
 12+    src_w: i32,
 13+    src_h: i32,
 14+    dest_set: bool,
 15+    dst_w: i32,
 16+    dst_h: i32,
 17+};
 18+
 19+const ViewportData = struct {
 20+    obj_id: u32,
 21+    client_id: u32,
 22+    surf_id: u32,
 23+    surf_client: u32,
 24+    state: ViewportState,
 25+};
 26+
 27+var viewport_data: [MAX_VIEWPORTS]?ViewportData = [_]?ViewportData{null} ** MAX_VIEWPORTS;
 28+
 29+pub fn init() void {
 30+    for (&viewport_data) |*v| v.* = null;
 31+}
 32+
 33+fn findFreeSlot() ?*ViewportData {
 34+    for (&viewport_data) |*maybe| {
 35+        if (maybe.* == null) {
 36+            maybe.* = ViewportData{
 37+                .obj_id = 0,
 38+                .client_id = 0,
 39+                .surf_id = 0,
 40+                .surf_client = 0,
 41+                .state = undefined,
 42+            };
 43+            return &(maybe.* orelse unreachable);
 44+        }
 45+    }
 46+    return null;
 47+}
 48+
 49+fn findViewport(client_id: u32, obj_id: u32) ?*ViewportData {
 50+    for (&viewport_data) |*maybe| {
 51+        const v = &(maybe.* orelse continue);
 52+        if (v.client_id == client_id and v.obj_id == obj_id) return v;
 53+    }
 54+    return null;
 55+}
 56+
 57+pub fn findViewportForSurface(surf_client: u32, surf_id: u32) ?*ViewportState {
 58+    for (&viewport_data) |*maybe| {
 59+        const v = &(maybe.* orelse continue);
 60+        if (v.surf_client == surf_client and v.surf_id == surf_id) return &v.state;
 61+    }
 62+    return null;
 63+}
 64+
 65+pub fn createViewport(client_id: u32, obj_id: u32, surf_client: u32, surf_id: u32) bool {
 66+    const slot = findFreeSlot() orelse return false;
 67+    slot.* = .{
 68+        .obj_id = obj_id,
 69+        .client_id = client_id,
 70+        .surf_id = surf_id,
 71+        .surf_client = surf_client,
 72+        .state = .{
 73+            .source_set = false,
 74+            .src_x = 0, .src_y = 0, .src_w = 0, .src_h = 0,
 75+            .dest_set = false,
 76+            .dst_w = 0, .dst_h = 0,
 77+        },
 78+    };
 79+    return true;
 80+}
 81+
 82+pub fn destroyViewport(client_id: u32, obj_id: u32) void {
 83+    for (&viewport_data) |*maybe| {
 84+        const v = &(maybe.* orelse continue);
 85+        if (v.client_id == client_id and v.obj_id == obj_id) {
 86+            maybe.* = null;
 87+            return;
 88+        }
 89+    }
 90+}
 91+
 92+pub fn destroyForSurface(surf_client: u32, surf_id: u32) void {
 93+    for (&viewport_data) |*maybe| {
 94+        const v = &(maybe.* orelse continue);
 95+        if (v.surf_client == surf_client and v.surf_id == surf_id) {
 96+            maybe.* = null;
 97+            return;
 98+        }
 99+    }
100+}
101+
102+pub fn handleViewporterRequest(client_id: u32, obj_id: u32, opcode: u16, args: []const wire.Arg) void {
103+    switch (opcode) {
104+        0 => { // destroy
105+            objects.remove(client_id, obj_id);
106+        },
107+        1 => { // get_viewport
108+            const new_id = args[0].new_id;
109+            const surf_id = args[1].object;
110+            if (findViewportForSurface(client_id, surf_id) != null) return;
111+            if (createViewport(client_id, new_id, client_id, surf_id)) {
112+                _ = objects.register(client_id, new_id, .wp_viewport);
113+            }
114+        },
115+        else => {},
116+    }
117+}
118+
119+pub fn handleViewportRequest(client_id: u32, obj_id: u32, opcode: u16, args: []const wire.Arg) void {
120+    const vp = findViewport(client_id, obj_id) orelse return;
121+
122+    const surf_valid = objects.lookup(vp.surf_client, vp.surf_id) != null;
123+
124+    switch (opcode) {
125+        0 => { // destroy
126+            objects.remove(client_id, obj_id);
127+            destroyViewport(client_id, obj_id);
128+        },
129+        1 => { // set_source
130+            if (!surf_valid) return;
131+            const x = args[0].@"fixed";
132+            const y = args[1].@"fixed";
133+            const w = args[2].@"fixed";
134+            const h = args[3].@"fixed";
135+
136+            if (x == -256 and y == -256 and w == -256 and h == -256) {
137+                vp.state.source_set = false;
138+                return;
139+            }
140+            if (x < 0 or y < 0 or w <= 0 or h <= 0) return;
141+
142+            vp.state.source_set = true;
143+            vp.state.src_x = x;
144+            vp.state.src_y = y;
145+            vp.state.src_w = w;
146+            vp.state.src_h = h;
147+        },
148+        2 => { // set_destination
149+            if (!surf_valid) return;
150+            const dw = args[0].int;
151+            const dh = args[1].int;
152+
153+            if (dw == -1 and dh == -1) {
154+                vp.state.dest_set = false;
155+                return;
156+            }
157+            if (dw <= 0 or dh <= 0) return;
158+
159+            vp.state.dest_set = true;
160+            vp.state.dst_w = dw;
161+            vp.state.dst_h = dh;
162+        },
163+        else => {},
164+    }
165+}
166+
167+pub const ViewportRect = struct {
168+    src_x: u32, src_y: u32, src_w: u32, src_h: u32,
169+    dst_w: u32, dst_h: u32,
170+};
171+
172+/// Compute effective source rect (in buffer pixels) and dest size for a surface.
173+/// Returns null if no viewport is active or if constraints are violated.
174+pub fn computeViewport(surf_client: u32, surf_id: u32, buf_w: u32, buf_h: u32) ?ViewportRect {
175+    const vs = findViewportForSurface(surf_client, surf_id) orelse return null;
176+    if (!vs.source_set and !vs.dest_set) return null;
177+
178+    // If source is set without destination, src_w/src_h must be integers
179+    if (vs.source_set and !vs.dest_set) {
180+        if ((vs.src_w & 0xFF) != 0 or (vs.src_h & 0xFF) != 0) return null;
181+    }
182+
183+    var sx: u32 = 0;
184+    var sy: u32 = 0;
185+    var sw: u32 = buf_w;
186+    var sh: u32 = buf_h;
187+
188+    if (vs.source_set) {
189+        sx = @as(u32, @intCast(@divTrunc(@max(0, vs.src_x), 256)));
190+        sy = @as(u32, @intCast(@divTrunc(@max(0, vs.src_y), 256)));
191+        sw = @as(u32, @intCast(@divTrunc(@max(0, vs.src_w), 256)));
192+        sh = @as(u32, @intCast(@divTrunc(@max(0, vs.src_h), 256)));
193+        if (sx > buf_w) sx = buf_w;
194+        if (sy > buf_h) sy = buf_h;
195+        if (sx + sw > buf_w) sw = buf_w - sx;
196+        if (sy + sh > buf_h) sh = buf_h - sy;
197+    }
198+
199+    const dw: u32 = if (vs.dest_set) @as(u32, @intCast(@max(0, vs.dst_w))) else sw;
200+    const dh: u32 = if (vs.dest_set) @as(u32, @intCast(@max(0, vs.dst_h))) else sh;
201+
202+    return .{
203+        .src_x = sx, .src_y = sy,
204+        .src_w = sw, .src_h = sh,
205+        .dst_w = dw, .dst_h = dh,
206+    };
207+}