cmd.h (5382B)
1 /* 2 * Copyright (c) 2023-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 #ifndef LTKD_CMD_H 18 #define LTKD_CMD_H 19 20 #include <stddef.h> 21 22 #include "err.h" 23 #include "ltkd.h" 24 #include "widget.h" 25 26 #include <ltk/ltk.h> 27 #include <ltk/util.h> 28 #include <ltk/color.h> 29 #include <ltk/graphics.h> 30 31 typedef struct { 32 char *name; 33 int (*func)(ltk_window *, ltkd_widget *, ltkd_cmd_token *, size_t, ltkd_error *); 34 int needs_all; 35 } ltkd_cmd_info; 36 37 typedef enum { 38 CMDARG_IGNORE, 39 CMDARG_STRING, /* nul-terminated string */ 40 CMDARG_DATA, /* also char*, but may contain nul */ 41 CMDARG_COLOR, 42 CMDARG_INT, 43 CMDARG_BOOL, 44 CMDARG_BORDERSIDES, 45 CMDARG_STICKY, 46 CMDARG_WIDGET, 47 CMDARG_ORIENTATION 48 } ltkd_cmdarg_datatype; 49 50 /* color needs to be destroyed by cmd handling function if it is not needed anymore */ 51 /* str must *not* be freed because it is a pointer to the original argument 52 -> it must be copied if it needs to be kept around */ 53 typedef struct { 54 ltkd_cmdarg_datatype type; 55 /* Note: Bool and int are both integers, but they are 56 separate just to make it a bit clearer (same goes for str/data) */ 57 union { 58 char *str; 59 char *data; 60 ltk_color *color; 61 int i; 62 int b; 63 ltk_border_sides border; 64 ltk_sticky_mask sticky; 65 ltk_orientation orient; 66 ltkd_widget *widget; 67 } val; 68 size_t len; /* only for data */ 69 int min, max; /* only for integers */ /* FIXME: which integer type is sensible here? */ 70 ltk_widget_type widget_type; /* only for widgets */ 71 int optional; 72 int initialized; 73 } ltkd_cmdarg_parseinfo; 74 75 /* Returns 1 on error, 0 on success */ 76 /* All optional arguments must be in one block at the end */ 77 int ltkd_parse_cmd( 78 ltkd_cmd_token *tokens, size_t num_tokens, 79 ltkd_cmdarg_parseinfo *parseinfo, size_t num_arg, ltkd_error *err 80 ); 81 82 /* FIXME: This doesn't really need to be a macro anymore since it doesn't have to be generic for different types anymore */ 83 #define GEN_CMD_HELPERS_PROTO(func_name) \ 84 int func_name(ltk_window *window, ltkd_cmd_token *tokens, size_t num_tokens, ltkd_error *err); 85 86 87 #define GEN_CMD_HELPERS(func_name, widget_type, array_name) \ 88 /* FIXME: maybe just get rid of this and rely on array already being sorted? */ \ 89 static int array_name##_sorted = 0; \ 90 \ 91 static int \ 92 array_name##_search_helper(const void *keyv, const void *entryv) { \ 93 const char *key = (const char *)keyv; \ 94 ltkd_cmd_info *entry = (ltkd_cmd_info *)entryv; \ 95 return strcmp(key, entry->name); \ 96 } \ 97 \ 98 static int \ 99 array_name##_sort_helper(const void *entry1v, const void *entry2v) { \ 100 ltkd_cmd_info *entry1 = (ltkd_cmd_info *)entry1v; \ 101 ltkd_cmd_info *entry2 = (ltkd_cmd_info *)entry2v; \ 102 return strcmp(entry1->name, entry2->name); \ 103 } \ 104 \ 105 int \ 106 func_name( \ 107 ltk_window *window, \ 108 ltkd_cmd_token *tokens, \ 109 size_t num_tokens, \ 110 ltkd_error *err) { \ 111 if (num_tokens < 3) { \ 112 err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS; \ 113 err->arg = -1; \ 114 return 1; \ 115 } \ 116 /* just in case */ \ 117 if (!array_name##_sorted) { \ 118 qsort( \ 119 array_name, LENGTH(array_name), \ 120 sizeof(array_name[0]), &array_name##_sort_helper); \ 121 array_name##_sorted = 1; \ 122 } \ 123 if (tokens[1].contains_nul) { \ 124 err->type = ERR_INVALID_ARGUMENT; \ 125 err->arg = 1; \ 126 return 1; \ 127 } else if (tokens[2].contains_nul) { \ 128 err->type = ERR_INVALID_ARGUMENT; \ 129 err->arg = 2; \ 130 return 1; \ 131 } \ 132 ltkd_cmd_info *e = bsearch( \ 133 tokens[2].text, array_name, LENGTH(array_name), \ 134 sizeof(array_name[0]), &array_name##_search_helper \ 135 ); \ 136 if (!e) { \ 137 err->type = ERR_INVALID_COMMAND; \ 138 err->arg = -1; \ 139 return 1; \ 140 } \ 141 if (e->needs_all) { \ 142 return e->func(window, NULL, tokens, num_tokens, err); \ 143 } else { \ 144 ltkd_widget *widget = ltkd_get_widget(tokens[1].text, widget_type, err); \ 145 if (!widget) { \ 146 err->arg = 1; \ 147 return 1; \ 148 } \ 149 int ret = e->func(window, widget, tokens + 3, num_tokens - 3, err); \ 150 if (ret && err->arg >= 0) \ 151 err->arg += 3; \ 152 return ret; \ 153 } \ 154 return 0; /* Well, I guess this is impossible anyways... */ \ 155 } 156 157 #endif /* LTKD_CMD_H */