copy example 1 -> example 2; simplify example 1
parent
ab10b7292c
commit
025ab53055
17
build.zig
17
build.zig
|
@ -46,9 +46,18 @@ pub fn build(b: *std.Build) void {
|
||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
client_connect_exe.root_module.addImport("wayland", module);
|
client_connect_exe.root_module.addImport("wayland", module);
|
||||||
client_connect_exe.root_module.addImport("xkbcommon", xkbcommon_module);
|
|
||||||
client_connect_exe.linkLibC();
|
|
||||||
client_connect_exe.linkSystemLibrary("xkbcommon");
|
|
||||||
client_connect_exe.addIncludePath(.{ .path = "deps/font8x8/" });
|
|
||||||
b.installArtifact(client_connect_exe);
|
b.installArtifact(client_connect_exe);
|
||||||
|
|
||||||
|
const text_editor_exe = b.addExecutable(.{
|
||||||
|
.name = "02_text_editor",
|
||||||
|
.root_source_file = .{ .path = "examples/02_text_editor.zig" },
|
||||||
|
.target = target,
|
||||||
|
.optimize = optimize,
|
||||||
|
});
|
||||||
|
text_editor_exe.root_module.addImport("wayland", module);
|
||||||
|
text_editor_exe.root_module.addImport("xkbcommon", xkbcommon_module);
|
||||||
|
text_editor_exe.linkLibC();
|
||||||
|
text_editor_exe.linkSystemLibrary("xkbcommon");
|
||||||
|
text_editor_exe.addIncludePath(.{ .path = "deps/font8x8/" });
|
||||||
|
b.installArtifact(text_editor_exe);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,5 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const wayland = @import("wayland");
|
const wayland = @import("wayland");
|
||||||
const xkbcommon = @import("xkbcommon");
|
|
||||||
const font8x8 = @cImport({
|
|
||||||
@cInclude("font8x8.h");
|
|
||||||
});
|
|
||||||
const PieceTable = @import("PieceTable.zig").PieceTable;
|
|
||||||
|
|
||||||
const Pixel = [4]u8;
|
const Pixel = [4]u8;
|
||||||
const Theme = struct {
|
const Theme = struct {
|
||||||
|
@ -58,8 +53,6 @@ pub fn main() !void {
|
||||||
const shm_id = ids[0] orelse return error.NeccessaryWaylandExtensionMissing;
|
const shm_id = ids[0] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
const compositor_id = ids[1] orelse return error.NeccessaryWaylandExtensionMissing;
|
const compositor_id = ids[1] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
const xdg_wm_base_id = ids[2] orelse return error.NeccessaryWaylandExtensionMissing;
|
const xdg_wm_base_id = ids[2] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
const wl_seat_id = ids[3] orelse return error.NeccessaryWaylandExtensionMissing;
|
|
||||||
const zwp_text_input_manager_v3 = ids[5] orelse return error.NeccessaryWaylandExtensionMissing;
|
|
||||||
|
|
||||||
const surface_id = id_pool.create();
|
const surface_id = id_pool.create();
|
||||||
try conn.send(
|
try conn.send(
|
||||||
|
@ -89,16 +82,6 @@ pub fn main() !void {
|
||||||
} },
|
} },
|
||||||
);
|
);
|
||||||
|
|
||||||
const zwp_text_input_v3_id = id_pool.create();
|
|
||||||
try conn.send(
|
|
||||||
wayland.zwp.TextInputManagerV3.Request,
|
|
||||||
zwp_text_input_manager_v3,
|
|
||||||
.{ .get_text_input = .{
|
|
||||||
.id = zwp_text_input_v3_id,
|
|
||||||
.seat = wl_seat_id,
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
|
|
||||||
var zxdg_toplevel_decoration_id_opt: ?u32 = null;
|
var zxdg_toplevel_decoration_id_opt: ?u32 = null;
|
||||||
if (ids[4]) |zxdg_decoration_manager_id| {
|
if (ids[4]) |zxdg_decoration_manager_id| {
|
||||||
zxdg_toplevel_decoration_id_opt = id_pool.create();
|
zxdg_toplevel_decoration_id_opt = id_pool.create();
|
||||||
|
@ -127,7 +110,6 @@ pub fn main() !void {
|
||||||
|
|
||||||
var done = false;
|
var done = false;
|
||||||
var surface_configured = false;
|
var surface_configured = false;
|
||||||
var seat_capabilties: ?wayland.core.Seat.Capability = null;
|
|
||||||
while (!done or !surface_configured) {
|
while (!done or !surface_configured) {
|
||||||
const header, const body = try conn.recv();
|
const header, const body = try conn.recv();
|
||||||
|
|
||||||
|
@ -158,18 +140,6 @@ pub fn main() !void {
|
||||||
switch (event) {
|
switch (event) {
|
||||||
.format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }),
|
.format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }),
|
||||||
}
|
}
|
||||||
} else if (header.object_id == wl_seat_id) {
|
|
||||||
const event = try wayland.deserialize(wayland.core.Seat.Event, header, body);
|
|
||||||
switch (event) {
|
|
||||||
.capabilities => |capabilities| {
|
|
||||||
const cap: wayland.core.Seat.Capability = @bitCast(capabilities.capability);
|
|
||||||
std.debug.print("<- wl_seat.capabilties = {}\n", .{cap});
|
|
||||||
seat_capabilties = cap;
|
|
||||||
},
|
|
||||||
.name => |name| {
|
|
||||||
std.debug.print("<- wl_seat.name = {s}\n", .{name.name});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else if (header.object_id == DISPLAY_ID) {
|
} else if (header.object_id == DISPLAY_ID) {
|
||||||
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) {
|
||||||
|
@ -184,35 +154,6 @@ pub fn main() !void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var wl_pointer_id_opt: ?u32 = null;
|
|
||||||
var wl_keyboard_id_opt: ?u32 = null;
|
|
||||||
if (seat_capabilties) |caps| {
|
|
||||||
if (caps.pointer) {
|
|
||||||
wl_pointer_id_opt = id_pool.create();
|
|
||||||
std.debug.print("wl pointer id: {}\n", .{wl_pointer_id_opt.?});
|
|
||||||
try conn.send(
|
|
||||||
wayland.core.Seat.Request,
|
|
||||||
wl_seat_id,
|
|
||||||
.{ .get_pointer = .{
|
|
||||||
.new_id = wl_pointer_id_opt.?,
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (caps.keyboard) {
|
|
||||||
wl_keyboard_id_opt = id_pool.create();
|
|
||||||
std.debug.print("wl keyboard id: {}\n", .{wl_keyboard_id_opt.?});
|
|
||||||
try conn.send(
|
|
||||||
wayland.core.Seat.Request,
|
|
||||||
wl_seat_id,
|
|
||||||
.{ .get_keyboard = .{
|
|
||||||
.new_id = wl_keyboard_id_opt.?,
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const wl_pointer_id = wl_pointer_id_opt orelse return error.MissingPointer;
|
|
||||||
const wl_keyboard_id = wl_keyboard_id_opt orelse return error.MissingKeyboard;
|
|
||||||
|
|
||||||
// allocate a shared memory file for display purposes
|
// allocate a shared memory file for display purposes
|
||||||
const framebuffer_size = [2]u32{ 128, 128 };
|
const framebuffer_size = [2]u32{ 128, 128 };
|
||||||
const pool_file_len = 1024 * framebuffer_size[0] * framebuffer_size[1] * @sizeOf(Pixel);
|
const pool_file_len = 1024 * framebuffer_size[0] * framebuffer_size[1] * @sizeOf(Pixel);
|
||||||
|
@ -289,31 +230,6 @@ pub fn main() !void {
|
||||||
);
|
);
|
||||||
|
|
||||||
var window_size: [2]u32 = [2]u32{ @intCast(framebuffer_size[0]), @intCast(framebuffer_size[1]) };
|
var window_size: [2]u32 = [2]u32{ @intCast(framebuffer_size[0]), @intCast(framebuffer_size[1]) };
|
||||||
const xkb_ctx = xkbcommon.Context.new(.no_flags) orelse return error.XKBInit;
|
|
||||||
defer xkb_ctx.unref();
|
|
||||||
|
|
||||||
var xkb_keymap_opt: ?*xkbcommon.Keymap = null;
|
|
||||||
defer if (xkb_keymap_opt) |xkb_keymap| {
|
|
||||||
xkb_keymap.unref();
|
|
||||||
};
|
|
||||||
var xkb_state_opt: ?*xkbcommon.State = null;
|
|
||||||
defer if (xkb_state_opt) |xkb_state| {
|
|
||||||
xkb_state.unref();
|
|
||||||
};
|
|
||||||
|
|
||||||
var piece_table = try PieceTable.init(gpa, "Hello, World!");
|
|
||||||
defer piece_table.deinit();
|
|
||||||
|
|
||||||
var edit_buffer: [1024]u8 = [1]u8{0} ** 1024;
|
|
||||||
var edit_slice: ?[]u8 = null;
|
|
||||||
|
|
||||||
var delete_before: usize = 0;
|
|
||||||
var delete_after: usize = 0;
|
|
||||||
|
|
||||||
// var preedit_buffer: [1024]u8 = [1]u8{0} ** 1024;
|
|
||||||
// var preedit_slice: ?[]u8 = null;
|
|
||||||
|
|
||||||
var cursor_pos: usize = piece_table.getTotalSize();
|
|
||||||
|
|
||||||
var running = true;
|
var running = true;
|
||||||
while (running) {
|
while (running) {
|
||||||
|
@ -338,11 +254,6 @@ pub fn main() !void {
|
||||||
// put some interesting colors into the new_framebuffer
|
// put some interesting colors into the new_framebuffer
|
||||||
renderGradient(new_framebuffer, window_size);
|
renderGradient(new_framebuffer, window_size);
|
||||||
|
|
||||||
const text = try piece_table.writeAllAlloc();
|
|
||||||
defer gpa.free(text);
|
|
||||||
// blit some characters
|
|
||||||
renderText(new_framebuffer, window_size, .{ 10, 10 }, text);
|
|
||||||
|
|
||||||
try conn.send(
|
try conn.send(
|
||||||
wayland.core.ShmPool.Request,
|
wayland.core.ShmPool.Request,
|
||||||
wl_shm_pool_id,
|
wl_shm_pool_id,
|
||||||
|
@ -411,205 +322,6 @@ pub fn main() !void {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
} else if (header.object_id == wl_pointer_id) {
|
|
||||||
const event = try wayland.deserialize(wayland.core.Pointer.Event, header, body);
|
|
||||||
std.debug.print("<- wl_pointer@{}\n", .{event});
|
|
||||||
} else if (header.object_id == wl_keyboard_id) {
|
|
||||||
const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, body);
|
|
||||||
switch (event) {
|
|
||||||
.keymap => |keymap| {
|
|
||||||
const fd = conn.fd_queue.orderedRemove(0);
|
|
||||||
std.debug.print("keymap format={}, size={}, fd={}\n", .{
|
|
||||||
keymap.format,
|
|
||||||
keymap.size,
|
|
||||||
fd,
|
|
||||||
});
|
|
||||||
const mem = try std.os.mmap(
|
|
||||||
null,
|
|
||||||
keymap.size,
|
|
||||||
std.os.PROT.READ,
|
|
||||||
std.os.MAP.PRIVATE,
|
|
||||||
fd,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
std.debug.print("---START xkb file---\n{s}\n---END xkb file---\n", .{mem});
|
|
||||||
xkb_keymap_opt = xkbcommon.Keymap.newFromString(xkb_ctx, @ptrCast(mem), .text_v1, .no_flags) orelse return error.XKBKeymap;
|
|
||||||
xkb_state_opt = xkbcommon.State.new(xkb_keymap_opt.?) orelse return error.XKBStateInit;
|
|
||||||
},
|
|
||||||
.modifiers => |mods| {
|
|
||||||
if (xkb_state_opt) |xkb_state| {
|
|
||||||
_ = xkb_state.updateMask(
|
|
||||||
mods.mods_depressed,
|
|
||||||
mods.mods_latched,
|
|
||||||
mods.mods_locked,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.key => |key| {
|
|
||||||
if (xkb_state_opt) |xkb_state| {
|
|
||||||
const keycode: xkbcommon.Keycode = key.key + 8;
|
|
||||||
const keysym: xkbcommon.Keysym = xkb_state.keyGetOneSym(keycode);
|
|
||||||
var buf: [64]u8 = undefined;
|
|
||||||
// const name_len = keysym.getName(&buf, buf.len);
|
|
||||||
// std.debug.print("{s}\n", .{buf[0..@intCast(name_len)]});
|
|
||||||
|
|
||||||
if (key.state == .pressed) {
|
|
||||||
const sym = xkbcommon.Keysym;
|
|
||||||
switch (@as(u32, @intFromEnum(keysym))) {
|
|
||||||
sym.BackSpace => {
|
|
||||||
try piece_table.delete(cursor_pos - 1, 1);
|
|
||||||
cursor_pos -= 1;
|
|
||||||
},
|
|
||||||
sym.Delete => {
|
|
||||||
piece_table.delete(cursor_pos, 1) catch |e| switch (e) {
|
|
||||||
error.OutOfBounds => {},
|
|
||||||
else => return e,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
sym.Left => {
|
|
||||||
cursor_pos -|= 1;
|
|
||||||
},
|
|
||||||
sym.Right => {
|
|
||||||
cursor_pos += 1;
|
|
||||||
cursor_pos = @min(cursor_pos, piece_table.getTotalSize());
|
|
||||||
},
|
|
||||||
else => if (key.state == .pressed) {
|
|
||||||
const size = xkb_state.keyGetUtf8(keycode, &buf);
|
|
||||||
try piece_table.insert(cursor_pos, buf[0..size]);
|
|
||||||
cursor_pos += size;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
const new_buffer_id, const new_framebuffer = try getFramebuffer(&framebuffers, &id_pool, pool_alloc, window_size);
|
|
||||||
|
|
||||||
// put some interesting colors into the new_framebuffer
|
|
||||||
renderGradient(new_framebuffer, window_size);
|
|
||||||
|
|
||||||
const text = try piece_table.writeAllAlloc();
|
|
||||||
defer gpa.free(text);
|
|
||||||
// blit some characters
|
|
||||||
renderText(new_framebuffer, window_size, .{ 10, 10 }, text);
|
|
||||||
|
|
||||||
try conn.send(
|
|
||||||
wayland.core.ShmPool.Request,
|
|
||||||
wl_shm_pool_id,
|
|
||||||
.{ .create_buffer = .{
|
|
||||||
.new_id = new_buffer_id,
|
|
||||||
.offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)),
|
|
||||||
.width = @intCast(window_size[0]),
|
|
||||||
.height = @intCast(window_size[1]),
|
|
||||||
.stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8),
|
|
||||||
.format = .argb8888,
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
|
|
||||||
try conn.send(
|
|
||||||
wayland.core.Surface.Request,
|
|
||||||
surface_id,
|
|
||||||
.{ .attach = .{
|
|
||||||
.buffer = new_buffer_id,
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
|
|
||||||
try conn.send(
|
|
||||||
wayland.core.Surface.Request,
|
|
||||||
surface_id,
|
|
||||||
.{ .damage = .{
|
|
||||||
.x = 0,
|
|
||||||
.y = 0,
|
|
||||||
.width = std.math.maxInt(i32),
|
|
||||||
.height = std.math.maxInt(i32),
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
|
|
||||||
// commit the configuration
|
|
||||||
try conn.send(
|
|
||||||
wayland.core.Surface.Request,
|
|
||||||
surface_id,
|
|
||||||
wayland.core.Surface.Request.commit,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
std.debug.print("<- wl_keyboard@{}\n", .{event});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else if (header.object_id == zwp_text_input_v3_id) {
|
|
||||||
const event = try wayland.deserialize(wayland.zwp.TextInputV3.Event, header, body);
|
|
||||||
std.debug.print("<- zwp_text_input_v3@{} event {}\n", .{ zwp_text_input_v3_id, event });
|
|
||||||
switch (event) {
|
|
||||||
.enter => |e| {
|
|
||||||
_ = e;
|
|
||||||
|
|
||||||
// if (e.surface == surface_id) {
|
|
||||||
try conn.send(
|
|
||||||
wayland.zwp.TextInputV3.Request,
|
|
||||||
zwp_text_input_v3_id,
|
|
||||||
.enable,
|
|
||||||
);
|
|
||||||
|
|
||||||
try conn.send(
|
|
||||||
wayland.zwp.TextInputV3.Request,
|
|
||||||
zwp_text_input_v3_id,
|
|
||||||
.{ .set_content_type = .{
|
|
||||||
.hint = .multiline,
|
|
||||||
.purpose = .normal,
|
|
||||||
} },
|
|
||||||
);
|
|
||||||
|
|
||||||
try conn.send(
|
|
||||||
wayland.zwp.TextInputV3.Request,
|
|
||||||
zwp_text_input_v3_id,
|
|
||||||
.commit,
|
|
||||||
);
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
.leave => |e| {
|
|
||||||
_ = e;
|
|
||||||
|
|
||||||
// if (e.surface == surface_id) {
|
|
||||||
try conn.send(
|
|
||||||
wayland.zwp.TextInputV3.Request,
|
|
||||||
zwp_text_input_v3_id,
|
|
||||||
.disable,
|
|
||||||
);
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
.preedit_string => {},
|
|
||||||
.commit_string => |commit| {
|
|
||||||
edit_slice = edit_buffer[0..commit.text.len];
|
|
||||||
@memcpy(edit_slice.?, commit.text);
|
|
||||||
},
|
|
||||||
.delete_surrounding_text => |offset| {
|
|
||||||
delete_before = offset.before_length;
|
|
||||||
delete_after = offset.after_length;
|
|
||||||
},
|
|
||||||
.done => |_| {
|
|
||||||
// 1 replace existing pre-edit string with cursor
|
|
||||||
// 2 delete requested surrounding text
|
|
||||||
const start = cursor_pos - delete_before;
|
|
||||||
const end = cursor_pos + delete_after;
|
|
||||||
const length = end - start;
|
|
||||||
if (length != 0) {
|
|
||||||
try piece_table.delete(start, length);
|
|
||||||
}
|
|
||||||
// 3 insert commit string with cursor at its end
|
|
||||||
if (edit_slice) |slice| {
|
|
||||||
try piece_table.insert(cursor_pos, slice);
|
|
||||||
cursor_pos += slice.len;
|
|
||||||
edit_slice = null;
|
|
||||||
}
|
|
||||||
// 4 calculate surrounding text to send
|
|
||||||
// 5 insert new preedit text in cursor position
|
|
||||||
// 6 place cursor inside predit text
|
|
||||||
},
|
|
||||||
}
|
|
||||||
} else if (framebuffers.get(header.object_id)) |framebuffer_slice| {
|
} else if (framebuffers.get(header.object_id)) |framebuffer_slice| {
|
||||||
const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body);
|
const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body);
|
||||||
switch (event) {
|
switch (event) {
|
||||||
|
@ -661,29 +373,3 @@ fn renderGradient(framebuffer: []Pixel, fb_size: [2]u32) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn textWidth(str: []const u8) usize {
|
|
||||||
return std.unicode.utf8CountCodepoints(str) catch 0; // incorrect, but I'm going with it
|
|
||||||
}
|
|
||||||
|
|
||||||
fn renderText(framebuffer: []Pixel, fb_size: [2]u32, pos: [2]usize, str: []const u8) void {
|
|
||||||
const top, const left = pos;
|
|
||||||
const bot = @min(top + 8, fb_size[1]);
|
|
||||||
const right = @min(left + textWidth(str) * 8, fb_size[0]);
|
|
||||||
|
|
||||||
for (top..bot) |y| {
|
|
||||||
const row = framebuffer[y * fb_size[0] .. (y + 1) * fb_size[0]];
|
|
||||||
for (row[left..right], left..right) |*pixel, x| {
|
|
||||||
const col = ((x - left) / 8);
|
|
||||||
const which_char = str[col];
|
|
||||||
if (!std.ascii.isPrint(which_char)) continue;
|
|
||||||
const char = font8x8.font8x8_basic[which_char];
|
|
||||||
const line = char[(y - top) % 8];
|
|
||||||
if ((line >> @intCast((x - left) % 8)) & 0x1 != 0) {
|
|
||||||
pixel.* = toPixel(Theme.foreground);
|
|
||||||
} else {
|
|
||||||
pixel.* = toPixel(Theme.background);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,689 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const wayland = @import("wayland");
|
||||||
|
const xkbcommon = @import("xkbcommon");
|
||||||
|
const font8x8 = @cImport({
|
||||||
|
@cInclude("font8x8.h");
|
||||||
|
});
|
||||||
|
const PieceTable = @import("PieceTable.zig").PieceTable;
|
||||||
|
|
||||||
|
const Pixel = [4]u8;
|
||||||
|
const Theme = struct {
|
||||||
|
const background = 0x282A36;
|
||||||
|
const current_line = 0x44475A;
|
||||||
|
const foreground = 0xF8F8F2;
|
||||||
|
const comment = 0x6272A4;
|
||||||
|
|
||||||
|
const cyan = 0x8BE9FD;
|
||||||
|
const green = 0x50FA7B;
|
||||||
|
const orange = 0xFFB86C;
|
||||||
|
const pink = 0xFF79C6;
|
||||||
|
const purple = 0xBD93F9;
|
||||||
|
const red = 0xFF5555;
|
||||||
|
const yellow = 0xF1FA8C;
|
||||||
|
};
|
||||||
|
|
||||||
|
fn toPixel(color: u24) Pixel {
|
||||||
|
return .{
|
||||||
|
@intCast(color & 0xFF),
|
||||||
|
@intCast(color >> 8 & 0xFF),
|
||||||
|
@intCast(color >> 16 & 0xFF),
|
||||||
|
0xFF,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
var general_allocator = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = general_allocator.deinit();
|
||||||
|
const gpa = general_allocator.allocator();
|
||||||
|
|
||||||
|
const display_path = try wayland.getDisplayPath(gpa);
|
||||||
|
defer gpa.free(display_path);
|
||||||
|
|
||||||
|
var conn = try wayland.Conn.init(gpa, display_path);
|
||||||
|
defer conn.deinit();
|
||||||
|
|
||||||
|
// Create an id pool to allocate ids for us
|
||||||
|
var id_pool = wayland.IdPool{};
|
||||||
|
|
||||||
|
const ids = try wayland.registerGlobals(gpa, &id_pool, conn.socket, &.{
|
||||||
|
wayland.core.Shm,
|
||||||
|
wayland.core.Compositor,
|
||||||
|
wayland.xdg.WmBase,
|
||||||
|
wayland.core.Seat,
|
||||||
|
wayland.zxdg.DecorationManagerV1,
|
||||||
|
wayland.zwp.TextInputManagerV3,
|
||||||
|
});
|
||||||
|
|
||||||
|
const DISPLAY_ID = 1;
|
||||||
|
const shm_id = ids[0] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
|
const compositor_id = ids[1] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
|
const xdg_wm_base_id = ids[2] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
|
const wl_seat_id = ids[3] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
|
const zwp_text_input_manager_v3 = ids[5] orelse return error.NeccessaryWaylandExtensionMissing;
|
||||||
|
|
||||||
|
const surface_id = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Compositor.Request,
|
||||||
|
compositor_id,
|
||||||
|
.{ .create_surface = .{
|
||||||
|
.new_id = surface_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
const xdg_surface_id = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.xdg.WmBase.Request,
|
||||||
|
xdg_wm_base_id,
|
||||||
|
.{ .get_xdg_surface = .{
|
||||||
|
.id = xdg_surface_id,
|
||||||
|
.surface = surface_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
const xdg_toplevel_id = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.xdg.Surface.Request,
|
||||||
|
xdg_surface_id,
|
||||||
|
.{ .get_toplevel = .{
|
||||||
|
.id = xdg_toplevel_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
const zwp_text_input_v3_id = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.zwp.TextInputManagerV3.Request,
|
||||||
|
zwp_text_input_manager_v3,
|
||||||
|
.{ .get_text_input = .{
|
||||||
|
.id = zwp_text_input_v3_id,
|
||||||
|
.seat = wl_seat_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
var zxdg_toplevel_decoration_id_opt: ?u32 = null;
|
||||||
|
if (ids[4]) |zxdg_decoration_manager_id| {
|
||||||
|
zxdg_toplevel_decoration_id_opt = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.zxdg.DecorationManagerV1.Request,
|
||||||
|
zxdg_decoration_manager_id,
|
||||||
|
.{ .get_toplevel_decoration = .{
|
||||||
|
.new_id = zxdg_toplevel_decoration_id_opt.?,
|
||||||
|
.toplevel = xdg_toplevel_id,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
wayland.core.Surface.Request.commit,
|
||||||
|
);
|
||||||
|
|
||||||
|
const registry_done_id = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Display.Request,
|
||||||
|
DISPLAY_ID,
|
||||||
|
.{ .sync = .{ .callback = registry_done_id } },
|
||||||
|
);
|
||||||
|
|
||||||
|
var done = false;
|
||||||
|
var surface_configured = false;
|
||||||
|
var seat_capabilties: ?wayland.core.Seat.Capability = null;
|
||||||
|
while (!done or !surface_configured) {
|
||||||
|
const header, const body = try conn.recv();
|
||||||
|
|
||||||
|
if (header.object_id == xdg_surface_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.configure => |conf| {
|
||||||
|
try conn.send(
|
||||||
|
wayland.xdg.Surface.Request,
|
||||||
|
xdg_surface_id,
|
||||||
|
.{ .ack_configure = .{
|
||||||
|
.serial = conf.serial,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
surface_configured = true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (zxdg_toplevel_decoration_id_opt != null and header.object_id == zxdg_toplevel_decoration_id_opt.?) {
|
||||||
|
const event = try wayland.deserialize(wayland.zxdg.ToplevelDecorationV1.Event, header, body);
|
||||||
|
std.debug.print("<- zxdg_toplevel_decoration@{}\n", .{event});
|
||||||
|
} else if (header.object_id == xdg_toplevel_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
|
||||||
|
std.debug.print("<- {}\n", .{event});
|
||||||
|
} else if (header.object_id == registry_done_id) {
|
||||||
|
done = true;
|
||||||
|
} else if (header.object_id == shm_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Shm.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.format => |format| std.debug.print("<- format {} {}\n", .{ format.format, std.zig.fmtEscapes(std.mem.asBytes(&format.format)) }),
|
||||||
|
}
|
||||||
|
} else if (header.object_id == wl_seat_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Seat.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.capabilities => |capabilities| {
|
||||||
|
const cap: wayland.core.Seat.Capability = @bitCast(capabilities.capability);
|
||||||
|
std.debug.print("<- wl_seat.capabilties = {}\n", .{cap});
|
||||||
|
seat_capabilties = cap;
|
||||||
|
},
|
||||||
|
.name => |name| {
|
||||||
|
std.debug.print("<- wl_seat.name = {s}\n", .{name.name});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (header.object_id == DISPLAY_ID) {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
|
||||||
|
.delete_id => |id| {
|
||||||
|
std.debug.print("id {} deleted\n", .{id});
|
||||||
|
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)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wl_pointer_id_opt: ?u32 = null;
|
||||||
|
var wl_keyboard_id_opt: ?u32 = null;
|
||||||
|
if (seat_capabilties) |caps| {
|
||||||
|
if (caps.pointer) {
|
||||||
|
wl_pointer_id_opt = id_pool.create();
|
||||||
|
std.debug.print("wl pointer id: {}\n", .{wl_pointer_id_opt.?});
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Seat.Request,
|
||||||
|
wl_seat_id,
|
||||||
|
.{ .get_pointer = .{
|
||||||
|
.new_id = wl_pointer_id_opt.?,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (caps.keyboard) {
|
||||||
|
wl_keyboard_id_opt = id_pool.create();
|
||||||
|
std.debug.print("wl keyboard id: {}\n", .{wl_keyboard_id_opt.?});
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Seat.Request,
|
||||||
|
wl_seat_id,
|
||||||
|
.{ .get_keyboard = .{
|
||||||
|
.new_id = wl_keyboard_id_opt.?,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const wl_pointer_id = wl_pointer_id_opt orelse return error.MissingPointer;
|
||||||
|
const wl_keyboard_id = wl_keyboard_id_opt orelse return error.MissingKeyboard;
|
||||||
|
|
||||||
|
// allocate a shared memory file for display purposes
|
||||||
|
const framebuffer_size = [2]u32{ 128, 128 };
|
||||||
|
const pool_file_len = 1024 * framebuffer_size[0] * framebuffer_size[1] * @sizeOf(Pixel);
|
||||||
|
|
||||||
|
const pool_fd = try std.os.memfd_create("my-wayland-framebuffer", 0);
|
||||||
|
try std.os.ftruncate(pool_fd, pool_file_len);
|
||||||
|
const pool_bytes = try std.os.mmap(null, pool_file_len, std.os.PROT.READ | std.os.PROT.WRITE, std.os.MAP.SHARED, pool_fd, 0);
|
||||||
|
var pool_fixed_buffer_allocator = std.heap.FixedBufferAllocator.init(pool_bytes);
|
||||||
|
var pool_general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){ .backing_allocator = pool_fixed_buffer_allocator.allocator() };
|
||||||
|
const pool_alloc = pool_general_purpose_allocator.allocator();
|
||||||
|
|
||||||
|
const framebuffer = try pool_alloc.alloc(Pixel, framebuffer_size[0] * framebuffer_size[1]);
|
||||||
|
|
||||||
|
// put some interesting colors into the framebuffer
|
||||||
|
renderGradient(framebuffer, framebuffer_size);
|
||||||
|
|
||||||
|
const wl_shm_pool_id = id_pool.create();
|
||||||
|
{
|
||||||
|
std.debug.print("framebuffer_fd: {}\n", .{pool_fd});
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Shm.Request,
|
||||||
|
shm_id,
|
||||||
|
.{ .create_pool = .{
|
||||||
|
.new_id = wl_shm_pool_id,
|
||||||
|
.fd = @enumFromInt(pool_fd),
|
||||||
|
.size = pool_file_len,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var framebuffers = std.AutoHashMap(u32, []Pixel).init(gpa);
|
||||||
|
defer framebuffers.deinit();
|
||||||
|
try framebuffers.put(wl_shm_pool_id, framebuffer);
|
||||||
|
|
||||||
|
const wl_buffer_id = id_pool.create();
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.ShmPool.Request,
|
||||||
|
wl_shm_pool_id,
|
||||||
|
.{ .create_buffer = .{
|
||||||
|
.new_id = wl_buffer_id,
|
||||||
|
.offset = 0,
|
||||||
|
.width = framebuffer_size[0],
|
||||||
|
.height = framebuffer_size[1],
|
||||||
|
.stride = framebuffer_size[0] * @sizeOf([4]u8),
|
||||||
|
.format = .argb8888,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
.{ .attach = .{
|
||||||
|
.buffer = wl_buffer_id,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
.{ .damage = .{
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = std.math.maxInt(i32),
|
||||||
|
.height = std.math.maxInt(i32),
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
wayland.core.Surface.Request.commit,
|
||||||
|
);
|
||||||
|
|
||||||
|
var window_size: [2]u32 = [2]u32{ @intCast(framebuffer_size[0]), @intCast(framebuffer_size[1]) };
|
||||||
|
const xkb_ctx = xkbcommon.Context.new(.no_flags) orelse return error.XKBInit;
|
||||||
|
defer xkb_ctx.unref();
|
||||||
|
|
||||||
|
var xkb_keymap_opt: ?*xkbcommon.Keymap = null;
|
||||||
|
defer if (xkb_keymap_opt) |xkb_keymap| {
|
||||||
|
xkb_keymap.unref();
|
||||||
|
};
|
||||||
|
var xkb_state_opt: ?*xkbcommon.State = null;
|
||||||
|
defer if (xkb_state_opt) |xkb_state| {
|
||||||
|
xkb_state.unref();
|
||||||
|
};
|
||||||
|
|
||||||
|
var piece_table = try PieceTable.init(gpa, "Hello, World!");
|
||||||
|
defer piece_table.deinit();
|
||||||
|
|
||||||
|
var edit_buffer: [1024]u8 = [1]u8{0} ** 1024;
|
||||||
|
var edit_slice: ?[]u8 = null;
|
||||||
|
|
||||||
|
var delete_before: usize = 0;
|
||||||
|
var delete_after: usize = 0;
|
||||||
|
|
||||||
|
// var preedit_buffer: [1024]u8 = [1]u8{0} ** 1024;
|
||||||
|
// var preedit_slice: ?[]u8 = null;
|
||||||
|
|
||||||
|
var cursor_pos: usize = piece_table.getTotalSize();
|
||||||
|
|
||||||
|
var running = true;
|
||||||
|
while (running) {
|
||||||
|
const header, const body = try conn.recv();
|
||||||
|
|
||||||
|
if (header.object_id == xdg_surface_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.Surface.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.configure => |conf| {
|
||||||
|
try conn.send(
|
||||||
|
wayland.xdg.Surface.Request,
|
||||||
|
xdg_surface_id,
|
||||||
|
.{ .ack_configure = .{
|
||||||
|
.serial = conf.serial,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
const new_buffer_id = id_pool.create();
|
||||||
|
const new_framebuffer = try pool_alloc.alloc(Pixel, window_size[0] * window_size[1]);
|
||||||
|
try framebuffers.put(new_buffer_id, new_framebuffer);
|
||||||
|
|
||||||
|
// put some interesting colors into the new_framebuffer
|
||||||
|
renderGradient(new_framebuffer, window_size);
|
||||||
|
|
||||||
|
const text = try piece_table.writeAllAlloc();
|
||||||
|
defer gpa.free(text);
|
||||||
|
// blit some characters
|
||||||
|
renderText(new_framebuffer, window_size, .{ 10, 10 }, text);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.ShmPool.Request,
|
||||||
|
wl_shm_pool_id,
|
||||||
|
.{ .create_buffer = .{
|
||||||
|
.new_id = new_buffer_id,
|
||||||
|
.offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)),
|
||||||
|
.width = @intCast(window_size[0]),
|
||||||
|
.height = @intCast(window_size[1]),
|
||||||
|
.stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8),
|
||||||
|
.format = .argb8888,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
.{ .attach = .{
|
||||||
|
.buffer = new_buffer_id,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
.{ .damage = .{
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = std.math.maxInt(i32),
|
||||||
|
.height = std.math.maxInt(i32),
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
// commit the configuration
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
wayland.core.Surface.Request.commit,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (header.object_id == xdg_toplevel_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.Toplevel.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.configure => |conf| {
|
||||||
|
std.debug.print("<- xdg_toplevel@{} configure <{}, {}> {any}\n", .{ header.object_id, conf.width, conf.height, conf.states });
|
||||||
|
window_size = .{
|
||||||
|
@intCast(conf.width),
|
||||||
|
@intCast(conf.height),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
.close => running = false,
|
||||||
|
else => |tag| std.debug.print("<- xdg_toplevel@{} {s} {}\n", .{ header.object_id, @tagName(tag), event }),
|
||||||
|
}
|
||||||
|
} else if (header.object_id == xdg_wm_base_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.xdg.WmBase.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.ping => |ping| {
|
||||||
|
try conn.send(
|
||||||
|
wayland.xdg.WmBase.Request,
|
||||||
|
xdg_wm_base_id,
|
||||||
|
.{ .pong = .{
|
||||||
|
.serial = ping.serial,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (header.object_id == wl_pointer_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Pointer.Event, header, body);
|
||||||
|
std.debug.print("<- wl_pointer@{}\n", .{event});
|
||||||
|
} else if (header.object_id == wl_keyboard_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Keyboard.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.keymap => |keymap| {
|
||||||
|
const fd = conn.fd_queue.orderedRemove(0);
|
||||||
|
std.debug.print("keymap format={}, size={}, fd={}\n", .{
|
||||||
|
keymap.format,
|
||||||
|
keymap.size,
|
||||||
|
fd,
|
||||||
|
});
|
||||||
|
const mem = try std.os.mmap(
|
||||||
|
null,
|
||||||
|
keymap.size,
|
||||||
|
std.os.PROT.READ,
|
||||||
|
std.os.MAP.PRIVATE,
|
||||||
|
fd,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
std.debug.print("---START xkb file---\n{s}\n---END xkb file---\n", .{mem});
|
||||||
|
xkb_keymap_opt = xkbcommon.Keymap.newFromString(xkb_ctx, @ptrCast(mem), .text_v1, .no_flags) orelse return error.XKBKeymap;
|
||||||
|
xkb_state_opt = xkbcommon.State.new(xkb_keymap_opt.?) orelse return error.XKBStateInit;
|
||||||
|
},
|
||||||
|
.modifiers => |mods| {
|
||||||
|
if (xkb_state_opt) |xkb_state| {
|
||||||
|
_ = xkb_state.updateMask(
|
||||||
|
mods.mods_depressed,
|
||||||
|
mods.mods_latched,
|
||||||
|
mods.mods_locked,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.key => |key| {
|
||||||
|
if (xkb_state_opt) |xkb_state| {
|
||||||
|
const keycode: xkbcommon.Keycode = key.key + 8;
|
||||||
|
const keysym: xkbcommon.Keysym = xkb_state.keyGetOneSym(keycode);
|
||||||
|
var buf: [64]u8 = undefined;
|
||||||
|
// const name_len = keysym.getName(&buf, buf.len);
|
||||||
|
// std.debug.print("{s}\n", .{buf[0..@intCast(name_len)]});
|
||||||
|
|
||||||
|
if (key.state == .pressed) {
|
||||||
|
const sym = xkbcommon.Keysym;
|
||||||
|
switch (@as(u32, @intFromEnum(keysym))) {
|
||||||
|
sym.BackSpace => {
|
||||||
|
try piece_table.delete(cursor_pos - 1, 1);
|
||||||
|
cursor_pos -= 1;
|
||||||
|
},
|
||||||
|
sym.Delete => {
|
||||||
|
piece_table.delete(cursor_pos, 1) catch |e| switch (e) {
|
||||||
|
error.OutOfBounds => {},
|
||||||
|
else => return e,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sym.Left => {
|
||||||
|
cursor_pos -|= 1;
|
||||||
|
},
|
||||||
|
sym.Right => {
|
||||||
|
cursor_pos += 1;
|
||||||
|
cursor_pos = @min(cursor_pos, piece_table.getTotalSize());
|
||||||
|
},
|
||||||
|
else => if (key.state == .pressed) {
|
||||||
|
const size = xkb_state.keyGetUtf8(keycode, &buf);
|
||||||
|
try piece_table.insert(cursor_pos, buf[0..size]);
|
||||||
|
cursor_pos += size;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const new_buffer_id, const new_framebuffer = try getFramebuffer(&framebuffers, &id_pool, pool_alloc, window_size);
|
||||||
|
|
||||||
|
// put some interesting colors into the new_framebuffer
|
||||||
|
renderGradient(new_framebuffer, window_size);
|
||||||
|
|
||||||
|
const text = try piece_table.writeAllAlloc();
|
||||||
|
defer gpa.free(text);
|
||||||
|
// blit some characters
|
||||||
|
renderText(new_framebuffer, window_size, .{ 10, 10 }, text);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.ShmPool.Request,
|
||||||
|
wl_shm_pool_id,
|
||||||
|
.{ .create_buffer = .{
|
||||||
|
.new_id = new_buffer_id,
|
||||||
|
.offset = @intCast(@intFromPtr(new_framebuffer.ptr) - @intFromPtr(pool_bytes.ptr)),
|
||||||
|
.width = @intCast(window_size[0]),
|
||||||
|
.height = @intCast(window_size[1]),
|
||||||
|
.stride = @as(i32, @intCast(window_size[0])) * @sizeOf([4]u8),
|
||||||
|
.format = .argb8888,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
.{ .attach = .{
|
||||||
|
.buffer = new_buffer_id,
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
.{ .damage = .{
|
||||||
|
.x = 0,
|
||||||
|
.y = 0,
|
||||||
|
.width = std.math.maxInt(i32),
|
||||||
|
.height = std.math.maxInt(i32),
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
// commit the configuration
|
||||||
|
try conn.send(
|
||||||
|
wayland.core.Surface.Request,
|
||||||
|
surface_id,
|
||||||
|
wayland.core.Surface.Request.commit,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.debug.print("<- wl_keyboard@{}\n", .{event});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (header.object_id == zwp_text_input_v3_id) {
|
||||||
|
const event = try wayland.deserialize(wayland.zwp.TextInputV3.Event, header, body);
|
||||||
|
std.debug.print("<- zwp_text_input_v3@{} event {}\n", .{ zwp_text_input_v3_id, event });
|
||||||
|
switch (event) {
|
||||||
|
.enter => |e| {
|
||||||
|
_ = e;
|
||||||
|
|
||||||
|
// if (e.surface == surface_id) {
|
||||||
|
try conn.send(
|
||||||
|
wayland.zwp.TextInputV3.Request,
|
||||||
|
zwp_text_input_v3_id,
|
||||||
|
.enable,
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.zwp.TextInputV3.Request,
|
||||||
|
zwp_text_input_v3_id,
|
||||||
|
.{ .set_content_type = .{
|
||||||
|
.hint = .multiline,
|
||||||
|
.purpose = .normal,
|
||||||
|
} },
|
||||||
|
);
|
||||||
|
|
||||||
|
try conn.send(
|
||||||
|
wayland.zwp.TextInputV3.Request,
|
||||||
|
zwp_text_input_v3_id,
|
||||||
|
.commit,
|
||||||
|
);
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
.leave => |e| {
|
||||||
|
_ = e;
|
||||||
|
|
||||||
|
// if (e.surface == surface_id) {
|
||||||
|
try conn.send(
|
||||||
|
wayland.zwp.TextInputV3.Request,
|
||||||
|
zwp_text_input_v3_id,
|
||||||
|
.disable,
|
||||||
|
);
|
||||||
|
// }
|
||||||
|
},
|
||||||
|
.preedit_string => {},
|
||||||
|
.commit_string => |commit| {
|
||||||
|
edit_slice = edit_buffer[0..commit.text.len];
|
||||||
|
@memcpy(edit_slice.?, commit.text);
|
||||||
|
},
|
||||||
|
.delete_surrounding_text => |offset| {
|
||||||
|
delete_before = offset.before_length;
|
||||||
|
delete_after = offset.after_length;
|
||||||
|
},
|
||||||
|
.done => |_| {
|
||||||
|
// 1 replace existing pre-edit string with cursor
|
||||||
|
// 2 delete requested surrounding text
|
||||||
|
const start = cursor_pos - delete_before;
|
||||||
|
const end = cursor_pos + delete_after;
|
||||||
|
const length = end - start;
|
||||||
|
if (length != 0) {
|
||||||
|
try piece_table.delete(start, length);
|
||||||
|
}
|
||||||
|
// 3 insert commit string with cursor at its end
|
||||||
|
if (edit_slice) |slice| {
|
||||||
|
try piece_table.insert(cursor_pos, slice);
|
||||||
|
cursor_pos += slice.len;
|
||||||
|
edit_slice = null;
|
||||||
|
}
|
||||||
|
// 4 calculate surrounding text to send
|
||||||
|
// 5 insert new preedit text in cursor position
|
||||||
|
// 6 place cursor inside predit text
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (framebuffers.get(header.object_id)) |framebuffer_slice| {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Buffer.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.release => {
|
||||||
|
_ = framebuffers.remove(header.object_id);
|
||||||
|
pool_alloc.free(framebuffer_slice);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if (header.object_id == DISPLAY_ID) {
|
||||||
|
const event = try wayland.deserialize(wayland.core.Display.Event, header, body);
|
||||||
|
switch (event) {
|
||||||
|
.@"error" => |err| std.debug.print("<- error({}): {} {s}\n", .{ err.object_id, err.code, err.message }),
|
||||||
|
.delete_id => |id| 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)) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmsg(comptime T: type) type {
|
||||||
|
const padding_size = (@sizeOf(T) + @sizeOf(c_long) - 1) & ~(@as(usize, @sizeOf(c_long)) - 1);
|
||||||
|
return extern struct {
|
||||||
|
len: c_ulong = @sizeOf(@This()) - padding_size,
|
||||||
|
level: c_int,
|
||||||
|
type: c_int,
|
||||||
|
data: T,
|
||||||
|
_padding: [padding_size]u8 align(1) = [_]u8{0} ** padding_size,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn getFramebuffer(framebuffers: *std.AutoHashMap(u32, []Pixel), id_pool: *wayland.IdPool, pool_alloc: std.mem.Allocator, fb_size: [2]u32) !struct { u32, []Pixel } {
|
||||||
|
const new_buffer_id = id_pool.create();
|
||||||
|
const new_framebuffer = try pool_alloc.alloc(Pixel, fb_size[0] * fb_size[1]);
|
||||||
|
try framebuffers.put(new_buffer_id, new_framebuffer);
|
||||||
|
return .{ new_buffer_id, new_framebuffer };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderGradient(framebuffer: []Pixel, fb_size: [2]u32) void {
|
||||||
|
for (0..fb_size[1]) |y| {
|
||||||
|
const row = framebuffer[y * fb_size[0] .. (y + 1) * fb_size[0]];
|
||||||
|
for (row, 0..fb_size[0]) |*pixel, x| {
|
||||||
|
pixel.* = .{
|
||||||
|
@truncate(x),
|
||||||
|
@truncate(y),
|
||||||
|
0x00,
|
||||||
|
0xFF,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn textWidth(str: []const u8) usize {
|
||||||
|
return std.unicode.utf8CountCodepoints(str) catch 0; // incorrect, but I'm going with it
|
||||||
|
}
|
||||||
|
|
||||||
|
fn renderText(framebuffer: []Pixel, fb_size: [2]u32, pos: [2]usize, str: []const u8) void {
|
||||||
|
const top, const left = pos;
|
||||||
|
const bot = @min(top + 8, fb_size[1]);
|
||||||
|
const right = @min(left + textWidth(str) * 8, fb_size[0]);
|
||||||
|
|
||||||
|
for (top..bot) |y| {
|
||||||
|
const row = framebuffer[y * fb_size[0] .. (y + 1) * fb_size[0]];
|
||||||
|
for (row[left..right], left..right) |*pixel, x| {
|
||||||
|
const col = ((x - left) / 8);
|
||||||
|
const which_char = str[col];
|
||||||
|
if (!std.ascii.isPrint(which_char)) continue;
|
||||||
|
const char = font8x8.font8x8_basic[which_char];
|
||||||
|
const line = char[(y - top) % 8];
|
||||||
|
if ((line >> @intCast((x - left) % 8)) & 0x1 != 0) {
|
||||||
|
pixel.* = toPixel(Theme.foreground);
|
||||||
|
} else {
|
||||||
|
pixel.* = toPixel(Theme.background);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue