summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--meson.build18
-rw-r--r--src/entry.c79
-rw-r--r--src/entry.h15
-rw-r--r--src/entry_backend/harfbuzz.c148
-rw-r--r--src/entry_backend/harfbuzz.h26
-rw-r--r--src/entry_backend/pango.c92
-rw-r--r--src/entry_backend/pango.h19
-rw-r--r--src/image.h1
-rw-r--r--src/main.c13
9 files changed, 327 insertions, 84 deletions
diff --git a/meson.build b/meson.build
index 06ce596..4b4b100 100644
--- a/meson.build
+++ b/meson.build
@@ -48,7 +48,7 @@ add_project_arguments(
language: 'c'
)
-sources = files(
+common_sources = files(
'src/main.c',
'src/color.c',
'src/compgen.c',
@@ -62,8 +62,14 @@ sources = files(
'src/xmalloc.c',
)
+pango_sources = files('src/entry_backend/pango.c')
+harfbuzz_sources = files('src/entry_backend/harfbuzz.c')
+
cc = meson.get_compiler('c')
+libm = cc.find_library('m', required: false)
+freetype = dependency('freetype2')
glib = dependency('glib-2.0')
+harfbuzz = dependency('harfbuzz')
pangocairo = dependency('pangocairo')
wayland_client = dependency('wayland-client')
wayland_protocols = dependency('wayland-protocols', native: true)
@@ -101,8 +107,16 @@ endforeach
executable(
'tofi',
- sources, wl_proto_src, wl_proto_headers,
+ common_sources, pango_sources, wl_proto_src, wl_proto_headers,
dependencies: [glib, pangocairo, wayland_client, xkbcommon],
+ c_args: '-DUSE_PANGO',
+ install: true
+)
+
+executable(
+ 'tofi-hb',
+ common_sources, harfbuzz_sources, wl_proto_src, wl_proto_headers,
+ dependencies: [libm, glib, freetype, harfbuzz, pangocairo, wayland_client, xkbcommon],
install: true
)
diff --git a/src/entry.c b/src/entry.c
index 845df0a..eb58695 100644
--- a/src/entry.c
+++ b/src/entry.c
@@ -1,7 +1,5 @@
#include <cairo/cairo.h>
#include <glib.h>
-#include <pango/pangocairo.h>
-#include <pango/pango.h>
#include <wchar.h>
#include "entry.h"
#include "log.h"
@@ -11,6 +9,7 @@ void entry_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t s
{
entry->image.width = width;
entry->image.height = height;
+ entry->image.scale = scale;
width /= scale;
height /= scale;
@@ -79,67 +78,19 @@ 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);
-
- /* Setup Pango. */
- log_debug("Creating Pango context.\n");
- PangoContext *context = pango_cairo_create_context(cr);
-
- log_debug("Creating Pango font description.\n");
- PangoFontDescription *font_description =
- pango_font_description_from_string(entry->font_name);
- pango_font_description_set_size(
- font_description,
- entry->font_size * PANGO_SCALE);
- pango_context_set_font_description(context, font_description);
- pango_font_description_free(font_description);
-
- entry->pango.prompt_layout = pango_layout_new(context);
- log_debug("Setting Pango text.\n");
- pango_layout_set_text(entry->pango.prompt_layout, "run: ", -1);
- int prompt_width;
- int prompt_height;
- log_debug("Get Pango pixel size.\n");
- pango_layout_get_pixel_size(entry->pango.prompt_layout, &prompt_width, &prompt_height);
- log_debug("First Pango draw.\n");
- pango_cairo_update_layout(cr, entry->pango.prompt_layout);
-
- /* Draw the prompt now, as this only needs to be done once */
- color = entry->foreground_color;
- cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- pango_cairo_show_layout(cr, entry->pango.prompt_layout);
-
- /* 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);
- cairo_clip(cr);
-
- log_debug("Creating Pango layout.\n");
- entry->pango.entry_layout = pango_layout_new(context);
- pango_layout_set_text(entry->pango.entry_layout, "", -1);
-
- for (size_t i = 0; i < 5; i++) {
- PangoLayout *layout = pango_layout_new(context);
- pango_layout_set_text(layout, "", -1);
- entry->pango.result_layouts[i] = layout;
- }
-
- entry->pango.context = context;
entry->cairo.surface = surface;
entry->cairo.cr = cr;
+ /* Setup the backend. */
+ entry_backend_init(entry, width, height, scale);
+
entry->image.buffer = cairo_image_surface_get_data(surface);
}
void entry_destroy(struct entry *entry)
{
- for (size_t i = 0; i < 5; i++) {
- g_object_unref(entry->pango.result_layouts[i]);
- }
- g_object_unref(entry->pango.entry_layout);
- g_object_unref(entry->pango.prompt_layout);
- g_object_unref(entry->pango.context);
+ entry_backend_destroy(entry);
cairo_destroy(entry->cairo.cr);
cairo_surface_destroy(entry->cairo.surface);
}
@@ -160,27 +111,13 @@ void entry_update(struct entry *entry)
color = entry->foreground_color;
cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
- pango_layout_set_text(entry->pango.entry_layout, entry->input_mb, -1);
- pango_cairo_update_layout(cr, entry->pango.entry_layout);
- pango_cairo_show_layout(cr, entry->pango.entry_layout);
-
- for (size_t i = 0; i < 5; i++) {
- cairo_translate(cr, 0, 50);
- PangoLayout *layout = entry->pango.result_layouts[i];
- const char *str;
- if (i < entry->results.count) {
- str = entry->results.buf[i];
- } else {
- str = "";
- }
- pango_layout_set_text(layout, str, -1);
- pango_cairo_update_layout(cr, layout);
- pango_cairo_show_layout(cr, layout);
- }
+ entry_backend_update(entry);
+
cairo_restore(cr);
}
void entry_set_scale(struct entry *entry, uint32_t scale)
{
+ entry->image.scale = scale;
cairo_surface_set_device_scale(entry->cairo.surface, scale, scale);
}
diff --git a/src/entry.h b/src/entry.h
index 6efe773..341e340 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -1,7 +1,13 @@
#ifndef ENTRY_H
#define ENTRY_H
-#include <pango/pangocairo.h>
+#ifdef USE_PANGO
+#include "entry_backend/pango.h"
+#else
+#include "entry_backend/harfbuzz.h"
+#endif
+
+#include <cairo/cairo.h>
#include "color.h"
#include "history.h"
#include "image.h"
@@ -12,12 +18,7 @@
struct entry {
struct image image;
- struct {
- PangoContext *context;
- PangoLayout *prompt_layout;
- PangoLayout *entry_layout;
- PangoLayout *result_layouts[5];
- } pango;
+ struct entry_backend backend;
struct {
cairo_surface_t *surface;
cairo_t *cr;
diff --git a/src/entry_backend/harfbuzz.c b/src/entry_backend/harfbuzz.c
new file mode 100644
index 0000000..d1f54e9
--- /dev/null
+++ b/src/entry_backend/harfbuzz.c
@@ -0,0 +1,148 @@
+#include <assert.h>
+#include <cairo/cairo.h>
+#include <glib.h>
+#include <harfbuzz/hb-ft.h>
+#include <harfbuzz/hb-glib.h>
+#include <math.h>
+#include <pango/pangocairo.h>
+#include <pango/pango.h>
+#include <wchar.h>
+#include "../entry.h"
+#include "../log.h"
+#include "../nelem.h"
+#include "../xmalloc.h"
+
+static void setup_hb_buffer(hb_buffer_t *buffer)
+{
+ hb_buffer_set_direction(buffer, HB_DIRECTION_LTR);
+ hb_buffer_set_script(buffer, HB_SCRIPT_LATIN);
+ hb_buffer_set_language(buffer, hb_language_from_string("en", -1));
+}
+
+
+static hb_buffer_t *create_hb_buffer(void)
+{
+ hb_buffer_t *buffer = hb_buffer_create();
+ hb_buffer_set_unicode_funcs(buffer, hb_glib_get_unicode_funcs());
+ setup_hb_buffer(buffer);
+
+ return buffer;
+}
+
+static uint32_t render_hb_buffer(cairo_t *cr, hb_buffer_t *buffer, uint32_t scale)
+{
+ cairo_save(cr);
+
+ cairo_font_extents_t font_extents;
+ cairo_font_extents(cr, &font_extents);
+ cairo_matrix_t font_matrix;
+ cairo_get_font_matrix(cr, &font_matrix);
+ double baseline = (font_matrix.xx - font_extents.height) / 2 + font_extents.ascent;
+ cairo_translate(cr, 0, baseline);
+
+ unsigned int glyph_count;
+ hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buffer, &glyph_count);
+ hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buffer, &glyph_count);
+ cairo_glyph_t *cairo_glyphs = xmalloc(sizeof(cairo_glyph_t) * glyph_count);
+
+ unsigned int width = 0;
+ for (unsigned int i=0; i < glyph_count; ++i) {
+ width += ceil(glyph_pos[i].x_advance / 64.0 / scale);
+ }
+
+ int x = 0;
+ int y = 0;
+ for (unsigned int i=0; i < glyph_count; ++i) {
+ cairo_glyphs[i].index = glyph_info[i].codepoint;
+ cairo_glyphs[i].x = x + ceil(glyph_pos[i].x_offset / 64.0 / scale);
+ cairo_glyphs[i].y = y - ceil(glyph_pos[i].y_offset / 64.0 / scale);
+ x += ceil(glyph_pos[i].x_advance / 64.0 / scale);
+ y -= ceil(glyph_pos[i].y_advance / 64.0 / scale);
+ }
+
+ cairo_show_glyphs(cr, cairo_glyphs, glyph_count);
+
+ free(cairo_glyphs);
+
+ cairo_restore(cr);
+
+ return width;
+}
+
+void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale)
+{
+ cairo_t *cr = entry->cairo.cr;
+
+ /* Setup FreeType. */
+ log_debug("Creating FreeType library.\n");
+ assert(!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));
+
+ log_debug("Creating Cairo font.\n");
+ entry->backend.cairo_face = cairo_ft_font_face_create_for_ft_face(entry->backend.ft_face, 0);
+
+ 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_font_extents_t font_extents;
+ cairo_font_extents(cr, &font_extents);
+
+ log_debug("Creating Harfbuzz font.\n");
+ entry->backend.hb_font = hb_ft_font_create_referenced(entry->backend.ft_face);
+
+ log_debug("Creating Harfbuzz buffer.\n");
+ entry->backend.hb_buffer = create_hb_buffer();
+
+ /* Draw the prompt now, as this only needs to be done once */
+ log_debug("Drawing prompt.\n");
+ hb_buffer_add_utf8(entry->backend.hb_buffer, "run: ", -1, 0, -1);
+ hb_shape(entry->backend.hb_font, entry->backend.hb_buffer, NULL, 0);
+
+ /* 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);
+ cairo_clip(cr);
+}
+
+void entry_backend_destroy(struct entry *entry)
+{
+ hb_buffer_destroy(entry->backend.hb_buffer);
+ hb_font_destroy(entry->backend.hb_font);
+ FT_Done_Face(entry->backend.ft_face);
+ FT_Done_FreeType(entry->backend.ft_library);
+}
+
+void entry_backend_update(struct entry *entry)
+{
+ cairo_t *cr = entry->cairo.cr;
+
+ hb_buffer_clear_contents(entry->backend.hb_buffer);
+ setup_hb_buffer(entry->backend.hb_buffer);
+ hb_buffer_add_utf8(entry->backend.hb_buffer, entry->input_mb, -1, 0, -1);
+ hb_shape(entry->backend.hb_font, entry->backend.hb_buffer, NULL, 0);
+ render_hb_buffer(cr, entry->backend.hb_buffer, entry->image.scale);
+
+ cairo_font_extents_t font_extents;
+ cairo_font_extents(cr, &font_extents);
+
+ for (size_t i = 0; i < 5; i++) {
+ cairo_translate(cr, 0, font_extents.height);
+
+ hb_buffer_t *buffer = entry->backend.hb_buffer;
+
+ hb_buffer_clear_contents(buffer);
+ setup_hb_buffer(buffer);
+ if (i < entry->results.count) {
+ hb_buffer_add_utf8(buffer, entry->results.buf[i], -1, 0, -1);
+ hb_shape(entry->backend.hb_font, buffer, NULL, 0);
+ render_hb_buffer(cr, buffer, entry->image.scale);
+ }
+ }
+}
diff --git a/src/entry_backend/harfbuzz.h b/src/entry_backend/harfbuzz.h
new file mode 100644
index 0000000..0b07538
--- /dev/null
+++ b/src/entry_backend/harfbuzz.h
@@ -0,0 +1,26 @@
+#ifndef ENTRY_BACKEND_HARFBUZZ_H
+#define ENTRY_BACKEND_HARFBUZZ_H
+
+#include <cairo/cairo-ft.h>
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include <harfbuzz/hb.h>
+#include <pango/pangocairo.h>
+
+struct entry;
+
+struct entry_backend {
+ FT_Library ft_library;
+ FT_Face ft_face;
+
+ cairo_font_face_t *cairo_face;
+
+ hb_font_t *hb_font;
+ hb_buffer_t *hb_buffer;
+};
+
+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);
+
+#endif /* ENTRY_BACKEND_HARFBUZZ_H */
diff --git a/src/entry_backend/pango.c b/src/entry_backend/pango.c
new file mode 100644
index 0000000..569a706
--- /dev/null
+++ b/src/entry_backend/pango.c
@@ -0,0 +1,92 @@
+#include <cairo/cairo.h>
+#include <glib.h>
+#include <pango/pangocairo.h>
+#include <pango/pango.h>
+#include <wchar.h>
+#include "../entry.h"
+#include "../log.h"
+#include "../nelem.h"
+
+void entry_backend_init(struct entry *entry, uint32_t width, uint32_t height, uint32_t scale)
+{
+ cairo_t *cr = entry->cairo.cr;
+
+ /* Setup Pango. */
+ log_debug("Creating Pango context.\n");
+ PangoContext *context = pango_cairo_create_context(cr);
+
+ log_debug("Creating Pango font description.\n");
+ PangoFontDescription *font_description =
+ pango_font_description_from_string(entry->font_name);
+ pango_font_description_set_size(
+ font_description,
+ entry->font_size * PANGO_SCALE);
+ pango_context_set_font_description(context, font_description);
+ pango_font_description_free(font_description);
+
+ entry->backend.prompt_layout = pango_layout_new(context);
+ log_debug("Setting Pango text.\n");
+ pango_layout_set_text(entry->backend.prompt_layout, "run: ", -1);
+ int prompt_width;
+ int prompt_height;
+ log_debug("Get Pango pixel size.\n");
+ pango_layout_get_pixel_size(entry->backend.prompt_layout, &prompt_width, &prompt_height);
+ log_debug("First Pango draw.\n");
+ pango_cairo_update_layout(cr, entry->backend.prompt_layout);
+
+ /* Draw the prompt now, as this only needs to be done once */
+ struct color color = entry->foreground_color;
+ cairo_set_source_rgba(cr, color.r, color.g, color.b, color.a);
+ pango_cairo_show_layout(cr, entry->backend.prompt_layout);
+
+ /* 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);
+ cairo_clip(cr);
+
+ log_debug("Creating Pango layout.\n");
+ entry->backend.entry_layout = pango_layout_new(context);
+ pango_layout_set_text(entry->backend.entry_layout, "", -1);
+
+ for (size_t i = 0; i < 5; i++) {
+ PangoLayout *layout = pango_layout_new(context);
+ pango_layout_set_text(layout, "", -1);
+ entry->backend.result_layouts[i] = layout;
+ }
+
+ entry->backend.context = context;
+}
+
+void entry_backend_destroy(struct entry *entry)
+{
+ for (size_t i = 0; i < 5; i++) {
+ g_object_unref(entry->backend.result_layouts[i]);
+ }
+ g_object_unref(entry->backend.entry_layout);
+ g_object_unref(entry->backend.prompt_layout);
+ g_object_unref(entry->backend.context);
+}
+
+void entry_backend_update(struct entry *entry)
+{
+ cairo_t *cr = entry->cairo.cr;
+
+ pango_layout_set_text(entry->backend.entry_layout, entry->input_mb, -1);
+ pango_cairo_update_layout(cr, entry->backend.entry_layout);
+ pango_cairo_show_layout(cr, entry->backend.entry_layout);
+
+ for (size_t i = 0; i < 5; i++) {
+ cairo_translate(cr, 0, 50);
+ PangoLayout *layout = entry->backend.result_layouts[i];
+ const char *str;
+ if (i < entry->results.count) {
+ str = entry->results.buf[i];
+ } else {
+ str = "";
+ }
+ pango_layout_set_text(layout, str, -1);
+ pango_cairo_update_layout(cr, layout);
+ pango_cairo_show_layout(cr, layout);
+ }
+}
diff --git a/src/entry_backend/pango.h b/src/entry_backend/pango.h
new file mode 100644
index 0000000..08572ae
--- /dev/null
+++ b/src/entry_backend/pango.h
@@ -0,0 +1,19 @@
+#ifndef ENTRY_BACKEND_PANGO_H
+#define ENTRY_BACKEND_PANGO_H
+
+#include <pango/pangocairo.h>
+
+struct entry;
+
+struct entry_backend {
+ PangoContext *context;
+ PangoLayout *prompt_layout;
+ PangoLayout *entry_layout;
+ PangoLayout *result_layouts[5];
+};
+
+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);
+
+#endif /* ENTRY_BACKEND_PANGO_H */
diff --git a/src/image.h b/src/image.h
index 2afaa4d..a366e59 100644
--- a/src/image.h
+++ b/src/image.h
@@ -8,6 +8,7 @@ struct image {
uint8_t *buffer;
uint32_t width;
uint32_t height;
+ uint32_t scale;
bool redraw;
struct {
uint32_t x;
diff --git a/src/main.c b/src/main.c
index d59f747..3bfb5a7 100644
--- a/src/main.c
+++ b/src/main.c
@@ -443,7 +443,7 @@ static void registry_global(
uint32_t version)
{
struct tofi *tofi = data;
- log_debug("Registry %u: %s v%u.\n", name, interface, version);
+ //log_debug("Registry %u: %s v%u.\n", name, interface, version);
if (!strcmp(interface, wl_compositor_interface.name)) {
tofi->wl_compositor = wl_registry_bind(
wl_registry,
@@ -786,7 +786,7 @@ int main(int argc, char *argv[])
log_debug("Third roundtrip done.\n");
/*
- * Initialise the Pango & Cairo structures for rendering the entry.
+ * Initialise the structures for rendering the entry.
* Cairo needs to know the size of the surface it's creating, and
* there's no way to resize it aside from tearing everything down and
* starting again, so we make sure to do this after we've determined
@@ -794,14 +794,14 @@ int main(int argc, char *argv[])
* scale factor after startup, but this is just a launcher, which
* shouldn't be moving between outputs while running.
*/
- log_debug("Initialising Pango / Cairo.\n");
+ log_debug("Initialising renderer.\n");
entry_init(
&tofi.window.entry,
tofi.window.surface.width,
tofi.window.surface.height,
tofi.window.scale);
entry_update(&tofi.window.entry);
- log_debug("Pango / Cairo initialised.\n");
+ log_debug("Renderer initialised.\n");
/*
* Create the various structures for each surface, and
@@ -855,6 +855,7 @@ int main(int argc, char *argv[])
*/
surface_destroy(&tofi.window.surface);
entry_destroy(&tofi.window.entry);
+ zwlr_layer_surface_v1_destroy(tofi.window.zwlr_layer_surface);
wl_surface_destroy(tofi.window.surface.wl_surface);
if (tofi.wl_keyboard != NULL) {
wl_keyboard_release(tofi.wl_keyboard);
@@ -865,16 +866,20 @@ int main(int argc, char *argv[])
wl_compositor_destroy(tofi.wl_compositor);
wl_seat_release(tofi.wl_seat);
wl_output_release(tofi.wl_output);
+ wl_shm_destroy(tofi.wl_shm);
+ zwlr_layer_shell_v1_destroy(tofi.zwlr_layer_shell);
xkb_state_unref(tofi.xkb_state);
xkb_keymap_unref(tofi.xkb_keymap);
xkb_context_unref(tofi.xkb_context);
wl_registry_destroy(tofi.wl_registry);
string_vec_destroy(&tofi.window.entry.commands);
+ string_vec_destroy(&tofi.window.entry.results);
history_destroy(&tofi.window.entry.history);
#endif
/*
* For release builds, skip straight to display disconnection and quit.
*/
+ wl_display_roundtrip(tofi.wl_display);
wl_display_disconnect(tofi.wl_display);
log_debug("Finished, exiting.\n");