feat: implement framebuffer allocator
parent
adadca6827
commit
f22a9ac2a9
78
src/core.zig
78
src/core.zig
|
@ -160,7 +160,7 @@ pub const ShmPool = struct {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_buffer(shm_pool: ShmPool, offset: i32, width: i32, height: i32, stride: i32, format: Shm.Format) u32 {
|
pub fn create_buffer(shm_pool: ShmPool, offset: i32, width: i32, height: i32, stride: i32, format: Shm.Format) !Buffer {
|
||||||
const new_id = shm_pool.conn.id_pool.create();
|
const new_id = shm_pool.conn.id_pool.create();
|
||||||
try shm_pool.conn.send(
|
try shm_pool.conn.send(
|
||||||
Request,
|
Request,
|
||||||
|
@ -174,7 +174,7 @@ pub const ShmPool = struct {
|
||||||
.format = format,
|
.format = format,
|
||||||
} },
|
} },
|
||||||
);
|
);
|
||||||
return Surface.init(shm_pool.conn, new_id);
|
return Buffer.init(shm_pool.conn, new_id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ pub const Shm = struct {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_pool(shm: *const Shm, pool_fd: u32, size: u32) ShmPool {
|
pub fn create_pool(shm: *const Shm, pool_fd: u32, size: u32) !ShmPool {
|
||||||
const new_id = shm.conn.id_pool.create();
|
const new_id = shm.conn.id_pool.create();
|
||||||
try shm.conn.send(
|
try shm.conn.send(
|
||||||
Request,
|
Request,
|
||||||
|
@ -284,6 +284,31 @@ pub const Surface = struct {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn attach(surface: Surface, buffer: Buffer, x: i32, y: i32) !void {
|
||||||
|
try surface.conn.send(
|
||||||
|
Request,
|
||||||
|
surface.id,
|
||||||
|
.{ .attach = .{
|
||||||
|
.buffer = buffer.id,
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn damage(surface: Surface, x: i32, y: i32, width: i32, height: i32) !void {
|
||||||
|
try surface.conn.send(
|
||||||
|
Request,
|
||||||
|
surface.id,
|
||||||
|
.{ .damage = .{
|
||||||
|
.x = x,
|
||||||
|
.y = y,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn commit(surface: Surface) !void {
|
pub fn commit(surface: Surface) !void {
|
||||||
try surface.conn.send(
|
try surface.conn.send(
|
||||||
Request,
|
Request,
|
||||||
|
@ -301,6 +326,13 @@ pub const Buffer = struct {
|
||||||
pub const Event = union(enum) {
|
pub const Event = union(enum) {
|
||||||
release: void,
|
release: void,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
conn: *Conn,
|
||||||
|
id: u32,
|
||||||
|
|
||||||
|
pub fn init(conn: *Conn, id: u32) Buffer {
|
||||||
|
return .{ .conn = conn, .id = id };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Seat = struct {
|
pub const Seat = struct {
|
||||||
|
@ -344,6 +376,40 @@ pub const Seat = struct {
|
||||||
pub fn init(conn: *Conn, id: u32) Seat {
|
pub fn init(conn: *Conn, id: u32) Seat {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_pointer(seat: Seat) !Pointer {
|
||||||
|
const new_id = seat.conn.id_pool.create();
|
||||||
|
try seat.conn.send(
|
||||||
|
Request,
|
||||||
|
seat.id,
|
||||||
|
.{ .get_pointer = .{
|
||||||
|
.new_id = new_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
return Pointer.init(seat.conn, new_id);
|
||||||
|
}
|
||||||
|
pub fn get_keyboard(seat: Seat) !Keyboard {
|
||||||
|
const new_id = seat.conn.id_pool.create();
|
||||||
|
try seat.conn.send(
|
||||||
|
Request,
|
||||||
|
seat.id,
|
||||||
|
.{ .get_keyboard = .{
|
||||||
|
.new_id = new_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
return Keyboard.init(seat.conn, new_id);
|
||||||
|
}
|
||||||
|
pub fn get_touch(seat: Seat) !Touch {
|
||||||
|
const new_id = seat.conn.id_pool.create();
|
||||||
|
try seat.conn.send(
|
||||||
|
Request,
|
||||||
|
seat.id,
|
||||||
|
.{ .get_touch = .{
|
||||||
|
.new_id = new_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
return Touch.init(seat.conn, new_id);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Pointer = struct {
|
pub const Pointer = struct {
|
||||||
|
@ -435,7 +501,7 @@ pub const Pointer = struct {
|
||||||
conn: *Conn,
|
conn: *Conn,
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
pub fn init(conn: *Conn, id: u32) Seat {
|
pub fn init(conn: *Conn, id: u32) Pointer {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -492,7 +558,7 @@ pub const Keyboard = struct {
|
||||||
conn: *Conn,
|
conn: *Conn,
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
pub fn init(conn: *Conn, id: u32) Seat {
|
pub fn init(conn: *Conn, id: u32) Keyboard {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -538,7 +604,7 @@ pub const Touch = struct {
|
||||||
conn: *Conn,
|
conn: *Conn,
|
||||||
id: u32,
|
id: u32,
|
||||||
|
|
||||||
pub fn init(conn: *Conn, id: u32) Seat {
|
pub fn init(conn: *Conn, id: u32) Touch {
|
||||||
return .{ .conn = conn, .id = id };
|
return .{ .conn = conn, .id = id };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
457
src/main.zig
457
src/main.zig
|
@ -63,6 +63,7 @@ pub fn main() !void {
|
||||||
var conn = try Conn.init(gpa, display_path);
|
var conn = try Conn.init(gpa, display_path);
|
||||||
defer conn.deinit();
|
defer conn.deinit();
|
||||||
|
|
||||||
|
// Register all globals
|
||||||
var ctx = try Context.init(&conn);
|
var ctx = try Context.init(&conn);
|
||||||
|
|
||||||
const compositor = try ctx.getGlobal(wayland.core.Compositor);
|
const compositor = try ctx.getGlobal(wayland.core.Compositor);
|
||||||
|
@ -75,58 +76,468 @@ pub fn main() !void {
|
||||||
try surface.commit();
|
try surface.commit();
|
||||||
|
|
||||||
const display = try ctx.getGlobal(wayland.core.Display);
|
const display = try ctx.getGlobal(wayland.core.Display);
|
||||||
const registry_done = try display.sync();
|
|
||||||
|
|
||||||
const shm = try ctx.getGlobal(wayland.core.Shm);
|
const shm = try ctx.getGlobal(wayland.core.Shm);
|
||||||
const seat = try ctx.getGlobal(wayland.core.Seat);
|
const seat = try ctx.getGlobal(wayland.core.Seat);
|
||||||
|
|
||||||
var done = false;
|
var app = App{
|
||||||
var surface_configured = false;
|
.conn = &conn,
|
||||||
var seat_capabilties: ?wayland.core.Seat.Capability = null;
|
.ctx = &ctx,
|
||||||
while (!done or !surface_configured) {
|
.ally = gpa,
|
||||||
|
.callbacks = std.AutoHashMap(u32, App.Callback).init(gpa),
|
||||||
|
.state = .{ .init = .{} },
|
||||||
|
.display = display,
|
||||||
|
.shm = shm,
|
||||||
|
.xdg_wmbase = xdg_wm_base,
|
||||||
|
.surface = surface,
|
||||||
|
.xdg_surface = xdg_surface,
|
||||||
|
.xdg_toplevel = xdg_toplevel,
|
||||||
|
};
|
||||||
|
defer app.deinit();
|
||||||
|
|
||||||
|
try app.callbacks.put(seat.id, seatInitHandler);
|
||||||
|
try app.callbacks.put(xdg_toplevel.id, toplevelInitHandler);
|
||||||
|
try app.callbacks.put(xdg_surface.id, surfaceInitHandler);
|
||||||
|
try app.callbacks.put(display.id, displayHandler);
|
||||||
|
|
||||||
|
while (app.state == .init) {
|
||||||
const header, const body = try conn.recv();
|
const header, const body = try conn.recv();
|
||||||
|
|
||||||
if (header.object_id == xdg_surface.id) {
|
if (app.callbacks.get(header.object_id)) |callback| {
|
||||||
|
try callback(&app, header, body);
|
||||||
|
} else {
|
||||||
|
const typename = if (ctx.getGlobalObjectName(header.object_id)) |name|
|
||||||
|
name
|
||||||
|
else
|
||||||
|
"UNKNOWN";
|
||||||
|
std.debug.print("{} ({s}) {x} \"{}\"\n", .{
|
||||||
|
header.object_id,
|
||||||
|
typename,
|
||||||
|
header.size_and_opcode.opcode,
|
||||||
|
std.zig.fmtEscapes(std.mem.sliceAsBytes(body)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pool = if (app.state == .run_mobile) &app.state.run_mobile.pool else &app.state.run_desktop.pool;
|
||||||
|
const width = if (app.state == .run_mobile) app.state.run_mobile.width else app.state.run_desktop.width;
|
||||||
|
const height = if (app.state == .run_mobile) app.state.run_mobile.height else app.state.run_desktop.height;
|
||||||
|
|
||||||
|
// render
|
||||||
|
const canvas = try pool.getFramebuffer(.{ width, height });
|
||||||
|
canvas.renderGradient();
|
||||||
|
try canvas.attach(surface);
|
||||||
|
try app.callbacks.put(canvas.buffer.id, bufferHandler);
|
||||||
|
|
||||||
|
while (app.state != .close) {
|
||||||
|
const header, const body = try conn.recv();
|
||||||
|
|
||||||
|
if (app.callbacks.get(header.object_id)) |callback| {
|
||||||
|
try callback(&app, header, body);
|
||||||
|
} else {
|
||||||
|
const typename = if (ctx.getGlobalObjectName(header.object_id)) |name|
|
||||||
|
name
|
||||||
|
else
|
||||||
|
"UNKNOWN";
|
||||||
|
std.debug.print("{} ({s}) {x} \"{}\"\n", .{
|
||||||
|
header.object_id,
|
||||||
|
typename,
|
||||||
|
header.size_and_opcode.opcode,
|
||||||
|
std.zig.fmtEscapes(std.mem.sliceAsBytes(body)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const App = struct {
|
||||||
|
conn: *Conn,
|
||||||
|
ctx: *Context,
|
||||||
|
ally: std.mem.Allocator,
|
||||||
|
callbacks: std.AutoHashMap(u32, Callback),
|
||||||
|
|
||||||
|
display: wayland.core.Display,
|
||||||
|
shm: wayland.core.Shm,
|
||||||
|
surface: wayland.core.Surface,
|
||||||
|
xdg_wmbase: wayland.xdg.WmBase,
|
||||||
|
xdg_surface: wayland.xdg.Surface,
|
||||||
|
xdg_toplevel: wayland.xdg.Toplevel,
|
||||||
|
|
||||||
|
state: union(enum) {
|
||||||
|
init: struct {
|
||||||
|
toplevel_config: ?SurfaceConfiguration = null,
|
||||||
|
surface_configured: bool = false,
|
||||||
|
seat_capabilities: ?wayland.core.Seat.Capability = null,
|
||||||
|
pointer: ?wayland.core.Pointer = null,
|
||||||
|
keyboard: ?wayland.core.Keyboard = null,
|
||||||
|
touch: ?wayland.core.Touch = null,
|
||||||
|
},
|
||||||
|
run_mobile: struct {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
pool: Pool,
|
||||||
|
touch: wayland.core.Touch,
|
||||||
|
},
|
||||||
|
run_desktop: struct {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
pool: Pool,
|
||||||
|
keyboard: wayland.core.Keyboard,
|
||||||
|
pointer: wayland.core.Pointer,
|
||||||
|
mouse: struct {
|
||||||
|
x: f32,
|
||||||
|
y: f32,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
close,
|
||||||
|
},
|
||||||
|
|
||||||
|
const SurfaceConfiguration = struct {
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Callback = *const fn (*App, wayland.Header, []const u32) anyerror!void;
|
||||||
|
|
||||||
|
fn deinit(app: *App) void {
|
||||||
|
app.callbacks.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn checkInit(app: *App) !void {
|
||||||
|
std.debug.assert(app.state == .init);
|
||||||
|
const init = app.state.init;
|
||||||
|
if (!init.surface_configured) return;
|
||||||
|
if (init.seat_capabilities == null) return;
|
||||||
|
|
||||||
|
app.callbacks.clearRetainingCapacity();
|
||||||
|
try app.callbacks.put(app.display.id, displayHandler);
|
||||||
|
|
||||||
|
if (init.touch != null and init.keyboard == null) {
|
||||||
|
// mobile
|
||||||
|
app.state = .{ .run_mobile = .{
|
||||||
|
.width = init.toplevel_config.?.width,
|
||||||
|
.height = init.toplevel_config.?.height,
|
||||||
|
.pool = try Pool.init(app.ally, app.shm),
|
||||||
|
.touch = init.touch.?,
|
||||||
|
} };
|
||||||
|
try app.callbacks.put(app.xdg_wmbase.id, wmbaseHandler);
|
||||||
|
try app.callbacks.put(app.xdg_toplevel.id, toplevelHandler);
|
||||||
|
// try app.callbacks.put(app.state.run_desktop.touch.id, touchMobileHandler);
|
||||||
|
} else if (init.keyboard != null and init.pointer != null) {
|
||||||
|
// desktop
|
||||||
|
app.state = .{ .run_desktop = .{
|
||||||
|
.width = init.toplevel_config.?.width,
|
||||||
|
.height = init.toplevel_config.?.height,
|
||||||
|
.pool = try Pool.init(app.ally, app.shm),
|
||||||
|
.keyboard = init.keyboard.?,
|
||||||
|
.pointer = init.pointer.?,
|
||||||
|
.mouse = .{ .x = 0, .y = 0 },
|
||||||
|
} };
|
||||||
|
try app.callbacks.put(app.state.run_desktop.pointer.id, pointerDesktopHandler);
|
||||||
|
try app.callbacks.put(app.state.run_desktop.keyboard.id, keyboardDesktopHandler);
|
||||||
|
try app.callbacks.put(app.xdg_surface.id, surfaceDesktopHandler);
|
||||||
|
try app.callbacks.put(app.xdg_wmbase.id, wmbaseHandler);
|
||||||
|
try app.callbacks.put(app.xdg_toplevel.id, toplevelHandler);
|
||||||
|
} else {
|
||||||
|
@panic("no keyboard or touch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn surfaceInitHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
|
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.configure => |conf| {
|
.configure => |conf| {
|
||||||
try xdg_surface.ack_configure(conf.serial);
|
try app.xdg_surface.ack_configure(conf.serial);
|
||||||
surface_configured = true;
|
std.debug.assert(app.state.init.toplevel_config != null);
|
||||||
|
app.state.init.surface_configured = true;
|
||||||
|
try app.checkInit();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if (header.object_id == xdg_toplevel.id) {
|
}
|
||||||
|
|
||||||
|
fn toplevelInitHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
|
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
|
||||||
std.debug.print("<- {}\n", .{event});
|
|
||||||
} else if (header.object_id == registry_done) {
|
|
||||||
done = true;
|
|
||||||
} else if (header.object_id == shm.id) {
|
|
||||||
const event = try wayland.deserialize(wayland.core.Shm.Event, header, body);
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }),
|
.configure => |conf| {
|
||||||
|
var width = conf.width;
|
||||||
|
var height = conf.height;
|
||||||
|
if (conf.width == 0) {
|
||||||
|
width = 128;
|
||||||
|
height = 128;
|
||||||
}
|
}
|
||||||
} else if (header.object_id == seat.id) {
|
app.state.init.toplevel_config = .{
|
||||||
|
.width = @intCast(width),
|
||||||
|
.height = @intCast(height),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.log.info("toplevel event: {}", .{event});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seatInitHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
const event = try wayland.deserialize(wayland.core.Seat.Event, header, body);
|
const event = try wayland.deserialize(wayland.core.Seat.Event, header, body);
|
||||||
|
const seat = wayland.core.Seat.init(app.conn, header.object_id); // TODO: pass into cb
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.capabilities => |capabilities| {
|
.capabilities => |capabilities| {
|
||||||
const cap: wayland.core.Seat.Capability = @bitCast(capabilities.capability);
|
const caps: wayland.core.Seat.Capability = @bitCast(capabilities.capability);
|
||||||
std.debug.print("<- wl_seat.capabilties = {}\n", .{cap});
|
app.state.init.seat_capabilities = caps;
|
||||||
seat_capabilties = cap;
|
|
||||||
|
if (caps.pointer) {
|
||||||
|
app.state.init.pointer = try seat.get_pointer();
|
||||||
|
}
|
||||||
|
if (caps.touch) {
|
||||||
|
app.state.init.touch = try seat.get_touch();
|
||||||
|
}
|
||||||
|
if (caps.keyboard) {
|
||||||
|
app.state.init.keyboard = try seat.get_keyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
try app.checkInit();
|
||||||
},
|
},
|
||||||
.name => |name| {
|
.name => |name| {
|
||||||
std.debug.print("<- wl_seat.name = {s}\n", .{name.name});
|
std.debug.print("<- wl_seat.name = {s}\n", .{name.name});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if (header.object_id == display.id) {
|
}
|
||||||
|
|
||||||
|
fn displayHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
|
const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
|
.@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
|
||||||
.delete_id => |id| {
|
.delete_id => |id| {
|
||||||
std.debug.print("id {} deleted\n", .{id});
|
std.debug.print("id {} deleted\n", .{id});
|
||||||
conn.id_pool.destroy(id.id);
|
app.conn.id_pool.destroy(id.id);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
std.debug.print("{} {x} \"{}\"\n", .{ header.object_id, header.size_and_opcode.opcode, std.zig.fmtEscapes(std.mem.sliceAsBytes(body)) });
|
|
||||||
|
const Pool = struct {
|
||||||
|
fd: std.os.fd_t,
|
||||||
|
shm: wayland.core.ShmPool,
|
||||||
|
mem: []u8,
|
||||||
|
file_length: usize,
|
||||||
|
allocationTable: std.AutoHashMap(u32, Buffer),
|
||||||
|
const Buffer = struct {
|
||||||
|
offset: usize,
|
||||||
|
len: usize,
|
||||||
|
width: usize = 0,
|
||||||
|
height: usize = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Memory to store 4k screen pixels
|
||||||
|
// const UPPER_BOUND = 33_177_600;
|
||||||
|
// Memory to store 8k screen pixels
|
||||||
|
// const UPPER_BOUND = 132_710_400;
|
||||||
|
|
||||||
|
const UPPER_BOUND = (7680 * 4320 * 4);
|
||||||
|
|
||||||
|
fn init(allocator: std.mem.Allocator, shm: wayland.core.Shm) !Pool {
|
||||||
|
const pool_fd_1 = try std.os.memfd_create("my-wayland-framebuffer-1", 0);
|
||||||
|
const pool_bytes_1 = try std.os.mmap(
|
||||||
|
null,
|
||||||
|
UPPER_BOUND,
|
||||||
|
std.os.PROT.READ | std.os.PROT.WRITE,
|
||||||
|
std.os.MAP.SHARED,
|
||||||
|
pool_fd_1,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
const shm1 = try shm.create_pool(@intCast(pool_fd_1), @intCast(pool_bytes_1.len));
|
||||||
|
return Pool{
|
||||||
|
.fd = pool_fd_1,
|
||||||
|
.shm = shm1,
|
||||||
|
.mem = pool_bytes_1,
|
||||||
|
.file_length = 0,
|
||||||
|
.allocationTable = std.AutoHashMap(u32, Buffer).init(allocator),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn getFramebuffer(pool: *Pool, size: [2]u32) !Canvas {
|
||||||
|
var iter = pool.allocationTable.iterator();
|
||||||
|
var first_offset: usize = std.math.maxInt(usize);
|
||||||
|
var total_len: usize = 0;
|
||||||
|
while (iter.next()) |entry| {
|
||||||
|
const offset = entry.value_ptr.offset;
|
||||||
|
const len = entry.value_ptr.len;
|
||||||
|
total_len += len;
|
||||||
|
if (offset < first_offset) {
|
||||||
|
if (first_offset != std.math.maxInt(usize)) {
|
||||||
|
total_len += first_offset - offset;
|
||||||
|
}
|
||||||
|
first_offset = offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pixel_count = size[0] * size[1];
|
||||||
|
const byte_count = pixel_count * @sizeOf(Pixel);
|
||||||
|
var alloc_offset: usize = 0;
|
||||||
|
if (byte_count < first_offset) {
|
||||||
|
alloc_offset = 0;
|
||||||
|
} else {
|
||||||
|
alloc_offset = first_offset + total_len;
|
||||||
|
}
|
||||||
|
const final_offset = alloc_offset + byte_count;
|
||||||
|
if (final_offset > pool.file_length) {
|
||||||
|
try std.os.ftruncate(pool.fd, final_offset);
|
||||||
|
pool.file_length = final_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = try pool.shm.create_buffer(
|
||||||
|
@intCast(alloc_offset),
|
||||||
|
@intCast(size[0]),
|
||||||
|
@intCast(size[1]),
|
||||||
|
@intCast(size[0] * @sizeOf(Pixel)),
|
||||||
|
.argb8888,
|
||||||
|
);
|
||||||
|
|
||||||
|
try pool.allocationTable.put(buffer.id, .{
|
||||||
|
.offset = alloc_offset,
|
||||||
|
.len = byte_count,
|
||||||
|
.width = size[0],
|
||||||
|
.height = size[1],
|
||||||
|
});
|
||||||
|
|
||||||
|
return .{
|
||||||
|
.pool = pool,
|
||||||
|
.buffer = buffer,
|
||||||
|
.fb = @as([*]Pixel, @ptrCast(pool.mem[alloc_offset..].ptr))[0..pixel_count],
|
||||||
|
.size = size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn freeFramebuffer(pool: *Pool, buffer_id: u32) !void {
|
||||||
|
std.debug.assert(pool.allocationTable.remove(buffer_id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Canvas = struct {
|
||||||
|
pool: *Pool,
|
||||||
|
fb: []Pixel,
|
||||||
|
buffer: wayland.core.Buffer,
|
||||||
|
size: [2]u32,
|
||||||
|
|
||||||
|
fn attach(canvas: Canvas, surface: wayland.core.Surface) !void {
|
||||||
|
try surface.attach(canvas.buffer, 0, 0);
|
||||||
|
try surface.damage(0, 0, std.math.maxInt(i32), std.math.maxInt(i32));
|
||||||
|
try surface.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderGradient(canvas: Canvas) void {
|
||||||
|
const fb = canvas.fb;
|
||||||
|
const width = canvas.size[0];
|
||||||
|
const height = canvas.size[1];
|
||||||
|
for (0..height) |y| {
|
||||||
|
const row = fb[y * width .. (y + 1) * width];
|
||||||
|
for (row, 0..width) |*pixel, x| {
|
||||||
|
pixel.* = .{
|
||||||
|
@truncate(x),
|
||||||
|
@truncate(y),
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fn wmbaseHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.WmBase.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.ping => |ping| {
|
||||||
|
try app.xdg_wmbase.pong(ping.serial);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toplevelHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.configure => |conf| {
|
||||||
|
var width = conf.width;
|
||||||
|
var height = conf.height;
|
||||||
|
if (conf.width == 0) {
|
||||||
|
width = 128;
|
||||||
|
height = 128;
|
||||||
|
}
|
||||||
|
if (app.state == .run_mobile) {
|
||||||
|
app.state.run_mobile.width = @intCast(width);
|
||||||
|
app.state.run_mobile.height = @intCast(height);
|
||||||
|
} else {
|
||||||
|
app.state.run_desktop.width = @intCast(width);
|
||||||
|
app.state.run_desktop.height = @intCast(height);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.log.info("toplevel event: {}", .{event});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn surfaceDesktopHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.configure => |conf| {
|
||||||
|
try app.xdg_surface.ack_configure(conf.serial);
|
||||||
|
const pool = if (app.state == .run_mobile) &app.state.run_mobile.pool else &app.state.run_desktop.pool;
|
||||||
|
const width = if (app.state == .run_mobile) app.state.run_mobile.width else app.state.run_desktop.width;
|
||||||
|
const height = if (app.state == .run_mobile) app.state.run_mobile.height else app.state.run_desktop.height;
|
||||||
|
|
||||||
|
// render
|
||||||
|
const canvas = try pool.getFramebuffer(.{ width, height });
|
||||||
|
canvas.renderGradient();
|
||||||
|
try canvas.attach(app.surface);
|
||||||
|
try app.callbacks.put(canvas.buffer.id, bufferHandler);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointerDesktopHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Pointer.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.enter => |enter| {
|
||||||
|
app.state.run_desktop.mouse.x = @floatFromInt(enter.surface_x);
|
||||||
|
app.state.run_desktop.mouse.y = @floatFromInt(enter.surface_y);
|
||||||
|
},
|
||||||
|
.leave => |_| {},
|
||||||
|
.motion => |motion| {
|
||||||
|
app.state.run_desktop.mouse.x = @floatFromInt(motion.surface_x);
|
||||||
|
app.state.run_desktop.mouse.y = @floatFromInt(motion.surface_y);
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.log.info("pointer event: {}", .{event});
|
||||||
|
},
|
||||||
|
// .button => |button| { _ = button; },
|
||||||
|
// .axis => |axis| { _ = axis;},
|
||||||
|
// .frame => {},
|
||||||
|
// .axis_source => |axis_source| { _ = axis_source; },
|
||||||
|
// .axis_stop => |axis_stop| { _ = axis_stop; },
|
||||||
|
// .axis_discrete => |axis_discrete| { _ = axis_discrete; },
|
||||||
|
// .axis_value120 => |axis_value120| { _ = axis_value120; },
|
||||||
|
// .axis_relative_direction => |axis_relative_direction| {_ = axis_relative_direction; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboardDesktopHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, body);
|
||||||
|
_ = app;
|
||||||
|
switch (event) {
|
||||||
|
else => {
|
||||||
|
std.log.info("keyboard event: {}", .{event});
|
||||||
|
},
|
||||||
|
// .button => |button| { _ = button; },
|
||||||
|
// .axis => |axis| { _ = axis;},
|
||||||
|
// .frame => {},
|
||||||
|
// .axis_source => |axis_source| { _ = axis_source; },
|
||||||
|
// .axis_stop => |axis_stop| { _ = axis_stop; },
|
||||||
|
// .axis_discrete => |axis_discrete| { _ = axis_discrete; },
|
||||||
|
// .axis_value120 => |axis_value120| { _ = axis_value120; },
|
||||||
|
// .axis_relative_direction => |axis_relative_direction| {_ = axis_relative_direction; },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bufferHandler(app: *App, header: wayland.Header, body: []const u32) !void {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body);
|
||||||
|
std.debug.assert(event == .release);
|
||||||
|
const pool = if (app.state == .run_mobile) &app.state.run_mobile.pool else &app.state.run_desktop.pool;
|
||||||
|
try pool.freeFramebuffer(header.object_id);
|
||||||
|
}
|
||||||
|
|
15
src/root.zig
15
src/root.zig
|
@ -557,8 +557,10 @@ pub fn Context(comptime T: []const type) type {
|
||||||
const Item = struct { version: u32, index: u32 };
|
const Item = struct { version: u32, index: u32 };
|
||||||
const Pair = struct { []const u8, Item };
|
const Pair = struct { []const u8, Item };
|
||||||
comptime var kvs_list: []const Pair = &[_]Pair{};
|
comptime var kvs_list: []const Pair = &[_]Pair{};
|
||||||
|
comptime var name_list: []const []const u8 = &[_][]const u8{};
|
||||||
inline for (T, 0..) |t, i| {
|
inline for (T, 0..) |t, i| {
|
||||||
kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }};
|
kvs_list = kvs_list ++ &[_]Pair{.{ t.INTERFACE, .{ .version = t.VERSION, .index = i } }};
|
||||||
|
name_list = name_list ++ &[_][]const u8{t.INTERFACE};
|
||||||
}
|
}
|
||||||
const stringToType = std.ComptimeStringMap(Item, kvs_list);
|
const stringToType = std.ComptimeStringMap(Item, kvs_list);
|
||||||
|
|
||||||
|
@ -566,6 +568,8 @@ pub fn Context(comptime T: []const type) type {
|
||||||
conn: *Conn,
|
conn: *Conn,
|
||||||
global_ids: [T.len]?u32,
|
global_ids: [T.len]?u32,
|
||||||
|
|
||||||
|
const global_names = name_list;
|
||||||
|
|
||||||
pub fn init(conn: *Conn) !@This() {
|
pub fn init(conn: *Conn) !@This() {
|
||||||
var ctx = @This(){
|
var ctx = @This(){
|
||||||
.conn = conn,
|
.conn = conn,
|
||||||
|
@ -575,6 +579,14 @@ pub fn Context(comptime T: []const type) type {
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getGlobalObjectName(ctx: *@This(), id: u32) ?[]const u8 {
|
||||||
|
for (ctx.global_ids, 0..) |gid_opt, i| {
|
||||||
|
const gid = gid_opt orelse continue;
|
||||||
|
if (gid == id) return global_names[i];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn getGlobal(ctx: *@This(), comptime G: type) !G {
|
pub fn getGlobal(ctx: *@This(), comptime G: type) !G {
|
||||||
if (G == core.Display) return .{ .id = 1, .conn = ctx.conn };
|
if (G == core.Display) return .{ .id = 1, .conn = ctx.conn };
|
||||||
const g = stringToType.get(G.INTERFACE) orelse return error.NoSuchGlobal;
|
const g = stringToType.get(G.INTERFACE) orelse return error.NoSuchGlobal;
|
||||||
|
@ -598,7 +610,6 @@ pub fn Context(comptime T: []const type) type {
|
||||||
try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message));
|
try ctx.conn.socket.writeAll(std.mem.sliceAsBytes(message));
|
||||||
}
|
}
|
||||||
|
|
||||||
// var ids: [T.len]?u32 = [_]?u32{null} ** T.len;
|
|
||||||
var message_buffer = std.ArrayList(u32).init(ctx.conn.allocator);
|
var message_buffer = std.ArrayList(u32).init(ctx.conn.allocator);
|
||||||
defer message_buffer.deinit();
|
defer message_buffer.deinit();
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -636,7 +647,7 @@ pub fn Context(comptime T: []const type) type {
|
||||||
} else if (header.object_id == registry_done_id) {
|
} else if (header.object_id == registry_done_id) {
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
std.log.info("{} {x} \"{}\"", .{
|
std.log.info("registerGlobals: {} {x} \"{}\"", .{
|
||||||
header.object_id,
|
header.object_id,
|
||||||
header.size_and_opcode.opcode,
|
header.size_and_opcode.opcode,
|
||||||
std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)),
|
std.zig.fmtEscapes(std.mem.sliceAsBytes(message_buffer.items)),
|
||||||
|
|
|
@ -63,6 +63,14 @@ pub const WmBase = struct {
|
||||||
);
|
);
|
||||||
return Surface.init(base.conn, new_id);
|
return Surface.init(base.conn, new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pong(base: WmBase, serial: u32) !void {
|
||||||
|
try base.conn.send(
|
||||||
|
Request,
|
||||||
|
base.id,
|
||||||
|
.{ .pong = .{ .serial = serial } },
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Surface = struct {
|
pub const Surface = struct {
|
||||||
|
|
Loading…
Reference in New Issue