ltk

Socket-based GUI for X11 (WIP)
git clone git://lumidify.org/ltk.git (fast, but not encrypted)
git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 4f891285a531b95894eb8ecb397d91f84285bfaa
parent 7d8235cc4e3c173b19461ff5eaf5991a43f15994
Author: lumidify <nobody@lumidify.org>
Date:   Mon, 22 Feb 2021 22:05:56 +0100

Move some functions from ltkd.c to widget.c and rect.c

Diffstat:
MMakefile | 4++++
Msrc/box.c | 2++
Msrc/box.h | 2+-
Msrc/button.c | 2++
Msrc/button.h | 2+-
Msrc/draw.c | 2++
Msrc/draw.h | 2+-
Msrc/grid.c | 2++
Msrc/grid.h | 2+-
Msrc/label.c | 2++
Msrc/label.h | 2+-
Msrc/ltk.h | 96+------------------------------------------------------------------------------
Msrc/ltkd.c | 239++-----------------------------------------------------------------------------
Asrc/rect.c | 33+++++++++++++++++++++++++++++++++
Asrc/rect.h | 15+++++++++++++++
Msrc/scrollbar.c | 2++
Msrc/scrollbar.h | 2+-
Msrc/text_pango.c | 2++
Msrc/text_stb.c | 2++
Asrc/widget.c | 221+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/widget.h | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
21 files changed, 396 insertions(+), 335 deletions(-)

diff --git a/Makefile b/Makefile @@ -37,6 +37,8 @@ OBJ = \ src/util.o \ src/memory.o \ src/color.o \ + src/rect.o \ + src/widget.o \ src/ltkd.o \ src/ini.o \ src/grid.o \ @@ -59,6 +61,8 @@ HDR = \ src/ini.h \ src/khash.h \ src/label.h \ + src/rect.h \ + src/widget.h \ src/ltk.h \ src/memory.h \ src/scrollbar.h \ diff --git a/src/box.c b/src/box.c @@ -32,6 +32,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "scrollbar.h" diff --git a/src/box.h b/src/box.h @@ -24,7 +24,7 @@ #ifndef _LTK_BOX_H_ #define _LTK_BOX_H_ -/* Requires the following includes: "scrollbar.h" "ltk.h" */ +/* Requires the following includes: "scrollbar.h", "rect.h", "widget.h", "ltk.h" */ typedef struct { ltk_widget widget; diff --git a/src/button.c b/src/button.c @@ -32,6 +32,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "text.h" diff --git a/src/button.h b/src/button.h @@ -24,7 +24,7 @@ #ifndef _LTK_BUTTON_H_ #define _LTK_BUTTON_H_ -/* Requires the following includes: <X11/Xlib.h>, "ltk.h", "color.h", "text.h" */ +/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h", "color.h", "text.h" */ typedef struct { ltk_widget widget; diff --git a/src/draw.c b/src/draw.c @@ -32,6 +32,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "draw.h" diff --git a/src/draw.h b/src/draw.h @@ -24,7 +24,7 @@ #ifndef _LTK_DRAW_H_ #define _LTK_DRAW_H_ -/* Requires the following includes: <X11/Xlib.h>, "ltk.h" */ +/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h" */ typedef struct { ltk_widget widget; diff --git a/src/grid.c b/src/grid.c @@ -39,6 +39,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "grid.h" diff --git a/src/grid.h b/src/grid.h @@ -24,7 +24,7 @@ #ifndef _LTK_GRID_H_ #define _LTK_GRID_H_ -/* Requires the following includes: "ltk.h" */ +/* Requires the following includes: "rect.h", "widget.h", "ltk.h" */ /* * Struct to represent a grid widget. diff --git a/src/label.c b/src/label.c @@ -32,6 +32,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "text.h" diff --git a/src/label.h b/src/label.h @@ -24,7 +24,7 @@ #ifndef _LTK_LABEL_H_ #define _LTK_LABEL_H_ -/* Requires the following includes: <X11/Xlib.h>, "ltk.h", "color.h", "text.h" */ +/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h", "color.h", "text.h" */ typedef struct { ltk_widget widget; diff --git a/src/ltk.h b/src/ltk.h @@ -24,7 +24,7 @@ #ifndef _LTK_H_ #define _LTK_H_ -/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, <stdarg.h> */ +/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "widget.h" */ typedef enum { LTK_EVENT_RESIZE = 1 << 0, @@ -33,88 +33,6 @@ typedef enum { } ltk_event_type; typedef struct { - int x; - int y; - int w; - int h; -} ltk_rect; - -typedef enum { - LTK_STICKY_LEFT = 1 << 0, - LTK_STICKY_RIGHT = 1 << 1, - LTK_STICKY_TOP = 1 << 2, - LTK_STICKY_BOTTOM = 1 << 3 -} ltk_sticky_mask; - -typedef enum { - LTK_VERTICAL, - LTK_HORIZONTAL -} ltk_orientation; - -typedef enum { - LTK_NORMAL, - LTK_PRESSED, - LTK_ACTIVE, - LTK_DISABLED -} ltk_widget_state; - -typedef enum { - /* for e.g. scrollbar, which can't be directly accessed by users */ - LTK_UNKNOWN, - LTK_GRID, - LTK_BUTTON, - LTK_DRAW, - LTK_LABEL, - LTK_WIDGET, - LTK_BOX -} ltk_widget_type; - -struct ltk_window; - -struct ltk_widget_vtable; - -typedef struct ltk_widget { - struct ltk_window *window; - struct ltk_widget *active_widget; - struct ltk_widget *parent; - char *id; - - struct ltk_widget_vtable *vtable; - - ltk_rect rect; - unsigned int ideal_w; - unsigned int ideal_h; - - ltk_widget_type type; - ltk_widget_state state; - unsigned int sticky; - unsigned short row; - unsigned short column; - unsigned short row_span; - unsigned short column_span; - unsigned char needs_redraw; -} ltk_widget; - -struct ltk_widget_vtable { - void (*key_press) (struct ltk_widget *, XEvent); - void (*key_release) (struct ltk_widget *, XEvent); - int (*mouse_press) (struct ltk_widget *, XEvent); - int (*mouse_release) (struct ltk_widget *, XEvent); - int (*mouse_wheel) (struct ltk_widget *, XEvent); - int (*motion_notify) (struct ltk_widget *, XEvent); - void (*mouse_leave) (struct ltk_widget *, XEvent); - void (*mouse_enter) (struct ltk_widget *, XEvent); - - void (*resize) (struct ltk_widget *); - void (*draw) (struct ltk_widget *, ltk_rect); - void (*change_state) (struct ltk_widget *); - void (*destroy) (struct ltk_widget *, int); - - void (*child_size_change) (struct ltk_widget *, struct ltk_widget *); - int (*remove_child) (struct ltk_window *, struct ltk_widget *, struct ltk_widget *, char **); -}; - -typedef struct { int border_width; uint16_t font_size; char *font; @@ -158,23 +76,11 @@ typedef struct ltk_window { struct ltk_event_queue *last_event; } ltk_window; -ltk_rect ltk_rect_intersect(ltk_rect r1, ltk_rect r2); -ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2); void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect); int ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col); void ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data); -int ltk_collide_rect(ltk_rect rect, int x, int y); void ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget); void ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget); -void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window, - struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type); -void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event); -void ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event); -void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event); -int ltk_widget_id_free(const char *id); -ltk_widget *ltk_get_widget(const char *id, ltk_widget_type type, char **errstr); -void ltk_set_widget(ltk_widget *widget, const char *id); -void ltk_remove_widget(const char *id); void ltk_quit(ltk_window *window); #endif diff --git a/src/ltkd.c b/src/ltkd.c @@ -1,5 +1,5 @@ /* FIXME: backslashes should be parsed properly! */ -/* FIXME Figure out how to properly print window id */ +/* FIXME: Figure out how to properly print window id */ /* FIXME: PROPERLY CLEANUP ALL THEMES */ /* FIXME: error checking in tokenizer (is this necessary?) */ /* FIXME: parsing doesn't work properly with bs? */ @@ -51,6 +51,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "text.h" @@ -90,16 +92,12 @@ static struct ltk_sock_info { struct token_list tokens; /* current tokens */ } sockets[MAX_SOCK_CONNS]; -KHASH_MAP_INIT_STR(widget, ltk_widget *) -static khash_t(widget) *widget_hash = NULL; - static int ltk_mainloop(ltk_window *window); static char *get_sock_path(char *basedir, Window id); static FILE *open_log(char *dir); static void daemonize(void); static ltk_window *ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int h); -static void ltk_destroy_widget_hash(void); static void ltk_destroy_window(ltk_window *window); static void ltk_redraw_window(ltk_window *window); static void ltk_window_other_event(ltk_window *window, XEvent event); @@ -116,7 +114,6 @@ static void process_commands(ltk_window *window, struct ltk_sock_info *sock); 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; @@ -147,8 +144,7 @@ int main(int argc, char *argv[]) { ltk_logfile = open_log(ltk_dir); if (!ltk_logfile) ltk_fatal_errno("Unable to open log file.\n"); - widget_hash = kh_init(widget); - if (!widget_hash) ltk_fatal_errno("Unable to initialize widget hash table.\n"); + ltk_widgets_init(); /* FIXME: set window size properly - I only run it in a tiling WM anyways, so it doesn't matter, but still... */ @@ -386,8 +382,7 @@ ltk_cleanup(void) { ltk_free(sockets[i].tokens.tokens); } - if (widget_hash) - ltk_destroy_widget_hash(); + ltk_widgets_cleanup(); if (main_window) ltk_destroy_window(main_window); } @@ -438,32 +433,6 @@ ltk_set_root_widget_cmd( return 0; } -/* FIXME */ -#undef MAX -#undef MIN -#define MAX(a, b) ((a) > (b) ? (a) : (b)) -#define MIN(a, b) ((a) < (b) ? (a) : (b)) - -ltk_rect -ltk_rect_intersect(ltk_rect r1, ltk_rect r2) { - ltk_rect i; - i.x = MAX(r1.x, r2.x); - i.y = MAX(r1.y, r2.y); - i.w = MIN(r1.x + r1.w, r2.x + r2.w) - i.x; - i.h = MIN(r1.y + r1.h, r2.y + r2.h) - i.y; - return i; -} - -ltk_rect -ltk_rect_union(ltk_rect r1, ltk_rect r2) { - ltk_rect u; - u.x = MIN(r1.x, r2.x); - u.y = MIN(r1.y, r2.y); - u.w = MAX(r1.x + r1.w, r2.x + r2.w) - u.x; - u.h = MAX(r1.y + r1.h, r2.y + r2.h) - u.y; - return u; -} - void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) { if (window->dirty_rect.w == 0 && window->dirty_rect.h == 0) @@ -611,19 +580,6 @@ ltk_create_window(const char *title, int x, int y, unsigned int w, unsigned int } static void -ltk_destroy_widget_hash(void) { - khint_t k; - ltk_widget *ptr; - for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) { - if (kh_exist(widget_hash, k)) { - ptr = kh_value(widget_hash, k); - ptr->vtable->destroy(ptr, 1); - } - } - kh_destroy(widget, widget_hash); -} - -static void ltk_destroy_window(ltk_window *window) { XDestroyWindow(window->dpy, window->xwindow); ltk_cleanup_text(); @@ -691,52 +647,6 @@ ltk_load_theme(ltk_window *window, const char *path) { } } -int -ltk_collide_rect(ltk_rect rect, int x, int y) { - return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y - && (rect.y + rect.h) >= y); -} - -void -ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window, - struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type) { - if (id) - widget->id = ltk_strdup(id); - else - widget->id = NULL; - widget->window = window; - widget->active_widget = NULL; - widget->parent = NULL; - widget->type = type; - - /* FIXME: possibly check that draw and destroy aren't NULL */ - widget->vtable = vtable; - - widget->needs_redraw = needs_redraw; - widget->state = LTK_NORMAL; - widget->row = 0; - widget->rect.x = 0; - widget->rect.y = 0; - widget->rect.w = 0; - widget->rect.h = 0; - widget->ideal_w = 0; - widget->ideal_h = 0; - - widget->row = 0; - widget->column = 0; - widget->row_span = 0; - widget->column_span = 0; - widget->sticky = 0; -} - -static void -ltk_widget_change_state(ltk_widget *widget) { - if (widget->vtable->change_state) - widget->vtable->change_state(widget); - if (widget->needs_redraw) - ltk_window_invalidate_rect(widget->window, widget->rect); -} - void ltk_window_set_active_widget(ltk_window *window, ltk_widget *widget) { if (window->active_widget == widget) @@ -772,59 +682,6 @@ ltk_window_set_pressed_widget(ltk_window *window, ltk_widget *widget) { } } -void -ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) { - if (!widget || widget->state == LTK_DISABLED) - return; - int default_handler = 1; - if (widget->vtable->mouse_press) - default_handler = widget->vtable->mouse_press(widget, event); - if (default_handler) { - if (event.xbutton.button == 1) - ltk_window_set_pressed_widget(widget->window, widget); - } -} - -void -ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) { - if (!widget || widget->state == LTK_DISABLED) - return; - int default_handler = 1; - if (widget->vtable->mouse_release) - default_handler = widget->vtable->mouse_release(widget, event); - if (default_handler) - ltk_window_set_pressed_widget(widget->window, NULL); -} - -/* FIXME: ONLY SET ACTIVE WIDGET AT BOTTOM OF HIERARCHY - -> Don't first set parent as active widget and then child */ -void -ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) { - if (!widget || widget->state == LTK_DISABLED) - return; - /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */ - /* - if (((widget->state == LTK_NORMAL) || - (widget->state == LTK_ACTIVE && widget->window->active_widget != widget)) && - !widget->window->pressed_widget) { - widget->state = LTK_ACTIVE; - if (widget->vtable->change_state) - widget->change_statevtable->(widget); - if (widget->vtable->mouse_enter) - widget->mouse_entervtable->(widget, event); - ltk_window_remove_active_widget(widget->window); - ltk_window_set_active_widget(widget); - } - */ - int default_handler = 1; - if (widget->window->pressed_widget && widget->window->pressed_widget->vtable->motion_notify) - default_handler = widget->window->pressed_widget->vtable->motion_notify(widget->window->pressed_widget, event); - else if (widget->vtable->motion_notify) - default_handler = widget->vtable->motion_notify(widget, event); - if (default_handler) - ltk_window_set_active_widget(widget->window, widget); -} - static void ltk_handle_event(ltk_window *window, XEvent event) { ltk_widget *root_widget = window->root_widget; @@ -851,53 +708,6 @@ ltk_handle_event(ltk_window *window, XEvent event) { } } -int -ltk_widget_id_free(const char *id) { - khint_t k; - k = kh_get(widget, widget_hash, id); - if (k != kh_end(widget_hash)) { - return 0; - } - return 1; -} - -ltk_widget * -ltk_get_widget(const char *id, ltk_widget_type type, char **errstr) { - khint_t k; - ltk_widget *widget; - k = kh_get(widget, widget_hash, id); - if (k == kh_end(widget_hash)) { - *errstr = "Widget with given ID doesn't exist.\n"; - return NULL; - } - widget = kh_value(widget_hash, k); - if (type != LTK_WIDGET && widget->type != type) { - *errstr = "Widget with given ID has wrong type.\n"; - return NULL; - } - return widget; -} - -void -ltk_set_widget(ltk_widget *widget, const char *id) { - int ret; - khint_t k; - /* apparently, khash requires the string to stay accessible */ - /* FIXME: How is this freed? */ - char *tmp = ltk_strdup(id); - k = kh_put(widget, widget_hash, tmp, &ret); - kh_value(widget_hash, k) = widget; -} - -void -ltk_remove_widget(const char *id) { - khint_t k; - k = kh_get(widget, widget_hash, id); - if (k != kh_end(widget_hash)) { - kh_del(widget, widget_hash, k); - } -} - /* Push a token onto `token_list`, resizing the buffer if necessary. Returns -1 on error, 0 otherwise. Note: The token is not copied, it is only added directly. */ @@ -1178,42 +988,3 @@ 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; - 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->vtable->remove_child) { - err = widget->parent->vtable->remove_child( - window, widget, widget->parent, errstr - ); - } - widget->vtable->destroy(widget, shallow); - - return err; -} diff --git a/src/rect.c b/src/rect.c @@ -0,0 +1,33 @@ +#include "rect.h" + +/* FIXME */ +#undef MAX +#undef MIN +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +ltk_rect +ltk_rect_intersect(ltk_rect r1, ltk_rect r2) { + ltk_rect i; + i.x = MAX(r1.x, r2.x); + i.y = MAX(r1.y, r2.y); + i.w = MIN(r1.x + r1.w, r2.x + r2.w) - i.x; + i.h = MIN(r1.y + r1.h, r2.y + r2.h) - i.y; + return i; +} + +ltk_rect +ltk_rect_union(ltk_rect r1, ltk_rect r2) { + ltk_rect u; + u.x = MIN(r1.x, r2.x); + u.y = MIN(r1.y, r2.y); + u.w = MAX(r1.x + r1.w, r2.x + r2.w) - u.x; + u.h = MAX(r1.y + r1.h, r2.y + r2.h) - u.y; + return u; +} + +int +ltk_collide_rect(ltk_rect rect, int x, int y) { + return (rect.x <= x && (rect.x + rect.w) >= x && rect.y <= y + && (rect.y + rect.h) >= y); +} diff --git a/src/rect.h b/src/rect.h @@ -0,0 +1,15 @@ +#ifndef _LTK_RECT_H_ +#define _LTK_RECT_H_ + +typedef struct { + int x; + int y; + int w; + int h; +} ltk_rect; + +ltk_rect ltk_rect_intersect(ltk_rect r1, ltk_rect r2); +ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2); +int ltk_collide_rect(ltk_rect rect, int x, int y); + +#endif /* _LTK_RECT_H_ */ diff --git a/src/scrollbar.c b/src/scrollbar.c @@ -32,6 +32,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "scrollbar.h" diff --git a/src/scrollbar.h b/src/scrollbar.h @@ -24,7 +24,7 @@ #ifndef _LTK_SCROLLBAR_H_ #define _LTK_SCROLLBAR_H_ -/* Requires: "ltk.h" */ +/* Requires: "rect.h", "widget.h", "ltk.h" */ typedef struct { ltk_widget widget; diff --git a/src/text_pango.c b/src/text_pango.c @@ -12,6 +12,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "text.h" diff --git a/src/text_stb.c b/src/text_stb.c @@ -37,6 +37,8 @@ #include "memory.h" #include "color.h" +#include "rect.h" +#include "widget.h" #include "ltk.h" #include "util.h" #include "text.h" diff --git a/src/widget.c b/src/widget.c @@ -0,0 +1,221 @@ +#include <stdarg.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#include "rect.h" +#include "widget.h" +#include "color.h" +#include "ltk.h" +#include "memory.h" +#include "util.h" +#include "khash.h" + +static void ltk_destroy_widget_hash(void); + +KHASH_MAP_INIT_STR(widget, ltk_widget *) +static khash_t(widget) *widget_hash = NULL; + +static void +ltk_destroy_widget_hash(void) { + khint_t k; + ltk_widget *ptr; + for (k = kh_begin(widget_hash); k != kh_end(widget_hash); k++) { + if (kh_exist(widget_hash, k)) { + ptr = kh_value(widget_hash, k); + ptr->vtable->destroy(ptr, 1); + } + } + kh_destroy(widget, widget_hash); +} + +void +ltk_widgets_init() { + widget_hash = kh_init(widget); + if (!widget_hash) ltk_fatal_errno("Unable to initialize widget hash table.\n"); +} + +void +ltk_widgets_cleanup() { + if (widget_hash) + ltk_destroy_widget_hash(); +} + +void +ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window, + struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type) { + if (id) + widget->id = ltk_strdup(id); + else + widget->id = NULL; + widget->window = window; + widget->active_widget = NULL; + widget->parent = NULL; + widget->type = type; + + /* FIXME: possibly check that draw and destroy aren't NULL */ + widget->vtable = vtable; + + widget->needs_redraw = needs_redraw; + widget->state = LTK_NORMAL; + widget->row = 0; + widget->rect.x = 0; + widget->rect.y = 0; + widget->rect.w = 0; + widget->rect.h = 0; + widget->ideal_w = 0; + widget->ideal_h = 0; + + widget->row = 0; + widget->column = 0; + widget->row_span = 0; + widget->column_span = 0; + widget->sticky = 0; +} + +void +ltk_widget_change_state(ltk_widget *widget) { + if (widget->vtable->change_state) + widget->vtable->change_state(widget); + if (widget->needs_redraw) + ltk_window_invalidate_rect(widget->window, widget->rect); +} + +void +ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event) { + if (!widget || widget->state == LTK_DISABLED) + return; + int default_handler = 1; + if (widget->vtable->mouse_press) + default_handler = widget->vtable->mouse_press(widget, event); + if (default_handler) { + if (event.xbutton.button == 1) + ltk_window_set_pressed_widget(widget->window, widget); + } +} + +void +ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event) { + if (!widget || widget->state == LTK_DISABLED) + return; + int default_handler = 1; + if (widget->vtable->mouse_release) + default_handler = widget->vtable->mouse_release(widget, event); + if (default_handler) + ltk_window_set_pressed_widget(widget->window, NULL); +} + +/* FIXME: ONLY SET ACTIVE WIDGET AT BOTTOM OF HIERARCHY + -> Don't first set parent as active widget and then child */ +void +ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) { + if (!widget || widget->state == LTK_DISABLED) + return; + /* FIXME: THIS WHOLE STATE HANDLING IS STILL PARTIALLY BROKEN */ + /* + if (((widget->state == LTK_NORMAL) || + (widget->state == LTK_ACTIVE && widget->window->active_widget != widget)) && + !widget->window->pressed_widget) { + widget->state = LTK_ACTIVE; + if (widget->vtable->change_state) + widget->vtable->change_state(widget); + if (widget->vtable->mouse_enter) + widget->vtable->mouse_enter(widget, event); + ltk_window_remove_active_widget(widget->window); + ltk_window_set_active_widget(widget); + } + */ + int default_handler = 1; + if (widget->window->pressed_widget && widget->window->pressed_widget->vtable->motion_notify) + default_handler = widget->window->pressed_widget->vtable->motion_notify(widget->window->pressed_widget, event); + else if (widget->vtable->motion_notify) + default_handler = widget->vtable->motion_notify(widget, event); + if (default_handler) + ltk_window_set_active_widget(widget->window, widget); +} + +int +ltk_widget_id_free(const char *id) { + khint_t k; + k = kh_get(widget, widget_hash, id); + if (k != kh_end(widget_hash)) { + return 0; + } + return 1; +} + +ltk_widget * +ltk_get_widget(const char *id, ltk_widget_type type, char **errstr) { + khint_t k; + ltk_widget *widget; + k = kh_get(widget, widget_hash, id); + if (k == kh_end(widget_hash)) { + *errstr = "Widget with given ID doesn't exist.\n"; + return NULL; + } + widget = kh_value(widget_hash, k); + if (type != LTK_WIDGET && widget->type != type) { + *errstr = "Widget with given ID has wrong type.\n"; + return NULL; + } + return widget; +} + +void +ltk_set_widget(ltk_widget *widget, const char *id) { + int ret; + khint_t k; + /* apparently, khash requires the string to stay accessible */ + /* FIXME: How is this freed? */ + char *tmp = ltk_strdup(id); + k = kh_put(widget, widget_hash, tmp, &ret); + kh_value(widget_hash, k) = widget; +} + +void +ltk_remove_widget(const char *id) { + khint_t k; + k = kh_get(widget, widget_hash, id); + if (k != kh_end(widget_hash)) { + kh_del(widget, widget_hash, k); + } +} + +int +ltk_widget_destroy( + ltk_window *window, + char **tokens, + size_t num_tokens, + char **errstr) { + int err = 0, shallow = 1; + 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->vtable->remove_child) { + err = widget->parent->vtable->remove_child( + window, widget, widget->parent, errstr + ); + } + widget->vtable->destroy(widget, shallow); + + return err; +} diff --git a/src/widget.h b/src/widget.h @@ -0,0 +1,95 @@ +/* Requires the following includes: <X11/Xlib.h>, <X11/Xutil.h>, "rect.h" */ + +#ifndef _LTK_WIDGET_H_ +#define _LTK_WIDGET_H_ + +typedef enum { + LTK_STICKY_LEFT = 1 << 0, + LTK_STICKY_RIGHT = 1 << 1, + LTK_STICKY_TOP = 1 << 2, + LTK_STICKY_BOTTOM = 1 << 3 +} ltk_sticky_mask; + +typedef enum { + LTK_VERTICAL, + LTK_HORIZONTAL +} ltk_orientation; + +typedef enum { + LTK_NORMAL, + LTK_PRESSED, + LTK_ACTIVE, + LTK_DISABLED +} ltk_widget_state; + +typedef enum { + /* for e.g. scrollbar, which can't be directly accessed by users */ + LTK_UNKNOWN, + LTK_GRID, + LTK_BUTTON, + LTK_DRAW, + LTK_LABEL, + LTK_WIDGET, + LTK_BOX +} ltk_widget_type; + +struct ltk_window; + +struct ltk_widget_vtable; + +typedef struct ltk_widget { + struct ltk_window *window; + struct ltk_widget *active_widget; + struct ltk_widget *parent; + char *id; + + struct ltk_widget_vtable *vtable; + + ltk_rect rect; + unsigned int ideal_w; + unsigned int ideal_h; + + ltk_widget_type type; + ltk_widget_state state; + unsigned int sticky; + unsigned short row; + unsigned short column; + unsigned short row_span; + unsigned short column_span; + unsigned char needs_redraw; +} ltk_widget; + +struct ltk_widget_vtable { + void (*key_press) (struct ltk_widget *, XEvent); + void (*key_release) (struct ltk_widget *, XEvent); + int (*mouse_press) (struct ltk_widget *, XEvent); + int (*mouse_release) (struct ltk_widget *, XEvent); + int (*mouse_wheel) (struct ltk_widget *, XEvent); + int (*motion_notify) (struct ltk_widget *, XEvent); + void (*mouse_leave) (struct ltk_widget *, XEvent); + void (*mouse_enter) (struct ltk_widget *, XEvent); + + void (*resize) (struct ltk_widget *); + void (*draw) (struct ltk_widget *, ltk_rect); + void (*change_state) (struct ltk_widget *); + void (*destroy) (struct ltk_widget *, int); + + void (*child_size_change) (struct ltk_widget *, struct ltk_widget *); + int (*remove_child) (struct ltk_window *, struct ltk_widget *, struct ltk_widget *, char **); +}; + +int ltk_widget_destroy(struct ltk_window *window, char **tokens, size_t num_tokens, char **errstr); +void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, struct ltk_window *window, + struct ltk_widget_vtable *vtable, unsigned int needs_redraw, ltk_widget_type type); +void ltk_widget_change_state(ltk_widget *widget); +void ltk_widget_mouse_press_event(ltk_widget *widget, XEvent event); +void ltk_widget_mouse_release_event(ltk_widget *widget, XEvent event); +void ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event); +int ltk_widget_id_free(const char *id); +ltk_widget *ltk_get_widget(const char *id, ltk_widget_type type, char **errstr); +void ltk_set_widget(ltk_widget *widget, const char *id); +void ltk_remove_widget(const char *id); +void ltk_widgets_cleanup(); +void ltk_widgets_init(); + +#endif /* _LTK_WIDGET_H_ */