commit c437b901b8ed5a6ebbc4c46806ee215adad4373c
parent a57cf5fb31150459013031565c1fd026e03901de
Author: lumidify <nobody@lumidify.org>
Date: Mon, 28 Dec 2020 23:23:21 +0100
Read sockets from common directory so multiple ltk programs can be running at the same time
Diffstat:
M | .gitignore | | | 1 | + |
M | TODO | | | 5 | +++++ |
M | ltkc.c | | | 33 | +++++++++++++++++++++++++++++++-- |
M | ltkd.c | | | 157 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------- |
M | test.sh | | | 17 | ++++------------- |
M | util.c | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
M | util.h | | | 1 | + |
7 files changed, 213 insertions(+), 41 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -3,3 +3,4 @@ ltkc
ltk.sock
*.o
*.core
+.ltk
diff --git a/TODO b/TODO
@@ -1,6 +1,11 @@
Convert points to pixels for stb rendering (currently, the size between
pango and stb is completely different).
+Implement broadcast command for communication between clients
+-> Maybe also some sort of client storage? (probably overkill)
+
+Catch signals in ltkc to send quit command to ltkd.
+
Random stuff:
* This is not really a general-purpose GUI toolkit - imagine building
a complex GUI with this. Especially things like having to respond
diff --git a/ltkc.c b/ltkc.c
@@ -53,6 +53,28 @@ int main(int argc, char *argv[]) {
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 15;
+ char *ltk_dir = NULL, *sock_path = NULL;
+ int path_size;
+
+ if (argc != 2) {
+ (void)fprintf(stderr, "USAGE: ltkc <socket id>\n");
+ return 1;
+ }
+
+ ltk_dir = ltk_setup_directory();
+ if (!ltk_dir) {
+ (void)fprintf(stderr, "Unable to setup ltk directory.\n");
+ return 1;
+ }
+
+ /* 7 because of "/", ".sock", and '\0' */
+ path_size = strlen(ltk_dir) + strlen(argv[1]) + 7;
+ sock_path = malloc(path_size);
+ if (!sock_path) {
+ (void)fprintf(stderr, "Unable to allocate memory for socket path.\n");
+ return 1;
+ }
+ snprintf(sock_path, path_size, "%s/%s.sock", ltk_dir, argv[1]);
if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("Socket error");
@@ -60,8 +82,12 @@ int main(int argc, char *argv[]) {
}
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
- strcpy(un.sun_path, "ltk.sock");
- if (connect(sockfd, (struct sockaddr *)&un, offsetof(struct sockaddr_un, sun_path) + 9) < 0) {
+ if (path_size > sizeof(un.sun_path)) {
+ (void)fprintf(stderr, "Socket path too long.\n");
+ return 1;
+ }
+ strcpy(un.sun_path, sock_path);
+ if (connect(sockfd, (struct sockaddr *)&un, offsetof(struct sockaddr_un, sun_path) + path_size) < 0) {
perror("Socket error");
return -2;
}
@@ -162,5 +188,8 @@ int main(int argc, char *argv[]) {
}
close(sockfd);
+ free(ltk_dir);
+ free(sock_path);
+
return 0;
}
diff --git a/ltkd.c b/ltkd.c
@@ -25,9 +25,11 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
+#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
+#include <signal.h>
#include <sys/stat.h>
#include <sys/select.h>
#include <sys/socket.h>
@@ -73,9 +75,14 @@ static struct ltk_sock_info {
} sockets[MAX_SOCK_CONNS];
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 *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_cleanup_gui(ltk_window *window);
+static void ltk_cleanup_nongui(void);
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);
@@ -95,9 +102,13 @@ static int add_client(int fd);
static int listen_sock(const char *sock_path);
static int accept_sock(int listenfd);
-static int maxsocket = -1;
-static char running = 1;
-static char sock_write_available = 0;
+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;
int main(int argc, char *argv[]) {
ltk_window *window = ltk_create_window("theme.ini", "Demo", 0, 0, 500, 500);
@@ -108,13 +119,20 @@ static int
ltk_mainloop(ltk_window *window) {
XEvent event;
fd_set rfds, wfds, rallfds, wallfds;
- int maxfd, listenfd;
+ int maxfd;
int rretval, wretval;
int clifd;
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 10;
+ ltk_dir = ltk_setup_directory();
+ if (!ltk_dir) ltk_fatal("Unable to setup ltk directory.\n");
+ ltk_logfile = open_log(ltk_dir);
+ if (!ltk_logfile) ltk_fatal("Unable to open log file.\n");
+ sock_path = get_sock_path(ltk_dir, window->xwindow);
+ if (!sock_path) ltk_fatal("Unable to allocate memory for socket path.\n");
+
/* Note: sockets should be initialized to 0 because it is static */
for (int i = 0; i < MAX_SOCK_CONNS; i++) {
sockets[i].fd = -1; /* socket unused */
@@ -127,14 +145,15 @@ ltk_mainloop(ltk_window *window) {
FD_ZERO(&rallfds);
FD_ZERO(&wallfds);
- if ((listenfd = listen_sock("ltk.sock")) < 0) {
- fprintf(stderr, "Error listening on ltk.sock\n");
- exit(1); /* FIXME: proper error handling */
- }
+ if ((listenfd = listen_sock(sock_path)) < 0)
+ ltk_fatal("Error listening on socket.\n");
FD_SET(listenfd, &rallfds);
maxfd = listenfd;
+ printf("%d", window->xwindow);
+ daemonize();
+
while (running) {
rfds = rallfds;
wfds = wallfds;
@@ -150,8 +169,8 @@ ltk_mainloop(ltk_window *window) {
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");
- exit(1); /* FIXME: proper error handling */
+ /* FIXME: Just log this! */
+ ltk_fatal("Error accepting socket connection.\n");
}
int i = add_client(clifd);
FD_SET(clifd, &rallfds);
@@ -196,7 +215,7 @@ ltk_mainloop(ltk_window *window) {
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 */
+ ltk_fatal("Unable to queue event.\n");
}
}
free(cur->data);
@@ -209,6 +228,97 @@ ltk_mainloop(ltk_window *window) {
}
+ ltk_cleanup_gui(window);
+ ltk_cleanup_nongui();
+
+ return 0;
+}
+
+/* largely copied from APUE... */
+/* am I breaking copyright here? */
+static void
+daemonize(void) {
+ pid_t pid;
+ struct sigaction sa;
+
+ fflush(stdout);
+
+ if ((pid = fork()) < 0)
+ ltk_fatal("Can't fork.\n");
+ else if (pid != 0)
+ exit(0);
+ setsid();
+
+ sa.sa_handler = SIG_IGN;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ ltk_fatal("Unable to ignore SIGHUP.\n");
+ if ((pid = fork()) < 0)
+ ltk_fatal("Can't fork.\n");
+ else if (pid != 0)
+ exit(0);
+
+ if (chdir("/") < 0)
+ ltk_fatal("Can't change directory to root.\n");
+
+ close(fileno(stdin));
+ close(fileno(stdout));
+ close(fileno(stderr));
+ open("/dev/null", O_RDWR);
+ dup(0);
+ dup(0);
+
+ /* FIXME: Is it guaranteed that this will work? Will these fds
+ always be opened on the lowest numbers? */
+}
+
+static char *
+get_sock_path(char *basedir, Window id) {
+ int len;
+ char *path;
+
+ len = strlen(basedir);
+ /* FIXME: MAKE SURE THIS IS ACTUALLY BIG ENOUGH! */
+ path = malloc(len + 20);
+ if (!path)
+ return NULL;
+ if (snprintf(path, len + 20, "%s/%d.sock", basedir, id) >= len + 20)
+ ltk_fatal("Tell lumidify to fix his code.\n");
+
+ return path;
+}
+
+static FILE *
+open_log(char *dir) {
+ int len;
+ FILE *f;
+ char *path;
+
+ len = strlen(dir);
+ path = malloc(len + 10);
+ if (!path)
+ return NULL;
+ snprintf(path, len + 10, "%s/ltkd.log", dir);
+ f = fopen(path, "a");
+ free(path);
+
+ return f;
+}
+
+static void
+ltk_cleanup_nongui(void) {
+ if (listenfd >= 0)
+ close(listenfd);
+ if (ltk_dir)
+ free(ltk_dir);
+ if (ltk_logfile)
+ fclose(ltk_logfile);
+ if (sock_path) {
+ unlink(sock_path);
+ free(sock_path);
+ }
+
for (int i = 0; i < MAX_SOCK_CONNS; i++) {
if (sockets[i].fd >= 0)
close(sockets[i].fd);
@@ -219,23 +329,16 @@ ltk_mainloop(ltk_window *window) {
if (sockets[i].tokens.tokens)
free(sockets[i].tokens.tokens);
}
-
- close(listenfd);
-
- unlink("ltk.sock");
-
- return 0;
}
-void
-ltk_clean_up(ltk_window *window) {
+static void
+ltk_cleanup_gui(ltk_window *window) {
ltk_destroy_theme(window->theme);
ltk_destroy_window(window);
}
void
ltk_quit(ltk_window *window) {
- ltk_clean_up(window);
running = 0;
}
@@ -283,8 +386,12 @@ ltk_window_invalidate_rect(ltk_window *window, ltk_rect rect) {
void
ltk_fatal(const char *msg) {
- (void)fprintf(stderr, msg);
- /* FIXME: clean up first */
+ FILE *errfile = ltk_logfile;
+ if (!errfile)
+ errfile = stderr;
+ (void)fprintf(errfile, msg);
+ /* FIXME: cleanup gui stuff too (need window for that) */
+ ltk_cleanup_nongui();
exit(1);
};
@@ -365,8 +472,7 @@ ltk_window_other_event(ltk_window *window, XEvent event) {
ltk_window_invalidate_rect(window, r);
} else if (event.type == ClientMessage
&& event.xclient.data.l[0] == window->wm_delete_msg) {
- ltk_destroy_window(window);
- exit(0); /* FIXME */
+ ltk_quit(window);
}
}
@@ -474,8 +580,7 @@ ltk_load_theme(ltk_window *window, const char *path) {
if (!window->theme->window) ltk_fatal("Unable to allocate memory for window theme.\n");
window->theme->button = NULL;
if (ini_parse(path, ltk_ini_handler, window) < 0) {
- (void)fprintf(stderr, "ERROR: Can't load theme %s\n.", path);
- exit(1);
+ ltk_fatal("Can't load theme.\n");
}
}
diff --git a/test.sh b/test.sh
@@ -5,23 +5,14 @@
# All events are still printed to the terminal curerntly because
# the second './ltkc' still prints everything - event masks aren't
# supported yet.
-#
-# Currently, everything just uses the socket 'ltk.sock'. My idea
-# was to have a directory containing sockets for all instances of
-# ltks, each of them named after the X Window ID used. This would
-# allow other tools (screenreader, etc.) to determine which socket
-# to use to communicate with a window. Probably ltkd would just
-# print out its window ID when starting and then daemonize itself,
-# so the calling script can just use save the output of ltkd and
-# use that to communicate with the socket using ltkc.
-./ltkd&
-sleep 0.2
+export LTKDIR="`pwd`/.ltk"
+ltk_id=`./ltkd`
-cat test.gui | ./ltkc | while read cmd
+cat test.gui | ./ltkc $ltk_id | while read cmd
do
case "$cmd" in
"btn1 button_click") echo "quit"
;;
esac
-done | ./ltkc
+done | ./ltkc $ltk_id
diff --git a/util.c b/util.c
@@ -21,8 +21,11 @@
* SOFTWARE.
*/
+#include <pwd.h>
+#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
void
ltk_err(const char *msg) {
@@ -61,3 +64,40 @@ ltk_grow_string(char **str, int *alloc_size, int needed) {
*alloc_size = new_size;
return 0;
}
+
+/* Get the directory to store ltk files in and create it if it doesn't exist yet.
+ This first checks the environment variable LTKDIR and, if that doesn't
+ exist, the home directory with "/.ltk" appended.
+ Returns NULL on error. */
+char *
+ltk_setup_directory(void) {
+ char *dir, *dir_orig;
+ struct passwd *pw;
+ uid_t uid;
+ int len;
+
+ dir_orig = getenv("LTKDIR");
+ if (dir_orig) {
+ dir = strdup(dir_orig);
+ if (!dir)
+ return NULL;
+ } else {
+ uid = getuid();
+ pw = getpwuid(uid);
+ if (!pw)
+ return NULL;
+ len = strlen(pw->pw_dir);
+ dir = malloc(len + 6);
+ if (!dir)
+ return NULL;
+ strcpy(dir, pw->pw_dir);
+ strcpy(dir + len, "/.ltk");
+ }
+
+ if (mkdir(dir, 0770) < 0) {
+ if (errno != EEXIST)
+ return NULL;
+ }
+
+ return dir;
+}
diff --git a/util.h b/util.h
@@ -30,3 +30,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);
+char *ltk_setup_directory(void);