URI: 
       tdraw.c - ltk - Socket-based GUI for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       tdraw.c (10979B)
       ---
            1 /*
            2  * Copyright (c) 2020 lumidify <nobody@lumidify.org>
            3  *
            4  * Permission to use, copy, modify, and/or distribute this software for any
            5  * purpose with or without fee is hereby granted, provided that the above
            6  * copyright notice and this permission notice appear in all copies.
            7  *
            8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
           10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
           15  */
           16 
           17 #include <stdio.h>
           18 #include <stdlib.h>
           19 #include <string.h>
           20 #include <stdarg.h>
           21 #include <stdint.h>
           22 
           23 #include <X11/Xlib.h>
           24 #include <X11/Xutil.h>
           25 
           26 #include "memory.h"
           27 #include "color.h"
           28 #include "rect.h"
           29 #include "widget.h"
           30 #include "ltk.h"
           31 #include "util.h"
           32 #include "draw.h"
           33 
           34 static void ltk_draw_draw(ltk_widget *self, ltk_rect clip_rect);
           35 static ltk_draw *ltk_draw_create(ltk_window *window,
           36     const char *id, int w, int h, const char *color);
           37 static void ltk_draw_resize(ltk_widget *self);
           38 static void ltk_draw_destroy(ltk_widget *self, int shallow);
           39 static void ltk_draw_clear(ltk_window *window, ltk_draw *draw);
           40 static void ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color);
           41 static void ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2);
           42 static void ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill);
           43 
           44 static struct ltk_widget_vtable vtable = {
           45         .draw = &ltk_draw_draw,
           46         .resize = &ltk_draw_resize,
           47         .destroy = &ltk_draw_destroy,
           48         .type = LTK_DRAW,
           49         .needs_redraw = 1,
           50         /* FIXME: use the widget pixmap here and store the drawn stuff
           51            logically as paths, not just on the pixmap */
           52         .needs_pixmap = 0
           53 };
           54 
           55 static int ltk_draw_cmd_clear(
           56     ltk_window *window,
           57     char **tokens,
           58     size_t num_tokens,
           59     char **errstr);
           60 static int ltk_draw_cmd_set_color(
           61     ltk_window *window,
           62     char **tokens,
           63     size_t num_tokens,
           64     char **errstr);
           65 static int ltk_draw_cmd_line(
           66     ltk_window *window,
           67     char **tokens,
           68     size_t num_tokens,
           69     char **errstr);
           70 static int ltk_draw_cmd_rect(
           71     ltk_window *window,
           72     char **tokens,
           73     size_t num_tokens,
           74     char **errstr);
           75 static int ltk_draw_cmd_create(
           76     ltk_window *window,
           77     char **tokens,
           78     size_t num_tokens,
           79     char **errstr);
           80 
           81 static void
           82 ltk_draw_draw(ltk_widget *self, ltk_rect clip_rect) {
           83         (void)clip_rect; /* FIXME: actually use this */
           84         ltk_draw *draw = (ltk_draw *)self;
           85         ltk_window *window = draw->widget.window;
           86         ltk_rect rect = draw->widget.rect;
           87         XCopyArea(window->dpy, draw->pix, window->drawable, window->gc, 0, 0, rect.w, rect.h, rect.x, rect.y);
           88 }
           89 
           90 
           91 static ltk_draw *
           92 ltk_draw_create(ltk_window *window, const char *id, int w, int h, const char *color) {
           93         ltk_draw *draw = ltk_malloc(sizeof(ltk_draw));
           94 
           95         ltk_fill_widget_defaults(&draw->widget, id, window, &vtable, w, h);
           96         draw->widget.rect.w = w;
           97         draw->widget.rect.h = h;
           98         draw->pix = XCreatePixmap(window->dpy, window->drawable, w, h, window->depth);
           99         if (!ltk_create_xcolor(window, color, &draw->bg)) {
          100                 ltk_free(draw);
          101                 ltk_fatal_errno("Unable to allocate XColor.\n");
          102         }
          103         draw->fg = draw->bg;
          104         XSetForeground(window->dpy, window->gc, draw->bg.pixel);
          105         XFillRectangle(window->dpy, draw->pix, window->gc, 0, 0, w, h);
          106 
          107         return draw;
          108 }
          109 
          110 static void
          111 ltk_draw_resize(ltk_widget *self) {
          112         ltk_draw *draw = (ltk_draw *)self;
          113         Window win;
          114         int x, y;
          115         unsigned int w, h, bw, d;
          116         unsigned int new_w, new_h;
          117         ltk_window *window = draw->widget.window;
          118         ltk_rect rect = draw->widget.rect;
          119         XGetGeometry(window->dpy, draw->pix, &win, &x, &y, &w, &h, &bw, &d);
          120 
          121         /* FIXME: get rid of casts */
          122         new_w = (int)w < rect.w ? rect.w : (int)w;
          123         new_h = (int)h < rect.h ? rect.h : (int)h;
          124         if (new_w < w && new_h < h)
          125                 return;
          126         Pixmap tmp = XCreatePixmap(window->dpy, window->drawable,
          127             new_w, new_h, window->depth);
          128         XSetForeground(window->dpy, window->gc, draw->bg.pixel);
          129         XFillRectangle(window->dpy, tmp, window->gc, 0, 0, new_w, new_h);
          130         XCopyArea(window->dpy, draw->pix, tmp, window->gc,
          131             0, 0, w, h, 0, 0);
          132         XFreePixmap(window->dpy, draw->pix);
          133         draw->pix = tmp;
          134 }
          135 
          136 static void
          137 ltk_draw_destroy(ltk_widget *self, int shallow) {
          138         (void)shallow;
          139         ltk_draw *draw = (ltk_draw *)self;
          140         if (!draw) {
          141                 ltk_warn("Tried to destroy NULL draw.\n");
          142                 return;
          143         }
          144         ltk_remove_widget(draw->widget.id);
          145         ltk_free(draw->widget.id);
          146         XFreePixmap(draw->widget.window->dpy, draw->pix);
          147         ltk_free(draw);
          148 }
          149 
          150 static void
          151 ltk_draw_clear(ltk_window *window, ltk_draw *draw) {
          152         Window win;
          153         int x, y;
          154         unsigned int w, h, bw, d;
          155         XGetGeometry(window->dpy, draw->pix, &win, &x, &y, &w, &h, &bw, &d);
          156         XSetForeground(window->dpy, window->gc, draw->bg.pixel);
          157         XFillRectangle(window->dpy, window->drawable, window->gc, 0, 0, w, h);
          158         ltk_draw_draw((ltk_widget *)draw, draw->widget.rect);
          159 }
          160 
          161 /* FIXME: Error return */
          162 static void
          163 ltk_draw_set_color(ltk_window *window, ltk_draw *draw, const char *color) {
          164         XColor tmp;
          165         if (ltk_create_xcolor(window, color, &tmp)) {
          166                 draw->fg = tmp;
          167         }
          168 }
          169 
          170 static void
          171 ltk_draw_line(ltk_window *window, ltk_draw *draw, int x1, int y1, int x2, int y2) {
          172         ltk_rect rect = draw->widget.rect;
          173         XSetForeground(window->dpy, window->gc, draw->fg.pixel);
          174         XSetLineAttributes(window->dpy, window->gc, 2, LineSolid, CapButt, JoinMiter);
          175         XDrawLine(window->dpy, draw->pix, window->gc, x1, y1, x2, y2);
          176         x1 += rect.x;
          177         y1 += rect.y;
          178         x2 += rect.x;
          179         y2 += rect.y;
          180         if (x1 > rect.x + rect.w) x1 = rect.x + rect.w;
          181         if (y1 > rect.y + rect.h) y1 = rect.y + rect.h;
          182         if (x2 > rect.x + rect.w) x2 = rect.x + rect.w;
          183         if (y2 > rect.y + rect.h) y2 = rect.y + rect.h;
          184         XDrawLine(window->dpy, window->drawable, window->gc, x1, y1, x2, y2);
          185 }
          186 
          187 static void
          188 ltk_draw_rect(ltk_window *window, ltk_draw *draw, int x, int y, int w, int h, int fill) {
          189         int x_win, y_win, w_win, h_win;
          190         ltk_rect rect = draw->widget.rect;
          191         XSetForeground(window->dpy, window->gc, draw->fg.pixel);
          192         x_win = x + rect.x;
          193         y_win = y + rect.y;
          194         w_win = x + w > rect.w ? rect.w - x : w;
          195         h_win = y + h > rect.h ? rect.h - y : h;
          196         if (fill) {
          197                 XFillRectangle(window->dpy, draw->pix, window->gc, x, y, w, h);
          198                 XFillRectangle(window->dpy, window->drawable, window->gc, x_win, y_win, w_win, h_win);
          199         } else {
          200                 XSetLineAttributes(window->dpy, window->gc, 2, LineSolid, CapButt, JoinMiter);
          201                 XDrawRectangle(window->dpy, draw->pix, window->gc, x, y, w, h);
          202                 XDrawRectangle(window->dpy, window->drawable, window->gc, x_win, y_win, w_win, h_win);
          203         }
          204 }
          205 
          206 static int
          207 ltk_draw_cmd_clear(
          208     ltk_window *window,
          209     char **tokens,
          210     size_t num_tokens,
          211     char **errstr) {
          212         ltk_draw *draw;
          213         if (num_tokens != 3) {
          214                 *errstr = "Invalid number of arguments.\n";
          215                 return 1;
          216         }
          217         draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
          218         if (!draw) return 1;
          219         ltk_draw_clear(window, draw);
          220 
          221         return 0;
          222 }
          223 
          224 static int
          225 ltk_draw_cmd_set_color(
          226     ltk_window *window,
          227     char **tokens,
          228     size_t num_tokens,
          229     char **errstr) {
          230         ltk_draw *draw;
          231         if (num_tokens != 4) {
          232                 *errstr = "Invalid number of arguments.\n";
          233                 return 1;
          234         }
          235         draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
          236         if (!draw) return 1;
          237         ltk_draw_set_color(window, draw, tokens[3]);
          238 
          239         return 0;
          240 }
          241 
          242 static int
          243 ltk_draw_cmd_line(
          244     ltk_window *window,
          245     char **tokens,
          246     size_t num_tokens,
          247     char **errstr) {
          248         ltk_draw *draw;
          249         int x1, y1, x2, y2;
          250         const char *errstr_num;
          251         if (num_tokens != 7) {
          252                 *errstr = "Invalid number of arguments.\n";
          253                 return 1;
          254         }
          255         draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
          256         if (!draw) return 1;
          257         x1 = ltk_strtonum(tokens[3], 0, 100000, &errstr_num);
          258         if (errstr_num) {
          259                 *errstr = "Invalid x1.\n";
          260                 return 1;
          261         }
          262         y1 = ltk_strtonum(tokens[4], 0, 100000, &errstr_num);
          263         if (errstr_num) {
          264                 *errstr = "Invalid y1.\n";
          265                 return 1;
          266         }
          267         x2 = ltk_strtonum(tokens[5], 0, 100000, &errstr_num);
          268         if (errstr_num) {
          269                 *errstr = "Invalid x2.\n";
          270                 return 1;
          271         }
          272         y2 = ltk_strtonum(tokens[6], 0, 100000, &errstr_num);
          273         if (errstr_num) {
          274                 *errstr = "Invalid y2.\n";
          275                 return 1;
          276         }
          277         ltk_draw_line(window, draw, x1, y1, x2, y2);
          278 
          279         return 0;
          280 }
          281 
          282 static int
          283 ltk_draw_cmd_rect(
          284     ltk_window *window,
          285     char **tokens,
          286     size_t num_tokens,
          287     char **errstr) {
          288         ltk_draw *draw;
          289         const char *errstr_num;
          290         int x, y, w, h, fill;
          291         if (num_tokens != 8) {
          292                 *errstr = "Invalid number of arguments.\n";
          293                 return 1;
          294         }
          295         draw = (ltk_draw *)ltk_get_widget(tokens[1], LTK_DRAW, errstr);
          296         if (!draw) return 1;
          297         x = ltk_strtonum(tokens[3], 0, 100000, &errstr_num);
          298         if (errstr_num) {
          299                 *errstr = "Invalid x.\n";
          300                 return 1;
          301         }
          302         y = ltk_strtonum(tokens[4], 0, 100000, &errstr_num);
          303         if (errstr_num) {
          304                 *errstr = "Invalid y.\n";
          305                 return 1;
          306         }
          307         w = ltk_strtonum(tokens[5], 1, 100000, &errstr_num);
          308         if (errstr_num) {
          309                 *errstr = "Invalid width.\n";
          310                 return 1;
          311         }
          312         h = ltk_strtonum(tokens[6], 1, 100000, &errstr_num);
          313         if (errstr_num) {
          314                 *errstr = "Invalid height.\n";
          315                 return 1;
          316         }
          317         fill = ltk_strtonum(tokens[7], 0, 1, &errstr_num);
          318         if (errstr_num) {
          319                 *errstr = "Invalid fill bool.\n";
          320                 return 1;
          321         }
          322         ltk_draw_rect(window, draw, x, y, w, h, fill);
          323 
          324         return 0;
          325 }
          326 
          327 /* draw <draw id> create <width> <height> <color> */
          328 static int
          329 ltk_draw_cmd_create(
          330     ltk_window *window,
          331     char **tokens,
          332     size_t num_tokens,
          333     char **errstr) {
          334         ltk_draw *draw;
          335         int w, h;
          336         const char *errstr_num;
          337         if (num_tokens != 6) {
          338                 *errstr = "Invalid number of arguments.\n";
          339                 return 1;
          340         }
          341         if (!ltk_widget_id_free(tokens[1])) {
          342                 *errstr = "Widget ID already taken.\n";
          343                 return 1;
          344         }
          345         w = ltk_strtonum(tokens[3], 1, 100000, &errstr_num);
          346         if (errstr_num) {
          347                 *errstr = "Invalid width.\n";
          348                 return 1;
          349         }
          350         h = ltk_strtonum(tokens[4], 1, 100000, &errstr_num);
          351         if (errstr_num) {
          352                 *errstr = "Invalid height.\n";
          353                 return 1;
          354         }
          355         draw = ltk_draw_create(window, tokens[1], w, h, tokens[5]);
          356         ltk_set_widget((ltk_widget *)draw, tokens[1]);
          357 
          358         return 0;
          359 }
          360 
          361 /* draw <draw id> <command> ... */
          362 int
          363 ltk_draw_cmd(
          364     ltk_window *window,
          365     char **tokens,
          366     size_t num_tokens,
          367     char **errstr) {
          368         if (num_tokens < 3) {
          369                 *errstr = "Invalid number of arguments.\n";
          370                 return 1;
          371         }
          372         if (strcmp(tokens[2], "create") == 0) {
          373                 return ltk_draw_cmd_create(window, tokens, num_tokens, errstr);
          374         } else if (strcmp(tokens[2], "clear") == 0) {
          375                 return ltk_draw_cmd_clear(window, tokens, num_tokens, errstr);
          376         } else if (strcmp(tokens[2], "set-color") == 0) {
          377                 return ltk_draw_cmd_set_color(window, tokens, num_tokens, errstr);
          378         } else if (strcmp(tokens[2], "line") == 0) {
          379                 return ltk_draw_cmd_line(window, tokens, num_tokens, errstr);
          380         } else if (strcmp(tokens[2], "rect") == 0) {
          381                 return ltk_draw_cmd_rect(window, tokens, num_tokens, errstr);
          382         } else {
          383                 *errstr = "Invalid command.\n";
          384                 return 1;
          385         }
          386 
          387         return 0;
          388 }