summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/entry.c79
-rw-r--r--src/entry.h5
-rw-r--r--src/entry_backend/harfbuzz.c35
-rw-r--r--src/entry_backend/harfbuzz.h2
-rw-r--r--src/entry_backend/pango.c10
-rw-r--r--src/entry_backend/pango.h2
-rw-r--r--src/image.h1
-rw-r--r--src/main.c18
-rw-r--r--src/surface.c11
-rw-r--r--src/surface.h2
10 files changed, 110 insertions, 55 deletions
diff --git a/src/entry.c b/src/entry.c
index 6b7c9e7..0348d06 100644
--- a/src/entry.c
+++ b/src/entry.c
@@ -5,26 +5,51 @@
#include "log.h"
#include "nelem.h"
-void entry_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale)
+void entry_init(struct entry *entry, uint8_t *restrict buffer, uint32_t width, uint32_t height, uint32_t scale)
{
entry->image.width = width;
entry->image.height = height;
entry->image.scale = scale;
- width /= scale;
- height /= scale;
-
/*
- * Create the cairo surface and context we'll be using.
+ * Create the cairo surfaces and contexts we'll be using.
+ *
+ * In order to avoid an unnecessary copy when passing the image to the
+ * Wayland server, we accept a pointer to the mmap-ed file that our
+ * Wayland buffers are created from. This is assumed to be
+ * (width * height * (sizeof(uint32_t) == 4) * 2) bytes,
+ * to allow for double buffering.
*/
- cairo_surface_t *surface = cairo_image_surface_create(
+ cairo_surface_t *surface = cairo_image_surface_create_for_data(
+ buffer,
CAIRO_FORMAT_ARGB32,
- width * scale,
- height * scale
+ width,
+ height,
+ width * sizeof(uint32_t)
);
cairo_surface_set_device_scale(surface, scale, scale);
cairo_t *cr = cairo_create(surface);
+ entry->cairo[0].surface = surface;
+ entry->cairo[0].cr = cr;
+
+ entry->cairo[1].surface = cairo_image_surface_create_for_data(
+ &buffer[width * height * sizeof(uint32_t)],
+ CAIRO_FORMAT_ARGB32,
+ width,
+ height,
+ width * sizeof(uint32_t)
+ );
+ cairo_surface_set_device_scale(entry->cairo[1].surface, scale, scale);
+ entry->cairo[1].cr = cairo_create(entry->cairo[1].surface);
+
+ /*
+ * Cairo drawing operations take the scale into account,
+ * so we account for that here.
+ */
+ width /= scale;
+ height /= scale;
+
/* Draw the outer outline */
struct color color = entry->border.outline_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
@@ -85,26 +110,43 @@ void entry_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t s
height -= 2 * entry->padding;
cairo_rectangle(cr, 0, 0, width, height);
cairo_clip(cr);
-
- entry->cairo.surface = surface;
- entry->cairo.cr = cr;
- entry->image.buffer = cairo_image_surface_get_data(surface);
/* Setup the backend. */
- entry_backend_init(entry, width, height, scale);
+ entry_backend_init(entry, &width, &height, scale);
+
+ /*
+ * To avoid performing all this drawing twice, we take a small
+ * shortcut. After performing all the drawing as normal on our first
+ * Cairo context, we can copy over just the important state (the
+ * transformation matrix and clip rectangle) and perform a memcpy()
+ * to initialise the other context.
+ */
+ cairo_matrix_t mat;
+ cairo_get_matrix(cr, &mat);
+ cairo_set_matrix(entry->cairo[1].cr, &mat);
+ cairo_rectangle(entry->cairo[1].cr, 0, 0, width, height);
+ cairo_clip(entry->cairo[1].cr);
+
+ memcpy(
+ cairo_image_surface_get_data(entry->cairo[1].surface),
+ cairo_image_surface_get_data(entry->cairo[0].surface),
+ entry->image.width * entry->image.height * sizeof(uint32_t)
+ );
}
void entry_destroy(struct entry *entry)
{
entry_backend_destroy(entry);
- cairo_destroy(entry->cairo.cr);
- cairo_surface_destroy(entry->cairo.surface);
+ cairo_destroy(entry->cairo[0].cr);
+ cairo_destroy(entry->cairo[1].cr);
+ cairo_surface_destroy(entry->cairo[0].surface);
+ cairo_surface_destroy(entry->cairo[1].surface);
}
void entry_update(struct entry *entry)
{
log_debug("Start rendering entry.\n");
- cairo_t *cr = entry->cairo.cr;
+ cairo_t *cr = entry->cairo[entry->index].cr;
cairo_save(cr);
/* Clear the image. */
@@ -120,10 +162,13 @@ void entry_update(struct entry *entry)
cairo_restore(cr);
log_debug("Finish rendering entry.\n");
+
+ entry->index = !entry->index;
}
void entry_set_scale(struct entry *entry, uint32_t scale)
{
entry->image.scale = scale;
- cairo_surface_set_device_scale(entry->cairo.surface, scale, scale);
+ cairo_surface_set_device_scale(entry->cairo[0].surface, scale, scale);
+ cairo_surface_set_device_scale(entry->cairo[1].surface, scale, scale);
}
diff --git a/src/entry.h b/src/entry.h
index 341e340..95cab5d 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -22,7 +22,8 @@ struct entry {
struct {
cairo_surface_t *surface;
cairo_t *cr;
- } cairo;
+ } cairo[2];
+ int index;
wchar_t input[MAX_INPUT_LENGTH];
/* Assume maximum of 4 bytes per wchar_t (for UTF-8) */
@@ -50,7 +51,7 @@ struct entry {
} border;
};
-void entry_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale);
+void entry_init(struct entry *entry, uint8_t *restrict buffer, uint32_t width, uint32_t height, uint32_t scale);
void entry_destroy(struct entry *entry);
void entry_update(struct entry *entry);
void entry_set_scale(struct entry *entry, uint32_t scale);
diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c
index d1f54e9..779cbd8 100644
--- a/src/entry_backend/harfbuzz.c
+++ b/src/entry_backend/harfbuzz.c
@@ -1,4 +1,3 @@
-#include <assert.h>
#include <cairo/cairo.h>
#include <glib.h>
#include <harfbuzz/hb-ft.h>
@@ -12,6 +11,12 @@
#include "../nelem.h"
#include "../xmalloc.h"
+/*
+ * Cairo / FreeType use 72 Pts per inch, but Pango uses 96 DPI, so we have to
+ * rescale for consistency.
+ */
+#define PT_TO_DPI (96.0 / 72.0)
+
static void setup_hb_buffer(hb_buffer_t *buffer)
{
hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
@@ -69,17 +74,22 @@ static uint32_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer, uint32_t scal
return width;
}
-void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale)
+void entry_backend_init(struct entry *entry, uint32_t *width, uint32_t *height, uint32_t scale)
{
- cairo_t *cr = entry->cairo.cr;
+ cairo_t *cr = entry->cairo[0].cr;
/* Setup FreeType. */
log_debug("Creating FreeType library.\n");
- assert(!FT_Init_FreeType(&entry->backend.ft_library));
+ FT_Init_FreeType(&entry->backend.ft_library);
log_debug("Loading FreeType font.\n");
- assert(!FT_New_Face(entry->backend.ft_library, "font.ttf", 0, &entry->backend.ft_face));
- assert(!FT_Set_Char_Size(entry->backend.ft_face, entry->font_size * 64, entry->font_size * 64, 0, 0));
+ FT_New_Face(entry->backend.ft_library, "font.ttf", 0, &entry->backend.ft_face);
+ FT_Set_Char_Size(
+ entry->backend.ft_face,
+ entry->font_size * 64 * scale * PT_TO_DPI,
+ entry->font_size * 64 * scale * PT_TO_DPI,
+ 0,
+ 0);
log_debug("Creating Cairo font.\n");
entry->backend.cairo_face = cairo_ft_font_face_create_for_ft_face(entry->backend.ft_face, 0);
@@ -87,10 +97,11 @@ void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, ui
struct color color = entry->foreground_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
cairo_set_font_face(cr, entry->backend.cairo_face);
- cairo_set_font_size(cr, entry->font_size * (96.0 / 72.0));
+ cairo_set_font_size(cr, entry->font_size * PT_TO_DPI);
- cairo_font_extents_t font_extents;
- cairo_font_extents(cr, &font_extents);
+ /* We also need to set up the font for our other Cairo context. */
+ cairo_set_font_face(entry->cairo[1].cr, entry->backend.cairo_face);
+ cairo_set_font_size(entry->cairo[1].cr, entry->font_size * PT_TO_DPI);
log_debug("Creating Harfbuzz font.\n");
entry->backend.hb_font = hb_ft_font_create_referenced(entry->backend.ft_face);
@@ -106,8 +117,8 @@ void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, ui
/* Move and clip so we don't draw over the prompt */
uint32_t prompt_width = render_hb_buffer(cr, entry->backend.hb_buffer, scale);
cairo_translate(cr, prompt_width, 0);
- width -= prompt_width;
- cairo_rectangle(cr, 0, 0, width, height);
+ *width -= prompt_width;
+ cairo_rectangle(cr, 0, 0, *width, *height);
cairo_clip(cr);
}
@@ -121,7 +132,7 @@ void entry_backend_destroy(struct entry *entry)
void entry_backend_update(struct entry *entry)
{
- cairo_t *cr = entry->cairo.cr;
+ cairo_t *cr = entry->cairo[entry->index].cr;
hb_buffer_clear_contents(entry->backend.hb_buffer);
setup_hb_buffer(entry->backend.hb_buffer);
diff --git a/src/entry_backend/harfbuzz.h b/src/entry_backend/harfbuzz.h
index 0b07538..52116a8 100644
--- a/src/entry_backend/harfbuzz.h
+++ b/src/entry_backend/harfbuzz.h
@@ -19,7 +19,7 @@ struct entry_backend {
hb_buffer_t *hb_buffer;
};
-void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale);
+void entry_backend_init(struct entry *entry, uint32_t *width, uint32_t *height, uint32_t scale);
void entry_backend_destroy(struct entry *entry);
void entry_backend_update(struct entry *entry);
diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c
index 569a706..4d43ce3 100644
--- a/src/entry_backend/pango.c
+++ b/src/entry_backend/pango.c
@@ -7,9 +7,9 @@
#include "../log.h"
#include "../nelem.h"
-void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale)
+void entry_backend_init(struct entry *entry, uint32_t *width, uint32_t *height, uint32_t scale)
{
- cairo_t *cr = entry->cairo.cr;
+ cairo_t *cr = entry->cairo[0].cr;
/* Setup Pango. */
log_debug("Creating Pango context.\n");
@@ -41,8 +41,8 @@ void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, ui
/* Move and clip so we don't draw over the prompt */
cairo_translate(cr, prompt_width, 0);
- width -= prompt_width;
- cairo_rectangle(cr, 0, 0, width, height);
+ *width -= prompt_width;
+ cairo_rectangle(cr, 0, 0, *width, *height);
cairo_clip(cr);
log_debug("Creating Pango layout.\n");
@@ -70,7 +70,7 @@ void entry_backend_destroy(struct entry *entry)
void entry_backend_update(struct entry *entry)
{
- cairo_t *cr = entry->cairo.cr;
+ cairo_t *cr = entry->cairo[entry->index].cr;
pango_layout_set_text(entry->backend.entry_layout, entry->input_mb, -1);
pango_cairo_update_layout(cr, entry->backend.entry_layout);
diff --git a/src/entry_backend/pango.h b/src/entry_backend/pango.h
index 08572ae..ef58be3 100644
--- a/src/entry_backend/pango.h
+++ b/src/entry_backend/pango.h
@@ -12,7 +12,7 @@ struct entry_backend {
PangoLayout *result_layouts[5];
};
-void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale);
+void entry_backend_init(struct entry *entry, uint32_t *width, uint32_t *height, uint32_t scale);
void entry_backend_destroy(struct entry *entry);
void entry_backend_update(struct entry *entry);
diff --git a/src/image.h b/src/image.h
index 81cb66a..090f32d 100644
--- a/src/image.h
+++ b/src/image.h
@@ -5,7 +5,6 @@
#include <stdint.h>
struct image {
- uint8_t *buffer;
uint32_t width;
uint32_t height;
uint32_t scale;
diff --git a/src/main.c b/src/main.c
index 39da872..f3fab1d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -785,6 +785,15 @@ int main(int argc, char *argv[])
wl_display_roundtrip(tofi.wl_display);
log_debug("Third roundtrip done.\n");
+
+ /*
+ * Create the various structures for our window surface. This needs to
+ * be done before calling entry_init as that performs some initial
+ * drawing, and surface_init allocates the buffers we'll be drawing to.
+ */
+ log_debug("Initialising window surface.\n");
+ surface_init(&tofi.window.surface, tofi.wl_shm);
+
/*
* Initialise the structures for rendering the entry.
* Cairo needs to know the size of the surface it's creating, and
@@ -797,19 +806,14 @@ int main(int argc, char *argv[])
log_debug("Initialising renderer.\n");
entry_init(
&tofi.window.entry,
+ tofi.window.surface.shm_pool_data,
tofi.window.surface.width,
tofi.window.surface.height,
tofi.window.scale);
entry_update(&tofi.window.entry);
log_debug("Renderer initialised.\n");
- /*
- * Create the various structures for each surface, and
- * perform an initial render of everything.
- */
- log_debug("Initialising main window surface.\n");
-
- surface_initialise(&tofi.window.surface, tofi.wl_shm);
+ /* Perform an initial render. */
surface_draw(
&tofi.window.surface,
&tofi.window.entry.background_color,
diff --git a/src/surface.c b/src/surface.c
index d02fd4f..30a46cd 100644
--- a/src/surface.c
+++ b/src/surface.c
@@ -8,7 +8,7 @@
#undef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
-void surface_initialise(
+void surface_init(
struct surface *surface,
struct wl_shm *wl_shm)
{
@@ -47,8 +47,6 @@ void surface_initialise(
log_debug("Created shm file with size %d KiB.\n",
surface->shm_pool_size / 1024);
-
- surface->index = 0;
}
void surface_destroy(struct surface *surface)
@@ -66,12 +64,9 @@ void surface_draw(
struct color *color,
struct image *texture)
{
- surface->index = !surface->index;
- int offset = surface->height * surface->stride * surface->index;
- uint32_t *pixels = (uint32_t *)&surface->shm_pool_data[offset];
- memcpy(pixels, texture->buffer, surface->height * surface->stride);
-
wl_surface_attach(surface->wl_surface, surface->buffers[surface->index], 0, 0);
wl_surface_damage_buffer(surface->wl_surface, 0, 0, INT32_MAX, INT32_MAX);
wl_surface_commit(surface->wl_surface);
+
+ surface->index = !surface->index;
}
diff --git a/src/surface.h b/src/surface.h
index 6016551..68f9a31 100644
--- a/src/surface.h
+++ b/src/surface.h
@@ -22,7 +22,7 @@ struct surface {
bool redraw;
};
-void surface_initialise(
+void surface_init(
struct surface *surface,
struct wl_shm *wl_shm);
void surface_destroy(struct surface *surface);