ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 85b5c97bb7c4e1b85f9c322d1beccd1911f0bf09
parent 017e677d873e000aea1af57bb49d4be48ff89445
Author: lumidify <nobody@lumidify.org>
Date:   Sat,  6 Jun 2020 19:17:19 +0200

Allow longer strings in commands; fix text rendering

Diffstat:
MLICENSE | 2+-
Mbutton.c | 6+++---
Mbutton.h | 2+-
Mgrid.c | 22+++++++++++-----------
Mgrid.h | 2+-
Mltk.c | 90+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mltk.h | 4----
Mtest.gui | 4++--
Mtext_line.c | 26++++++++++++++++----------
Mtext_line.h | 2++
Mtheme.ini | 2+-
11 files changed, 98 insertions(+), 64 deletions(-)

diff --git a/LICENSE b/LICENSE @@ -1,4 +1,4 @@ -See khash.h, ini.*, and strtonum.c for third-party licenses. +See khash.h, ini.*, stb_truetype.*, and strtonum.c for third-party licenses. MIT/X Consortium License diff --git a/button.c b/button.c @@ -43,7 +43,7 @@ static void ltk_button_destroy(ltk_button *button, int shallow); static void ltk_grid_cmd_create( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); void @@ -187,7 +187,7 @@ ltk_button_destroy(ltk_button *button, int shallow) { static void ltk_button_cmd_create( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { ltk_button *button; if (num_tokens != 4) { @@ -204,7 +204,7 @@ ltk_button_cmd_create( void ltk_button_cmd( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { if (num_tokens < 3) { (void)fprintf(stderr, "button: Invalid number of arguments.\n"); diff --git a/button.h b/button.h @@ -57,7 +57,7 @@ void ltk_button_ini_handler(ltk_window *window, const char *prop, const char *va void ltk_button_cmd( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); #endif diff --git a/grid.c b/grid.c @@ -54,23 +54,23 @@ static void ltk_grid_motion_notify(ltk_grid *grid, XEvent event); static void ltk_grid_cmd_add( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); static void ltk_grid_cmd_ungrid( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); static void ltk_grid_cmd_create( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); static void ltk_grid_cmd_set_row_weight( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); static void ltk_grid_cmd_set_column_weight( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens); static void @@ -364,7 +364,7 @@ ltk_grid_motion_notify(ltk_grid *grid, XEvent event) { static void ltk_grid_cmd_add( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { ltk_grid *grid; ltk_widget *widget; @@ -410,7 +410,7 @@ ltk_grid_cmd_add( static void ltk_grid_cmd_ungrid( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { ltk_grid *grid; ltk_widget *widget; @@ -428,7 +428,7 @@ ltk_grid_cmd_ungrid( static void ltk_grid_cmd_create( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { int rows, columns; ltk_grid *grid; @@ -457,7 +457,7 @@ ltk_grid_cmd_create( static void ltk_grid_cmd_set_row_weight( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { ltk_grid *grid; int row, weight; @@ -485,7 +485,7 @@ ltk_grid_cmd_set_row_weight( static void ltk_grid_cmd_set_column_weight( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { ltk_grid *grid; int column, weight; @@ -513,7 +513,7 @@ ltk_grid_cmd_set_column_weight( void ltk_grid_cmd( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, size_t num_tokens) { if (num_tokens < 3) { (void)fprintf(stderr, "grid: Invalid number of arguments.\n"); diff --git a/grid.h b/grid.h @@ -42,6 +42,6 @@ typedef struct { unsigned int *column_pos; } ltk_grid; -void ltk_grid_cmd(ltk_window *window, char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], size_t num_tokens); +void ltk_grid_cmd(ltk_window *window, char **tokens, size_t num_tokens); #endif diff --git a/ltk.c b/ltk.c @@ -46,6 +46,12 @@ static void ltk_load_theme(ltk_window *window, const char *path); static void ltk_destroy_theme(ltk_theme *theme); static int running = 1; +static char *cmd_input = NULL; +static char **tokens = NULL; +static size_t cmd_bufsize = 0; +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) { @@ -128,7 +134,7 @@ ltk_queue_event(ltk_window *window, const char *id, const char *name) { static void ltk_set_root_widget_cmd( ltk_window *window, - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], + char **tokens, int num_tokens) { ltk_widget *widget; if (num_tokens != 2) { @@ -149,57 +155,82 @@ ltk_set_root_widget_cmd( /* copied from suckless ii */ static int -read_line(int fd, char *buf, size_t bufsiz) -{ - size_t i = 0; +read_cmdline(int fd) { char c = '\0'; + char *tmp = NULL; + cmd_len = 0; do { + if (cmd_len >= cmd_bufsize) { + cmd_bufsize = !cmd_bufsize ? 1 : cmd_bufsize; + tmp = realloc(cmd_input, cmd_bufsize * 2 * sizeof(char)); + if (!tmp) ltk_fatal("Out of memory while reading command.\n"); + cmd_input = tmp; + cmd_bufsize *= 2; + } if (read(fd, &c, sizeof(char)) != sizeof(char)) return -1; - buf[i++] = c; - } while (c != '\n' && i < bufsiz); - buf[i - 1] = '\0'; /* eliminates '\n' */ + cmd_input[cmd_len++] = c; + } while (c != '\n'); + cmd_input[cmd_len - 1] = '\0'; /* eliminates '\n' */ return 0; } -static size_t -tokenize(char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH], char *buf) { +static int +tokenize(void) { char *c; - if (!buf || buf[0] == '\0') return 0; - for (c = buf; *c == ' '; c++) + if (!cmd_input || cmd_input[0] == '\0') return 0; + for (c = cmd_input; *c == ' '; c++) ; + tokens_len = 0; size_t cur_tok = 0; size_t cur_tok_len = 0; + size_t cur_tok_bufsize = 0; + int in_str = 0; + char **tmp; + char *tmp2; while (*c != '\0') { - if (cur_tok >= MAX_TOKENS) { - return 0; - } else if (*c == ' ') { + if (cur_tok >= tokens_bufsize) { + tokens_bufsize = !tokens_bufsize ? 1 : tokens_bufsize; + tmp = realloc(tokens, tokens_bufsize * 2 * sizeof(char *)); + if (!tmp) ltk_fatal("Out of memory while parsing command.\n"); + tokens = tmp; + tokens_bufsize *= 2; + } + if (cur_tok_len + 1 >= cur_tok_bufsize) { + cur_tok_bufsize = !cur_tok_bufsize ? 1 : cur_tok_bufsize; + if (cur_tok_len == 0) + tokens[cur_tok] = NULL; /* so realloc works the first time */ + tmp2 = realloc(tokens[cur_tok], cur_tok_bufsize * 2 * sizeof(char)); + if (!tmp2) ltk_fatal("Out of memory while parsing command.\n"); + tokens[cur_tok] = tmp2; + cur_tok_bufsize *= 2; + } + if (*c == '"') { + in_str = !in_str; + } else if (*c == ' ' && !in_str) { tokens[cur_tok][cur_tok_len] = '\0'; cur_tok++; cur_tok_len = 0; - } else if (cur_tok_len >= MAX_TOKEN_LENGTH - 1) { - return 0; + cur_tok_bufsize = 0; } else { tokens[cur_tok][cur_tok_len++] = *c; } c++; } tokens[cur_tok][cur_tok_len] = '\0'; - return cur_tok + 1; + tokens_len = cur_tok + 1; } static void -proc_cmds(ltk_window *window, char *buf) { - /* FIXME: resizable */ - char tokens[MAX_TOKENS][MAX_TOKEN_LENGTH]; - int num_tokens; - if (!(num_tokens = tokenize(tokens, buf))) return; +proc_cmds(ltk_window *window) { + if (!tokenize()) return; + if (!tokens_len) return; if (strcmp(tokens[0], "grid") == 0) { - ltk_grid_cmd(window, tokens, num_tokens); + ltk_grid_cmd(window, tokens, tokens_len); } else if (strcmp(tokens[0], "button") == 0) { - ltk_button_cmd(window, tokens, num_tokens); + ltk_button_cmd(window, tokens, tokens_len); } else if (strcmp(tokens[0], "set-root-widget") == 0) { - ltk_set_root_widget_cmd(window, tokens, num_tokens); + ltk_set_root_widget_cmd(window, tokens, tokens_len); } else { (void)fprintf(stderr, "Invalid command.\n"); } @@ -213,7 +244,6 @@ ltk_mainloop(ltk_window *window) { struct timeval tv; fd_set rfds; int fd_in = fileno(stdin); - char buf[MAX_CMD_LENGTH]; int retval; tick.tv_sec = 0; tick.tv_nsec = 10000; @@ -228,7 +258,9 @@ ltk_mainloop(ltk_window *window) { XNextEvent(window->dpy, &event); ltk_handle_event(window, event); } - if (window->dirty_rect.w != 0 && window->dirty_rect.h != 0) { + if (retval > 0 && !read_cmdline(fd_in)) { + proc_cmds(window); + } else if (window->dirty_rect.w != 0 && window->dirty_rect.h != 0) { ltk_redraw_window(window); window->dirty_rect.w = 0; window->dirty_rect.h = 0; @@ -244,9 +276,7 @@ ltk_mainloop(ltk_window *window) { free(last); } while (cur); window->first_event = window->last_event = NULL; - } else if (retval > 0 && !read_line(fd_in, buf, sizeof(buf))) { - proc_cmds(window, buf); - } + } /* yes, this should be improved */ nanosleep(&tick, NULL); diff --git a/ltk.h b/ltk.h @@ -24,10 +24,6 @@ #ifndef _LTK_H_ #define _LTK_H_ -#define MAX_TOKENS 20 -#define MAX_TOKEN_LENGTH 20 -#define MAX_CMD_LENGTH 400 - /* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "drw.h" */ typedef struct { diff --git a/test.gui b/test.gui @@ -4,7 +4,7 @@ grid grd1 set-row-weight 1 1 grid grd1 set-column-weight 0 1 grid grd1 set-column-weight 1 1 set-root-widget grd1 -button btn1 create Button1 +button btn1 create "I'm a button!" grid grd1 add btn1 0 0 1 1 0 -button btn2 create Button2 +button btn2 create "I'm also a button!" grid grd1 add btn2 1 0 1 2 3 diff --git a/text_line.c b/text_line.c @@ -56,15 +56,17 @@ ltk_create_ximage(Display *dpy, int w, int h, int depth, XColor bg) { /* based on http://codemadness.org/git/dwm-font/file/drw.c.html#l315 */ static void -ltk_text_line_draw_glyph(ltk_glyph *glyph, XImage *img, XColor fg) { +ltk_text_line_draw_glyph(ltk_glyph *glyph, int xoff, int yoff, XImage *img, XColor fg) { + int x = glyph->x + xoff; + int y = glyph->y + yoff; double a; int b; for (int i = 0; i < glyph->info->h; i++) { for (int j = 0; j < glyph->info->w; j++) { - if (glyph->y + i >= img->height || glyph->x + j >= img->width || - glyph->y + i < 0 || glyph->x + i < 0) + if (y + i >= img->height || x + j >= img->width || + y + i < 0 || x + i < 0) continue; - b = (glyph->y + i) * img->bytes_per_line + (glyph->x + j) * 4; + b = (y + i) * img->bytes_per_line + (x + j) * 4; a = glyph->info->alphamap[i * glyph->info->w + j] / 255.0; img->data[b] = (fg.blue * a + (1 - a) * (uint16_t)img->data[b] * 257) / 257; img->data[b + 1] = (fg.green * a + (1 - a) * (uint16_t)img->data[b + 1] * 257) / 257; @@ -91,7 +93,7 @@ ltk_text_line_render( /* FIXME: pass old image; if it has same dimensions, just clear it */ XImage *img = ltk_create_ximage(dpy, tl->w, tl->h, depth, bg); for (int i = 0; i < tl->glyph_len; i++) { - ltk_text_line_draw_glyph(&tl->glyphs[i], img, fg); + ltk_text_line_draw_glyph(&tl->glyphs[i], -tl->x_min, -tl->y_min, img, fg); } return img; } @@ -103,7 +105,7 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) { int index; char *file; size_t inc = 0; - int x = 0, y, x1, x2, y1, y2, kern_advance, ax; + int x = 0, y, kern_advance, ax; int x1_abs, x2_abs; float scale; int ascent, descent, line_gap; @@ -145,16 +147,17 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) { ascent *= scale; descent *= scale; } - stbtt_GetGlyphBitmapBox(&font->info, gid, scale, scale, &x1, &y1, &x2, &y2); - y = ascent + y1; - x1_abs = x + x1; + ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache); + y = ascent + ginfo->yoff; + x1_abs = x + ginfo->xoff; + tl->glyphs[i].x = x1_abs; tl->glyphs[i].y = y; + stbtt_GetGlyphHMetrics(&font->info, gid, &ax, 0); x += (int) (ax * scale); x2_abs = x; - ginfo = ltk_get_glyph_info(font, gid, scale, glyph_cache); tl->glyphs[i].info = ginfo; if (x1_abs < x_min) x_min = x1_abs; if (y < y_min) y_min = y; @@ -168,6 +171,9 @@ ltk_text_line_create_glyphs(ltk_window *window, struct ltk_text_line *tl) { } c1 = c2; }; + /* for drawing the glyphs at the right position on the image */ + tl->x_min = x_min; + tl->y_min = y_min; tl->w = x_max - x_min; tl->h = y_max - y_min; } diff --git a/text_line.h b/text_line.h @@ -38,6 +38,8 @@ struct ltk_text_line { uint16_t font_size; int w; int h; + int x_min; + int y_min; }; XImage *ltk_text_line_render(struct ltk_text_line *tl, diff --git a/theme.ini b/theme.ini @@ -3,7 +3,7 @@ font_size = 15 border_width = 0 bg = #000000 fg = #FFFFFF -font = Awami Nastaliq +font = Liberation Mono [button] border_width = 2