commit 99531db6c1821736ff1a975ec9872fc033d94df7
parent 488ed473efaa6153752374f9c27f67fe45dc4823
Author: lumidify <nobody@lumidify.org>
Date: Fri, 24 Jun 2022 14:31:59 +0200
Add event masks
This is very weird and buggy right now.
See socket_format.txt for some open problems.
Diffstat:
21 files changed, 703 insertions(+), 162 deletions(-)
diff --git a/Makefile b/Makefile
@@ -55,8 +55,8 @@ OBJ = \
src/graphics_xlib.o \
src/surface_cache.o \
src/event_xlib.o \
+ src/err.c \
$(EXTRA_OBJ)
-# src/draw.o \
# Note: This could be improved so a change in a header only causes the .c files
# which include that header to be recompiled, but the compile times are
@@ -83,8 +83,9 @@ HDR = \
src/surface_cache.h \
src/macros.h \
src/event.h \
- src/xlib_shared.h
-# src/draw.h \
+ src/xlib_shared.h \
+ src/err.h \
+ src/proto_types.h
all: src/ltkd src/ltkc
diff --git a/README.md b/README.md
@@ -20,3 +20,5 @@ Also read the comment in './test.sh'.
Note: I know the default theme is butt-ugly at the moment. It is mainly
to test things, not to look pretty.
+
+Note: Read 'socket_format.txt' for some documentation and open problems.
diff --git a/socket_format.txt b/socket_format.txt
@@ -1,5 +1,4 @@
-Note: This is not fully implemented yet; it is just here to
-collect my thoughts while I keep working.
+Requests:
<widget type> <widget id> <command> <args>
> grid grd1 create 2 2
@@ -14,12 +13,73 @@ within a string.
Double quotes must be escaped in strings, like so:
> button btn1 create "Bla\"bla"
-When the server sends a reply, the format is the same, but
-there are some special cases, such as "get-text". When the
-client asks to get the text for a widget, only the text is
-sent back, but still inside double quotes, with double quotes
-belonging to the text escaped.
-
Essentially, the individual messages are separated by line
breaks (\n), but line breaks within strings don't break the
message.
+
+Replies:
+
+Not properly implemented yet.
+The requests will probably have to include a sequence number eventually, which
+the replies will also give back so it's possible to know when the appropriate
+reply has been received.
+
+Errors:
+
+err <error number> <number of bad argument or -1> <string description of error>
+
+Events:
+
+event[l] <widget id> <widget type or "widget" for generic events> <event name> [further data]
+
+By default, no events are reported. An event mask has to be set first:
+
+mask-add <widget id> <type> <event name> [lock]
+mask-remove <widget id> <type> <event name> [lock]
+mask-set <widget id> <type> <event name> [lock]
+
+<type> is either "widget" for generic events (mousepress, mouserelease,
+mousemotion, configure, statechange) or the widget type for specific events.
+The only specific event currently supported is "press" for both "button"
+and "menuentry". Note that currently, only a single mask type can be
+added/removed at once instead of combining multiple in one request
+(i.e. it isn't possible to do something like "mousepress|mouserelease" yet).
+
+If "lock" is set, the "lock mask" is manipulated instead of the normal one.
+If an event occurs that is included in the lock mask, the event will start
+with "eventl" instead of "event", and the ltk server blocks until it gets the
+request "event-unlock [true/false]" from the client that received the event.
+Note that if multiple clients have an event in their lock mask, all of them will
+receive the event, but only one of them is chosen to listen for the event-unlock
+(basically, this is unspecified behavior that should be avoided). If event-unlock
+includes "true", the event is not processed further by the ltk server. If "false"
+is given instead, the event is processed as usual by the ltk server.
+
+This was added to allow functionality like in regular GUI toolkits where it is
+possible to override events completely. The problem is that it currently isn't
+really clear where exactly the command should be emitted and whether it really
+makes sense to block all further processing (some processing has to be done
+even now for it to make any sense at all). That could possibly lead to very
+weird bugs. It also currently isn't possible to do much after locking because
+no useful low-level functions for widgets exist (yet?). All in all, I'm not
+entirely sure how to make this work nicely so it is actually useful.
+Since all of this is pushed over a socket and will probably be able to run
+over a network connection eventually, it will also cause problems with latency.
+
+Miscellaneous:
+
+It probably isn't too great for security when anyone can do anything with the
+window. Maybe it would be better to allow different clients to have different
+permissions? For instance, maybe only the main client could change things, but
+other clients could have readonly permissions for things like screenreaders.
+That would probably get very over-complicated, though.
+
+I'm also seriously considering switching to a binary socket format. It's nice
+to have a text format, but it's an absolute pain to process, because everything
+has to be converted from/to text. It also isn't nearly as efficient, especially
+if more complicated things are done, such as listening for all mousemotion events.
+Of course, it could be made much more efficient with various lookup tables
+(it isn't implemented very efficiently currently), but still not nearly as good
+as a binary protocol. The idea would be to have a binary protocol, but to still
+have something like ltkc that converts the protocol to a text format so simple
+shell clients can still exist, but more complicated programs aren't hindered by it.
diff --git a/src/box.c b/src/box.c
@@ -59,7 +59,7 @@ static struct ltk_widget_vtable vtable = {
.get_child_at_pos = <k_box_get_child_at_pos,
.mouse_leave = NULL,
.mouse_enter = NULL,
- .type = LTK_BOX,
+ .type = LTK_WIDGET_BOX,
.flags = 0,
};
@@ -358,8 +358,8 @@ ltk_box_cmd_add(
err->arg = -1;
return 1;
}
- box = (ltk_box *)ltk_get_widget(tokens[1], LTK_BOX, err);
- widget = ltk_get_widget(tokens[3], LTK_WIDGET, err);
+ box = (ltk_box *)ltk_get_widget(tokens[1], LTK_WIDGET_BOX, err);
+ widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err);
if (!box) {
err->arg = 1;
return 1;
@@ -409,8 +409,8 @@ ltk_box_cmd_remove(
err->arg = -1;
return 1;
}
- box = (ltk_box *)ltk_get_widget(tokens[1], LTK_BOX, err);
- widget = ltk_get_widget(tokens[3], LTK_WIDGET, err);
+ box = (ltk_box *)ltk_get_widget(tokens[1], LTK_WIDGET_BOX, err);
+ widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err);
if (!box) {
err->arg = 1;
return 1;
@@ -441,7 +441,6 @@ ltk_box_cmd_create(
err->arg = -1;
return 1;
}
- /* FIXME: race condition */
if (!ltk_widget_id_free(tokens[1])) {
err->type = ERR_WIDGET_ID_IN_USE;
err->arg = 1;
diff --git a/src/button.c b/src/button.c
@@ -20,6 +20,7 @@
#include <string.h>
#include <stdarg.h>
+#include "proto_types.h"
#include "event.h"
#include "memory.h"
#include "color.h"
@@ -59,8 +60,8 @@ static struct ltk_widget_vtable vtable = {
.destroy = <k_button_destroy,
.child_size_change = NULL,
.remove_child = NULL,
- .type = LTK_BUTTON,
- .flags = LTK_NEEDS_REDRAW | LTK_NEEDS_SURFACE | LTK_ACTIVATABLE_ALWAYS,
+ .type = LTK_WIDGET_BUTTON,
+ .flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_ALWAYS,
};
static struct {
@@ -116,13 +117,15 @@ ltk_button_uninitialize_theme(ltk_window *window) {
ltk_theme_uninitialize(window, parseinfo, LENGTH(parseinfo));
}
+/* FIXME: only keep text in surface to avoid large surface */
static void
ltk_button_draw(ltk_widget *self, ltk_rect clip) {
ltk_button *button = (ltk_button *)self;
ltk_rect rect = button->widget.rect;
ltk_rect clip_final = ltk_rect_intersect(clip, rect);
ltk_surface *s;
- if (!ltk_surface_cache_get_surface(self->surface_key, &s) || self->dirty)
+ ltk_surface_cache_request_surface_size(button->key, self->rect.w, self->rect.h);
+ if (!ltk_surface_cache_get_surface(button->key, &s) || self->dirty)
ltk_button_redraw_surface(button, s);
ltk_surface_copy(s, self->window->surface, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
}
@@ -165,9 +168,8 @@ ltk_button_redraw_surface(ltk_button *button, ltk_surface *s) {
static int
ltk_button_mouse_release(ltk_widget *self, ltk_button_event *event) {
- ltk_button *button = (ltk_button *)self;
if ((self->state & LTK_PRESSED) && event->button == LTK_BUTTONL && ltk_collide_rect(self->rect, event->x, event->y)) {
- ltk_queue_event(button->widget.window, LTK_EVENT_BUTTON, button->widget.id, "button_click");
+ ltk_queue_specific_event(self, "button", LTK_PWEVENTMASK_BUTTON_PRESS, "press");
return 1;
}
return 0;
@@ -184,6 +186,7 @@ ltk_button_create(ltk_window *window, const char *id, char *text) {
button->widget.ideal_w = text_w + theme.border_width * 2 + theme.pad * 2;
button->widget.ideal_h = text_h + theme.border_width * 2 + theme.pad * 2;
ltk_fill_widget_defaults(&button->widget, id, window, &vtable, button->widget.ideal_w, button->widget.ideal_h);
+ button->key = ltk_surface_cache_get_unnamed_key(window->surface_cache, button->widget.ideal_w, button->widget.ideal_h);
button->widget.dirty = 1;
return button;
@@ -197,8 +200,7 @@ ltk_button_destroy(ltk_widget *self, int shallow) {
ltk_warn("Tried to destroy NULL button.\n");
return;
}
- /* FIXME: this should be generic part of widget */
- ltk_surface_cache_release_key(self->surface_key);
+ ltk_surface_cache_release_key(button->key);
ltk_text_line_destroy(button->tl);
ltk_remove_widget(self->id);
ltk_remove_widget(button->widget.id);
diff --git a/src/button.h b/src/button.h
@@ -14,8 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef _LTK_BUTTON_H_
-#define _LTK_BUTTON_H_
+#ifndef LTK_BUTTON_H
+#define LTK_BUTTON_H
/* Requires the following includes: <X11/Xlib.h>, "rect.h", "widget.h", "ltk.h", "color.h", "text.h" */
@@ -24,6 +24,7 @@
typedef struct {
ltk_widget widget;
ltk_text_line *tl;
+ ltk_surface_cache_key *key;
} ltk_button;
int ltk_button_ini_handler(ltk_window *window, const char *prop, const char *value);
@@ -37,4 +38,4 @@ int ltk_button_cmd(
ltk_error *
);
-#endif /* _LTK_BUTTON_H_ */
+#endif /* LTK_BUTTON_H */
diff --git a/src/grid.c b/src/grid.c
@@ -68,7 +68,7 @@ static struct ltk_widget_vtable vtable = {
.mouse_enter = NULL,
.key_press = NULL,
.key_release = NULL,
- .type = LTK_GRID,
+ .type = LTK_WIDGET_GRID,
.flags = 0,
};
@@ -414,8 +414,8 @@ ltk_grid_cmd_add(
err->arg = -1;
return 1;
}
- grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_GRID, err);
- widget = ltk_get_widget(tokens[3], LTK_WIDGET, err);
+ grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err);
+ widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err);
if (!grid) {
err->arg = 1;
return 1;
@@ -490,8 +490,8 @@ ltk_grid_cmd_ungrid(
err->arg = -1;
return 1;
}
- grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_GRID, err);
- widget = ltk_get_widget(tokens[3], LTK_WIDGET, err);
+ grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err);
+ widget = ltk_get_widget(tokens[3], LTK_WIDGET_ANY, err);
if (!grid) {
err->arg = 1;
return 1;
@@ -560,7 +560,7 @@ ltk_grid_cmd_set_row_weight(
err->arg = -1;
return 1;
}
- grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_GRID, err);
+ grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err);
if (!grid) {
err->arg = 1;
return 1;
@@ -598,7 +598,7 @@ ltk_grid_cmd_set_column_weight(
err->arg = -1;
return 1;
}
- grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_GRID, err);
+ grid = (ltk_grid *)ltk_get_widget(tokens[1], LTK_WIDGET_GRID, err);
if (!grid) {
err->arg = 1;
return 1;
diff --git a/src/label.c b/src/label.c
@@ -57,8 +57,8 @@ static struct ltk_widget_vtable vtable = {
.motion_notify = NULL,
.mouse_leave = NULL,
.mouse_enter = NULL,
- .type = LTK_LABEL,
- .flags = LTK_NEEDS_REDRAW | LTK_NEEDS_SURFACE,
+ .type = LTK_WIDGET_LABEL,
+ .flags = LTK_NEEDS_REDRAW,
};
static struct {
@@ -96,7 +96,8 @@ ltk_label_draw(ltk_widget *self, ltk_rect clip) {
ltk_rect rect = label->widget.rect;
ltk_rect clip_final = ltk_rect_intersect(clip, rect);
ltk_surface *s;
- if (!ltk_surface_cache_get_surface(self->surface_key, &s) || self->dirty)
+ ltk_surface_cache_request_surface_size(label->key, self->rect.w, self->rect.h);
+ if (!ltk_surface_cache_get_surface(label->key, &s) || self->dirty)
ltk_label_redraw_surface(label, s);
ltk_surface_copy(s, self->window->surface, ltk_rect_relative(rect, clip_final), clip_final.x, clip_final.y);
}
@@ -126,6 +127,7 @@ ltk_label_create(ltk_window *window, const char *id, char *text) {
label->widget.ideal_w = text_w + theme.pad * 2;
label->widget.ideal_h = text_h + theme.pad * 2;
ltk_fill_widget_defaults(&label->widget, id, window, &vtable, label->widget.ideal_w, label->widget.ideal_h);
+ label->key = ltk_surface_cache_get_unnamed_key(window->surface_cache, label->widget.ideal_w, label->widget.ideal_h);
return label;
}
@@ -138,7 +140,7 @@ ltk_label_destroy(ltk_widget *self, int shallow) {
ltk_warn("Tried to destroy NULL label.\n");
return;
}
- ltk_surface_cache_release_key(self->surface_key);
+ ltk_surface_cache_release_key(label->key);
ltk_text_line_destroy(label->tl);
ltk_remove_widget(self->id);
ltk_free(self->id);
diff --git a/src/label.h b/src/label.h
@@ -24,6 +24,7 @@
typedef struct {
ltk_widget widget;
ltk_text_line *tl;
+ ltk_surface_cache_key *key;
} ltk_label;
int ltk_label_ini_handler(ltk_window *window, const char *prop, const char *value);
diff --git a/src/ltk.h b/src/ltk.h
@@ -18,6 +18,7 @@
#define _LTK_H_
#include <stdint.h>
+#include "proto_types.h"
typedef enum {
LTK_EVENT_RESIZE = 1 << 0,
@@ -100,5 +101,9 @@ int ltk_register_timer(long first, long repeat, void (*callback)(void *), void *
void ltk_window_register_popup(ltk_window *window, ltk_widget *popup);
void ltk_window_unregister_popup(ltk_window *window, ltk_widget *popup);
void ltk_window_unregister_all_popups(ltk_window *window);
+int ltk_handle_lock_client(ltk_window *window, int client);
+int ltk_queue_specific_event(ltk_widget *widget, const char *type, uint32_t mask, const char *data);
+int ltk_queue_sock_write(int client, const char *str, int len);
+int ltk_queue_sock_write_fmt(int client, const char *fmt, ...);
#endif
diff --git a/src/ltkd.c b/src/ltkd.c
@@ -1,6 +1,5 @@
/* FIXME: backslashes should be parsed properly! */
/* 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? */
/* FIXME: strip whitespace at end of lines in socket format */
@@ -125,11 +124,9 @@ static int read_sock(struct ltk_sock_info *sock);
static int push_token(struct token_list *tl, char *token);
static int read_sock(struct ltk_sock_info *sock);
static int write_sock(struct ltk_sock_info *sock);
-static int queue_sock_write(struct ltk_sock_info *sock, const char *str, int len);
-static int queue_sock_write_fmt(struct ltk_sock_info *sock, const char *fmt, ...);
static int tokenize_command(struct ltk_sock_info *sock);
static int ltk_set_root_widget_cmd(ltk_window *window, char **tokens, int num_tokens, ltk_error *err);
-static void process_commands(ltk_window *window, struct ltk_sock_info *sock);
+static int process_commands(ltk_window *window, int client);
static int add_client(int fd);
static int listen_sock(const char *sock_path);
static int accept_sock(int listenfd);
@@ -137,7 +134,6 @@ static int accept_sock(int listenfd);
static short maxsocket = -1;
static short running = 1;
static short sock_write_available = 0;
-static int listenfd = -1;
static char *ltk_dir = NULL;
static FILE *ltk_logfile = NULL;
static char *sock_path = NULL;
@@ -187,25 +183,91 @@ int main(int argc, char *argv[]) {
return ltk_mainloop(main_window);
}
+/* FIXME: need to recalculate maxfd when removing client */
+static struct {
+ fd_set rallfds, wallfds;
+ int maxfd;
+ int listenfd;
+} sock_state;
+
+int
+ltk_handle_lock_client(ltk_window *window, int client) {
+ if (client < 0 || client >= MAX_SOCK_CONNS || sockets[client].fd == -1)
+ return 0;
+ fd_set rfds, wfds, rallfds, wallfds;
+ int clifd = sockets[client].fd;
+ FD_ZERO(&rallfds);
+ FD_ZERO(&wallfds);
+ FD_SET(clifd, &rallfds);
+ FD_SET(clifd, &wallfds);
+ int rretval, wretval;
+ struct timeval tv, tv_master;
+ tv_master.tv_sec = 0;
+ tv_master.tv_usec = 20000;
+ while (1) {
+ rfds = rallfds;
+ wfds = wallfds;
+ /* separate these because the writing fds are usually
+ always ready for writing */
+ tv = tv_master;
+ rretval = select(clifd + 1, &rfds, NULL, NULL, &tv);
+ /* value of tv doesn't really matter anymore here because the
+ necessary framerate-limiting delay is already done */
+ wretval = select(clifd + 1, NULL, &wfds, NULL, &tv);
+
+ if (rretval > 0 || ((sockets[client].write_cur != sockets[client].write_len) && wretval > 0)) {
+ if (FD_ISSET(clifd, &rfds)) {
+ if (read_sock(&sockets[client]) == 0) {
+ FD_CLR(clifd, &sock_state.rallfds);
+ FD_CLR(clifd, &sock_state.wallfds);
+ ltk_widget_remove_client(client);
+ sockets[clifd].fd = -1;
+ close(clifd);
+ int newmaxsocket = -1;
+ for (int j = 0; j <= maxsocket; j++) {
+ if (sockets[j].fd >= 0)
+ newmaxsocket = j;
+ }
+ maxsocket = newmaxsocket;
+ if (maxsocket == -1) {
+ ltk_quit(window);
+ break;
+ }
+ return 0;
+ } else {
+ int ret;
+ if ((ret = process_commands(window, client)) == 1)
+ return 1;
+ else if (ret == -1)
+ return 0;
+ }
+ }
+ if (FD_ISSET(clifd, &wfds)) {
+ write_sock(&sockets[client]);
+ }
+ }
+ }
+ return 0;
+}
+
static int
ltk_mainloop(ltk_window *window) {
ltk_event event;
- fd_set rfds, wfds, rallfds, wallfds;
- int maxfd;
+ fd_set rfds, wfds;
int rretval, wretval;
int clifd;
struct timeval tv, tv_master;
tv_master.tv_sec = 0;
tv_master.tv_usec = 20000;
- FD_ZERO(&rallfds);
- FD_ZERO(&wallfds);
+ FD_ZERO(&sock_state.rallfds);
+ FD_ZERO(&sock_state.wallfds);
- if ((listenfd = listen_sock(sock_path)) < 0)
+ if ((sock_state.listenfd = listen_sock(sock_path)) < 0)
ltk_fatal_errno("Error listening on socket.\n");
- FD_SET(listenfd, &rallfds);
- maxfd = listenfd;
+ FD_SET(sock_state.listenfd, &sock_state.rallfds);
+ sock_state.maxfd = sock_state.listenfd;
printf("%lu", renderer_get_window_id(main_window->renderdata));
fflush(stdout);
@@ -221,31 +283,31 @@ ltk_mainloop(ltk_window *window) {
/* FIXME: framerate limiting for draw */
while (running) {
- rfds = rallfds;
- wfds = wallfds;
+ rfds = sock_state.rallfds;
+ wfds = sock_state.wallfds;
/* separate these because the writing fds are usually
always ready for writing */
tv = tv_master;
- rretval = select(maxfd + 1, &rfds, NULL, NULL, &tv);
+ rretval = select(sock_state.maxfd + 1, &rfds, NULL, NULL, &tv);
/* value of tv doesn't really matter anymore here because the
necessary framerate-limiting delay is already done */
- wretval = select(maxfd + 1, NULL, &wfds, NULL, &tv);
+ wretval = select(sock_state.maxfd + 1, NULL, &wfds, NULL, &tv);
while (ltk_events_pending(window->renderdata)) {
ltk_next_event(window->renderdata, &event);
ltk_handle_event(window, &event);
}
if (rretval > 0 || (sock_write_available && wretval > 0)) {
- if (FD_ISSET(listenfd, &rfds)) {
- if ((clifd = accept_sock(listenfd)) < 0) {
+ if (FD_ISSET(sock_state.listenfd, &rfds)) {
+ if ((clifd = accept_sock(sock_state.listenfd)) < 0) {
/* FIXME: Just log this! */
ltk_fatal_errno("Error accepting socket connection.\n");
}
int i = add_client(clifd);
- FD_SET(clifd, &rallfds);
- FD_SET(clifd, &wallfds);
- if (clifd > maxfd)
- maxfd = clifd;
+ FD_SET(clifd, &sock_state.rallfds);
+ FD_SET(clifd, &sock_state.wallfds);
+ if (clifd > sock_state.maxfd)
+ sock_state.maxfd = clifd;
if (i > maxsocket)
maxsocket = i;
continue;
@@ -255,8 +317,9 @@ ltk_mainloop(ltk_window *window) {
continue;
if (FD_ISSET(clifd, &rfds)) {
if (read_sock(&sockets[i]) == 0) {
- FD_CLR(clifd, &rallfds);
- FD_CLR(clifd, &wallfds);
+ ltk_widget_remove_client(i);
+ FD_CLR(clifd, &sock_state.rallfds);
+ FD_CLR(clifd, &sock_state.wallfds);
sockets[i].fd = -1;
close(clifd);
int newmaxsocket = -1;
@@ -270,7 +333,7 @@ ltk_mainloop(ltk_window *window) {
break;
}
} else {
- process_commands(window, &sockets[i]);
+ process_commands(window, i);
}
}
if (FD_ISSET(clifd, &wfds)) {
@@ -315,7 +378,7 @@ ltk_mainloop(ltk_window *window) {
int event_len = strlen(cur->data);
for (int i = 0; i <= maxsocket; i++) {
if (sockets[i].fd != -1 && sockets[i].event_mask & cur->event_type) {
- if (queue_sock_write(&sockets[i], cur->data, event_len) < 0)
+ if (ltk_queue_sock_write(i, cur->data, event_len) < 0)
ltk_fatal_errno("Unable to queue event.\n");
}
}
@@ -414,8 +477,8 @@ open_log(char *dir) {
void
ltk_cleanup(void) {
- if (listenfd >= 0)
- close(listenfd);
+ if (sock_state.listenfd >= 0)
+ close(sock_state.listenfd);
if (ltk_dir)
ltk_free(ltk_dir);
if (ltk_logfile)
@@ -479,7 +542,7 @@ ltk_set_root_widget_cmd(
err->arg = -1;
return 1;
}
- widget = ltk_get_widget(tokens[1], LTK_WIDGET, err);
+ widget = ltk_get_widget(tokens[1], LTK_WIDGET_ANY, err);
if (!widget) {
err->arg = 1;
return 1;
@@ -501,24 +564,29 @@ ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) {
window->dirty_rect = ltk_rect_union(rect, window->dirty_rect);
}
-void
-ltk_queue_event(ltk_window *window, ltk_userevent_type type, const char *id, const char *data) {
- /* FIXME: make it nicer and safer */
- struct ltk_event_queue *new = ltk_malloc(sizeof(struct ltk_event_queue));
- new->event_type = type;
- int id_len = strlen(id);
- int data_len = strlen(data);
- new->data = ltk_malloc(id_len + data_len + 3);
- strcpy(new->data, id);
- new->data[id_len] = ' ';
- strcpy(new->data + id_len + 1, data);
- new->data[id_len + data_len + 1] = '\n';
- new->data[id_len + data_len + 2] = '\0';
- new->next = window->first_event;
- window->first_event = new;
- new->prev = NULL;
- if (!window->last_event)
- window->last_event = new;
+/* FIXME: generic event handling functions that take the actual uint32_t event type, etc. */
+int
+ltk_queue_specific_event(ltk_widget *widget, const char *type, uint32_t mask, const char *data) {
+ int lock_client = -1;
+ for (size_t i = 0; i < widget->masks_num; i++) {
+ if (widget->event_masks[i].lwmask & mask) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "eventl %s %s %s\n", widget->id, type, data
+ );
+ lock_client = widget->event_masks[i].client;
+ } else if (widget->event_masks[i].wmask & mask) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "event %s %s %s\n", widget->id, type, data
+ );
+ }
+ }
+ if (lock_client >= 0) {
+ if (ltk_handle_lock_client(widget->window, lock_client))
+ return 1;
+ }
+ return 0;
}
static void
@@ -1115,8 +1183,11 @@ move_write_pos(struct ltk_sock_info *sock) {
Returns -1 on error, 0 otherwise.
Note: The string must include all '\n', etc. as defined in the protocol. This
function just adds the given string verbatim. */
-static int
-queue_sock_write(struct ltk_sock_info *sock, const char *str, int len) {
+int
+ltk_queue_sock_write(int client, const char *str, int len) {
+ if (client < 0 || client >= MAX_SOCK_CONNS || sockets[client].fd == -1)
+ return 1;
+ struct ltk_sock_info *sock = &sockets[client];
move_write_pos(sock);
if (len < 0)
len = strlen(str);
@@ -1132,8 +1203,11 @@ queue_sock_write(struct ltk_sock_info *sock, const char *str, int len) {
return 0;
}
-static int
-queue_sock_write_fmt(struct ltk_sock_info *sock, const char *fmt, ...) {
+int
+ltk_queue_sock_write_fmt(int client, const char *fmt, ...) {
+ if (client < 0 || client >= MAX_SOCK_CONNS || sockets[client].fd == -1)
+ return 1;
+ struct ltk_sock_info *sock = &sockets[client];
move_write_pos(sock);
va_list args;
va_start(args, fmt);
@@ -1193,13 +1267,136 @@ tokenize_command(struct ltk_sock_info *sock) {
return 1;
}
+/* FIXME: currently no type-checking when setting specific widget mask */
+/* FIXME: this is really ugly and inefficient right now - it will be replaced with something
+ more generic at some point (or maybe just with a binary protocol?) */
+static int
+handle_mask_command(int client, char **tokens, size_t num_tokens, ltk_error *err) {
+ if (num_tokens != 4 && num_tokens != 5) {
+ err->type = ERR_INVALID_NUMBER_OF_ARGUMENTS;
+ err->arg = -1;
+ return 1;
+ }
+ uint32_t mask = 0;
+ int lock = 0;
+ int special = 0;
+ ltk_widget *widget = ltk_get_widget(tokens[1], LTK_WIDGET_ANY, err);
+ if (!widget) {
+ err->arg = 1;
+ return 1;
+ }
+ if (!strcmp(tokens[2], "widget")) {
+ if (!strcmp(tokens[3], "mousepress")) {
+ mask = LTK_PEVENTMASK_MOUSEPRESS;
+ } else if (!strcmp(tokens[3], "mouserelease")) {
+ mask = LTK_PEVENTMASK_MOUSERELEASE;
+ } else if (!strcmp(tokens[3], "mousemotion")) {
+ mask = LTK_PEVENTMASK_MOUSEMOTION;
+ } else if (!strcmp(tokens[3], "configure")) {
+ mask = LTK_PEVENTMASK_CONFIGURE;
+ } else if (!strcmp(tokens[3], "statechange")) {
+ mask = LTK_PEVENTMASK_STATECHANGE;
+ } else if (!strcmp(tokens[3], "none")) {
+ mask = LTK_PEVENTMASK_NONE;
+ } else {
+ err->type = ERR_INVALID_ARGUMENT;
+ err->arg = 3;
+ return 1;
+ }
+ } else if (!strcmp(tokens[2], "menuentry")) {
+ if (!strcmp(tokens[3], "press")) {
+ mask = LTK_PWEVENTMASK_MENUENTRY_PRESS;
+ } else if (!strcmp(tokens[3], "none")) {
+ mask = LTK_PWEVENTMASK_MENUENTRY_NONE;
+ } else {
+ err->type = ERR_INVALID_ARGUMENT;
+ err->arg = 3;
+ return 1;
+ }
+ special = 1;
+ } else if (!strcmp(tokens[2], "button")) {
+ if (!strcmp(tokens[3], "press")) {
+ mask = LTK_PWEVENTMASK_BUTTON_PRESS;
+ } else if (!strcmp(tokens[3], "none")) {
+ mask = LTK_PWEVENTMASK_BUTTON_NONE;
+ } else {
+ err->type = ERR_INVALID_ARGUMENT;
+ err->arg = 3;
+ return 1;
+ }
+ special = 1;
+ } else {
+ err->type = ERR_INVALID_ARGUMENT;
+ err->arg = 2;
+ }
+ if (num_tokens == 5) {
+ if (!strcmp(tokens[4], "lock")) {
+ lock = 1;
+ } else {
+ err->type = ERR_INVALID_ARGUMENT;
+ err->arg = 4;
+ return 1;
+ }
+ }
+
+ if (!strcmp(tokens[0], "mask-add")) {
+ if (lock) {
+ if (special)
+ ltk_widget_add_to_event_lwmask(widget, client, mask);
+ else
+ ltk_widget_add_to_event_lmask(widget, client, mask);
+ } else {
+ if (special)
+ ltk_widget_add_to_event_wmask(widget, client, mask);
+ else
+ ltk_widget_add_to_event_mask(widget, client, mask);
+ }
+ } else if (!strcmp(tokens[0], "mask-set")) {
+ if (lock) {
+ if (special)
+ ltk_widget_set_event_lwmask(widget, client, mask);
+ else
+ ltk_widget_set_event_lmask(widget, client, mask);
+ } else {
+ if (special)
+ ltk_widget_set_event_wmask(widget, client, mask);
+ else
+ ltk_widget_set_event_mask(widget, client, mask);
+ }
+ } else if (!strcmp(tokens[0], "mask-remove")) {
+ if (lock) {
+ if (special)
+ ltk_widget_remove_from_event_lwmask(widget, client, mask);
+ else
+ ltk_widget_remove_from_event_lmask(widget, client, mask);
+ } else {
+ if (special)
+ ltk_widget_remove_from_event_wmask(widget, client, mask);
+ else
+ ltk_widget_remove_from_event_mask(widget, client, mask);
+ }
+ } else {
+ err->type = ERR_INVALID_COMMAND;
+ err->arg = 0;
+ return 1;
+ }
+ return 0;
+}
+
/* Process the commands as they are read from the socket. */
-static void
-process_commands(ltk_window *window, struct ltk_sock_info *sock) {
+/* Returns 1 if command was 'event-unlock true',
+ -1 if command was 'event-unlock false', 0 otherwise. */
+static int
+process_commands(ltk_window *window, int client) {
+ if (client < 0 || client >= MAX_SOCK_CONNS || sockets[client].fd == -1)
+ return 0;
+ struct ltk_sock_info *sock = &sockets[client];
char **tokens;
int num_tokens;
ltk_error errdetail = {ERR_NONE, -1};
int err;
+ int retval = 0;
+ int last = 0;
while (!tokenize_command(sock)) {
err = 0;
tokens = sock->tokens.tokens;
@@ -1226,6 +1423,22 @@ process_commands(ltk_window *window, struct ltk_sock_info *sock) {
ltk_quit(window);
} else if (strcmp(tokens[0], "destroy") == 0) {
err = ltk_widget_destroy_cmd(window, tokens, num_tokens, &errdetail);
+ } else if (strncmp(tokens[0], "mask", 4) == 0) {
+ err = handle_mask_command(client, tokens, num_tokens, &errdetail);
+ } else if (strcmp(tokens[0], "event-unlock") == 0) {
+ if (num_tokens != 2) {
+ errdetail.type = ERR_INVALID_NUMBER_OF_ARGUMENTS;
+ err = 1;
+ } else if (strcmp(tokens[1], "true") == 0) {
+ retval = 1;
+ } else if (strcmp(tokens[1], "false") == 0) {
+ retval = -1;
+ } else {
+ err = 1;
+ errdetail.type = ERR_INVALID_ARGUMENT;
+ errdetail.arg = 1;
+ }
+ last = 1;
} else {
errdetail.type = ERR_INVALID_COMMAND;
errdetail.arg = -1;
@@ -1234,9 +1447,11 @@ process_commands(ltk_window *window, struct ltk_sock_info *sock) {
sock->tokens.num_tokens = 0;
if (err) {
const char *errmsg = errtype_to_string(errdetail.type);
- if (queue_sock_write_fmt(sock, "err %d arg %d msg \"%s\"\n", errdetail.type, errdetail.arg, errmsg) < 0)
+ if (ltk_queue_sock_write_fmt(client, "err %d %d \"%s\"\n", errdetail.type, errdetail.arg, errmsg) < 0)
ltk_fatal("Unable to queue socket write.\n");
}
+ if (last)
+ break;
}
if (sock->tokens.num_tokens > 0 && sock->tokens.tokens[0] != sock->read) {
memmove(sock->read, sock->tokens.tokens[0], sock->read + sock->read_len - sock->tokens.tokens[0]);
@@ -1251,4 +1466,5 @@ process_commands(ltk_window *window, struct ltk_sock_info *sock) {
sock->read_len = 0;
sock->read_cur = 0;
}
+ return retval;
}
diff --git a/src/menu.c b/src/menu.c
@@ -23,6 +23,7 @@
#include <stdarg.h>
#include <math.h>
+#include "proto_types.h"
#include "event.h"
#include "memory.h"
#include "color.h"
@@ -116,7 +117,7 @@ static void ltk_menuentry_detach_submenu(ltk_menuentry *e);
static int ltk_menu_remove_child(ltk_widget *widget, ltk_widget *self, ltk_error *err);
-#define IN_SUBMENU(e) (e->widget.parent && e->widget.parent->vtable->type == LTK_MENU && ((ltk_menu *)e->widget.parent)->is_submenu)
+#define IN_SUBMENU(e) (e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU && ((ltk_menu *)e->widget.parent)->is_submenu)
static struct ltk_widget_vtable vtable = {
.key_press = NULL,
@@ -134,7 +135,7 @@ static struct ltk_widget_vtable vtable = {
.destroy = <k_menu_destroy,
.child_size_change = &recalc_ideal_menu_size,
.remove_child = <k_menu_remove_child,
- .type = LTK_MENU,
+ .type = LTK_WIDGET_MENU,
.flags = LTK_NEEDS_REDRAW,
};
@@ -154,7 +155,7 @@ static struct ltk_widget_vtable entry_vtable = {
.destroy = <k_menuentry_destroy,
.child_size_change = NULL,
.remove_child = NULL,
- .type = LTK_MENUENTRY,
+ .type = LTK_WIDGET_MENUENTRY,
.flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_ALWAYS | LTK_HOVER_IS_ACTIVE,
};
@@ -294,7 +295,7 @@ static void
ltk_menuentry_change_state(ltk_widget *self, ltk_widget_state old_state) {
ltk_menuentry *e = (ltk_menuentry *)self;
int in_submenu = IN_SUBMENU(e);
- int submenus_opened = self->parent && self->parent->vtable->type == LTK_MENU && ((ltk_menu *)self->parent)->popup_submenus;
+ int submenus_opened = self->parent && self->parent->vtable->type == LTK_WIDGET_MENU && ((ltk_menu *)self->parent)->popup_submenus;
if (!(self->state & (LTK_ACTIVE | LTK_PRESSED))) {
/* Note: This only has to take care of the submenu that is the direct child
of e because ltk_window_set_active_widget already calls change_state for
@@ -306,7 +307,7 @@ ltk_menuentry_change_state(ltk_widget *self, ltk_widget_state old_state) {
((self->state & LTK_ACTIVE) && (in_submenu || submenus_opened))) &&
e->submenu && e->submenu->widget.hidden) {
popup_active_menu(e);
- if (self->parent && self->parent->vtable->type == LTK_MENU)
+ if (self->parent && self->parent->vtable->type == LTK_WIDGET_MENU)
((ltk_menu *)self->parent)->popup_submenus = 1;
}
}
@@ -614,15 +615,16 @@ ltk_menuentry_mouse_release(ltk_widget *self, ltk_button_event *event) {
(void)event;
ltk_menuentry *e = (ltk_menuentry *)self;
int in_submenu = IN_SUBMENU(e);
- int keep_popup = self->parent && self->parent->vtable->type == LTK_MENU && ((ltk_menu *)self->parent)->popup_submenus;
+ int keep_popup = self->parent && self->parent->vtable->type == LTK_WIDGET_MENU && ((ltk_menu *)self->parent)->popup_submenus;
/* FIXME: problem when scrolling because actual shown rect may not be entire rect */
if ((self->state & LTK_PRESSED) && event->button == LTK_BUTTONL && ltk_collide_rect(self->rect, event->x, event->y)) {
if (in_submenu || !keep_popup) {
ltk_window_unregister_all_popups(self->window);
}
- ltk_queue_event(self->window, LTK_EVENT_MENU, self->id, "menu_entry_click");
+ ltk_queue_specific_event(self, "menu", LTK_PWEVENTMASK_MENUENTRY_PRESS, "press");
+ return 1;
}
- return 1;
+ return 0;
}
static int
@@ -662,8 +664,8 @@ ltk_menu_hide(ltk_widget *self) {
ltk_window_unregister_popup(self->window, self);
ltk_window_invalidate_rect(self->window, self->rect);
/* FIXME: this is really ugly/hacky */
- if (menu->unpopup_submenus_on_hide && self->parent && self->parent->vtable->type == LTK_MENUENTRY &&
- self->parent->parent && self->parent->parent->vtable->type == LTK_MENU) {
+ if (menu->unpopup_submenus_on_hide && self->parent && self->parent->vtable->type == LTK_WIDGET_MENUENTRY &&
+ self->parent->parent && self->parent->parent->vtable->type == LTK_WIDGET_MENU) {
((ltk_menu *)self->parent->parent)->popup_submenus = 0;
}
menu->unpopup_submenus_on_hide = 1;
@@ -677,7 +679,7 @@ popup_active_menu(ltk_menuentry *e) {
int in_submenu = 0, was_opened_left = 0;
ltk_rect menu_rect = e->widget.rect;
ltk_rect entry_rect = e->widget.rect;
- if (e->widget.parent && e->widget.parent->vtable->type == LTK_MENU) {
+ if (e->widget.parent && e->widget.parent->vtable->type == LTK_WIDGET_MENU) {
ltk_menu *menu = (ltk_menu *)e->widget.parent;
in_submenu = menu->is_submenu;
was_opened_left = menu->was_opened_left;
@@ -861,7 +863,7 @@ static void
recalc_ideal_menu_size(ltk_widget *self, ltk_widget *widget) {
ltk_menu *menu = (ltk_menu *)self;
/* If widget with size change is submenu, it doesn't affect this menu */
- if (widget && widget->vtable->type == LTK_MENU) {
+ if (widget && widget->vtable->type == LTK_WIDGET_MENU) {
ltk_widget_resize(widget);
return;
}
@@ -1143,12 +1145,12 @@ ltk_menu_cmd_insert_entry(
err->arg = -1;
return 1;
}
- menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_MENU, err);
+ menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err);
if (!menu) {
err->arg = 1;
return 1;
}
- e = (ltk_menuentry *)ltk_get_widget(tokens[3], LTK_MENUENTRY, err);
+ e = (ltk_menuentry *)ltk_get_widget(tokens[3], LTK_WIDGET_MENUENTRY, err);
if (!e) {
err->arg = 3;
return 1;
@@ -1181,12 +1183,12 @@ ltk_menu_cmd_add_entry(
err->arg = -1;
return 1;
}
- menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_MENU, err);
+ menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err);
if (!menu) {
err->arg = 1;
return 1;
}
- e = (ltk_menuentry *)ltk_get_widget(tokens[3], LTK_MENUENTRY, err);
+ e = (ltk_menuentry *)ltk_get_widget(tokens[3], LTK_WIDGET_MENUENTRY, err);
if (!e) {
err->arg = 3;
return 1;
@@ -1213,7 +1215,7 @@ ltk_menu_cmd_remove_entry_index(
err->arg = -1;
return 1;
}
- menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_MENU, err);
+ menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err);
if (!menu) {
err->arg = 1;
return 1;
@@ -1246,7 +1248,7 @@ ltk_menu_cmd_remove_entry_id(
err->arg = -1;
return 1;
}
- menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_MENU, err);
+ menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err);
if (!menu) {
err->arg = 1;
return 1;
@@ -1273,7 +1275,7 @@ ltk_menu_cmd_remove_all_entries(
err->arg = -1;
return 1;
}
- menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_MENU, err);
+ menu = (ltk_menu *)ltk_get_widget(tokens[1], LTK_WIDGET_MENU, err);
if (!menu) {
err->arg = 1;
return 1;
@@ -1322,12 +1324,12 @@ ltk_menuentry_cmd_attach_submenu(
err->arg = -1;
return 1;
}
- e = (ltk_menuentry *)ltk_get_widget(tokens[1], LTK_MENUENTRY, err);
+ e = (ltk_menuentry *)ltk_get_widget(tokens[1], LTK_WIDGET_MENUENTRY, err);
if (!e) {
err->arg = 1;
return 1;
}
- submenu = (ltk_menu *)ltk_get_widget(tokens[3], LTK_MENU, err);
+ submenu = (ltk_menu *)ltk_get_widget(tokens[3], LTK_WIDGET_MENU, err);
if (!submenu) {
err->arg = 3;
return 1;
@@ -1354,7 +1356,7 @@ ltk_menuentry_cmd_detach_submenu(
err->arg = -1;
return 1;
}
- e = (ltk_menuentry *)ltk_get_widget(tokens[1], LTK_MENUENTRY, err);
+ e = (ltk_menuentry *)ltk_get_widget(tokens[1], LTK_WIDGET_MENUENTRY, err);
if (!e) {
err->arg = 1;
return 1;
diff --git a/src/proto_types.h b/src/proto_types.h
@@ -0,0 +1,51 @@
+#ifndef LTK_PROTO_TYPES_H
+#define LTK_PROTO_TYPES_H
+
+#define LTK_WIDGET_UNKNOWN 0
+#define LTK_WIDGET_ANY 1
+#define LTK_WIDGET_GRID 2
+#define LTK_WIDGET_BUTTON 3
+#define LTK_WIDGET_LABEL 4
+#define LTK_WIDGET_BOX 5
+#define LTK_WIDGET_MENU 6
+#define LTK_WIDGET_MENUENTRY 7
+#define LTK_NUM_WIDGETS 8
+
+#define LTK_WIDGETMASK_UNKNOWN (UINT32_C(1) << LTK_WIDGET_UNKNOWN)
+#define LTK_WIDGETMASK_ANY (UINT32_C(0xFFFF))
+#define LTK_WIDGETMASK_GRID (UINT32_C(1) << LTK_WIDGET_GRID)
+#define LTK_WIDGETMASK_BUTTON (UINT32_C(1) << LTK_WIDGET_BUTTON)
+#define LTK_WIDGETMASK_LABEL (UINT32_C(1) << LTK_WIDGET_LABEL)
+#define LTK_WIDGETMASK_BOX (UINT32_C(1) << LTK_WIDGET_BOX)
+#define LTK_WIDGETMASK_MENU (UINT32_C(1) << LTK_WIDGET_MENU)
+#define LTK_WIDGETMASK_MENUENTRY (UINT32_C(1) << LTK_WIDGET_MENUENTRY)
+
+/* P == protocol; W == widget */
+
+#define LTK_PEVENT_MOUSEPRESS 0
+#define LTK_PEVENT_MOUSERELEASE 1
+#define LTK_PEVENT_MOUSEMOTION 2
+#define LTK_PEVENT_KEYPRESS 3
+#define LTK_PEVENT_KEYRELEASE 4
+#define LTK_PEVENT_CONFIGURE 5
+#define LTK_PEVENT_STATECHANGE 6
+
+#define LTK_PEVENTMASK_NONE (UINT32_C(0))
+#define LTK_PEVENTMASK_MOUSEPRESS (UINT32_C(1) << LTK_PEVENT_MOUSEPRESS)
+#define LTK_PEVENTMASK_MOUSERELEASE (UINT32_C(1) << LTK_PEVENT_MOUSERELEASE)
+#define LTK_PEVENTMASK_MOUSEMOTION (UINT32_C(1) << LTK_PEVENT_MOUSEMOTION)
+#define LTK_PEVENTMASK_KEYPRESS (UINT32_C(1) << LTK_PEVENT_KEYPRESS)
+#define LTK_PEVENTMASK_KEYRELEASE (UINT32_C(1) << LTK_PEVENT_KEYRELEASE)
+#define LTK_PEVENTMASK_CONFIGURE (UINT32_C(1) << LTK_PEVENT_CONFIGURE)
+#define LTK_PEVENTMASK_EXPOSE (UINT32_C(1) << LTK_PEVENT_EXPOSE)
+#define LTK_PEVENTMASK_STATECHANGE (UINT32_C(1) << LTK_PEVENT_STATECHANGE)
+
+#define LTK_PWEVENT_MENUENTRY_PRESS 0
+#define LTK_PWEVENTMASK_MENUENTRY_NONE (UINT32_C(0))
+#define LTK_PWEVENTMASK_MENUENTRY_PRESS (UINT32_C(1) << LTK_PWEVENT_MENUENTRY_PRESS)
+
+#define LTK_PWEVENT_BUTTON_PRESS 0
+#define LTK_PWEVENTMASK_BUTTON_NONE (UINT32_C(0))
+#define LTK_PWEVENTMASK_BUTTON_PRESS (UINT32_C(1) << LTK_PWEVENT_BUTTON_PRESS)
+
+#endif /* LTK_PROTO_TYPES_H */
diff --git a/src/scrollbar.c b/src/scrollbar.c
@@ -52,9 +52,9 @@ static struct ltk_widget_vtable vtable = {
.mouse_enter = NULL,
.child_size_change = NULL,
.remove_child = NULL,
- .type = LTK_UNKNOWN, /* FIXME */
+ .type = LTK_WIDGET_UNKNOWN, /* FIXME */
/* FIXME: need different activatable state so arrow keys don't move onto scrollbar */
- .flags = LTK_NEEDS_REDRAW | LTK_NEEDS_SURFACE | LTK_ACTIVATABLE_ALWAYS,
+ .flags = LTK_NEEDS_REDRAW | LTK_ACTIVATABLE_ALWAYS,
};
static struct {
@@ -147,7 +147,8 @@ ltk_scrollbar_draw(ltk_widget *self, ltk_rect clip) {
fg = &theme.fg_normal;
}
ltk_surface *s;
- ltk_surface_cache_get_surface(self->surface_key, &s);
+ ltk_surface_cache_request_surface_size(scrollbar->key, self->rect.w, self->rect.h);
+ ltk_surface_cache_get_surface(scrollbar->key, &s);
ltk_surface_fill_rect(s, bg, (ltk_rect){0, 0, rect.w, rect.h});
/* FIXME: maybe too much calculation in draw function - move to
resizing function? */
@@ -242,6 +243,7 @@ ltk_scrollbar_create(ltk_window *window, ltk_orientation orient, void (*callback
sc->widget.rect.w = theme.size;
sc->callback = callback;
sc->callback_data = data;
+ sc->key = ltk_surface_cache_get_unnamed_key(window->surface_cache, sc->widget.ideal_w, sc->widget.ideal_h);
return sc;
}
@@ -249,6 +251,7 @@ ltk_scrollbar_create(ltk_window *window, ltk_orientation orient, void (*callback
static void
ltk_scrollbar_destroy(ltk_widget *self, int shallow) {
(void)shallow;
- ltk_surface_cache_release_key(self->surface_key);
+ ltk_scrollbar *sc = (ltk_scrollbar *)self;
+ ltk_surface_cache_release_key(sc->key);
ltk_free(self);
}
diff --git a/src/scrollbar.h b/src/scrollbar.h
@@ -28,6 +28,7 @@ typedef struct {
int last_mouse_x;
int last_mouse_y;
ltk_orientation orient;
+ ltk_surface_cache_key *key;
} ltk_scrollbar;
void ltk_scrollbar_set_virtual_size(ltk_scrollbar *scrollbar, int virtual_size);
diff --git a/src/surface_cache.c b/src/surface_cache.c
@@ -143,7 +143,7 @@ ltk_surface_cache_get_unnamed_key(ltk_surface_cache *cache, int min_w, int min_h
key->min_w = min_w;
key->min_h = min_h;
key->is_named = 0;
- key->widget_type = LTK_UNKNOWN;
+ key->widget_type = LTK_WIDGET_UNKNOWN;
key->id = -1;
key->refcount = 1;
return key;
diff --git a/src/widget.c b/src/widget.c
@@ -55,6 +55,117 @@ ltk_destroy_widget_hash(void) {
hash_locked = 0;
}
+/* FIXME: any way to optimize the whole event mask handling a bit? */
+void
+ltk_widget_remove_client(int client) {
+ 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);
+ for (size_t i = 0; i < ptr->masks_num; i++) {
+ if (ptr->event_masks[i].client == client) {
+ memmove(ptr->event_masks + i, ptr->event_masks + i + 1, ptr->masks_num - i - 1);
+ ptr->masks_num--;
+ size_t sz = ideal_array_size(ptr->masks_alloc, ptr->masks_num - 1);
+ if (sz != ptr->masks_alloc) {
+ ptr->masks_alloc = sz;
+ ptr->event_masks = ltk_reallocarray(ptr->event_masks, sz, sizeof(client_event_mask));
+ }
+ break;
+ }
+ }
+ }
+ }
+}
+
+static client_event_mask *
+get_mask_struct(ltk_widget *widget, int client) {
+ for (size_t i = 0; i < widget->masks_num; i++) {
+ if (widget->event_masks[i].client == client)
+ return &widget->event_masks[i];
+ }
+ widget->masks_alloc = ideal_array_size(widget->masks_alloc, widget->masks_num + 1);
+ widget->event_masks = ltk_reallocarray(widget->event_masks, widget->masks_alloc, sizeof(client_event_mask));
+ client_event_mask *m = &widget->event_masks[widget->masks_num];
+ widget->masks_num++;
+ m->client = client;
+ m->mask = m->lmask = m->wmask = m->lwmask = 0;
+ return m;
+}
+
+void
+ltk_widget_set_event_mask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->mask = mask;
+}
+
+void
+ltk_widget_set_event_lmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->lmask = mask;
+}
+
+void
+ltk_widget_set_event_wmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->wmask = mask;
+}
+
+void
+ltk_widget_set_event_lwmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->lwmask = mask;
+}
+
+void
+ltk_widget_add_to_event_mask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->mask |= mask;
+}
+
+void
+ltk_widget_add_to_event_lmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->lmask |= mask;
+}
+
+void
+ltk_widget_add_to_event_wmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->wmask |= mask;
+}
+
+void
+ltk_widget_add_to_event_lwmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->lwmask |= mask;
+}
+
+void
+ltk_widget_remove_from_event_mask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->mask &= ~mask;
+}
+
+void
+ltk_widget_remove_from_event_lmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->lmask &= ~mask;
+}
+
+void
+ltk_widget_remove_from_event_wmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->wmask &= ~mask;
+}
+
+void
+ltk_widget_remove_from_event_lwmask(ltk_widget *widget, int client, uint32_t mask) {
+ client_event_mask *m = get_mask_struct(widget, client);
+ m->lwmask &= ~mask;
+}
+
void
ltk_widgets_init() {
widget_hash = kh_init(widget);
@@ -77,11 +188,6 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
widget->window = window;
widget->parent = NULL;
- if (vtable->flags & LTK_NEEDS_SURFACE)
- widget->surface_key = ltk_surface_cache_get_unnamed_key(window->surface_cache, w, h);
- else
- widget->surface_key = NULL;
-
/* FIXME: possibly check that draw and destroy aren't NULL */
widget->vtable = vtable;
@@ -92,6 +198,9 @@ ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window *window,
widget->rect.w = w;
widget->rect.h = h;
+ widget->event_masks = NULL;
+ widget->masks_num = widget->masks_alloc = 0;
+
widget->row = 0;
widget->column = 0;
widget->row_span = 0;
@@ -146,17 +255,56 @@ ltk_widget_hide(ltk_widget *widget) {
That would make a bit more sense */
void
ltk_widget_resize(ltk_widget *widget) {
- /* FIXME: should surface maybe be resized first? */
+ int lock_client = -1;
+ for (size_t i = 0; i < widget->masks_num; i++) {
+ if (widget->event_masks[i].lmask & LTK_PEVENTMASK_CONFIGURE) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "eventl %s widget configure %d %d %d %d\n",
+ widget->id, widget->rect.x, widget->rect.y,
+ widget->rect.w, widget->rect.h
+ );
+ lock_client = widget->event_masks[i].client;
+ } else if (widget->event_masks[i].mask & LTK_PEVENTMASK_CONFIGURE) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "event %s widget configure %d %d %d %d\n",
+ widget->id, widget->rect.x, widget->rect.y,
+ widget->rect.w, widget->rect.h
+ );
+ }
+ }
+ if (lock_client >= 0) {
+ if (ltk_handle_lock_client(widget->window, lock_client))
+ return;
+ }
if (widget->vtable->resize)
widget->vtable->resize(widget);
- if (!(widget->vtable->flags & LTK_NEEDS_SURFACE))
- return;
- ltk_surface_cache_request_surface_size(widget->surface_key, widget->rect.w, widget->rect.h);
widget->dirty = 1;
}
void
ltk_widget_change_state(ltk_widget *widget, ltk_widget_state old_state) {
+ int lock_client = -1;
+ /* FIXME: give old and new state in event */
+ for (size_t i = 0; i < widget->masks_num; i++) {
+ if (widget->event_masks[i].lmask & LTK_PEVENTMASK_CONFIGURE) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "eventl %s widget statechange\n", widget->id
+ );
+ lock_client = widget->event_masks[i].client;
+ } else if (widget->event_masks[i].mask & LTK_PEVENTMASK_CONFIGURE) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "event %s widget statechange\n", widget->id
+ );
+ }
+ }
+ if (lock_client >= 0) {
+ if (ltk_handle_lock_client(widget->window, lock_client))
+ return;
+ }
if (widget->vtable->change_state)
widget->vtable->change_state(widget, old_state);
if (widget->vtable->flags & LTK_NEEDS_REDRAW) {
@@ -195,6 +343,34 @@ is_parent(ltk_widget *parent, ltk_widget *child) {
return child != NULL;
}
+static int
+queue_mouse_event(ltk_widget *widget, char *type, uint32_t mask, int x, int y) {
+ int lock_client = -1;
+ for (size_t i = 0; i < widget->masks_num; i++) {
+ if (widget->event_masks[i].lmask & mask) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "eventl %s widget %s %d %d %d %d\n",
+ widget->id, type, x, y,
+ x - widget->rect.x, y - widget->rect.y
+ );
+ lock_client = widget->event_masks[i].client;
+ } else if (widget->event_masks[i].mask & mask) {
+ ltk_queue_sock_write_fmt(
+ widget->event_masks[i].client,
+ "event %s widget %s %d %d %d %d\n",
+ widget->id, type, x, y,
+ x - widget->rect.x, y - widget->rect.y
+ );
+ }
+ }
+ if (lock_client >= 0) {
+ if (ltk_handle_lock_client(widget->window, lock_client))
+ return 1;
+ }
+ return 0;
+}
+
/* FIXME: This is still weird. */
void
ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event) {
@@ -228,7 +404,9 @@ ltk_window_mouse_press_event(ltk_window *window, ltk_button_event *event) {
if (cur_widget->state != LTK_DISABLED) {
/* FIXME: figure out whether this makes sense - currently, all widgets (unless disabled)
get mouse press, but they are only set to pressed if they are activatable */
- if (cur_widget->vtable->mouse_press)
+ if (queue_mouse_event(cur_widget, "mousepress", LTK_PEVENTMASK_MOUSEPRESS, event->x, event->y))
+ handled = 1;
+ else if (cur_widget->vtable->mouse_press)
handled = cur_widget->vtable->mouse_press(cur_widget, event);
/* set first non-disabled widget to pressed widget */
if (first && event->button == LTK_BUTTONL && (cur_widget->vtable->flags & LTK_ACTIVATABLE_ALWAYS)) {
@@ -257,8 +435,11 @@ ltk_window_mouse_release_event(ltk_window *window, ltk_button_event *event) {
widget = get_widget_under_pointer(widget, event->x, event->y);
}
/* FIXME: loop up to top of hierarchy if not handled */
- if (widget && widget->vtable->mouse_release)
+ if (widget && queue_mouse_event(widget, "mouserelease", LTK_PEVENTMASK_MOUSERELEASE, event->x, event->y)) {
+ /* NOP */
+ } else if (widget && widget->vtable->mouse_release) {
widget->vtable->mouse_release(widget, event);
+ }
if (event->button == LTK_BUTTONL) {
ltk_window_set_pressed_widget(window, NULL);
/* send motion notify to widget under pointer */
@@ -290,7 +471,9 @@ ltk_window_motion_notify_event(ltk_window *window, ltk_motion_event *event) {
while (cur_widget) {
int handled = 0;
if (cur_widget->state != LTK_DISABLED) {
- if (cur_widget->vtable->motion_notify)
+ if (queue_mouse_event(cur_widget, "mousemotion", LTK_PEVENTMASK_MOUSEMOTION, event->x, event->y))
+ handled = 1;
+ else if (cur_widget->vtable->motion_notify)
handled = cur_widget->vtable->motion_notify(cur_widget, event);
/* set first non-disabled widget to hover widget */
/* FIXME: should enter/leave event be sent to parent
@@ -329,7 +512,7 @@ ltk_get_widget(const char *id, ltk_widget_type type, ltk_error *err) {
return NULL;
}
widget = kh_value(widget_hash, k);
- if (type != LTK_WIDGET && widget->vtable->type != type) {
+ if (type != LTK_WIDGET_ANY && widget->vtable->type != type) {
err->type = ERR_INVALID_WIDGET_TYPE;
return NULL;
}
@@ -397,7 +580,7 @@ ltk_widget_destroy_cmd(
return 1;
}
}
- ltk_widget *widget = ltk_get_widget(tokens[1], LTK_WIDGET, err);
+ ltk_widget *widget = ltk_get_widget(tokens[1], LTK_WIDGET_ANY, err);
if (!widget) {
err->arg = 1;
return 1;
diff --git a/src/widget.h b/src/widget.h
@@ -25,6 +25,7 @@
typedef struct ltk_widget ltk_widget;
+typedef uint32_t ltk_widget_type;
typedef enum {
LTK_ACTIVATABLE_NORMAL = 1,
@@ -32,8 +33,7 @@ typedef enum {
LTK_ACTIVATABLE_ALWAYS = 1|2,
LTK_GRABS_INPUT = 4,
LTK_NEEDS_REDRAW = 8,
- LTK_NEEDS_SURFACE = 16, /* FIXME: let widgets handle this themselves */
- LTK_HOVER_IS_ACTIVE = 32,
+ LTK_HOVER_IS_ACTIVE = 16,
} ltk_widget_flags;
typedef enum {
@@ -49,47 +49,44 @@ typedef enum {
} ltk_orientation;
typedef enum {
- /* FIXME: maybe remove normal or set it to 0 */
- LTK_NORMAL = 1,
- LTK_HOVER = 2,
- LTK_PRESSED = 4,
- LTK_ACTIVE = 8,
- LTK_HOVERACTIVE = 2 | 8,
- LTK_DISABLED = 16,
+ LTK_NORMAL = 0,
+ LTK_HOVER = 1,
+ LTK_PRESSED = 2,
+ LTK_ACTIVE = 4,
+ LTK_HOVERACTIVE = 1 | 4,
+ LTK_DISABLED = 8,
} ltk_widget_state;
-typedef enum {
- /* for e.g. scrollbar, which can't be directly accessed by users */
- LTK_UNKNOWN = 0,
- LTK_GRID,
- LTK_BUTTON,
- LTK_DRAW,
- LTK_LABEL,
- LTK_WIDGET,
- LTK_BOX,
- LTK_MENU,
- LTK_MENUENTRY,
- LTK_NUM_WIDGETS
-} ltk_widget_type;
-
#include "surface_cache.h"
struct ltk_window;
struct ltk_widget_vtable;
+typedef struct {
+ int client; /* index of client */
+ uint32_t mask; /* generic event mask */
+ uint32_t lmask; /* generic lock mask */
+ uint32_t wmask; /* event mask for specific widget type */
+ uint32_t lwmask; /* lock event mask for specific widget type */
+} client_event_mask;
+
struct ltk_widget {
struct ltk_window *window;
struct ltk_widget *parent;
char *id;
- ltk_surface_cache_key *surface_key;
struct ltk_widget_vtable *vtable;
ltk_rect rect;
unsigned int ideal_w;
unsigned int ideal_h;
+ client_event_mask *event_masks;
+ /* FIXME: kind of a waste of space to use size_t here */
+ size_t masks_num;
+ size_t masks_alloc;
+
ltk_widget_state state;
unsigned int sticky;
unsigned short row;
@@ -152,5 +149,18 @@ void ltk_remove_widget(const char *id);
void ltk_widgets_cleanup();
void ltk_widgets_init();
void ltk_widget_resize(ltk_widget *widget);
+void ltk_widget_remove_client(int client);
+void ltk_widget_set_event_mask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_set_event_lmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_set_event_wmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_set_event_lwmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_add_to_event_mask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_add_to_event_lmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_add_to_event_wmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_add_to_event_lwmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_remove_from_event_mask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_remove_from_event_lmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_remove_from_event_wmask(ltk_widget *widget, int client, uint32_t mask);
+void ltk_widget_remove_from_event_lwmask(ltk_widget *widget, int client, uint32_t mask);
#endif /* LTK_WIDGET_H */
diff --git a/test.gui b/test.gui
@@ -19,3 +19,4 @@ button btn6 create "2 I'm another boring button."
box box2 add btn4 ew
box box2 add btn5 e
box box2 add btn6
+mask-add btn1 button press
diff --git a/test.sh b/test.sh
@@ -16,7 +16,7 @@ fi
cat test.gui | ./src/ltkc $ltk_id | while read cmd
do
case "$cmd" in
- "btn1 button_click")
+ "event btn1 button press")
echo "quit"
;;
*)
diff --git a/testbox.sh b/testbox.sh
@@ -7,18 +7,19 @@ if [ $? -ne 0 ]; then
exit 1
fi
-cmds="box box1 create vertical\nset-root-widget box1\nbutton exit_btn create \"Exit\"\nbox box1 add exit_btn
+cmds="box box1 create vertical\nset-root-widget box1\nbutton exit_btn create \"Exit\"\nmask-add exit_btn button press\nbox box1 add exit_btn
$(curl -s gopher://lumidify.org | awk -F'\t' '
BEGIN {btn = 0; lbl = 0;}
/^i/ { printf "label lbl%s create \"%s\"\nbox box1 add lbl%s w\n", lbl, substr($1, 2), lbl; lbl++ }
-/^[1gI]/ { printf "button btn%s create \"%s\"\nbox box1 add btn%s w\n", btn, substr($1, 2), btn; btn++ }')"
+/^[1gI]/ { printf "button btn%s create \"%s\"\nbox box1 add btn%s w\n", btn, substr($1, 2), btn; btn++ }')
+mask-add btn0 button press"
echo "$cmds" | ./src/ltkc $ltk_id | while read cmd
do
case "$cmd" in
- "exit_btn button_click")
+ "event exit_btn button press")
echo "quit"
;;
- "btn0 button_click")
+ "event btn0 button press")
echo "button bla create \"safhaskfldshk\"\nbox box1 add bla w"
;;
*)