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 a664daed365f583f4a5dc42b31f2f80ceeb83b4d
parent 85b5c97bb7c4e1b85f9c322d1beccd1911f0bf09
Author: lumidify <nobody@lumidify.org>
Date:   Sat,  6 Jun 2020 20:48:53 +0200

Add basic drawing area

Diffstat:
MMakefile | 2+-
Mbutton.c | 18+++++++++---------
Adraw.c | 335+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adraw.h | 42++++++++++++++++++++++++++++++++++++++++++
Mltk.c | 21+++++++++++++--------
Mltk.h | 5+++--
Atest_draw.gui | 10++++++++++
7 files changed, 413 insertions(+), 20 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,7 +1,7 @@ LIBS = -lm `pkg-config --libs x11 fontconfig` STD = -std=c99 CFLAGS = -g -w -fcommon -Wall -Werror -Wextra `pkg-config --cflags x11 fontconfig` -pedantic -OBJ = text_line.o text_common.o stb_truetype.o ltk.o ini.o grid.o button.o +OBJ = text_line.o text_common.o stb_truetype.o ltk.o ini.o grid.o button.o draw.o COMPATOBJ = # Uncomment if not using OpenBSD #COMPATOBJ = strtonum.o diff --git a/button.c b/button.c @@ -58,23 +58,23 @@ 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) { - theme->button->border = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->border); } else if (strcmp(prop, "fill") == 0) { - theme->button->fill = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->fill); } else if (strcmp(prop, "border_pressed") == 0) { - theme->button->border_pressed = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->border_pressed); } else if (strcmp(prop, "fill_pressed") == 0) { - theme->button->fill_pressed = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->fill_pressed); } else if (strcmp(prop, "border_active") == 0) { - theme->button->border_active = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->border_active); } else if (strcmp(prop, "fill_active") == 0) { - theme->button->fill_active = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->fill_active); } else if (strcmp(prop, "border_disabled") == 0) { - theme->button->border_disabled = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->border_disabled); } else if (strcmp(prop, "fill_disabled") == 0) { - theme->button->fill_disabled = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->fill_disabled); } else if (strcmp(prop, "text_color") == 0) { - theme->button->text_color = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &theme->button->text_color); } else { (void)printf("WARNING: Unknown property \"%s\" for button style.\n", prop); } diff --git a/draw.c b/draw.c @@ -0,0 +1,335 @@ +/* + * 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> +#include <stdlib.h> +#include <string.h> +#include <X11/Xlib.h> +#include <X11/Xutil.h> +#include "util.h" +#include "khash.h" +#include "stb_truetype.h" +#include <fontconfig/fontconfig.h> +#include "text_common.h" +#include "ltk.h" +#include "draw.h" + +static void ltk_draw_draw(ltk_draw *draw); +static ltk_draw *ltk_draw_create(ltk_window *window, + const char *id, int w, int h, const char *color); +static void ltk_draw_resize(ltk_draw *draw, int orig_w, int orig_h); +static void ltk_draw_destroy(ltk_draw *draw, int shallow); +static void ltk_draw_clear(ltk_window *window, ltk_draw *draw); +static void ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color); +static void ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2); +static void ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill); +static void ltk_draw_cmd_clear( + ltk_window *window, + char **tokens, + size_t num_tokens); +static void ltk_draw_cmd_set_color( + ltk_window *window, + char **tokens, + size_t num_tokens); +static void ltk_draw_cmd_line( + ltk_window *window, + char **tokens, + size_t num_tokens); +static void ltk_draw_cmd_rect( + ltk_window *window, + char **tokens, + size_t num_tokens); +static void ltk_draw_cmd_create( + ltk_window *window, + char **tokens, + size_t num_tokens); + +static void +ltk_draw_draw(ltk_draw *draw) { + ltk_window *window = draw->widget.window; + ltk_rect rect = draw->widget.rect; + XCopyArea(window->dpy, draw->pix, window->xwindow, window->gc, 0, 0, rect.w, rect.h, rect.x, rect.y); +} + + +static ltk_draw * +ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *color) { + XWindowAttributes attrs; + ltk_draw *draw = malloc(sizeof(ltk_draw)); + if (!draw) ltk_fatal("ERROR: Unable to allocate memory for ltk_draw.\n"); + + ltk_fill_widget_defaults(&draw->widget, id, window, + &ltk_draw_draw, &ltk_draw_destroy, 1, LTK_DRAW); + draw->widget.resize = &ltk_draw_resize; + draw->widget.rect.w = w; + draw->widget.rect.h = h; + XGetWindowAttributes(window->dpy, window->xwindow, &attrs); + draw->depth = attrs.depth; + draw->pix = XCreatePixmap(window->dpy, window->xwindow, w, h, draw->depth); + if (!ltk_create_xcolor(window, color, &draw->bg)) { + free(draw); + return NULL; + } + draw->fg = draw->bg; + XSetForeground(window->dpy, window->gc, draw->bg.pixel); + XFillRectangle(window->dpy, draw->pix, window->gc, 0, 0, w, h); + + return draw; +} + +static void +ltk_draw_resize(ltk_draw *draw, int orig_w, int orig_h) { + Window win; + int x, y, w, h, bw, d; + int new_w, new_h; + ltk_window *window = draw->widget.window; + ltk_rect rect = draw->widget.rect; + XGetGeometry(window->dpy, draw->pix, &win, &x, &y, &w, &h, &bw, &d); + + new_w = w < rect.w ? rect.w : w; + new_h = h < rect.h ? rect.h : h; + if (new_w < w && new_h < h) + return; + Pixmap tmp = XCreatePixmap(window->dpy, window->xwindow, + new_w, new_h, draw->depth); + XSetForeground(window->dpy, window->gc, draw->bg.pixel); + XFillRectangle(window->dpy, tmp, window->gc, 0, 0, new_w, new_h); + XCopyArea(window->dpy, draw->pix, tmp, window->gc, + 0, 0, w, h, 0, 0); + XFreePixmap(window->dpy, draw->pix); + draw->pix = tmp; +} + +static void +ltk_draw_destroy(ltk_draw *draw, int shallow) { + if (!draw) { + (void)printf("WARNING: Tried to destroy NULL draw.\n"); + return; + } + ltk_remove_widget(draw->widget.window, draw->widget.id); + free(draw->widget.id); + XFreePixmap(draw->widget.window->dpy, draw->pix); + free(draw); +} + +static void +ltk_draw_clear(ltk_window *window, ltk_draw *draw) { + Window win; + int x, y, w, h, bw, d; + XGetGeometry(window->dpy, draw->pix, &win, &x, &y, &w, &h, &bw, &d); + XSetForeground(window->dpy, window->gc, draw->bg.pixel); + XFillRectangle(window->dpy, window->xwindow, window->gc, 0, 0, w, h); + ltk_window_invalidate_rect(window, draw->widget.rect); +} + +static void +ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color) { + XColor tmp; + if (ltk_create_xcolor(window, color, &tmp)) { + draw->fg = tmp; + } +} + +static void +ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2) { + XSetForeground(window->dpy, window->gc, draw->fg.pixel); + XSetLineAttributes(window->dpy, window->gc, 2, LineSolid, CapButt, JoinMiter); + XDrawLine(window->dpy, draw->pix, window->gc, x1, y1, x2, y2); + ltk_window_invalidate_rect(window, draw->widget.rect); +} + +static void +ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill) { + XSetForeground(window->dpy, window->gc, draw->fg.pixel); + if (fill) { + XFillRectangle(window->dpy, window->xwindow, window->gc, x, y, w, h); + } else { + XSetLineAttributes(window->dpy, window->gc, 2, LineSolid, CapButt, JoinMiter); + XDrawRectangle(window->dpy, draw->pix, window->gc, x, y, w, h); + } + ltk_window_invalidate_rect(window, draw->widget.rect); +} + +static void +ltk_draw_cmd_clear( + ltk_window *window, + char **tokens, + size_t num_tokens) { + ltk_draw *draw; + if (num_tokens != 3) { + (void)fprintf(stderr, "draw clear: Invalid number of arguments.\n"); + return; + } + draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw clear"); + if (!draw) return; + ltk_draw_clear(window, draw); +} + +static void +ltk_draw_cmd_set_color( + ltk_window *window, + char **tokens, + size_t num_tokens) { + ltk_draw *draw; + if (num_tokens != 4) { + (void)fprintf(stderr, "draw set-color: Invalid number of arguments.\n"); + return; + } + draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw set-color"); + if (!draw) return; + ltk_draw_set_color(window, draw, tokens[3]); +} + +static void +ltk_draw_cmd_line( + ltk_window *window, + char **tokens, + size_t num_tokens) { + ltk_draw *draw; + int x1, y1, x2, y2; + const char *errstr; + if (num_tokens != 7) { + (void)fprintf(stderr, "draw line: Invalid number of arguments.\n"); + return; + } + draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw line"); + if (!draw) return; + x1 = strtonum(tokens[3], 0, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw line: Invalid x1: %s\n", errstr); + return; + } + y1 = strtonum(tokens[4], 0, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw line: Invalid y1: %s\n", errstr); + return; + } + x2 = strtonum(tokens[5], 0, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw line: Invalid x2: %s\n", errstr); + return; + } + y2 = strtonum(tokens[6], 0, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw line: Invalid y2: %s\n", errstr); + return; + } + ltk_draw_line(window, draw, x1, y1, x2, y2); +} + +static void +ltk_draw_cmd_rect( + ltk_window *window, + char **tokens, + size_t num_tokens) { + ltk_draw *draw; + const char *errstr; + int x, y, w, h, fill; + if (num_tokens != 8) { + (void)fprintf(stderr, "draw clear: Invalid number of arguments.\n"); + return; + } + draw = ltk_get_widget(window, tokens[1], LTK_DRAW, "draw clear"); + if (!draw) return; + x = strtonum(tokens[3], 0, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw rect: Invalid x: %s\n", errstr); + return; + } + y = strtonum(tokens[4], 0, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw rect: Invalid y: %s\n", errstr); + return; + } + w = strtonum(tokens[5], 1, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw rect: Invalid width: %s\n", errstr); + return; + } + h = strtonum(tokens[6], 1, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw rect: Invalid height: %s\n", errstr); + return; + } + fill = strtonum(tokens[7], 0, 1, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw rect: Invalid fill bool: %s\n", errstr); + return; + } + ltk_draw_rect(window, draw, x, y, w, h, fill); +} + +/* draw <draw id> create <width> <height> <color> */ +static void +ltk_draw_cmd_create( + ltk_window *window, + char **tokens, + size_t num_tokens) { + ltk_draw *draw; + int w, h; + const char *errstr; + if (num_tokens != 6) { + (void)fprintf(stderr, "draw create: Invalid number of arguments.\n"); + return; + } + if (!ltk_check_widget_id_free(window, tokens[1], "draw create")) + return; + w = strtonum(tokens[3], 1, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw create: Invalid width: %s\n", errstr); + return; + } + h = strtonum(tokens[4], 1, 100000, &errstr); + if (errstr) { + (void)fprintf(stderr, "draw create: Invalid height: %s\n", errstr); + return; + } + draw = ltk_draw_create(window, tokens[1], w, h, tokens[5]); + if (draw) + ltk_set_widget(window, draw, tokens[1]); +} + +/* draw <draw id> <command> ... */ +void +ltk_draw_cmd( + ltk_window *window, + char **tokens, + size_t num_tokens) { + if (num_tokens < 3) { + (void)fprintf(stderr, "draw: Invalid number of arguments.\n"); + return; + } + if (strcmp(tokens[2], "create") == 0) { + ltk_draw_cmd_create(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "clear") == 0) { + ltk_draw_cmd_clear(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "set-color") == 0) { + ltk_draw_cmd_set_color(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "line") == 0) { + ltk_draw_cmd_line(window, tokens, num_tokens); + } else if (strcmp(tokens[2], "rect") == 0) { + ltk_draw_cmd_rect(window, tokens, num_tokens); + } else { + (void)fprintf(stderr, "draw: Invalid command.\n"); + } +} diff --git a/draw.h b/draw.h @@ -0,0 +1,42 @@ +/* + * 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 _LTK_DRAW_H_ +#define _LTK_DRAW_H_ + +/* Requires the following includes: <X11/Xlib.h>, "ltk.h" */ + +typedef struct { + ltk_widget widget; + Pixmap pix; + int depth; + XColor fg; + XColor bg; +} ltk_draw; + +void ltk_draw_cmd( + ltk_window *window, + char **tokens, + size_t num_tokens); + +#endif /* _LTK_DRAW_H_ */ diff --git a/ltk.c b/ltk.c @@ -41,6 +41,7 @@ #include "grid.h" #include "text_line.h" #include "button.h" +#include "draw.h" static void ltk_load_theme(ltk_window *window, const char *path); static void ltk_destroy_theme(ltk_theme *theme); @@ -109,13 +110,15 @@ ltk_fatal(const char *msg) { exit(1); }; -XColor -ltk_create_xcolor(ltk_window *window, const char *hex) { - XColor color; - XParseColor(window->dpy, window->cm, hex, &color); - XAllocColor(window->dpy, window->cm, &color); +int +ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col) { + if (!XParseColor(window->dpy, window->cm, hex, col)) { + (void)fprintf(stderr, "Invalid color: %s\n", hex); + return 0; + } + XAllocColor(window->dpy, window->cm, col); - return color; + return 1; } void @@ -231,6 +234,8 @@ proc_cmds(ltk_window *window) { ltk_button_cmd(window, tokens, tokens_len); } else if (strcmp(tokens[0], "set-root-widget") == 0) { ltk_set_root_widget_cmd(window, tokens, tokens_len); + } else if (strcmp(tokens[0], "draw") == 0) { + ltk_draw_cmd(window, tokens, tokens_len); } else { (void)fprintf(stderr, "Invalid command.\n"); } @@ -406,9 +411,9 @@ ltk_window_ini_handler(ltk_window *window, const char *prop, const char *value) if (strcmp(prop, "border_width") == 0) { window->theme->window->border_width = atoi(value); } else if (strcmp(prop, "bg") == 0) { - window->theme->window->bg = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &window->theme->window->bg); } else if (strcmp(prop, "fg") == 0) { - window->theme->window->fg = ltk_create_xcolor(window, value); + ltk_create_xcolor(window, value, &window->theme->window->fg); } else if (strcmp(prop, "font") == 0) { window->theme->window->font = strdup(value); } else if (strcmp(prop, "font_size") == 0) { diff --git a/ltk.h b/ltk.h @@ -24,7 +24,7 @@ #ifndef _LTK_H_ #define _LTK_H_ -/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "drw.h" */ +/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h> */ typedef struct { int x; @@ -50,6 +50,7 @@ typedef enum { typedef enum { LTK_GRID, LTK_BUTTON, + LTK_DRAW, LTK_WIDGET } ltk_widget_type; @@ -133,7 +134,7 @@ void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect); void ltk_fatal(const char *msg); -XColor ltk_create_xcolor(ltk_window *window, const char *hex); +int ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col); void ltk_queue_event(ltk_window *window, const char *id, const char *name); diff --git a/test_draw.gui b/test_draw.gui @@ -0,0 +1,10 @@ +grid grd1 create 2 2 +grid grd1 set-row-weight 0 1 +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 +draw drw1 create 100 100 #fff +grid grd1 add drw1 0 1 1 1 15 +draw drw1 set-color #000 +draw drw1 line 0 0 100 100