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+}