URI: 
       tAdd 'destroy' command; make grid sticky format more readable - 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
       ---
   DIR commit 87d191828a7c0dd4eec3f61dc07fd40768a45f8c
   DIR parent 700df2e2739b914fa2738404f949f791814cf7d4
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Thu,  7 Jan 2021 20:41:23 +0100
       
       Add 'destroy' command; make grid sticky format more readable
       
       Diffstat:
         M grid.c                              |      29 +++++++++++++++++++++++------
         M ltk.h                               |      16 +++++++++-------
         M ltkd.c                              |      45 +++++++++++++++++++++++++++++++
         M test.gui                            |       4 ++--
         M test.sh                             |       8 ++++++--
       
       5 files changed, 85 insertions(+), 17 deletions(-)
       ---
   DIR diff --git a/grid.c b/grid.c
       t@@ -111,6 +111,7 @@ ltk_grid_create(ltk_window *window, const char *id, int rows, int columns) {
                grid->widget.mouse_release = &ltk_grid_mouse_release;
                grid->widget.motion_notify = &ltk_grid_motion_notify;
                grid->widget.resize = &ltk_recalculate_grid;
       +        grid->widget.remove_child = &ltk_grid_ungrid;
        
                grid->rows = rows;
                grid->columns = columns;
       t@@ -258,6 +259,7 @@ ltk_recalculate_grid(ltk_grid *grid) {
                }
        }
        
       +/* FIXME: Check if widget already exists at position */
        static int
        ltk_grid_add(ltk_window *window, ltk_widget *widget, ltk_grid *grid,
            int row, int column, int row_span, int column_span, unsigned short sticky, char **errstr) {
       t@@ -376,18 +378,22 @@ ltk_grid_cmd_add(
            char **tokens,
            size_t num_tokens,
            char **errstr) {
       +        const char *c;
                ltk_grid *grid;
                ltk_widget *widget;
                const char *errstr_num;
        
       -        int row, column, row_span, column_span, sticky;
       +        int row, column, row_span, column_span, sticky = 0;
                if (num_tokens != 9) {
                        *errstr = "Invalid number of arguments.\n";
                        return 1;
                }
                grid = ltk_get_widget(tokens[1], LTK_GRID, errstr);
                widget = ltk_get_widget(tokens[3], LTK_WIDGET, errstr);
       -        if (!grid || !widget) return 1;
       +        if (!grid || !widget) {
       +                *errstr = "Invalid widget ID.\n";
       +                return 1;
       +        }
                row         = strtonum(tokens[4], 0, grid->rows - 1, &errstr_num);
                if (errstr_num) {
                        *errstr = "Invalid row number.\n";
       t@@ -408,11 +414,22 @@ ltk_grid_cmd_add(
                        *errstr = "Invalid column span.\n";
                        return 1;
                }
       -        sticky      = strtonum(tokens[8], 0, 15, &errstr_num);
       -        if (errstr_num) {
       -                *errstr = "Invalid sticky setting.\n";
       -                return 1;
       +
       +        for (c = tokens[8]; *c != '\0'; c++) {
       +                if (*c == 'n') {
       +                        sticky |= LTK_STICKY_TOP;
       +                } else if (*c == 's') {
       +                        sticky |= LTK_STICKY_BOTTOM;
       +                } else if (*c == 'e') {
       +                        sticky |= LTK_STICKY_RIGHT;
       +                } else if (*c == 'w') {
       +                        sticky |= LTK_STICKY_LEFT;
       +                } else if (*c != 'u') {
       +                        *errstr = "Invalid sticky specification.\n";
       +                        return 1;
       +                }
                }
       +
                return ltk_grid_add(window, widget, grid, row, column, row_span, column_span, sticky, errstr);
        }
        
   DIR diff --git a/ltk.h b/ltk.h
       t@@ -70,19 +70,21 @@ typedef struct ltk_widget {
                struct ltk_widget *parent;
                char *id;
        
       -        void (*key_press) (void *, XEvent event);
       -        void (*key_release) (void *, XEvent event);
       -        void (*mouse_press) (void *, XEvent event);
       -        void (*mouse_release) (void *, XEvent event);
       -        void (*motion_notify) (void *, XEvent event);
       -        void (*mouse_leave) (void *, XEvent event);
       -        void (*mouse_enter) (void *, XEvent event);
       +        void (*key_press) (void *, XEvent);
       +        void (*key_release) (void *, XEvent);
       +        void (*mouse_press) (void *, XEvent);
       +        void (*mouse_release) (void *, XEvent);
       +        void (*motion_notify) (void *, XEvent);
       +        void (*mouse_leave) (void *, XEvent);
       +        void (*mouse_enter) (void *, XEvent);
        
                void (*resize) (void *, int, int);
                void (*draw) (void *);
                void (*change_state) (void *);
                void (*destroy) (void *, int);
        
       +        int (*remove_child) (ltk_window *, void *, void *, char **);
       +
                ltk_widget_type type;
                ltk_widget_state state;
                unsigned int sticky;
   DIR diff --git a/ltkd.c b/ltkd.c
       t@@ -1,5 +1,6 @@
        /* FIXME: error checking in tokenizer (is this necessary?) */
        /* FIXME: parsing doesn't work properly with bs? */
       +/* FIXME: strip whitespace at end of lines in socket format */
        /*
         * This file is part of the Lumidify ToolKit (LTK)
         * Copyright (c) 2016, 2017, 2018, 2020, 2021 lumidify <nobody@lumidify.org>
       t@@ -111,6 +112,7 @@ static ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2);
        static int add_client(int fd);
        static int listen_sock(const char *sock_path);
        static int accept_sock(int listenfd);
       +static int ltk_widget_destroy(ltk_window *window, char **tokens, size_t num_tokens, char **errstr);
        
        static short maxsocket = -1;
        static short running = 1;
       t@@ -753,6 +755,7 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
                widget->draw = draw;
                widget->change_state = change_state;
                widget->destroy = destroy;
       +        widget->remove_child = NULL;
        
                widget->needs_redraw = needs_redraw;
                widget->state = LTK_NORMAL;
       t@@ -1151,6 +1154,8 @@ process_commands(ltk_window *window, struct ltk_sock_info *sock) {
                                err = ltk_draw_cmd(window, tokens, num_tokens, &errstr);
                        } else if (strcmp(tokens[0], "quit") == 0) {
                                ltk_quit(window);
       +                } else if (strcmp(tokens[0], "destroy") == 0) {
       +                        err = ltk_widget_destroy(window, tokens, num_tokens, &errstr);
                        } else {
                                errstr = "Invalid command.\n";
                                err = 1;
       t@@ -1175,3 +1180,43 @@ process_commands(ltk_window *window, struct ltk_sock_info *sock) {
                        sock->read_cur = 0;
                }
        }
       +
       +static int
       +ltk_widget_destroy(
       +    ltk_window *window,
       +    char **tokens,
       +    size_t num_tokens,
       +    char **errstr) {
       +        int err = 0, shallow = 1;
       +        khint_t k;
       +        if (num_tokens != 2 && num_tokens != 3) {
       +                *errstr = "Invalid number of arguments.\n";
       +                return 1;
       +        }
       +        if (num_tokens == 3) {
       +                if (strcmp(tokens[2], "deep") == 0) {
       +                        shallow = 0;
       +                } else if (strcmp(tokens[2], "shallow") == 0) {
       +                        shallow = 1;
       +                } else {
       +                        *errstr = "Invalid argument: must be 'shallow' or 'deep'.\n";
       +                        return 1;
       +                }
       +        }
       +        ltk_widget *widget = ltk_get_widget(tokens[1], LTK_WIDGET, errstr);
       +        if (!widget) {
       +                *errstr = "Invalid widget ID.\n";
       +                return 1;
       +        }
       +        ltk_remove_widget(tokens[1]);
       +        /* widget->parent->remove_child should never be NULl because of the fact that
       +           the widget is set as parent, but let's just check anyways... */
       +        if (widget->parent && widget->parent->remove_child) {
       +                err = widget->parent->remove_child(
       +                    window, widget, widget->parent, errstr
       +                );
       +        }
       +        widget->destroy(widget, shallow);
       +
       +        return err;
       +}
   DIR diff --git a/test.gui b/test.gui
       t@@ -5,6 +5,6 @@ grid grd1 set-column-weight 0 1
        grid grd1 set-column-weight 1 1
        set-root-widget grd1
        button btn1 create "I'm a button!"
       -grid grd1 add btn1 0 0 1 1 0
       +grid grd1 add btn1 0 0 1 1 u
        button btn2 create "I'm also a button!"
       -grid grd1 add btn2 1 0 1 2 3
       +grid grd1 add btn2 1 0 1 2 ew
   DIR diff --git a/test.sh b/test.sh
       t@@ -16,7 +16,11 @@ fi
        cat test.gui | ./ltkc $ltk_id | while read cmd
        do
                case "$cmd" in
       -        "btn1 button_click") echo "quit"
       -        ;;
       +        "btn1 button_click")
       +                echo "quit"
       +                ;;
       +        *)
       +                printf "%s\n" "$cmd" >&2
       +                ;;
                esac
        done | ./ltkc $ltk_id