URI: 
       widget.c - ltk - GUI toolkit for X11 (WIP)
  HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/ltk.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/ltk.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       widget.c (17833B)
       ---
            1 /*
            2  * Copyright (c) 2021-2024 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 <string.h>
           18 
           19 #include "khash.h"
           20 
           21 #include "err.h"
           22 #include "ltkd.h"
           23 #include "widget.h"
           24 #include "proto_types.h"
           25 
           26 #include <ltk/ltk.h>
           27 #include <ltk/event.h>
           28 #include <ltk/eventdefs.h>
           29 #include <ltk/memory.h>
           30 #include <ltk/rect.h>
           31 #include <ltk/util.h>
           32 
           33 KHASH_MAP_INIT_STR(widget, ltkd_widget *)
           34 static khash_t(widget) *widget_hash = NULL;
           35 /* Hack to make ltkd_destroy_widget_hash work */
           36 /* FIXME: any better way to do this? */
           37 static int hash_locked = 0;
           38 
           39 static int ltkd_widget_button_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
           40 static int ltkd_widget_motion_notify(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
           41 static int ltkd_widget_scroll_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
           42 static int ltkd_widget_resize(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
           43 static int ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg);
           44 
           45 /* FIXME: generic event handling functions that take the actual uint32_t event type, etc. */
           46 int
           47 ltkd_widget_queue_specific_event(ltkd_widget *widget, const char *type, uint32_t mask, const char *data) {
           48         for (size_t i = 0; i < widget->masks_num; i++) {
           49                 if (widget->event_masks[i].lwmask & mask) {
           50                         ltkd_queue_sock_write_fmt(
           51                             widget->event_masks[i].client,
           52                             "eventl %s %s %s\n", widget->id, type, data
           53                         );
           54                         if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
           55                                 return 1;
           56                 } else if (widget->event_masks[i].wmask & mask) {
           57                         ltkd_queue_sock_write_fmt(
           58                             widget->event_masks[i].client,
           59                             "event %s %s %s\n", widget->id, type, data
           60                         );
           61                 }
           62         }
           63         return 0;
           64 }
           65 
           66 static int
           67 ltkd_widget_resize(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
           68         (void)widget_unused;
           69         (void)args;
           70         ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
           71         for (size_t i = 0; i < widget->masks_num; i++) {
           72                 if (widget->event_masks[i].lmask & LTKD_PEVENTMASK_RESIZE) {
           73                         ltkd_queue_sock_write_fmt(
           74                             widget->event_masks[i].client,
           75                             "eventl %s widget configure %d %d %d %d\n",
           76                             widget->id, widget->widget->lrect.x, widget->widget->lrect.y,
           77                             widget->widget->lrect.w, widget->widget->lrect.h
           78                         );
           79                         if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
           80                                 return 1;
           81                 } else if (widget->event_masks[i].mask & LTKD_PEVENTMASK_RESIZE) {
           82                         ltkd_queue_sock_write_fmt(
           83                             widget->event_masks[i].client,
           84                             "event %s widget configure %d %d %d %d\n",
           85                             widget->id, widget->widget->lrect.x, widget->widget->lrect.y,
           86                             widget->widget->lrect.w, widget->widget->lrect.h
           87                         );
           88                 }
           89         }
           90         return 0;
           91 }
           92 
           93 static int
           94 ltkd_widget_change_state(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
           95         (void)widget_unused;
           96         (void)args;
           97         ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
           98         /* FIXME: give old and new state in event */
           99         for (size_t i = 0; i < widget->masks_num; i++) {
          100                 if (widget->event_masks[i].lmask & LTKD_PEVENTMASK_STATECHANGE) {
          101                         ltkd_queue_sock_write_fmt(
          102                             widget->event_masks[i].client,
          103                             "eventl %s widget statechange\n", widget->id
          104                         );
          105                         if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
          106                                 return 1;
          107                 } else if (widget->event_masks[i].mask & LTKD_PEVENTMASK_STATECHANGE) {
          108                         ltkd_queue_sock_write_fmt(
          109                             widget->event_masks[i].client,
          110                             "event %s widget statechange\n", widget->id
          111                         );
          112                 }
          113         }
          114         return 0;
          115 }
          116 
          117 static void
          118 ltkd_destroy_widget_hash(void) {
          119         hash_locked = 1;
          120         khint_t k;
          121         ltkd_widget *ptr;
          122         for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) {
          123                 if (kh_exist(widget_hash, k)) {
          124                         ptr = kh_value(widget_hash, k);
          125                         ltk_free((char *)kh_key(widget_hash, k));
          126                         ltkd_widget_destroy(ptr, 1);
          127                 }
          128         }
          129         kh_destroy(widget, widget_hash);
          130         widget_hash = NULL;
          131         hash_locked = 0;
          132 }
          133 
          134 void
          135 ltkd_widgets_cleanup(void) {
          136         if (widget_hash)
          137                 ltkd_destroy_widget_hash();
          138 }
          139 
          140 static client_event_mask *
          141 get_mask_struct(ltkd_widget *widget, int client) {
          142         for (size_t i = 0; i < widget->masks_num; i++) {
          143                 if (widget->event_masks[i].client == client)
          144                         return &widget->event_masks[i];
          145         }
          146         widget->masks_alloc = ideal_array_size(widget->masks_alloc, widget->masks_num + 1);
          147         widget->event_masks = ltk_reallocarray(widget->event_masks, widget->masks_alloc, sizeof(client_event_mask));
          148         client_event_mask *m = &widget->event_masks[widget->masks_num];
          149         widget->masks_num++;
          150         m->client = client;
          151         m->mask = m->lmask = m->wmask = m->lwmask = 0;
          152         return m;
          153 }
          154 
          155 static ltkd_event_handler widget_handlers[] = {
          156         {&ltkd_widget_button_event, LTK_WIDGET_SIGNAL_MOUSE_PRESS},
          157         {&ltkd_widget_button_event, LTK_WIDGET_SIGNAL_MOUSE_RELEASE},
          158         {&ltkd_widget_motion_notify, LTK_WIDGET_SIGNAL_MOTION_NOTIFY},
          159         {&ltkd_widget_scroll_event, LTK_WIDGET_SIGNAL_MOUSE_SCROLL},
          160         {NULL, LTK_WIDGET_SIGNAL_KEY_PRESS}, /* FIXME: add key press here */
          161         {NULL, LTK_WIDGET_SIGNAL_KEY_RELEASE},
          162         {&ltkd_widget_resize, LTK_WIDGET_SIGNAL_RESIZE},
          163         {&ltkd_widget_change_state, LTK_WIDGET_SIGNAL_CHANGE_STATE},
          164 };
          165 
          166 static uint32_t
          167 get_widget_mask(ltkd_widget *widget) {
          168         uint32_t cur_mask = 0;
          169         for (size_t i = 0; i < widget->masks_num; i++) {
          170                 cur_mask |= widget->event_masks[i].mask;
          171                 cur_mask |= widget->event_masks[i].lmask;
          172         }
          173         return cur_mask;
          174 }
          175 
          176 static uint32_t
          177 get_widget_special_mask(ltkd_widget *widget) {
          178         uint32_t cur_mask = 0;
          179         for (size_t i = 0; i < widget->masks_num; i++) {
          180                 cur_mask |= widget->event_masks[i].wmask;
          181                 cur_mask |= widget->event_masks[i].lwmask;
          182         }
          183         return cur_mask;
          184 }
          185 
          186 static void
          187 set_event_handlers(ltkd_widget *widget, uint32_t before, uint32_t after, ltkd_event_handler *handlers, size_t num_handlers) {
          188         for (size_t i = 0; i < num_handlers; i++) {
          189                 if (!(before & 1) && (after & 1)) {
          190                         if (handlers[i].callback) {
          191                                 ltk_widget_register_signal_handler(
          192                                         widget->widget, handlers[i].type,
          193                                         handlers[i].callback, LTK_MAKE_ARG_VOIDP(widget)
          194                                 );
          195                         }
          196                 } else if ((before & 1) && !(after & 1)) {
          197                         ltk_widget_remove_signal_handler_by_callback(widget->widget, handlers[i].callback);
          198                 }
          199                 before >>= 1;
          200                 after >>= 1;
          201         }
          202 }
          203 
          204 static void
          205 ltkd_widget_set_event_handlers(ltkd_widget *widget, uint32_t *set_mask, uint32_t new_mask) {
          206         uint32_t before = get_widget_mask(widget);
          207         *set_mask = new_mask;
          208         uint32_t after = get_widget_mask(widget);
          209         set_event_handlers(widget, before, after, widget_handlers, LENGTH(widget_handlers));
          210 }
          211 
          212 static void
          213 ltkd_widget_set_special_event_handlers(ltkd_widget *widget, uint32_t *set_mask, uint32_t new_mask) {
          214         uint32_t before = get_widget_special_mask(widget);
          215         *set_mask = new_mask;
          216         uint32_t after = get_widget_special_mask(widget);
          217         set_event_handlers(widget, before, after, widget->event_handlers, widget->num_event_handlers);
          218 }
          219 
          220 void
          221 ltkd_widget_set_event_mask(ltkd_widget *widget, int client, uint32_t mask) {
          222         client_event_mask *m = get_mask_struct(widget, client);
          223         ltkd_widget_set_event_handlers(widget, &m->mask, mask);
          224 }
          225 
          226 void
          227 ltkd_widget_set_event_lmask(ltkd_widget *widget, int client, uint32_t mask) {
          228         client_event_mask *m = get_mask_struct(widget, client);
          229         ltkd_widget_set_event_handlers(widget, &m->lmask, mask);
          230 }
          231 
          232 void
          233 ltkd_widget_set_event_wmask(ltkd_widget *widget, int client, uint32_t mask) {
          234         client_event_mask *m = get_mask_struct(widget, client);
          235         ltkd_widget_set_special_event_handlers(widget, &m->wmask, mask);
          236 }
          237 
          238 void
          239 ltkd_widget_set_event_lwmask(ltkd_widget *widget, int client, uint32_t mask) {
          240         client_event_mask *m = get_mask_struct(widget, client);
          241         ltkd_widget_set_special_event_handlers(widget, &m->lwmask, mask);
          242 }
          243 
          244 void
          245 ltkd_widget_add_to_event_mask(ltkd_widget *widget, int client, uint32_t mask) {
          246         client_event_mask *m = get_mask_struct(widget, client);
          247         ltkd_widget_set_event_handlers(widget, &m->mask, m->mask | mask);
          248 }
          249 
          250 void
          251 ltkd_widget_add_to_event_lmask(ltkd_widget *widget, int client, uint32_t mask) {
          252         client_event_mask *m = get_mask_struct(widget, client);
          253         ltkd_widget_set_event_handlers(widget, &m->lmask, m->lmask | mask);
          254 }
          255 
          256 void
          257 ltkd_widget_add_to_event_wmask(ltkd_widget *widget, int client, uint32_t mask) {
          258         client_event_mask *m = get_mask_struct(widget, client);
          259         ltkd_widget_set_special_event_handlers(widget, &m->wmask, m->wmask | mask);
          260 }
          261 
          262 void
          263 ltkd_widget_add_to_event_lwmask(ltkd_widget *widget, int client, uint32_t mask) {
          264         client_event_mask *m = get_mask_struct(widget, client);
          265         ltkd_widget_set_special_event_handlers(widget, &m->lwmask, m->lwmask | mask);
          266 }
          267 
          268 void
          269 ltkd_widget_remove_from_event_mask(ltkd_widget *widget, int client, uint32_t mask) {
          270         client_event_mask *m = get_mask_struct(widget, client);
          271         ltkd_widget_set_event_handlers(widget, &m->mask, m->mask & ~mask);
          272 }
          273 
          274 void
          275 ltkd_widget_remove_from_event_lmask(ltkd_widget *widget, int client, uint32_t mask) {
          276         client_event_mask *m = get_mask_struct(widget, client);
          277         ltkd_widget_set_event_handlers(widget, &m->lmask, m->lmask & ~mask);
          278 }
          279 
          280 void
          281 ltkd_widget_remove_from_event_wmask(ltkd_widget *widget, int client, uint32_t mask) {
          282         client_event_mask *m = get_mask_struct(widget, client);
          283         ltkd_widget_set_special_event_handlers(widget, &m->wmask, m->wmask & ~mask);
          284 }
          285 
          286 void
          287 ltkd_widget_remove_from_event_lwmask(ltkd_widget *widget, int client, uint32_t mask) {
          288         client_event_mask *m = get_mask_struct(widget, client);
          289         ltkd_widget_set_special_event_handlers(widget, &m->lwmask, m->lwmask & ~mask);
          290 }
          291 
          292 /* FIXME: any way to optimize the whole event mask handling a bit? */
          293 void
          294 ltkd_widget_remove_client(int client) {
          295         khint_t k;
          296         ltkd_widget *ptr;
          297         for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) {
          298                 if (kh_exist(widget_hash, k)) {
          299                         ptr = kh_value(widget_hash, k);
          300                         for (size_t i = 0; i < ptr->masks_num; i++) {
          301                                 if (ptr->event_masks[i].client == client) {
          302                                         uint32_t before = get_widget_mask(ptr);
          303                                         uint32_t befores = get_widget_special_mask(ptr);
          304                                         memmove(ptr->event_masks + i, ptr->event_masks + i + 1, ptr->masks_num - i - 1);
          305                                         ptr->masks_num--;
          306                                         /* FIXME: maybe reset to NULL in that case? */
          307                                         if (ptr->masks_num > 0) {
          308                                                 size_t sz = ideal_array_size(ptr->masks_alloc, ptr->masks_num);
          309                                                 if (sz != ptr->masks_alloc) {
          310                                                         ptr->masks_alloc = sz;
          311                                                         ptr->event_masks = ltk_reallocarray(ptr->event_masks, sz, sizeof(client_event_mask));
          312                                                 }
          313                                         }
          314                                         uint32_t after = get_widget_mask(ptr);
          315                                         uint32_t afters = get_widget_special_mask(ptr);
          316                                         set_event_handlers(ptr, before, after, widget_handlers, LENGTH(widget_handlers));
          317                                         set_event_handlers(ptr, befores, afters, ptr->event_handlers, ptr->num_event_handlers);
          318                                         break;
          319                                 }
          320                         }
          321                 }
          322         }
          323 }
          324 
          325 void
          326 ltkd_widgets_init() {
          327         widget_hash = kh_init(widget);
          328         if (!widget_hash) ltkd_fatal_errno("Unable to initialize widget hash table.\n");
          329 }
          330 
          331 /* FIXME: fix global and local coordinates! */
          332 static int
          333 queue_mouse_event(ltkd_widget *widget, ltk_event_type type, int x, int y) {
          334         uint32_t mask;
          335         char *typename;
          336         switch (type) {
          337         case LTK_MOTION_EVENT:
          338                 mask = LTKD_PEVENTMASK_MOUSEMOTION;
          339                 typename = "mousemotion";
          340                 break;
          341         case LTK_2BUTTONPRESS_EVENT:
          342                 mask = LTKD_PEVENTMASK_MOUSEPRESS;
          343                 typename = "2mousepress";
          344                 break;
          345         case LTK_3BUTTONPRESS_EVENT:
          346                 mask = LTKD_PEVENTMASK_MOUSEPRESS;
          347                 typename = "3mousepress";
          348                 break;
          349         case LTK_BUTTONRELEASE_EVENT:
          350                 mask = LTKD_PEVENTMASK_MOUSERELEASE;
          351                 typename = "mouserelease";
          352                 break;
          353         case LTK_2BUTTONRELEASE_EVENT:
          354                 mask = LTKD_PEVENTMASK_MOUSERELEASE;
          355                 typename = "2mouserelease";
          356                 break;
          357         case LTK_3BUTTONRELEASE_EVENT:
          358                 mask = LTKD_PEVENTMASK_MOUSERELEASE;
          359                 typename = "3mouserelease";
          360                 break;
          361         case LTK_BUTTONPRESS_EVENT:
          362         default:
          363                 mask = LTKD_PEVENTMASK_MOUSEPRESS;
          364                 typename = "mousepress";
          365                 break;
          366         }
          367         for (size_t i = 0; i < widget->masks_num; i++) {
          368                 if (widget->event_masks[i].lmask & mask) {
          369                         ltkd_queue_sock_write_fmt(
          370                             widget->event_masks[i].client,
          371                             "eventl %s widget %s %d %d %d %d\n",
          372                             widget->id, typename, x, y, x, y
          373                             /* x - widget->rect.x, y - widget->rect.y */
          374                         );
          375                         if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
          376                                 return 1;
          377                 } else if (widget->event_masks[i].mask & mask) {
          378                         ltkd_queue_sock_write_fmt(
          379                             widget->event_masks[i].client,
          380                             "event %s widget %s %d %d %d %d\n",
          381                             widget->id, typename, x, y, x, y
          382                             /* x - widget->rect.x, y - widget->rect.y */
          383                         );
          384                 }
          385         }
          386         return 0;
          387 }
          388 
          389 static int
          390 ltkd_widget_button_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
          391         (void)widget_unused;
          392         ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
          393         ltk_button_event *event = LTK_GET_ARG_BUTTON_EVENT(args, 0);
          394         return queue_mouse_event(widget, event->type, event->x, event->y);
          395 }
          396 
          397 static int
          398 ltkd_widget_motion_notify(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
          399         (void)widget_unused;
          400         ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
          401         ltk_motion_event *event = LTK_GET_ARG_MOTION_EVENT(args, 0);
          402         return queue_mouse_event(widget, event->type, event->x, event->y);
          403 }
          404 
          405 /* FIXME: global/local coords (like above) */
          406 static int
          407 ltkd_widget_scroll_event(ltk_widget *widget_unused, ltk_callback_arglist args, ltk_callback_arg arg) {
          408         (void)widget_unused;
          409         ltk_scroll_event *event = LTK_GET_ARG_SCROLL_EVENT(args, 0);
          410         ltkd_widget *widget = LTK_CAST_ARG_VOIDP(arg);
          411         uint32_t mask = LTKD_PEVENTMASK_MOUSESCROLL;
          412         for (size_t i = 0; i < widget->masks_num; i++) {
          413                 if (widget->event_masks[i].lmask & mask) {
          414                         ltkd_queue_sock_write_fmt(
          415                             widget->event_masks[i].client,
          416                             "eventl %s widget %s %d %d %d %d %d %d\n",
          417                             widget->id, "mousescroll", event->x, event->y, event->x, event->y, event->dx, event->dy
          418                             /* x - widget->widget->rect.x, y - widget->widget->rect.y */
          419                         );
          420                         if (ltkd_handle_lock_client(widget->widget->window, widget->event_masks[i].client))
          421                                 return 1;
          422                 } else if (widget->event_masks[i].mask & mask) {
          423                         ltkd_queue_sock_write_fmt(
          424                             widget->event_masks[i].client,
          425                             "event %s widget %s %d %d %d %d %d %d\n",
          426                             widget->id, "mousescroll", event->x, event->y, event->x, event->y, event->dx, event->dy
          427                             /* x - widget->widget->rect.x, y - widget->widget->rect.y */
          428                         );
          429                 }
          430         }
          431         return 0;
          432 }
          433 
          434 static int
          435 ltkd_widget_id_free(const char *id) {
          436         khint_t k;
          437         k = kh_get(widget, widget_hash, id);
          438         if (k != kh_end(widget_hash)) {
          439                 return 0;
          440         }
          441         return 1;
          442 }
          443 
          444 ltkd_widget *
          445 ltkd_get_widget(const char *id, ltk_widget_type type, ltkd_error *err) {
          446         khint_t k;
          447         ltkd_widget *widget;
          448         k = kh_get(widget, widget_hash, id);
          449         if (k == kh_end(widget_hash)) {
          450                 err->type = ERR_INVALID_WIDGET_ID;
          451                 return NULL;
          452         }
          453         widget = kh_value(widget_hash, k);
          454         if (type != LTK_WIDGET_UNKNOWN && widget->widget->vtable->type != type) {
          455                 err->type = ERR_INVALID_WIDGET_TYPE;
          456                 return NULL;
          457         }
          458         return widget;
          459 }
          460 
          461 static void
          462 ltkd_set_widget(ltkd_widget *widget, const char *id) {
          463         int ret;
          464         khint_t k;
          465         /* FIXME: make sure no widget is overwritten here */
          466         char *tmp = ltk_strdup(id);
          467         k = kh_put(widget, widget_hash, tmp, &ret);
          468         kh_value(widget_hash, k) = widget;
          469 }
          470 
          471 static void
          472 ltkd_remove_widget(const char *id) {
          473         if (hash_locked)
          474                 return;
          475         khint_t k;
          476         k = kh_get(widget, widget_hash, id);
          477         if (k != kh_end(widget_hash)) {
          478                 ltk_free((char *)kh_key(widget_hash, k));
          479                 kh_del(widget, widget_hash, k);
          480         }
          481 }
          482 
          483 ltkd_widget *
          484 ltkd_widget_create(
          485     ltk_widget *widget, const char *id,
          486     ltkd_event_handler *event_handlers, size_t num_event_handlers, ltkd_error *err){
          487         if (!ltkd_widget_id_free(id)) {
          488                 err->type = ERR_WIDGET_ID_IN_USE;
          489                 return NULL;
          490         }
          491         ltkd_widget *w = ltk_malloc(sizeof(ltkd_widget));
          492         w->widget = widget;
          493         w->id = ltk_strdup(id);
          494         w->event_masks = NULL;
          495         w->masks_num = w->masks_alloc = 0;
          496         w->event_handlers = event_handlers;
          497         w->num_event_handlers = num_event_handlers;
          498         ltkd_set_widget(w, id);
          499         return w;
          500 }
          501 
          502 void
          503 ltkd_widget_destroy(ltkd_widget *widget, int shallow) {
          504         ltkd_remove_widget(widget->id);
          505         ltk_free(widget->id);
          506         widget->id = NULL;
          507         ltk_free(widget->event_masks);
          508         widget->event_masks = NULL;
          509         ltk_widget_destroy(widget->widget, shallow);
          510         ltk_free(widget);
          511 }
          512 
          513 int
          514 ltkd_widget_destroy_cmd(
          515     ltk_window *window,
          516     ltkd_cmd_token *tokens,
          517     size_t num_tokens,
          518     ltkd_error *err) {
          519         (void)window;
          520         int shallow = 1;
          521         if (num_tokens != 2 && num_tokens != 3) {
          522                 err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS;
          523                 err->arg = -1;
          524                 return 1;
          525         }
          526         if (tokens[1].contains_nul) {
          527                 err->type = ERR_INVALID_ARGUMENT;
          528                 err->arg = 1;
          529                 return 1;
          530         } else if (num_tokens == 3 && tokens[2].contains_nul) {
          531                 err->type = ERR_INVALID_ARGUMENT;
          532                 err->arg = 2;
          533                 return 1;
          534         }
          535         if (num_tokens == 3) {
          536                 if (strcmp(tokens[2].text, "deep") == 0) {
          537                         shallow = 0;
          538                 } else if (strcmp(tokens[2].text, "shallow") == 0) {
          539                         shallow = 1;
          540                 } else {
          541                         err->type = ERR_INVALID_ARGUMENT;
          542                         err->arg = 2;
          543                         return 1;
          544                 }
          545         }
          546         ltkd_widget *widget = ltkd_get_widget(tokens[1].text, LTK_WIDGET_UNKNOWN, err);
          547         if (!widget) {
          548                 err->arg = 1;
          549                 return 1;
          550         }
          551         ltkd_widget_destroy(widget, shallow);
          552         return 0;
          553 }