From d6b29868389141b89bc9c648430905eecacce7a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vlad=20P=C4=83n=C4=83zan?= Date: Mon, 29 Apr 2024 21:16:40 +0200 Subject: [PATCH] scrollable wip --- apps/fontviewer/fontviewer.zig | 10 ++- src/client.zig | 5 +- toolkit/App.zig | 28 ++++---- toolkit/Surface.zig | 114 ++++++++++++++++++--------------- toolkit/font/bdf.zig | 21 +++--- toolkit/widget.zig | 16 +++++ toolkit/widgets/Scrollable.zig | 28 ++++++++ 7 files changed, 141 insertions(+), 81 deletions(-) create mode 100644 toolkit/widgets/Scrollable.zig diff --git a/apps/fontviewer/fontviewer.zig b/apps/fontviewer/fontviewer.zig index 7bc3f21..cb44b4e 100644 --- a/apps/fontviewer/fontviewer.zig +++ b/apps/fontviewer/fontviewer.zig @@ -29,7 +29,7 @@ const PopupHandler = struct { const rect = data.layout.get(idx, .rect); const popup = app.new_surface(.{ .xdg_popup = .{ .anchor = rect, - .parent = data.parent.wl.xdg_toplevel.xdg_surface, + .parent = data.parent.role.xdg_toplevel.xdg_surface, } }, data.widget) catch unreachable; data.wl_surface = popup.wl_surface; }, @@ -111,6 +111,7 @@ pub fn main() !void { .font = app.font, }; + var scrollable: WidgetIdx = undefined; const main_widget = b: { const flex = layout.add2(.flex, .{ .orientation = .vertical }); const menu_bar = c: { @@ -130,8 +131,11 @@ pub fn main() !void { .columns = 32, .font = app.font, }); + scrollable = layout.add2(.scrollable, .{}); + layout.set(scrollable, .flex, 1); + layout.set_handler2(font_map, &font_map_handler, &s); - layout.set(flex, .children, &.{ menu_bar, font_map, font_view }); + layout.set(flex, .children, &.{ menu_bar, font_map, font_view, scrollable }); s.connect(.selected_range, font_map, FontMap, .selected_range); s.connect(.selected_glyph, font_map, FontMap, .selected_code_point); @@ -161,7 +165,7 @@ pub fn main() !void { }; const btn = layout.add2(.button, .{}); const sub_w = try app.new_surface(.{ .wl_subsurface = .{ .parent = bar.wl_surface } }, btn); - _ = sub_w; // autofix + layout.set(scrollable, .subsurface, sub_w.wl_surface); try app.client.recvEvents(); } diff --git a/src/client.zig b/src/client.zig index 4803ef2..cf1b447 100644 --- a/src/client.zig +++ b/src/client.zig @@ -323,10 +323,11 @@ pub const Client = struct { } } pub fn bind(client: *Client, idx: wl.Registry, _name: u32, comptime T: type, _version: u32) T { + const v = @min(T.interface.version, _version); var _args = [_]Argument{ .{ .uint = _name }, .{ .string = T.interface.name }, - .{ .uint = _version }, + .{ .uint = v }, .{ .new_id = 0 }, }; const proxy = Proxy{ .client = client, .id = @intFromEnum(idx) }; @@ -341,7 +342,7 @@ fn displayListener(client: *Client, _: wl.Display, event: wl.Display.Event, _: ? }, .delete_id => |del| { const id = del.id; - std.log.info("del id {}", .{id}); + // std.log.info("del id {}", .{id}); std.debug.assert(client.objects.items(.is_free)[id] == false); client.objects.items(.is_free)[id] = true; }, diff --git a/toolkit/App.zig b/toolkit/App.zig index be4a24f..84a6754 100644 --- a/toolkit/App.zig +++ b/toolkit/App.zig @@ -19,7 +19,6 @@ cursor_shape: wp.CursorShapeDeviceV1.Shape = .default, // surfaces: std.ArrayListUnmanaged(Surface) = .{}, surfaces: std.AutoHashMapUnmanaged(wl.Surface, Surface) = .{}, -subsurfaces: std.AutoHashMapUnmanaged(wl.Subsurface, Surface) = .{}, active_surface: ?wl.Surface = null, layout: Layout = .{}, @@ -56,10 +55,10 @@ pub fn deinit(app: *App) void { alloc.destroy(app); } -pub fn new_surface(app: *App, opts: Surface.SurfaceInitOpts, root_widget: WidgetIdx) !*Surface { +pub fn new_surface(app: *App, opts: Surface.SurfaceRoleInit, root_widget: WidgetIdx) !*Surface { const surf = try new_common(app, root_widget); - surf.wl = Surface.init_wl(surf, opts); + surf.role = Surface.init_role(surf, opts); app.client.request(surf.wl_surface, .commit, {}); return surf; @@ -74,12 +73,13 @@ pub fn new_common(app: *App, root_widget: WidgetIdx) !*Surface { const surf = result.value_ptr; const size = app.layout.call(root_widget, .size, .{Size.Minmax.ZERO}); + app.layout.set(root_widget, .rect, size.to_rect()); surf.* = .{ .app = app, .wl_surface = wl_surface, - .wl = undefined, - .size = size, + .role = undefined, .min_size = size, + .size = size, .last_frame = 0, .root = root_widget, }; @@ -101,19 +101,19 @@ pub fn registry_listener(client: *wlnd.Client, registry: wl.Registry, event: wl. switch (event) { .global => |global| { if (std.mem.orderZ(u8, global.interface, wl.Compositor.interface.name) == .eq) { - context.compositor = client.bind(registry, global.name, wl.Compositor, 1); + context.compositor = client.bind(registry, global.name, wl.Compositor, global.version); } else if (std.mem.orderZ(u8, global.interface, wl.Shm.interface.name) == .eq) { - context.shm = client.bind(registry, global.name, wl.Shm, 1); + context.shm = client.bind(registry, global.name, wl.Shm, global.version); } else if (std.mem.orderZ(u8, global.interface, xdg.WmBase.interface.name) == .eq) { - context.wm_base = client.bind(registry, global.name, xdg.WmBase, 1); + context.wm_base = client.bind(registry, global.name, xdg.WmBase, global.version); } else if (std.mem.orderZ(u8, global.interface, zwlr.LayerShellV1.interface.name) == .eq) { - context.layer_shell = client.bind(registry, global.name, zwlr.LayerShellV1, 1); + context.layer_shell = client.bind(registry, global.name, zwlr.LayerShellV1, global.version); } else if (std.mem.orderZ(u8, global.interface, wp.CursorShapeManagerV1.interface.name) == .eq) { - context.cursor_shape_manager = client.bind(registry, global.name, wp.CursorShapeManagerV1, 1); + context.cursor_shape_manager = client.bind(registry, global.name, wp.CursorShapeManagerV1, global.version); } else if (std.mem.orderZ(u8, global.interface, wl.Subcompositor.interface.name) == .eq) { - context.subcompositor = client.bind(registry, global.name, wl.Subcompositor, 1); + context.subcompositor = client.bind(registry, global.name, wl.Subcompositor, global.version); } else if (std.mem.orderZ(u8, global.interface, wl.Seat.interface.name) == .eq) { - context.seat = client.bind(registry, global.name, wl.Seat, 1); + context.seat = client.bind(registry, global.name, wl.Seat, global.version); client.set_listener(context.seat.?, *App, seat_listener, context); } }, @@ -167,6 +167,10 @@ fn pointer_listener(client: *wlnd.Client, _: wl.Pointer, _event: wl.Pointer.Even .button => |ev| blk: { break :blk .{ .button = .{ .button = @enumFromInt(ev.button), .state = ev.state } }; }, + .frame => |_| blk: { + // TODO + break :blk null; + }, else => |d| blk: { std.log.info("pointer event: {}", .{d}); break :blk null; diff --git a/toolkit/Surface.zig b/toolkit/Surface.zig index 96c217b..78a832c 100644 --- a/toolkit/Surface.zig +++ b/toolkit/Surface.zig @@ -2,7 +2,8 @@ app: *App, root: WidgetIdx = undefined, wl_surface: wl.Surface, -wl: union(SurfaceType) { +role: union(SurfaceRole) { + const Role = @This(); xdg_toplevel: struct { xdg_surface: xdg.Surface, xdg_toplevel: xdg.Toplevel, @@ -12,7 +13,17 @@ wl: union(SurfaceType) { xdg_popup: xdg.Popup, }, wlr_layer_surface: zwlr.LayerSurfaceV1, - wl_subsurface: wl.Subsurface, + wl_subsurface: struct { + wl_subsurface: wl.Subsurface, + pub fn set_position(self: *@This(), x: u32, y: u32) void { + const role: *Role = @fieldParentPtr("wl_subsurface", self); + const surface: *Surface = @alignCast(@fieldParentPtr("role", role)); + const client = surface.app.client; + + client.request(self.wl_subsurface, .set_position, .{ .x = @intCast(x), .y = @intCast(y) }); + client.request(surface.wl_surface, .commit, {}); + } + }, }, size: Size, min_size: Size, @@ -20,27 +31,27 @@ last_frame: u32, frame_done: bool = true, initial_draw: bool = false, -pub const SurfaceType = enum { +pub const SurfaceRole = enum { xdg_toplevel, xdg_popup, wlr_layer_surface, wl_subsurface, }; -pub const SurfaceInitOpts = union(SurfaceType) { +pub const SurfaceRoleInit = union(SurfaceRole) { xdg_toplevel: void, xdg_popup: struct { parent: xdg.Surface, anchor: Rect }, wlr_layer_surface: void, wl_subsurface: struct { parent: wl.Surface }, }; -pub fn init_wl( +pub fn init_role( surface: *Surface, - stype: SurfaceInitOpts, -) std.meta.FieldType(Surface, .wl) { + role: SurfaceRoleInit, +) std.meta.FieldType(Surface, .role) { const app = surface.app; const client = app.client; - switch (stype) { + switch (role) { .wlr_layer_surface => { std.debug.assert(app.layer_shell != null); const layer_surface = client.request(app.layer_shell.?, .get_layer_surface, .{ @@ -67,8 +78,8 @@ pub fn init_wl( client.set_listener(xdg_toplevel, *Surface, Surface.xdg_toplevel_listener, surface); client.request(xdg_toplevel, .set_title, .{ .title = "Demo" }); client.request(xdg_toplevel, .set_min_size, .{ - .width = @intCast(surface.size.width), - .height = @intCast(surface.size.height), + .width = @intCast(surface.min_size.width), + .height = @intCast(surface.min_size.height), }); return .{ .xdg_toplevel = .{ @@ -82,10 +93,12 @@ pub fn init_wl( .parent = opts.parent, }); // errdefer client.request(wl_subsurface), .destroy, {}); - client.request(wl_subsurface, .set_sync, {}); + client.request(wl_subsurface, .set_desync, {}); client.request(wl_subsurface, .set_position, .{ .x = 500, .y = 22 }); - return .{ .wl_subsurface = wl_subsurface }; + surface.draw(); + + return .{ .wl_subsurface = .{ .wl_subsurface = wl_subsurface } }; }, .xdg_popup => |opts| { const xdg_surface = client.request(app.wm_base.?, .get_xdg_surface, .{ .surface = surface.wl_surface }); @@ -95,8 +108,8 @@ pub fn init_wl( defer client.request(positioner, .destroy, {}); client.request(positioner, .set_size, .{ - .width = @intCast(surface.size.width), - .height = @intCast(surface.size.height), + .width = @intCast(surface.min_size.width), + .height = @intCast(surface.min_size.height), }); const anchor_rect = opts.anchor; client.request(positioner, .set_anchor_rect, .{ @@ -129,20 +142,16 @@ pub fn destroy(self: *Surface) void { const client = self.app.client; // const index = (@intFromPtr(self) - @intFromPtr(self.app.surfaces.items.ptr)) / @sizeOf(Surface); // std.log.info("index: {}", .{index}); - switch (self.wl) { + switch (self.role) { .xdg_popup => |x| { client.request(x.xdg_popup, .destroy, {}); client.request(x.xdg_surface, .destroy, {}); }, - .xdg_toplevel => |x| { - _ = x; // autofix - }, + .xdg_toplevel => |_| {}, - .wlr_layer_surface => |x| { - _ = x; // autofix - }, + .wlr_layer_surface => |_| {}, .wl_subsurface => |x| { - client.request(x, .destroy, {}); + client.request(x.wl_subsurface, .destroy, {}); }, } client.request(self.wl_surface, .destroy, {}); @@ -159,10 +168,11 @@ pub fn schedule_redraw(self: *Surface) void { } pub fn draw(self: *Surface) void { - std.log.info("draw {}", .{std.meta.activeTag(self.wl)}); + std.log.info("draw {}", .{std.meta.activeTag(self.role)}); const client = self.app.client; - const buf = Buffer.get(self.app.client, self.app.shm.?, self.size.width, self.size.height) catch unreachable; - if (self.size.contains(self.min_size)) { + const size = self.app.layout.get(self.root, .rect).get_size(); + const buf = Buffer.get(self.app.client, self.app.shm.?, size.width, size.height) catch unreachable; + if (size.contains(self.min_size)) { const ctx = PaintCtx{ .buffer = @ptrCast(std.mem.bytesAsSlice(u32, buf.pool.mmap)), //TODO: use ptrCast directly when implemented in zig @@ -176,7 +186,7 @@ pub fn draw(self: *Surface) void { @memset(buf.pool.mmap, 200); } client.request(self.wl_surface, .attach, .{ .buffer = buf.wl_buffer, .x = 0, .y = 0 }); - client.request(self.wl_surface, .damage, .{ + client.request(self.wl_surface, .damage_buffer, .{ .x = 0, .y = 0, .width = std.math.maxInt(i32), @@ -222,12 +232,14 @@ fn layer_suface_listener(client: *wlnd.Client, layer_suface: zwlr.LayerSurfaceV1 surf.initial_draw = true; } - surf.size = .{ + const size = Size{ .width = configure.width, .height = configure.height, }; - std.log.info("w: {} h: {}", .{ surf.size.width, surf.size.height }); + surf.app.layout.set(surf.root, .rect, size.to_rect()); + + // std.log.info("w: {} h: {}", .{ surf.size.width, surf.size.height }); // if (self.anchor.top and self.anchor.bottom) { // self.size.height = @as(usize, @intCast(configure.height)); @@ -255,53 +267,53 @@ fn frame_listener(_: *wlnd.Client, _: wl.Callback, event: wl.Callback.Event, sur } } -fn xdg_surface_listener(client: *wlnd.Client, xdg_surface: xdg.Surface, event: xdg.Surface.Event, win: *Surface) void { +fn xdg_surface_listener(client: *wlnd.Client, xdg_surface: xdg.Surface, event: xdg.Surface.Event, surf: *Surface) void { switch (event) { .configure => |configure| { + std.log.info("configure={}", .{configure}); client.request(xdg_surface, .ack_configure, .{ .serial = configure.serial }); - if (!win.initial_draw) { - win.draw(); - var it = win.app.surfaces.valueIterator(); - while (it.next()) |surface| { - if (surface.wl == .wl_subsurface) { - surface.draw(); - } - } - win.initial_draw = true; + if (!surf.initial_draw) { + surf.draw(); + // for (surf.subsurfaces.items) |wl_surface|{ + // const subs = surf.app.surfaces.getPtr(wl_surface).?; + // subs.draw(); + // } + surf.initial_draw = true; } }, } } -fn xdg_toplevel_listener(_: *wlnd.Client, _: xdg.Toplevel, event: xdg.Toplevel.Event, win: *Surface) void { +fn xdg_toplevel_listener(_: *wlnd.Client, _: xdg.Toplevel, event: xdg.Toplevel.Event, surf: *Surface) void { switch (event) { .configure => |configure| { + std.log.info("configure_top={}", .{configure}); const configure_size = Size{ .width = @intCast(configure.width), .height = @intCast(configure.height), }; if (configure_size.is_zero()) return; - const new_size = configure_size.unite(win.min_size); - if (new_size.is_eql(win.size)) return; + const new_size = configure_size.unite(surf.min_size); + // const old_size = surf.app.layout.get(surf.root, .rect).get_size(); + // if (new_size.is_eql(old_size)) { + // std.log.info("old_size={}", .{old_size}); + // return; + // } - std.log.warn("configure event {}", .{configure}); + // surf.size = new_size; - win.size = new_size; - win.schedule_redraw(); + surf.app.layout.set_size(surf.root, Size.Minmax.tight(new_size)); + surf.schedule_redraw(); - _ = win.app.layout.call(win.root, .size, .{ - Size.Minmax.tight(win.size), - }); - std.log.info("w: {} h: {}", .{ win.size.width, win.size.height }); + std.log.info("w: {} h: {}", .{ new_size.width, new_size.height }); }, .close => { - win.app.client.connection.is_running = false; + surf.app.client.connection.is_running = false; }, else => {}, } } -fn xdg_popup_listener(client: *wlnd.Client, xdg_popup: xdg.Popup, event: xdg.Popup.Event, win: *Surface) void { - _ = client; // autofix +fn xdg_popup_listener(_: *wlnd.Client, xdg_popup: xdg.Popup, event: xdg.Popup.Event, win: *Surface) void { switch (event) { .configure => |configure| { std.log.info("popup configure :{}", .{configure}); diff --git a/toolkit/font/bdf.zig b/toolkit/font/bdf.zig index 0f645f3..5d729d5 100644 --- a/toolkit/font/bdf.zig +++ b/toolkit/font/bdf.zig @@ -128,10 +128,8 @@ pub const BdfParser = struct { // state = .nochar; // continue; } else { - std.log.info("value={s}", .{value}); c.encoding = std.fmt.parseInt(u21, value, 0) catch @panic("TODO"); } - std.log.info("encoding={any}", .{c.encoding}); } else if (parse_prop(line, "BITMAP")) |_| { const start_i = ci.it.index + 1; for (0..c.bbx.height) |_| { @@ -167,7 +165,7 @@ pub const BdfParser = struct { const glyph_size: u32 = p.font.glyph_size(); p.font.glyph_data = try alloc.alloc(u8, masks.count() * 256 * glyph_size); p.font.glyph_widths = try alloc.alloc(u8, masks.count() * 256); - std.log.info("masks.count()={}", .{masks.count()}); + // std.log.info("masks.count()={}", .{masks.count()}); @memset(p.font.glyph_data, 0); @memset(p.font.glyph_widths, 0); p.font.range_masks = masks.masks; @@ -190,7 +188,6 @@ pub const BdfParser = struct { for (top_offset..top_offset + height) |i| { const s = bitmap_it.next().?; - std.log.info("s={s}", .{s}); const bytes_per_line = char.bbx.width / 9 + 1; for (0..bytes_per_line) |b| { @@ -216,21 +213,19 @@ pub const BdfParser = struct { .it = std.mem.tokenizeScalar(u8, buf, '\n'), }; while (char_it.next()) |char| { - std.log.info("char={s}", .{char.bitmap}); const range = char.encoding / 256; const i = range / 64; if (i > max_range_i) max_range_i = i; const mask = @as(usize, 1) << @as(u6, @intCast(range % 64)); - std.debug.print("char name {s}", .{char.name}); range_mask[i] |= mask; } - for (range_mask, 0..) |m, m_i| { - for (0..64) |i| { - if (m & (@as(usize, 1) << @intCast(i)) != 0) { - std.log.info("bit={}", .{m_i * 64 + i}); - } - } - } + // for (range_mask, 0..) |m, m_i| { + // for (0..64) |i| { + // if (m & (@as(usize, 1) << @intCast(i)) != 0) { + // std.log.info("bit={}", .{m_i * 64 + i}); + // } + // } + // } // std.posix.exit(66); // std.log.info("max_range_i={}", .{max_range_i}); return .{ .masks = alloc.dupe(usize, range_mask[0 .. max_range_i + 1]) catch @panic("OOM") }; diff --git a/toolkit/widget.zig b/toolkit/widget.zig index a07213f..73b7657 100644 --- a/toolkit/widget.zig +++ b/toolkit/widget.zig @@ -14,6 +14,7 @@ const WidgetAttrs = struct { data: usize = undefined, event_handler: ?*const fn (*anyopaque, WidgetIdx, *const anyopaque) void = null, event_handler_data: *anyopaque = undefined, + subsurface: ?@import("wayland").wl.Surface = null, }; const root = @import("root"); @@ -22,6 +23,7 @@ const common_w_types = .{ .flex = @import("widgets/Flex.zig"), .button = @import("widgets/Button.zig"), .label = @import("widgets/Label.zig"), + .scrollable = @import("widgets/Scrollable.zig"), // .menu_bar = @import("widgets/MenuBar.zig"), }; const widget_names = std.meta.fieldNames(@TypeOf(root_w_types)) ++ std.meta.fieldNames(@TypeOf(common_w_types)); @@ -173,6 +175,20 @@ pub const Layout = struct { value: std.meta.FieldType(WidgetAttrs, item), ) void { self.widgets.items(item)[@intFromEnum(idx)] = value; + if (item != .rect) return; + if (self.get(idx, .subsurface)) |wl_surface| { + const subs = self.get_app().surfaces.getPtr(wl_surface).?; + subs.role.wl_subsurface.set_position(value.x, value.y); + } + } + + pub fn set_size( + self: *Layout, + idx: WidgetIdx, + constraints: Size.Minmax, + ) void { + const size = self.call(idx, .size, .{constraints}); + self.set(idx, .rect, size.to_rect()); } pub fn call_void( diff --git a/toolkit/widgets/Scrollable.zig b/toolkit/widgets/Scrollable.zig new file mode 100644 index 0000000..eb4c53f --- /dev/null +++ b/toolkit/widgets/Scrollable.zig @@ -0,0 +1,28 @@ +const std = @import("std"); + +const tk = @import("../toolkit.zig"); +const PaintCtx = tk.PaintCtx; +const widget = tk.widget; +const Layout = widget.Layout; +const WidgetIdx = widget.WidgetIdx; + +pub fn draw(layout: *Layout, idx: WidgetIdx, rect: tk.Rect, paint_ctx: PaintCtx) bool { + _ = idx; // autofix + const font = layout.get_app().font; + + paint_ctx.text("Hello", .{ .rect = rect, .font = font, .color = .blue }); + paint_ctx.fill(.{ .rect = rect, .color = .teal }); + // std.log.info("btn {} hover {}", .{ @intFromEnum(idx), hover }); + + return true; +} + +pub fn handle_event(layout: *Layout, idx: WidgetIdx, event: tk.Event) void { + layout.request_draw(idx); + std.log.info("event: {}", .{event}); +} + +pub fn size(_: *Layout, _: WidgetIdx, _: tk.Size.Minmax) tk.Size { + std.log.info("size", .{}); + return .{ .width = 60, .height = 20 }; +}