commit a57cf5fb31150459013031565c1fd026e03901de
parent 5d5aaf08dc19d798f31cab139e1030918c1f62ac
Author: lumidify <nobody@lumidify.org>
Date: Sun, 27 Dec 2020 22:03:51 +0100
Cleanup
Diffstat:
M | Makefile | | | 4 | ++-- |
M | ltk.h | | | 25 | ------------------------- |
M | ltkc.c | | | 57 | ++++++++++++++++++++++++++++++++++----------------------- |
M | ltkd.c | | | 760 | ++++++++++++++++++++++++++++++++++++++++++------------------------------------- |
M | util.c | | | 15 | +++++++++++++++ |
M | util.h | | | 1 | + |
6 files changed, 455 insertions(+), 407 deletions(-)
diff --git a/Makefile b/Makefile
@@ -7,8 +7,8 @@ all: ltkd ltkc
ltkd: $(OBJ) $(COMPATOBJ)
$(CC) -o $@ $(OBJ) $(COMPATOBJ) $(LDFLAGS)
-ltkc: ltkc.o
- $(CC) -o $@ ltkc.o
+ltkc: ltkc.o util.o
+ $(CC) -o $@ ltkc.o util.o
%.o: %.c
$(CC) -c -o $@ $< $(CFLAGS)
diff --git a/ltk.h b/ltk.h
@@ -137,44 +137,19 @@ typedef struct ltk_window {
} ltk_window;
void ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect);
-
void ltk_fatal(const char *msg);
-
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_mainloop(ltk_window *window);
-
-ltk_window *ltk_create_window(
- const char *theme_path, const char *title,
- int x, int y, unsigned int w, unsigned int h);
-
-void ltk_redraw_window(ltk_window *window);
-
-void ltk_destroy_window(ltk_window *window);
-
-void ltk_window_other_event(ltk_window *window, XEvent event);
-
int ltk_collide_rect(ltk_rect rect, int x, int y);
-
void ltk_remove_active_widget(ltk_widget *widget);
-
void ltk_set_active_widget(ltk_window *window, ltk_widget *widget);
-
void ltk_fill_widget_defaults(ltk_widget *widget, const char *id, ltk_window * window,
void (*draw) (void *), void (*change_state) (void *),
void (*destroy) (void *, int), 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);
-
-void ltk_handle_event(ltk_window *window, XEvent event);
-
int ltk_check_widget_id_free(ltk_window *window, const char *id,
const char *caller);
ltk_widget *ltk_get_widget(ltk_window *window, const char *id,
diff --git a/ltkc.c b/ltkc.c
@@ -24,7 +24,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <stdint.h>
#include <stddef.h>
#include <unistd.h>
#include <time.h>
@@ -32,24 +31,15 @@
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include "util.h"
#define BLK_SIZE 128
-/* If `needed` is larger than `*alloc_size`, resize `*str` to `*alloc_size * 2`. */
-static int
-grow_string(char **str, int *alloc_size, int needed) {
- if (needed <= *alloc_size) return 0;
- char *new = realloc(*str, *alloc_size * 2);
- if (!new) return 1;
- *str = new;
- *alloc_size = *alloc_size * 2;
- return 0;
-}
static struct {
- char *in_buffer;
+ char *in_buffer; /* text that is read from stdin and written to the socket */
int in_len;
int in_alloc;
- char *out_buffer;
+ char *out_buffer; /* text that is read from the socket and written to stdout */
int out_len;
int out_alloc;
} io_buffers;
@@ -97,7 +87,10 @@ int main(int argc, char *argv[]) {
maxwfd = sockfd > outfd ? sockfd : outfd;
while (1) {
- if (!FD_ISSET(infd, &rallfds) && !FD_ISSET(sockfd, &rallfds) && io_buffers.in_len == 0 && io_buffers.out_len == 0)
+ if (!FD_ISSET(infd, &rallfds) &&
+ !FD_ISSET(sockfd, &rallfds) &&
+ io_buffers.in_len == 0 &&
+ io_buffers.out_len == 0)
break;
rfds = rallfds;
wfds = wallfds;
@@ -105,9 +98,14 @@ int main(int argc, char *argv[]) {
leading to the loop looping way too fast */
select(maxrfd + 1, &rfds, NULL, NULL, &tv);
select(maxwfd + 1, NULL, &wfds, NULL, &tv);
+
if (FD_ISSET(sockfd, &rfds)) {
- grow_string(&io_buffers.out_buffer, &io_buffers.out_alloc, io_buffers.out_len + BLK_SIZE);
- int nread = read(sockfd, io_buffers.out_buffer + io_buffers.out_len, BLK_SIZE);
+ ltk_grow_string(&io_buffers.out_buffer,
+ &io_buffers.out_alloc,
+ io_buffers.out_len + BLK_SIZE);
+ int nread = read(sockfd,
+ io_buffers.out_buffer + io_buffers.out_len,
+ BLK_SIZE);
if (nread < 0) {
return 2;
} else if (nread == 0) {
@@ -117,9 +115,14 @@ int main(int argc, char *argv[]) {
io_buffers.out_len += nread;
}
}
+
if (FD_ISSET(infd, &rfds)) {
- grow_string(&io_buffers.in_buffer, &io_buffers.in_alloc, io_buffers.in_len + BLK_SIZE);
- int nread = read(infd, io_buffers.in_buffer + io_buffers.in_len, BLK_SIZE);
+ ltk_grow_string(&io_buffers.in_buffer,
+ &io_buffers.in_alloc,
+ io_buffers.in_len + BLK_SIZE);
+ int nread = read(infd,
+ io_buffers.in_buffer + io_buffers.in_len,
+ BLK_SIZE);
if (nread < 0) {
return 2;
} else if (nread == 0) {
@@ -128,28 +131,36 @@ int main(int argc, char *argv[]) {
io_buffers.in_len += nread;
}
}
+
if (FD_ISSET(sockfd, &wfds)) {
- int maxwrite = BLK_SIZE > io_buffers.in_len ? io_buffers.in_len : BLK_SIZE;
+ int maxwrite = BLK_SIZE > io_buffers.in_len ?
+ io_buffers.in_len : BLK_SIZE;
int nwritten = write(sockfd, io_buffers.in_buffer, maxwrite);
if (nwritten < 0) {
return 2;
} else {
- memmove(io_buffers.in_buffer, io_buffers.in_buffer + nwritten, io_buffers.in_len - nwritten);
+ memmove(io_buffers.in_buffer,
+ io_buffers.in_buffer + nwritten,
+ io_buffers.in_len - nwritten);
io_buffers.in_len -= nwritten;
}
}
+
if (FD_ISSET(outfd, &wfds)) {
- int maxwrite = BLK_SIZE > io_buffers.out_len ? io_buffers.out_len : BLK_SIZE;
+ int maxwrite = BLK_SIZE > io_buffers.out_len ?
+ io_buffers.out_len : BLK_SIZE;
int nwritten = write(outfd, io_buffers.out_buffer, maxwrite);
if (nwritten < 0) {
return 2;
} else {
- memmove(io_buffers.out_buffer, io_buffers.out_buffer + nwritten, io_buffers.out_len - nwritten);
+ memmove(io_buffers.out_buffer,
+ io_buffers.out_buffer + nwritten,
+ io_buffers.out_len - nwritten);
io_buffers.out_len -= nwritten;
}
}
}
- /* FIXME: close socket */
+ close(sockfd);
return 0;
}
diff --git a/ltkd.c b/ltkd.c
@@ -25,7 +25,6 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
-#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
@@ -73,356 +72,43 @@ static struct ltk_sock_info {
struct token_list tokens; /* current tokens */
} sockets[MAX_SOCK_CONNS];
+static int ltk_mainloop(ltk_window *window);
+static ltk_window *ltk_create_window(const char *theme_path, const char
+ *title, int x, int y, unsigned int w, unsigned int h);
+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);
+static void ltk_handle_event(ltk_window *window, XEvent event);
static void ltk_load_theme(ltk_window *window, const char *path);
static void ltk_destroy_theme(ltk_theme *theme);
static ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2);
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 tokenize_command(struct ltk_sock_info *sock);
+static void ltk_set_root_widget_cmd(ltk_window *window, char **tokens, int num_tokens);
+static void process_commands(ltk_window *window, struct ltk_sock_info *sock);
+static ltk_rect ltk_rect_union(ltk_rect r1, ltk_rect r2);
+static int add_client(int fd);
+static int listen_sock(const char *sock_path);
+static int accept_sock(int listenfd);
-static int running = 1;
-
-static int
-push_token(struct token_list *tl, char *token) {
- int new_size;
- if (tl->num_tokens >= tl->num_alloc) {
- new_size = (tl->num_alloc * 2) > (tl->num_tokens + 1) ?
- (tl->num_alloc * 2) : (tl->num_tokens + 1);
- char **new = realloc(tl->tokens, new_size * sizeof(char *));
- if (!new) return -1;
- tl->tokens = new;
- tl->num_alloc = new_size;
- }
- tl->tokens[tl->num_tokens++] = token;
-
- return 0;
-}
-
-/* If `needed` is larger than `*alloc_size`, resize `*str` to `*alloc_size * 2`. */
-static int
-grow_string(char **str, int *alloc_size, int needed) {
- if (needed <= *alloc_size) return 0;
- int new_size = needed > (*alloc_size * 2) ? needed : (*alloc_size * 2);
- char *new = realloc(*str, new_size);
- if (!new) return 1;
- *str = new;
- *alloc_size = new_size;
- return 0;
-}
-/* FIXME: non-blocking io? */
-/* Read up to READ_BLK_SIZE bytes from the socket.
- Returns -1 if an error occurred, 0 if the connection was closed, 1 otherwise. */
-static int
-read_sock(struct ltk_sock_info *sock) {
- int nread;
- char *old = sock->read;
- int ret = grow_string(&sock->read, &sock->read_alloc, sock->read_len + READ_BLK_SIZE);
- if (ret) return -1; /* fixme: errno? */
- /* move tokens to new addresses - this was added as an
- afterthought and really needs to be cleaned up */
- if (sock->read != old) {
- for (int i = 0; i < sock->tokens.num_tokens; i++) {
- sock->tokens.tokens[i] = sock->read + (sock->tokens.tokens[i] - old);
- }
- }
- nread = read(sock->fd, sock->read + sock->read_len, READ_BLK_SIZE);
- if (nread == -1 || nread == 0)
- return nread;
- sock->read_len += nread;
-
- return 1;
-}
-
-/* Write up to WRITE_BLK_SIZE bytes to the socket.
- Returns -1 on error, 0 otherwise. */
-static int
-write_sock(struct ltk_sock_info *sock) {
- if (!sock->write_len)
- return 0;
- int write_len = WRITE_BLK_SIZE > sock->write_len - sock->write_cur ?
- sock->write_len - sock->write_cur : WRITE_BLK_SIZE;
- int nwritten = write(sock->fd, sock->to_write + sock->write_cur, write_len);
- if (nwritten == -1)
- return nwritten;
- sock->write_cur += nwritten;
- return 0;
-}
-
-/* Queue str to be written to the socket. If len is < 0, it is set to strlen(str).
- 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) {
- if (sock->write_cur > 0) {
- memmove(sock->to_write, sock->to_write + sock->write_cur,
- sock->write_len - sock->write_cur);
- sock->write_len -= sock->write_cur;
- sock->write_cur = 0;
- }
-
- if (len < 0)
- len = strlen(str);
-
- if (sock->write_alloc - sock->write_len < len &&
- grow_string(&sock->to_write, &sock->write_alloc, sock->write_len + len))
- return -1;
-
- (void)strncpy(sock->to_write + sock->write_len, str, len);
- sock->write_len += len;
-
- return 0;
-}
-
-/* Returns 0 if the end of a command was encountered, 1 otherwise */
-static int
-tokenize_command(struct ltk_sock_info *sock) {
- for (; sock->read_cur < sock->read_len; sock->read_cur++) {
- if (!sock->in_token) {
- push_token(&sock->tokens, sock->read + sock->read_cur - sock->offset);
- sock->in_token = 1;
- }
- if (sock->read[sock->read_cur] == '\\') {
- sock->bs++;
- sock->bs %= 2;
- sock->read[sock->read_cur-sock->offset] = '\\';
- } else if (sock->read[sock->read_cur] == '\n' && !sock->in_str) {
- sock->read[sock->read_cur-sock->offset] = '\0';
- sock->read_cur++;
- sock->offset = 0;
- sock->in_token = 0;
- return 0;
- } else if (sock->read[sock->read_cur] == '"') {
- sock->offset++;
- if (sock->bs) {
- sock->read[sock->read_cur-sock->offset] = '"';
- sock->bs = 0;
- } else {
- sock->in_str = !sock->in_str;
- }
- } else if (sock->read[sock->read_cur] == ' ' && !sock->in_str) {
- sock->read[sock->read_cur-sock->offset] = '\0';
- sock->in_token = !sock->in_token;
- } else {
- sock->read[sock->read_cur-sock->offset] = sock->read[sock->read_cur];
- sock->bs = 0;
- }
- }
-
- return 1;
-}
-
-void
-ltk_clean_up(ltk_window *window) {
- ltk_destroy_theme(window->theme);
- ltk_destroy_window(window);
-}
-
-void
-ltk_quit(ltk_window *window) {
- ltk_clean_up(window);
- running = 0;
-}
-
-static void
-ltk_set_root_widget_cmd(
- ltk_window *window,
- char **tokens,
- int num_tokens) {
- ltk_widget *widget;
- if (num_tokens != 2) {
- (void)fprintf(stderr, "set-root-widget: Invalid number of arguments.\n");
- return;
- }
- widget = ltk_get_widget(window, tokens[1], LTK_WIDGET, "set-root-widget");
- if (!widget) return;
- window->root_widget = widget;
- int w = widget->rect.w;
- int h = widget->rect.h;
- widget->rect.w = window->rect.w;
- widget->rect.h = window->rect.h;
- if (widget->resize) {
- widget->resize(widget, w, h);
- }
-}
-
-static void
-process_commands(ltk_window *window, struct ltk_sock_info *sock) {
- char **tokens;
- int num_tokens;
- while (!tokenize_command(sock)) {
- tokens = sock->tokens.tokens;
- num_tokens = sock->tokens.num_tokens;
- if (num_tokens < 1)
- continue;
- if (strcmp(tokens[0], "grid") == 0) {
- ltk_grid_cmd(window, tokens, num_tokens);
- } else if (strcmp(tokens[0], "button") == 0) {
- ltk_button_cmd(window, tokens, num_tokens);
- } else if (strcmp(tokens[0], "set-root-widget") == 0) {
- ltk_set_root_widget_cmd(window, tokens, num_tokens);
- } else if (strcmp(tokens[0], "draw") == 0) {
- ltk_draw_cmd(window, tokens, num_tokens);
- } else if (strcmp(tokens[0], "quit") == 0) {
- ltk_quit(window);
- } else {
- /* FIXME... */
- (void)fprintf(stderr, "Invalid command.\n");
- }
- sock->tokens.num_tokens = 0;
- }
- 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]);
- ptrdiff_t offset = sock->tokens.tokens[0] - sock->read;
- /* Hmm, seems a bit ugly... */
- for (int i = 0; i < sock->tokens.num_tokens; i++) {
- sock->tokens.tokens[i] -= offset;
- }
- sock->read_len -= offset;
- sock->read_cur -= offset;
- } else if (sock->tokens.num_tokens == 0) {
- sock->read_len = 0;
- sock->read_cur = 0;
- }
-}
-
-static ltk_rect
-ltk_rect_union(ltk_rect r1, ltk_rect r2) {
- ltk_rect u;
- u.x = r1.x < r2.x ? r1.x : r2.x;
- u.y = r1.y < r2.y ? r1.y : r2.y;
- int x2 = r1.x + r1.w < r2.x + r2.w ? r2.x + r2.w : r1.x + r1.w;
- int y2 = r1.y + r1.h < r2.y + r2.h ? r2.y + r2.h : r1.y + r1.h;
- u.w = x2 - u.x;
- u.h = y2 - 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)
- window->dirty_rect = rect;
- else
- window->dirty_rect = ltk_rect_union(rect, window->dirty_rect);
-}
-
-void
-ltk_fatal(const char *msg) {
- (void)fprintf(stderr, msg);
- /* FIXME: clean up first */
- exit(1);
-};
-
-int
-ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col) {
- if (!XParseColor(window->dpy, window->cm, hex, col)) {
- (void)fprintf(stderr, "Invalid color: %s\n", hex);
- return 0;
- }
- XAllocColor(window->dpy, window->cm, col);
-
- return 1;
-}
-
-void
-ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data) {
- /* FIXME: make it nicer and safer */
- struct ltk_event_queue *new = malloc(sizeof(struct ltk_event_queue));
- if (!new) ltk_fatal("Unable to queue event.\n");
- new->event_type = type;
- int id_len = strlen(id);
- int data_len = strlen(data);
- new->data = malloc(id_len + data_len + 3);
- if (!new->data) ltk_fatal("Unable to queue event.\n");
- 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;
-}
-
-static int
-add_client(int fd) {
- for (int i = 0; i < MAX_SOCK_CONNS; i++) {
- if (sockets[i].fd == -1) {
- sockets[i].fd = fd;
- sockets[i].event_mask = ~0; /* FIXME */
- sockets[i].read_len = 0;
- sockets[i].write_len = 0;
- sockets[i].write_cur = 0;
- sockets[i].offset = 0;
- sockets[i].in_str = 0;
- sockets[i].read_cur = 0;
- sockets[i].bs = 0;
- sockets[i].tokens.num_tokens = 0;
- return i;
- }
- }
-
- return -1;
-}
-
-/* largely copied from APUE */
-static int
-listen_sock(const char *sock_path) {
- int fd, len, err, rval;
- struct sockaddr_un un;
-
- if (strlen(sock_path) >= sizeof(un.sun_path)) {
- errno = ENAMETOOLONG;
- return -1;
- }
-
- if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
- return -2;
-
- unlink(sock_path);
-
- memset(&un, 0, sizeof(un));
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path, sock_path);
- len = offsetof(struct sockaddr_un, sun_path) + strlen(sock_path);
- if (bind(fd, (struct sockaddr *)&un, len) < 0) {
- rval = -3;
- goto errout;
- }
-
- if (listen(fd, 10) < 0) {
- rval = -4;
- goto errout;
- }
-
- return fd;
+static int maxsocket = -1;
+static char running = 1;
+static char sock_write_available = 0;
-errout:
- err = errno;
- close(fd);
- errno = err;
- return rval;
+int main(int argc, char *argv[]) {
+ ltk_window *window = ltk_create_window("theme.ini", "Demo", 0, 0, 500, 500);
+ return ltk_mainloop(window);
}
static int
-accept_sock(int listenfd) {
- int clifd;
- socklen_t len;
- struct sockaddr_un un;
-
- len = sizeof(un);
- if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) {
- return -1;
- }
-
- return clifd;
-}
-
-int
ltk_mainloop(ltk_window *window) {
XEvent event;
fd_set rfds, wfds, rallfds, wallfds;
- int maxi, maxfd, listenfd;
+ int maxfd, listenfd;
int rretval, wretval;
int clifd;
struct timeval tv;
@@ -448,7 +134,6 @@ ltk_mainloop(ltk_window *window) {
FD_SET(listenfd, &rallfds);
maxfd = listenfd;
- maxi = -1;
while (running) {
rfds = rallfds;
@@ -461,11 +146,8 @@ ltk_mainloop(ltk_window *window) {
XNextEvent(window->dpy, &event);
ltk_handle_event(window, event);
}
- /* FIXME: somehow keep track of whether anything has to be written,
- otherwise it always has to loop over all fds to check - the writing
- fds are usually always set, so this is really run on every loop
- iteration, which is bad */
- if (rretval > 0 || wretval > 0) {
+
+ if (rretval > 0 || (sock_write_available && wretval > 0)) {
if (FD_ISSET(listenfd, &rfds)) {
if ((clifd = accept_sock(listenfd)) < 0) {
fprintf(stderr, "Error accepting socket connection\n");
@@ -476,11 +158,11 @@ ltk_mainloop(ltk_window *window) {
FD_SET(clifd, &wallfds);
if (clifd > maxfd)
maxfd = clifd;
- if (i > maxi)
- maxi = i;
+ if (i > maxsocket)
+ maxsocket = i;
continue;
}
- for (int i = 0; i <= maxi; i++) {
+ for (int i = 0; i <= maxsocket; i++) {
if ((clifd = sockets[i].fd) < 0)
continue;
if (FD_ISSET(clifd, &rfds)) {
@@ -499,17 +181,19 @@ ltk_mainloop(ltk_window *window) {
}
}
}
+
if (window->dirty_rect.w != 0 && window->dirty_rect.h != 0) {
ltk_redraw_window(window);
window->dirty_rect.w = 0;
window->dirty_rect.h = 0;
}
+
if (window->last_event && running) {
struct ltk_event_queue *cur = window->last_event;
struct ltk_event_queue *last;
do {
int event_len = strlen(cur->data);
- for (int i = 0; i <= maxi; i++) {
+ 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)
exit(1); /* FIXME: error handling */
@@ -524,7 +208,10 @@ ltk_mainloop(ltk_window *window) {
}
}
+
for (int i = 0; i < MAX_SOCK_CONNS; i++) {
+ if (sockets[i].fd >= 0)
+ close(sockets[i].fd);
if (sockets[i].read)
free(sockets[i].read);
if (sockets[i].to_write)
@@ -533,12 +220,108 @@ ltk_mainloop(ltk_window *window) {
free(sockets[i].tokens.tokens);
}
+ close(listenfd);
+
unlink("ltk.sock");
return 0;
}
-void
+void
+ltk_clean_up(ltk_window *window) {
+ ltk_destroy_theme(window->theme);
+ ltk_destroy_window(window);
+}
+
+void
+ltk_quit(ltk_window *window) {
+ ltk_clean_up(window);
+ running = 0;
+}
+
+static void
+ltk_set_root_widget_cmd(
+ ltk_window *window,
+ char **tokens,
+ int num_tokens) {
+ ltk_widget *widget;
+ if (num_tokens != 2) {
+ (void)fprintf(stderr, "set-root-widget: Invalid number of arguments.\n");
+ return;
+ }
+ widget = ltk_get_widget(window, tokens[1], LTK_WIDGET, "set-root-widget");
+ if (!widget) return;
+ window->root_widget = widget;
+ int w = widget->rect.w;
+ int h = widget->rect.h;
+ widget->rect.w = window->rect.w;
+ widget->rect.h = window->rect.h;
+ if (widget->resize) {
+ widget->resize(widget, w, h);
+ }
+}
+
+static ltk_rect
+ltk_rect_union(ltk_rect r1, ltk_rect r2) {
+ ltk_rect u;
+ u.x = r1.x < r2.x ? r1.x : r2.x;
+ u.y = r1.y < r2.y ? r1.y : r2.y;
+ int x2 = r1.x + r1.w < r2.x + r2.w ? r2.x + r2.w : r1.x + r1.w;
+ int y2 = r1.y + r1.h < r2.y + r2.h ? r2.y + r2.h : r1.y + r1.h;
+ u.w = x2 - u.x;
+ u.h = y2 - 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)
+ window->dirty_rect = rect;
+ else
+ window->dirty_rect = ltk_rect_union(rect, window->dirty_rect);
+}
+
+void
+ltk_fatal(const char *msg) {
+ (void)fprintf(stderr, msg);
+ /* FIXME: clean up first */
+ exit(1);
+};
+
+int
+ltk_create_xcolor(ltk_window *window, const char *hex, XColor *col) {
+ if (!XParseColor(window->dpy, window->cm, hex, col)) {
+ (void)fprintf(stderr, "Invalid color: %s\n", hex);
+ return 0;
+ }
+ XAllocColor(window->dpy, window->cm, col);
+
+ return 1;
+}
+
+void
+ltk_queue_event(ltk_window *window, ltk_event_type type, const char *id, const char *data) {
+ /* FIXME: make it nicer and safer */
+ struct ltk_event_queue *new = malloc(sizeof(struct ltk_event_queue));
+ if (!new) ltk_fatal("Unable to queue event.\n");
+ new->event_type = type;
+ int id_len = strlen(id);
+ int data_len = strlen(data);
+ new->data = malloc(id_len + data_len + 3);
+ if (!new->data) ltk_fatal("Unable to queue event.\n");
+ 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;
+}
+
+static void
ltk_redraw_window(ltk_window *window) {
ltk_widget *ptr;
if (!window) return;
@@ -555,7 +338,7 @@ ltk_redraw_window(ltk_window *window) {
ptr->draw(ptr);
}
-void
+static void
ltk_window_other_event(ltk_window *window, XEvent event) {
ltk_widget *ptr = window->root_widget;
if (event.type == ConfigureNotify) {
@@ -587,7 +370,7 @@ ltk_window_other_event(ltk_window *window, XEvent event) {
}
}
-ltk_window *
+static ltk_window *
ltk_create_window(const char *theme_path, const char *title, int x, int y, unsigned int w, unsigned int h) {
ltk_window *window = malloc(sizeof(ltk_window));
if (!window)
@@ -638,7 +421,7 @@ ltk_create_window(const char *theme_path, const char *title, int x, int y, unsig
return window;
}
-void
+static void
ltk_destroy_window(ltk_window *window) {
khint_t k;
ltk_widget *ptr;
@@ -832,7 +615,7 @@ ltk_widget_motion_notify_event(ltk_widget *widget, XEvent event) {
widget->motion_notify(widget, event);
}
-void
+static void
ltk_handle_event(ltk_window *window, XEvent event) {
ltk_widget *root_widget = window->root_widget;
switch (event.type) {
@@ -906,7 +689,270 @@ ltk_remove_widget(ltk_window *window, const char *id) {
}
}
-int main(int argc, char *argv[]) {
- ltk_window *window = ltk_create_window("theme.ini", "Demo", 0, 0, 500, 500);
- return ltk_mainloop(window);
+/* 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. */
+static int
+push_token(struct token_list *tl, char *token) {
+ int new_size;
+ if (tl->num_tokens >= tl->num_alloc) {
+ new_size = (tl->num_alloc * 2) > (tl->num_tokens + 1) ?
+ (tl->num_alloc * 2) : (tl->num_tokens + 1);
+ char **new = realloc(tl->tokens, new_size * sizeof(char *));
+ if (!new) return -1;
+ tl->tokens = new;
+ tl->num_alloc = new_size;
+ }
+ tl->tokens[tl->num_tokens++] = token;
+
+ return 0;
+}
+
+/* Add a new client to the socket list and return the index in `sockets`.
+ Returns -1 if there is no space for a new client. */
+static int
+add_client(int fd) {
+ for (int i = 0; i < MAX_SOCK_CONNS; i++) {
+ if (sockets[i].fd == -1) {
+ sockets[i].fd = fd;
+ sockets[i].event_mask = ~0; /* FIXME */
+ sockets[i].read_len = 0;
+ sockets[i].write_len = 0;
+ sockets[i].write_cur = 0;
+ sockets[i].offset = 0;
+ sockets[i].in_str = 0;
+ sockets[i].read_cur = 0;
+ sockets[i].bs = 0;
+ sockets[i].tokens.num_tokens = 0;
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/* largely copied from APUE */
+/* Listen on the socket at `sock_path`.
+ Returns the file descriptor of the opened socket on success.
+ Returns -1 if `sock_path` is too long
+ -2 if the socket could not be created
+ -3 if the socket could not be bound to the path
+ -4 if the socket could not be listened on */
+static int
+listen_sock(const char *sock_path) {
+ int fd, len, err, rval;
+ struct sockaddr_un un;
+
+ if (strlen(sock_path) >= sizeof(un.sun_path)) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -2;
+
+ unlink(sock_path);
+
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, sock_path);
+ len = offsetof(struct sockaddr_un, sun_path) + strlen(sock_path);
+ if (bind(fd, (struct sockaddr *)&un, len) < 0) {
+ rval = -3;
+ goto errout;
+ }
+
+ if (listen(fd, 10) < 0) {
+ rval = -4;
+ goto errout;
+ }
+
+ return fd;
+
+errout:
+ err = errno;
+ close(fd);
+ errno = err;
+ return rval;
+}
+
+/* Accept a socket connection on the listening socket `listenfd`.
+ Returns the file descriptor of the accepted client on success.
+ Returns -1 if there was an error. */
+static int
+accept_sock(int listenfd) {
+ int clifd;
+ socklen_t len;
+ struct sockaddr_un un;
+
+ len = sizeof(un);
+ if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0) {
+ return -1;
+ }
+
+ return clifd;
+}
+
+/* Read up to READ_BLK_SIZE bytes from the socket `sock`.
+ Returns -1 if an error occurred, 0 if the connection was closed, 1 otherwise.
+ Note: Returning 1 on success is weird, but it could also be confusing to
+ return 0 on success when `read` returns that to mean that the connection
+ was closed. */
+static int
+read_sock(struct ltk_sock_info *sock) {
+ int nread;
+ char *old = sock->read;
+ int ret = ltk_grow_string(&sock->read, &sock->read_alloc, sock->read_len + READ_BLK_SIZE);
+ if (ret) return -1; /* fixme: errno? */
+ /* move tokens to new addresses - this was added as an
+ afterthought and really needs to be cleaned up */
+ if (sock->read != old) {
+ for (int i = 0; i < sock->tokens.num_tokens; i++) {
+ sock->tokens.tokens[i] = sock->read + (sock->tokens.tokens[i] - old);
+ }
+ }
+ nread = read(sock->fd, sock->read + sock->read_len, READ_BLK_SIZE);
+ if (nread == -1 || nread == 0)
+ return nread;
+ sock->read_len += nread;
+
+ return 1;
+}
+
+/* Write up to WRITE_BLK_SIZE bytes to the socket.
+ Returns -1 on error, 0 otherwise. */
+static int
+write_sock(struct ltk_sock_info *sock) {
+ if (!sock->write_len)
+ return 0;
+ int write_len = WRITE_BLK_SIZE > sock->write_len - sock->write_cur ?
+ sock->write_len - sock->write_cur : WRITE_BLK_SIZE;
+ int nwritten = write(sock->fd, sock->to_write + sock->write_cur, write_len);
+ if (nwritten == -1)
+ return nwritten;
+ sock->write_cur += nwritten;
+
+ /* check if any sockets have text to write */
+ if (sock->write_cur == sock->write_len) {
+ int found = 0;
+ for (int i = 0; i < maxsocket; i++) {
+ if (sockets[i].fd != -1 &&
+ sockets[i].write_cur != sockets[i].write_len) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ sock_write_available = 0;
+ }
+
+ return 0;
+}
+
+/* Queue `str` to be written to the socket. If len is < 0, it is set to `strlen(str)`.
+ 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) {
+ if (sock->write_cur > 0) {
+ memmove(sock->to_write, sock->to_write + sock->write_cur,
+ sock->write_len - sock->write_cur);
+ sock->write_len -= sock->write_cur;
+ sock->write_cur = 0;
+ }
+
+ if (len < 0)
+ len = strlen(str);
+
+ if (sock->write_alloc - sock->write_len < len &&
+ ltk_grow_string(&sock->to_write, &sock->write_alloc, sock->write_len + len))
+ return -1;
+
+ (void)strncpy(sock->to_write + sock->write_len, str, len);
+ sock->write_len += len;
+
+ sock_write_available = 1;
+
+ return 0;
+}
+
+/* Tokenize the current read buffer in `sock`.
+ Returns 0 immediately if the end of a command was encountered, 1 otherwise. */
+static int
+tokenize_command(struct ltk_sock_info *sock) {
+ for (; sock->read_cur < sock->read_len; sock->read_cur++) {
+ if (!sock->in_token) {
+ push_token(&sock->tokens, sock->read + sock->read_cur - sock->offset);
+ sock->in_token = 1;
+ }
+ if (sock->read[sock->read_cur] == '\\') {
+ sock->bs++;
+ sock->bs %= 2;
+ sock->read[sock->read_cur-sock->offset] = '\\';
+ } else if (sock->read[sock->read_cur] == '\n' && !sock->in_str) {
+ sock->read[sock->read_cur-sock->offset] = '\0';
+ sock->read_cur++;
+ sock->offset = 0;
+ sock->in_token = 0;
+ return 0;
+ } else if (sock->read[sock->read_cur] == '"') {
+ sock->offset++;
+ if (sock->bs) {
+ sock->read[sock->read_cur-sock->offset] = '"';
+ sock->bs = 0;
+ } else {
+ sock->in_str = !sock->in_str;
+ }
+ } else if (sock->read[sock->read_cur] == ' ' && !sock->in_str) {
+ sock->read[sock->read_cur-sock->offset] = '\0';
+ sock->in_token = !sock->in_token;
+ } else {
+ sock->read[sock->read_cur-sock->offset] = sock->read[sock->read_cur];
+ sock->bs = 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Process the commands as they are read from the socket. */
+static void
+process_commands(ltk_window *window, struct ltk_sock_info *sock) {
+ char **tokens;
+ int num_tokens;
+ while (!tokenize_command(sock)) {
+ tokens = sock->tokens.tokens;
+ num_tokens = sock->tokens.num_tokens;
+ if (num_tokens < 1)
+ continue;
+ if (strcmp(tokens[0], "grid") == 0) {
+ ltk_grid_cmd(window, tokens, num_tokens);
+ } else if (strcmp(tokens[0], "button") == 0) {
+ ltk_button_cmd(window, tokens, num_tokens);
+ } else if (strcmp(tokens[0], "set-root-widget") == 0) {
+ ltk_set_root_widget_cmd(window, tokens, num_tokens);
+ } else if (strcmp(tokens[0], "draw") == 0) {
+ ltk_draw_cmd(window, tokens, num_tokens);
+ } else if (strcmp(tokens[0], "quit") == 0) {
+ ltk_quit(window);
+ } else {
+ /* FIXME... */
+ (void)fprintf(stderr, "Invalid command.\n");
+ }
+ sock->tokens.num_tokens = 0;
+ }
+ 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]);
+ ptrdiff_t offset = sock->tokens.tokens[0] - sock->read;
+ /* Hmm, seems a bit ugly... */
+ for (int i = 0; i < sock->tokens.num_tokens; i++) {
+ sock->tokens.tokens[i] -= offset;
+ }
+ sock->read_len -= offset;
+ sock->read_cur -= offset;
+ } else if (sock->tokens.num_tokens == 0) {
+ sock->read_len = 0;
+ sock->read_cur = 0;
+ }
}
diff --git a/util.c b/util.c
@@ -46,3 +46,18 @@ ltk_read_file(const char *path, unsigned long *len) {
return file_contents;
}
+
+/* If `needed` is larger than `*alloc_size`, resize `*str` to
+ `max(needed, *alloc_size * 2)`.
+ Returns 1 on error, 0 otherwise. If an error occurs, `*str` and
+ `*alloc_size` are not modified. */
+int
+ltk_grow_string(char **str, int *alloc_size, int needed) {
+ if (needed <= *alloc_size) return 0;
+ int new_size = needed > (*alloc_size * 2) ? needed : (*alloc_size * 2);
+ char *new = realloc(*str, new_size);
+ if (!new) return 1;
+ *str = new;
+ *alloc_size = new_size;
+ return 0;
+}
diff --git a/util.h b/util.h
@@ -29,3 +29,4 @@ strtonum(const char *numstr, long long minval, long long maxval,
void ltk_err(const char *msg);
char *ltk_read_file(const char *path, unsigned long *len);
+int ltk_grow_string(char **str, int *alloc_size, int needed);