commit a664daed365f583f4a5dc42b31f2f80ceeb83b4d
parent 85b5c97bb7c4e1b85f9c322d1beccd1911f0bf09
Author: lumidify <nobody@lumidify.org>
Date: Sat, 6 Jun 2020 20:48:53 +0200
Add basic drawing area
Diffstat:
M | Makefile | | | 2 | +- |
M | button.c | | | 18 | +++++++++--------- |
A | draw.c | | | 335 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | draw.h | | | 42 | ++++++++++++++++++++++++++++++++++++++++++ |
M | ltk.c | | | 21 | +++++++++++++-------- |
M | ltk.h | | | 5 | +++-- |
A | test_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,
+ <k_draw_draw, <k_draw_destroy, 1, LTK_DRAW);
+ draw->widget.resize = <k_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