commit 38fe277a39863666574934ce221dcf9bd9ee4cad
parent 5aff106f8dd7610fe450e26880243949481ba5af
Author: lumidify <nobody@lumidify.org>
Date: Thu, 10 Sep 2020 22:25:49 +0200
Add pango text support; break other text support :(
Diffstat:
M | Makefile | | | 2 | +- |
M | README.md | | | 5 | +++++ |
M | button.c | | | 140 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
M | button.h | | | 31 | ++++++++++++++----------------- |
A | color.c | | | 17 | +++++++++++++++++ |
A | color.h | | | 11 | +++++++++++ |
M | config.mk | | | 5 | +++-- |
A | defs.h | | | 2 | ++ |
M | draw.c | | | 2 | +- |
M | grid.c | | | 2 | +- |
M | ltk.c | | | 34 | ++++++++++++++-------------------- |
M | ltk.h | | | 4 | +++- |
A | text.h | | | 16 | ++++++++++++++++ |
A | text_pango.c | | | 95 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | text_pango.h | | | 51 | +++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | util.c | | | 46 | ++++++++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 26 | ++++++++++++++++++++++++++ |
17 files changed, 380 insertions(+), 109 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,6 +1,6 @@
include config.mk
-OBJ = text_line.o text_common.o stb_truetype.o ltk.o ini.o grid.o button.o draw.o
+OBJ += color.o util.o ltk.o ini.o grid.o button.o draw.o
ltk: $(OBJ) $(COMPATOBJ)
$(CC) -o $@ $(OBJ) $(COMPATOBJ) $(LDFLAGS)
diff --git a/README.md b/README.md
@@ -1,5 +1,10 @@
Not much to see here.
+WARNING: DON'T TRY TO USE THIS! IT IS ONLY A PLACE FOR ME TO TRY OUT MY
+WILDEST FANTASIES, NOT ACTUAL WORKING CODE.
+
+Also, it currently only works with pango, until I fix the basic text again.
+
To test:
make
diff --git a/button.c b/button.c
@@ -29,9 +29,8 @@
#include <X11/Xutil.h>
#include "util.h"
#include "khash.h"
-#include "text_common.h"
#include "ltk.h"
-#include "text_line.h"
+#include "text.h"
#include "button.h"
static void ltk_button_draw(ltk_button *button);
@@ -40,11 +39,6 @@ static ltk_button *ltk_button_create(ltk_window *window,
const char *id, const char *text);
static void ltk_button_destroy(ltk_button *button, int shallow);
-static void ltk_grid_cmd_create(
- ltk_window *window,
- char **tokens,
- size_t num_tokens);
-
void
ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value) {
ltk_theme *theme = window->theme;
@@ -57,23 +51,32 @@ ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value)
} else if (strcmp(prop, "pad") == 0) {
theme->button->pad = atoi(value);
} else if (strcmp(prop, "border") == 0) {
- ltk_create_xcolor(window, value, &theme->button->border);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->border);
} else if (strcmp(prop, "fill") == 0) {
- ltk_create_xcolor(window, value, &theme->button->fill);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->fill);
} else if (strcmp(prop, "border_pressed") == 0) {
- ltk_create_xcolor(window, value, &theme->button->border_pressed);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->border_pressed);
} else if (strcmp(prop, "fill_pressed") == 0) {
- ltk_create_xcolor(window, value, &theme->button->fill_pressed);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->fill_pressed);
} else if (strcmp(prop, "border_active") == 0) {
- ltk_create_xcolor(window, value, &theme->button->border_active);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->border_active);
} else if (strcmp(prop, "fill_active") == 0) {
- ltk_create_xcolor(window, value, &theme->button->fill_active);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->fill_active);
} else if (strcmp(prop, "border_disabled") == 0) {
- ltk_create_xcolor(window, value, &theme->button->border_disabled);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->border_disabled);
} else if (strcmp(prop, "fill_disabled") == 0) {
- ltk_create_xcolor(window, value, &theme->button->fill_disabled);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->fill_disabled);
} else if (strcmp(prop, "text_color") == 0) {
- ltk_create_xcolor(window, value, &theme->button->text_color);
+ ltk_color_create(window->dpy, window->screen, window->cm,
+ value, &theme->button->text_color);
} else {
(void)printf("WARNING: Unknown property \"%s\" for button style.\n", prop);
}
@@ -85,64 +88,69 @@ ltk_button_draw(ltk_button *button) {
ltk_button_theme *theme = window->theme->button;
ltk_rect rect = button->widget.rect;
int bw = theme->border_width;
- XColor border;
- XColor fill;
- XImage *img = NULL;
+ LtkColor *border;
+ LtkColor *fill;
switch (button->widget.state) {
case LTK_NORMAL:
- img = button->text_normal;
- border = theme->border;
- fill = theme->fill;
+ border = &theme->border;
+ fill = &theme->fill;
break;
case LTK_PRESSED:
- img = button->text_pressed;
- border = theme->border_pressed;
- fill = theme->fill_pressed;
+ border = &theme->border_pressed;
+ fill = &theme->fill_pressed;
break;
case LTK_ACTIVE:
- img = button->text_active;
- border = theme->border_active;
- fill = theme->fill_active;
+ border = &theme->border_active;
+ fill = &theme->fill_active;
break;
case LTK_DISABLED:
- img = button->text_disabled;
- border = theme->border_disabled;
- fill = theme->fill_disabled;
+ border = &theme->border_disabled;
+ fill = &theme->fill_disabled;
break;
default:
ltk_fatal("No style found for button!\n");
}
- XSetForeground(window->dpy, window->gc, fill.pixel);
- XFillRectangle(window->dpy, window->xwindow, window->gc, rect.x, rect.y, rect.w, rect.h);
+ XSetForeground(window->dpy, window->gc, fill->xcolor.pixel);
+ XFillRectangle(window->dpy, window->xwindow, window->gc, rect.x,
+ rect.y, rect.w, rect.h);
/* FIXME: Why did I do this? */
if (bw < 1) return;
- XSetForeground(window->dpy, window->gc, border.pixel);
- XSetLineAttributes(window->dpy, window->gc, bw, LineSolid, CapButt, JoinMiter);
- XDrawRectangle(window->dpy, window->xwindow, window->gc, rect.x + bw / 2, rect.y + bw / 2, rect.w - bw, rect.h - bw);
- if (!img) {
- img = ltk_text_line_render(button->tl, window->dpy,
- window->xwindow, window->gc, window->cm,
- theme->text_color, fill);
- switch (button->widget.state) {
- case LTK_NORMAL:
- button->text_normal = img;
- break;
- case LTK_PRESSED:
- button->text_pressed = img;
- break;
- case LTK_ACTIVE:
- button->text_active = img;
- break;
- case LTK_DISABLED:
- button->text_disabled = img;
- break;
- }
- }
- int text_x = rect.x + (rect.w - button->tl->w) / 2;
- int text_y = rect.y + (rect.h - button->tl->h) / 2;
- XPutImage(window->dpy, window->xwindow, window->gc, img, 0, 0, text_x, text_y, button->tl->w, button->tl->h);
+ XSetForeground(window->dpy, window->gc, border->xcolor.pixel);
+ XSetLineAttributes(window->dpy, window->gc, bw, LineSolid,
+ CapButt, JoinMiter);
+ XDrawRectangle(window->dpy, window->xwindow, window->gc,
+ rect.x + bw / 2, rect.y + bw / 2, rect.w - bw, rect.h - bw);
+
+ int text_w, text_h;
+ ltk_text_line_get_size(button->tl, &text_w, &text_h);
+ int text_x = rect.x + (rect.w - text_w) / 2;
+ int text_y = rect.y + (rect.h - text_h) / 2;
+ ltk_text_line_draw(button->tl, window->gc, text_x, text_y);
}
+static void
+ltk_button_change_state(ltk_button *button) {
+ ltk_window *window = button->widget.window;
+ ltk_button_theme *theme = window->theme->button;
+ LtkColor *fill;
+ switch (button->widget.state) {
+ case LTK_NORMAL:
+ fill = &theme->fill;
+ break;
+ case LTK_PRESSED:
+ fill = &theme->fill_pressed;
+ break;
+ case LTK_ACTIVE:
+ fill = &theme->fill_active;
+ break;
+ case LTK_DISABLED:
+ fill = &theme->fill_disabled;
+ break;
+ default:
+ ltk_fatal("No style found for button!\n");
+ }
+ ltk_text_line_render(button->tl, fill, &theme->text_color);
+}
static void
ltk_button_mouse_release(ltk_button *button, XEvent event) {
@@ -155,17 +163,17 @@ ltk_button_create(ltk_window *window, const char *id, const char *text) {
if (!button) ltk_fatal("ERROR: Unable to allocate memory for ltk_button.\n");
ltk_fill_widget_defaults(&button->widget, id, window,
- <k_button_draw, <k_button_destroy, 1, LTK_BUTTON);
+ <k_button_draw, <k_button_change_state, <k_button_destroy, 1, LTK_BUTTON);
button->widget.mouse_release = <k_button_mouse_release;
uint16_t font_size = window->theme->window->font_size;
- button->tl = ltk_text_line_create(font_size, strdup(text));
+ button->tl = ltk_text_line_create(window->xwindow, font_size, strdup(text), -1);
+ int text_w, text_h;
+ ltk_text_line_get_size(button->tl, &text_w, &text_h);
ltk_button_theme *theme = window->theme->button;
- button->widget.rect.w = button->tl->w + theme->border_width * 2 + theme->pad * 2;
- button->widget.rect.h = button->tl->h + theme->border_width * 2 + theme->pad * 2;
- button->text_normal = NULL;
- button->text_pressed = NULL;
- button->text_active = NULL;
- button->text_disabled = NULL;
+ button->widget.rect.w = text_w + theme->border_width * 2 + theme->pad * 2;
+ button->widget.rect.h = text_h + theme->border_width * 2 + theme->pad * 2;
+ /* render text */
+ ltk_button_change_state(button);
return button;
}
diff --git a/button.h b/button.h
@@ -1,6 +1,6 @@
/*
* This file is part of the Lumidify ToolKit (LTK)
- * Copyright (c) 2016, 2017, 2018 lumidify <nobody@lumidify.org>
+ * Copyright (c) 2016, 2017, 2018, 2020 lumidify <nobody@lumidify.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@@ -24,33 +24,30 @@
#ifndef _LTK_BUTTON_H_
#define _LTK_BUTTON_H_
-/* Requires the following includes: <X11/Xlib.h>, "ltk.h" */
+/* Requires the following includes: <X11/Xlib.h>, "ltk.h", "color.h", "text.h" */
typedef struct {
ltk_widget widget;
- struct ltk_text_line *tl;
- XImage *text_normal;
- XImage *text_active;
- XImage *text_pressed;
- XImage *text_disabled;
+ LtkTextLine *tl;
+ Pixmap text_pixmap;
} ltk_button;
typedef struct ltk_button_theme {
int border_width;
- XColor text_color;
+ LtkColor text_color;
int pad;
- XColor border;
- XColor fill;
+ LtkColor border;
+ LtkColor fill;
- XColor border_pressed;
- XColor fill_pressed;
+ LtkColor border_pressed;
+ LtkColor fill_pressed;
- XColor border_active;
- XColor fill_active;
+ LtkColor border_active;
+ LtkColor fill_active;
- XColor border_disabled;
- XColor fill_disabled;
+ LtkColor border_disabled;
+ LtkColor fill_disabled;
} ltk_button_theme;
void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value);
@@ -60,4 +57,4 @@ void ltk_button_cmd(
char **tokens,
size_t num_tokens);
-#endif
+#endif /* _LTK_BUTTON_H_ */
diff --git a/color.c b/color.c
@@ -0,0 +1,17 @@
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include "util.h"
+#include "color.h"
+
+void
+ltk_color_create(Display *dpy, int screen, Colormap cm, const char *hex, LtkColor *col) {
+ if (!XParseColor(dpy, cm, hex, &col->xcolor)) {
+ /* FIXME: better error reporting */
+ ltk_err("ltk_color_create");
+ }
+ XAllocColor(dpy, cm, &col->xcolor);
+ /* FIXME: replace with XftColorAllocValue; error checking */
+ #ifdef USE_XFT
+ XftColorAllocName(dpy, DefaultVisual(dpy, screen), cm, hex, &col->xftcolor);
+ #endif
+}
diff --git a/color.h b/color.h
@@ -0,0 +1,11 @@
+#include "defs.h"
+#ifdef USE_XFT
+ #include <X11/Xft/Xft.h>
+#endif
+
+typedef struct {
+ XColor xcolor;
+ #ifdef USE_XFT
+ XftColor xftcolor;
+ #endif
+} LtkColor;
diff --git a/config.mk b/config.mk
@@ -1,7 +1,8 @@
VERSION = -999
-CFLAGS = -g -std=c99 -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 fontconfig` -pedantic
-LDFLAGS = -lm `pkg-config --libs x11 fontconfig`
+CFLAGS = -g -std=c99 -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 fontconfig pangoxft` -pedantic
+LDFLAGS = -lm `pkg-config --libs x11 fontconfig pangoxft`
+OBJ = text_pango.o
# OpenBSD
COMPATOBJ =
diff --git a/defs.h b/defs.h
@@ -0,0 +1,2 @@
+#define USE_PANGO
+#define USE_XFT
diff --git a/draw.c b/draw.c
@@ -80,7 +80,7 @@ ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *co
if (!draw) ltk_fatal("ERROR: Unable to allocate memory for ltk_draw.\n");
ltk_fill_widget_defaults(&draw->widget, id, window,
- <k_draw_draw, <k_draw_destroy, 1, LTK_DRAW);
+ <k_draw_draw, NULL, <k_draw_destroy, 1, LTK_DRAW);
draw->widget.resize = <k_draw_resize;
draw->widget.rect.w = w;
draw->widget.rect.h = h;
diff --git a/grid.c b/grid.c
@@ -100,7 +100,7 @@ ltk_grid_create(ltk_window *window, const char *id, int rows, int columns) {
ltk_grid *grid = malloc(sizeof(ltk_grid));
ltk_fill_widget_defaults(&grid->widget, id, window, <k_grid_draw,
- <k_grid_destroy, 0, LTK_GRID);
+ NULL, <k_grid_destroy, 0, LTK_GRID);
grid->widget.mouse_press = <k_grid_mouse_press;
grid->widget.mouse_release = <k_grid_mouse_release;
grid->widget.motion_notify = <k_grid_motion_notify;
diff --git a/ltk.c b/ltk.c
@@ -35,10 +35,9 @@
#include "util.h"
#include "khash.h"
#include "ini.h"
-#include "text_common.h"
+#include "text.h"
#include "ltk.h"
#include "grid.h"
-#include "text_line.h"
#include "button.h"
#include "draw.h"
@@ -54,22 +53,6 @@ static size_t tokens_bufsize = 0;
static size_t cmd_len = 0;
static size_t tokens_len = 0;
-char *
-ltk_read_file(const char *path, unsigned long *len) {
- FILE *f;
- char *file_contents;
- f = fopen(path, "rb");
- fseek(f, 0, SEEK_END);
- *len = ftell(f);
- fseek(f, 0, SEEK_SET);
- file_contents = malloc(*len + 1);
- fread(file_contents, 1, *len, f);
- file_contents[*len] = '\0';
- fclose(f);
-
- return file_contents;
-}
-
static ltk_rect
ltk_rect_union(ltk_rect r1, ltk_rect r2) {
ltk_rect u;
@@ -322,7 +305,6 @@ ltk_create_window(const char *theme_path, const char *title, int x, int y, unsig
window->wm_delete_msg = XInternAtom(window->dpy, "WM_DELETE_WINDOW", False);
ltk_window_theme *wtheme = window->theme->window;
- ltk_init_default_font(wtheme->font);
window->xwindow =
XCreateSimpleWindow(window->dpy, DefaultRootWindow(window->dpy), x, y,
w, h, wtheme->border_width,
@@ -335,6 +317,8 @@ ltk_create_window(const char *theme_path, const char *title, int x, int y, unsig
XSetWMProtocols(window->dpy, window->xwindow, &window->wm_delete_msg, 1);
window->root_widget = NULL;
+ ltk_init_text(wtheme->font, window->dpy, window->screen, window->cm);
+
window->other_event = <k_window_other_event;
window->rect.w = 0;
@@ -466,6 +450,8 @@ ltk_window_remove_active_widget(ltk_window *window) {
while (widget) {
widget->state = LTK_NORMAL;
widget->active_widget = NULL;
+ if (widget->change_state)
+ widget->change_state(widget);
if (widget->needs_redraw)
ltk_window_invalidate_rect(window, widget->rect);
widget = widget->parent;
@@ -488,7 +474,8 @@ ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) {
void
ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
- void (*draw) (void *), void (*destroy) (void *, int), unsigned int needs_redraw,
+ void (*draw) (void *), void (*change_state) (void *),
+ void (*destroy) (void *, int), unsigned int needs_redraw,
ltk_widget_type type) {
widget->id = strdup(id);
widget->window = window;
@@ -506,6 +493,7 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
widget->resize = NULL;
widget->draw = draw;
+ widget->change_state = change_state;
widget->destroy = destroy;
widget->needs_redraw = needs_redraw;
@@ -530,6 +518,8 @@ ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) {
if (event.xbutton.button == 1) {
ltk_widget *parent = widget->parent;
widget->state = LTK_PRESSED;
+ if (widget->change_state)
+ widget->change_state(widget);
if (widget->needs_redraw)
ltk_window_invalidate_rect(widget->window, widget->rect);
}
@@ -544,6 +534,8 @@ ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) {
return;
if (widget->state == LTK_PRESSED) {
widget->state = LTK_ACTIVE;
+ if (widget->change_state)
+ widget->change_state(widget);
if (widget->needs_redraw)
ltk_window_invalidate_rect(widget->window, widget->rect);
}
@@ -558,6 +550,8 @@ ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
short pressed = (event.xmotion.state & Button1Mask) == Button1Mask;
if ((widget->state == LTK_NORMAL) && !pressed) {
widget->state = LTK_ACTIVE;
+ if (widget->change_state)
+ widget->change_state(widget);
if (widget->mouse_enter)
widget->mouse_enter(widget, event);
/* FIXME: do this properly */
diff --git a/ltk.h b/ltk.h
@@ -73,6 +73,7 @@ typedef struct ltk_widget {
void (*resize) (void *, int, int);
void (*draw) (void *);
+ void (*change_state) (void *);
void (*destroy) (void *, int);
ltk_rect rect;
@@ -156,7 +157,8 @@ void ltk_remove_active_widget(ltk_widget *widget);
void ltk_set_active_widget(ltk_window *window, ltk_widget *widget);
void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window * window,
- void (*draw) (void *), void (*destroy) (void *, int), unsigned int needs_redraw,
+ void (*draw) (void *), void (*change_state) (void *),
+ void (*destroy) (void *, int), unsigned int needs_redraw,
ltk_widget_type type);
void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event);
diff --git a/text.h b/text.h
@@ -0,0 +1,16 @@
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <stdint.h>
+#include "defs.h"
+#include "color.h"
+
+#ifdef USE_PANGO
+ #include <pango/pangoxft.h>
+ #include "text_pango.h"
+#endif
+
+/* Basic */
+#ifdef USE_BASIC_TEXT
+ #include "text_common.h"
+ #include "text_line.h"
+#endif
diff --git a/text_pango.c b/text_pango.c
@@ -0,0 +1,95 @@
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xos.h>
+#include <pango/pangoxft.h>
+#include "color.h"
+#include "text_pango.h"
+#include "util.h"
+
+struct {
+ PangoFontMap *fontmap;
+ PangoContext *context;
+ char *default_font;
+ Display *dpy;
+ int screen;
+ Colormap cm;
+} tm = {NULL, NULL, NULL, NULL, 0, 0};
+
+void
+ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm) {
+ tm.fontmap = pango_xft_get_font_map(dpy, screen);
+ tm.context = pango_font_map_create_context(tm.fontmap);
+ tm.default_font = strdup(default_font);
+ if (!tm.default_font) ltk_err("ltk_init_text (pango)");
+ tm.dpy = dpy;
+ tm.screen = screen;
+ tm.cm = cm;
+}
+
+void
+ltk_cleanup_text(void) {
+ if (tm.default_font) free(tm.default_font);
+ /* FIXME: destroy fontmap and context */
+}
+
+LtkTextLine *
+ltk_text_line_create(Window window, uint16_t font_size, char *text, int width) {
+ if (!tm.context)
+ ltk_err("ltk_text_line_create (pango): text not initialized yet");
+ /* FIXME: respect font size */
+ /*
+ PangoFontDescription *desc = pango_font_description_from_string("Sans Bold 27");
+ pango_layout_set_font_description(layout, desc);
+ pango_font_description_free(desc);
+ */
+ LtkTextLine *line = malloc(sizeof(LtkTextLine));
+ if (!line) ltk_err("ltk_text_line_create (pango)");
+ line->text = text;
+ line->font_size = font_size;
+ line->layout = pango_layout_new(tm.context);
+ if (width > 0) {
+ pango_layout_set_width(line->layout, width * PANGO_SCALE);
+ pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR);
+ }
+ pango_layout_set_text(line->layout, text, -1);
+ pango_layout_get_size(line->layout, &line->w, &line->h);
+ line->w /= PANGO_SCALE;
+ line->h /= PANGO_SCALE;
+ XWindowAttributes attrs;
+ XGetWindowAttributes(tm.dpy, window, &attrs);
+ line->pixmap = XCreatePixmap(tm.dpy, window, line->w, line->h, attrs.depth);
+ line->draw = XftDrawCreate(tm.dpy, line->pixmap, DefaultVisual(tm.dpy, tm.screen), tm.cm);
+ line->window = window;
+
+ return line;
+}
+
+void
+ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg) {
+ XftDrawRect(tl->draw, &bg->xftcolor, 0, 0, tl->w, tl->h);
+ pango_xft_render_layout(tl->draw, &fg->xftcolor, tl->layout, 0, 0);
+}
+
+void
+ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y) {
+ XCopyArea(tm.dpy, tl->pixmap, tl->window, gc, 0, 0, tl->w, tl->h, x, y);
+}
+
+void
+ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h) {
+ *w = tl->w;
+ *h = tl->h;
+}
+
+void
+ltk_text_line_destroy(LtkTextLine *tl) {
+ g_object_unref(tl->layout);
+ XftDrawDestroy(tl->draw);
+ XFreePixmap(tm.dpy, tl->pixmap);
+ free(tl->text);
+ free(tl);
+}
diff --git a/text_pango.h b/text_pango.h
@@ -0,0 +1,51 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2020 lumidify <nobody@lumidify.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef _TEXT_PANGO_H_
+#define _TEXT_PANGO_H_
+
+/*
+Requires the following includes:
+<X11/Xlib.h>, <X11/Xutil.h>, <stdint.h>, <pango/pangoxft.h>
+*/
+
+typedef struct {
+ char *text;
+ uint16_t font_size;
+ int w;
+ int h;
+ Window window;
+ PangoLayout *layout;
+ XftDraw *draw;
+ Pixmap pixmap;
+} LtkTextLine;
+
+void ltk_init_text(const char *default_font, Display *dpy, int screen, Colormap cm);
+void ltk_cleanup_text(void);
+LtkTextLine *ltk_text_line_create(Window window, uint16_t font_size, char *text, int width);
+void ltk_text_line_render(LtkTextLine *tl, LtkColor *bg, LtkColor *fg);
+void ltk_text_line_draw(LtkTextLine *tl, GC gc, int x, int y);
+void ltk_text_line_get_size(LtkTextLine *tl, int *w, int *h);
+void ltk_text_line_destroy(LtkTextLine *tl);
+
+#endif /* _TEXT_PANGO_H_ */
diff --git a/util.c b/util.c
@@ -0,0 +1,46 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2020 lumidify <nobody@lumidify.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include <stdio.h>
+
+void
+ltk_err(const char *msg) {
+ perror(msg);
+ exit(1);
+}
+
+char *
+ltk_read_file(const char *path, unsigned long *len) {
+ FILE *f;
+ char *file_contents;
+ f = fopen(path, "rb");
+ fseek(f, 0, SEEK_END);
+ *len = ftell(f);
+ fseek(f, 0, SEEK_SET);
+ file_contents = malloc(*len + 1);
+ fread(file_contents, 1, *len, f);
+ file_contents[*len] = '\0';
+ fclose(f);
+
+ return file_contents;
+}
diff --git a/util.h b/util.h
@@ -1,5 +1,31 @@
+/*
+ * This file is part of the Lumidify ToolKit (LTK)
+ * Copyright (c) 2020 lumidify <nobody@lumidify.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
#ifndef __OpenBSD__
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp);
#endif
+
+void ltk_err(const char *msg);
+char *ltk_read_file(const char *path, unsigned long *len);