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