aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYaroslav de la Peña Smirnov <yps@yaroslavps.com>2024-09-12 22:51:07 +0300
committerYaroslav de la Peña Smirnov <yps@yaroslavps.com>2024-09-12 22:51:07 +0300
commit7f5de6c15243bd8f3f9952ed091e4dd15e809020 (patch)
treeecd2dbd26de670ea33481e944bf8374148c0763e
parentb34919056ef006d78fde1a50867d6f050ee58254 (diff)
downloadzigway-7f5de6c15243bd8f3f9952ed091e4dd15e809020.tar.gz
zigway-7f5de6c15243bd8f3f9952ed091e4dd15e809020.zip
pango and cairo with shm
Use pango and cairo to render text to the shared mem buffer.
-rw-r--r--build.zig1
-rw-r--r--src/main.zig51
-rw-r--r--src/pangocairo.zig142
3 files changed, 192 insertions, 2 deletions
diff --git a/build.zig b/build.zig
index 5e885fc..f9d7704 100644
--- a/build.zig
+++ b/build.zig
@@ -28,6 +28,7 @@ pub fn build(b: *std.Build) void {
exe.root_module.addImport("wayland", wayland);
exe.linkLibC();
exe.linkSystemLibrary("wayland-client");
+ exe.linkSystemLibrary("pangocairo");
scanner.addCSource(exe);
diff --git a/src/main.zig b/src/main.zig
index 4902176..436e14d 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -6,6 +6,10 @@ const wayland = @import("wayland");
const wl = wayland.client.wl;
const xdg = wayland.client.xdg;
+const pangocairo = @import("pangocairo.zig");
+const cairo = pangocairo.cairo;
+const pango = pangocairo.pango;
+
/// Global Wayland registry context; contains pointers to the globals we expect
/// from the compositor.
const RegistryContext = struct {
@@ -22,6 +26,8 @@ const WindowBuffer = struct {
wl_buffer: *wl.Buffer,
/// Backing data slice from memory mapping for this buffer
map: []u8,
+ /// Cairo surface for painting with cairo
+ c_surface: cairo.Surface,
/// Are we being read by the compositor right now?
busy: bool = false,
};
@@ -41,6 +47,8 @@ pub const Window = struct {
buffers: [2]WindowBuffer = undefined,
/// Memory mapped for both buffers
buffers_map: []align(mem.page_size) u8 = undefined,
+ /// Pango font description for rendering our text
+ font: pango.FontDescription,
/// This window's title
title: [*:0]const u8,
/// Width in pixels
@@ -51,6 +59,8 @@ pub const Window = struct {
offset: f32 = 0.0,
/// Last frame's timestamp in ms
last_frame: u32 = 0,
+ /// Frames per second statistic
+ current_fps: f32 = 0.0,
/// Should we keep this window open?
open: bool = true,
@@ -64,10 +74,16 @@ pub const Window = struct {
.allocator = allocator,
.wl_surface = try compositor.createSurface(),
.title = title,
+ // .font = try pango.FontDescription.fromString("monospace 24"),
+ .font = try pango.FontDescription.create(),
};
win.xdg_surface = try wm_base.getXdgSurface(win.wl_surface);
win.xdg_toplevel = try win.xdg_surface.getToplevel();
+ win.font.setFamily("monospace");
+ win.font.setWeight(600);
+ win.font.setSizePt(18);
+
try win.allocBuffers();
win.xdg_surface.setListener(*Window, xdgSurfaceListener, win);
@@ -86,6 +102,7 @@ pub const Window = struct {
/// Destroy this window and all its associated resources
pub fn destroy(win: *Window) void {
win.destroyBuffers();
+ win.font.destroy();
win.xdg_toplevel.destroy();
win.xdg_surface.destroy();
win.wl_surface.destroy();
@@ -94,7 +111,8 @@ pub const Window = struct {
/// Allocate new buffers for this window
fn allocBuffers(win: *Window) !void {
- const stride = win.width * 4;
+ const c_format = cairo.Format.argb32;
+ const stride = cairo.Surface.formatStrideForWidth(c_format, win.width);
const buffer_size = stride * win.height;
const total_size = buffer_size * 2; // we need two buffers
@@ -124,12 +142,21 @@ pub const Window = struct {
const bsize: usize = @intCast(buffer_size);
buffer.busy = false;
buffer.map = win.buffers_map[bsize *% i .. bsize *% (i + 1)];
+ buffer.c_surface = try cairo.Surface.createImageForData(
+ buffer.map,
+ c_format,
+ win.width,
+ win.height,
+ stride,
+ );
buffer.wl_buffer.setListener(*WindowBuffer, bufferListener, buffer);
}
}
/// Destroy this window's buffers
fn destroyBuffers(win: *Window) void {
+ win.buffers[0].c_surface.destroy();
+ win.buffers[1].c_surface.destroy();
win.buffers[0].wl_buffer.destroy();
win.buffers[1].wl_buffer.destroy();
posix.munmap(win.buffers_map);
@@ -163,6 +190,25 @@ pub const Window = struct {
}
}
+ const layout = try pango.Layout.createForCairo(&buffer.c_surface);
+ defer layout.unref();
+ buffer.c_surface.setSourceRgba(1, 1, 1, 1);
+
+ layout.setFont(win.font);
+ layout.setText("Hello, wayland!", -1);
+ buffer.c_surface.moveTo(5, 5);
+ layout.show(&buffer.c_surface);
+
+ var fps_buf: [32]u8 = undefined;
+ const fps_output = std.fmt.bufPrintZ(
+ &fps_buf,
+ "{d} FPS",
+ .{win.current_fps},
+ ) catch unreachable;
+ layout.setText(fps_output, -1);
+ buffer.c_surface.moveTo(5, 40);
+ layout.show(&buffer.c_surface);
+
buffer.busy = true;
return buffer.wl_buffer;
}
@@ -230,8 +276,9 @@ pub const Window = struct {
if (win.last_frame != 0) {
const elapsed: f32 = @floatFromInt(done.callback_data -% win.last_frame);
win.offset -= elapsed / 1000.0 * 32.0;
+ win.current_fps = 1000.0 / elapsed;
}
- defer win.last_frame = done.callback_data;
+ win.last_frame = done.callback_data;
const wl_buffer = win.drawFrame() catch |err| {
std.debug.print("error drawing frame {}\n", .{err});
diff --git a/src/pangocairo.zig b/src/pangocairo.zig
new file mode 100644
index 0000000..3995ba7
--- /dev/null
+++ b/src/pangocairo.zig
@@ -0,0 +1,142 @@
+const c = @cImport({
+ @cInclude("pango/pangocairo.h");
+});
+
+pub const pango = struct {
+ pub const Layout = struct {
+ layout: *c.PangoLayout,
+
+ pub fn createForCairo(s: *cairo.Surface) !Layout {
+ const layout = c.pango_cairo_create_layout(
+ s.cairo,
+ ) orelse return error.PangoCairoError;
+ return .{ .layout = layout };
+ }
+
+ pub fn setFont(l: Layout, f: FontDescription) void {
+ c.pango_layout_set_font_description(l.layout, f.font);
+ }
+
+ pub fn setFontFromString(l: Layout, font: [*:0]const u8) !void {
+ const pfont = c.pango_font_description_from_string(
+ font,
+ ) orelse return error.BadFont;
+ defer c.pango_font_description_free(pfont);
+ c.pango_layout_set_font_description(l.layout, pfont);
+ }
+
+ pub fn setText(l: Layout, text: [*:0]const u8, len: i32) void {
+ c.pango_layout_set_text(l.layout, text, len);
+ }
+
+ pub fn show(l: Layout, s: *cairo.Surface) void {
+ c.pango_cairo_show_layout(s.cairo, l.layout);
+ }
+
+ pub fn unref(l: Layout) void {
+ c.g_object_unref(l.layout);
+ }
+ };
+
+ pub const FontDescription = struct {
+ font: *c.PangoFontDescription,
+
+ pub fn create() !FontDescription {
+ const font = c.pango_font_description_new() orelse return error.PangoError;
+ return .{ .font = font };
+ }
+
+ pub fn fromString(str: [*:0]const u8) !FontDescription {
+ const font = c.pango_font_description_from_string(str) orelse return error.PangoError;
+ return .{ .font= font };
+ }
+
+ pub fn setFamily(f: FontDescription, name: [*:0]const u8) void {
+ c.pango_font_description_set_family(f.font, name);
+ }
+
+ pub fn setWeight(f: FontDescription, weight: u32) void {
+ c.pango_font_description_set_weight(f.font, weight);
+ }
+
+ pub fn setSize(f: FontDescription, size: i32) void {
+ c.pango_font_description_set_size(f.font, size);
+ }
+
+ pub fn setSizePt(f: FontDescription, pt: i32) void {
+ c.pango_font_description_set_size(f.font, pt * c.PANGO_SCALE);
+ }
+
+ pub fn destroy(f: FontDescription) void {
+ c.pango_font_description_free(f.font);
+ }
+ };
+};
+
+pub const cairo = struct {
+ pub const Format = enum(c_int) {
+ invalid = -1,
+ argb32,
+ rgb24,
+ };
+
+ pub const Surface = struct {
+ cairo: *c.cairo_t,
+ surface: *c.cairo_surface_t,
+
+ pub fn formatStrideForWidth(format: Format, width: i32) i32 {
+ return c.cairo_format_stride_for_width(@intFromEnum(format), width);
+ }
+
+ pub fn createImage(format: Format, width: i32, height: i32) !Surface {
+ const surface = c.cairo_image_surface_create(
+ @intFromEnum(format),
+ width,
+ height,
+ ) orelse return error.CairoError;
+ const cr = c.cairo_create(surface) orelse return error.CairoError;
+ return .{ .cairo = cr, .surface = surface };
+ }
+
+ pub fn createImageForData(
+ data: []u8,
+ format: Format,
+ width: i32,
+ height: i32,
+ stride: i32,
+ ) !Surface {
+ const surface = c.cairo_image_surface_create_for_data(
+ data.ptr,
+ @intFromEnum(format),
+ width,
+ height,
+ stride,
+ ) orelse return error.CairoError;
+ const cr = c.cairo_create(surface) orelse return error.CairoError;
+ return .{ .cairo = cr, .surface = surface };
+ }
+
+ pub fn moveTo(s: Surface, x: f64, y: f64) void {
+ c.cairo_move_to(s.cairo, x, y);
+ }
+
+ pub fn setSourceRgba(
+ s: Surface,
+ red: f64,
+ green: f64,
+ blue: f64,
+ alpha: f64,
+ ) void {
+ c.cairo_set_source_rgba(s.cairo, red, green, blue, alpha);
+ }
+
+ pub fn paint(s: Surface) void {
+ c.cairo_paint(s.cairo);
+ }
+
+ pub fn destroy(s: Surface) void {
+ c.cairo_destroy(s.cairo);
+ c.cairo_surface_destroy(s.surface);
+ }
+ };
+};