ltk

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

ltkc.c (7123B)


      1 /*
      2  * Copyright (c) 2021-2023 lumidify <nobody@lumidify.org>
      3  *
      4  * Permission to use, copy, modify, and/or distribute this software for any
      5  * purpose with or without fee is hereby granted, provided that the above
      6  * copyright notice and this permission notice appear in all copies.
      7  *
      8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <stdlib.h>
     19 #include <string.h>
     20 #include <stdarg.h>
     21 #include <stddef.h>
     22 #include <unistd.h>
     23 #include <time.h>
     24 #include <errno.h>
     25 #include <inttypes.h>
     26 #include <sys/select.h>
     27 #include <sys/socket.h>
     28 #include <sys/un.h>
     29 #include "util.h"
     30 #include "memory.h"
     31 #include "macros.h"
     32 
     33 #define BLK_SIZE 128
     34 char tmp_buf[BLK_SIZE];
     35 
     36 static struct {
     37 	char *in_buffer;  /* text that is read from stdin and written to the socket */
     38 	int in_len;
     39 	int in_alloc;
     40 	char *out_buffer; /* text that is read from the socket and written to stdout */
     41 	int out_len;
     42 	int out_alloc;
     43 } io_buffers;
     44 
     45 static char *ltk_dir = NULL;
     46 static char *sock_path = NULL;
     47 static int sockfd = -1;
     48 
     49 int main(int argc, char *argv[]) {
     50 	char num[12];
     51 	int bs = 0;
     52 	int last_newline = 1;
     53 	int in_str = 0;
     54 	uint32_t seq = 0;
     55 	int maxfd;
     56 	int infd = fileno(stdin);
     57 	int outfd = fileno(stdout);
     58 	struct sockaddr_un un;
     59 	fd_set rfds, wfds, rallfds, wallfds;
     60 	struct timeval tv;
     61 	tv.tv_sec = 0;
     62 	tv.tv_usec = 20000;
     63 	size_t path_size;
     64 
     65 	if (argc != 2) {
     66 		(void)fprintf(stderr, "USAGE: ltkc <socket id>\n");
     67 		return 1;
     68 	}
     69 
     70 	ltk_dir = ltk_setup_directory();
     71 	if (!ltk_dir) {
     72 		(void)fprintf(stderr, "Unable to setup ltk directory.\n");
     73 		return 1;
     74 	}
     75 
     76 	/* 7 because of "/", ".sock", and '\0' */
     77 	path_size = strlen(ltk_dir) + strlen(argv[1]) + 7;
     78 	sock_path = ltk_malloc(path_size);
     79 	snprintf(sock_path, path_size, "%s/%s.sock", ltk_dir, argv[1]);
     80 
     81 	if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
     82 		perror("Socket error");
     83 		return -1;
     84 	}
     85 	memset(&un, 0, sizeof(un));
     86 	un.sun_family = AF_UNIX;
     87 	if (path_size > sizeof(un.sun_path)) {
     88 		(void)fprintf(stderr, "Socket path too long.\n");
     89 		return 1;
     90 	}
     91 	strcpy(un.sun_path, sock_path);
     92 	if (connect(sockfd, (struct sockaddr *)&un, offsetof(struct sockaddr_un, sun_path) + path_size) < 0) {
     93 		perror("Socket error");
     94 		return -2;
     95 	}
     96 	if (set_nonblock(sockfd)) {
     97 		(void)fprintf(stderr, "Unable to set socket to non-blocking mode.\n");
     98 		return 1;
     99 	} else if (set_nonblock(infd)) {
    100 		(void)fprintf(stderr, "Unable to set stdin to non-blocking mode.\n");
    101 		return 1;
    102 	} else if (set_nonblock(outfd)) {
    103 		(void)fprintf(stderr, "Unable to set stdout to non-blocking mode.\n");
    104 		return 1;
    105 	}
    106 
    107 	io_buffers.in_buffer = ltk_malloc(BLK_SIZE);
    108 	io_buffers.in_alloc = BLK_SIZE;
    109 
    110 	io_buffers.out_buffer = ltk_malloc(BLK_SIZE);
    111 	io_buffers.out_alloc = BLK_SIZE;
    112 
    113 	FD_ZERO(&rallfds);
    114 	FD_ZERO(&wallfds);
    115 
    116 	FD_SET(sockfd, &rallfds);
    117 	FD_SET(infd, &rallfds);
    118 	FD_SET(sockfd, &wallfds);
    119 	FD_SET(outfd, &wallfds);
    120 	maxfd = sockfd > infd ? sockfd : infd;
    121 	if (maxfd < outfd)
    122 		maxfd = outfd;
    123 
    124 	struct timespec now, elapsed, last, sleep_time;
    125 	clock_gettime(CLOCK_MONOTONIC, &last);
    126 	sleep_time.tv_sec = 0;
    127 
    128 	while (1) {
    129 		if (!FD_ISSET(sockfd, &rallfds) && io_buffers.out_len == 0)
    130 			break;
    131 		rfds = rallfds;
    132 		wfds = wallfds;
    133 		select(maxfd + 1, &rfds, &wfds, NULL, &tv);
    134 
    135 		/* FIXME: make all this buffer handling a bit more intelligent */
    136 		if (FD_ISSET(sockfd, &rfds)) {
    137 			while (1) {
    138 				ltk_grow_string(&io_buffers.out_buffer,
    139 						&io_buffers.out_alloc,
    140 						io_buffers.out_len + BLK_SIZE);
    141 				int nread = read(sockfd,
    142 						 io_buffers.out_buffer + io_buffers.out_len,
    143 						 BLK_SIZE);
    144 				if (nread < 0) {
    145 					/* FIXME: distinguish errors */
    146 					break;
    147 				} else if (nread == 0) {
    148 					FD_CLR(sockfd, &rallfds);
    149 					FD_CLR(sockfd, &wallfds);
    150 					break;
    151 				} else {
    152 					io_buffers.out_len += nread;
    153 				}
    154 			}
    155 		}
    156 
    157 		if (FD_ISSET(infd, &rfds)) {
    158 			while (1) {
    159 				int nread = read(infd, tmp_buf, BLK_SIZE);
    160 				if (nread < 0) {
    161 					break;
    162 				} else if (nread == 0) {
    163 					FD_CLR(infd, &rallfds);
    164 					break;
    165 				} else {
    166 					for (int i = 0; i < nread; i++) {
    167 						if (last_newline) {
    168 							int numlen = snprintf(num, sizeof(num), "%"PRIu32" ", seq);
    169 							if (numlen < 0 || (unsigned)numlen >= sizeof(num))
    170 								ltk_fatal("There's a bug in the universe.\n");
    171 							ltk_grow_string(
    172 							    &io_buffers.in_buffer,
    173 							    &io_buffers.in_alloc,
    174 							    io_buffers.in_len + numlen
    175 							);
    176 							memcpy(io_buffers.in_buffer + io_buffers.in_len, num, numlen);
    177 							io_buffers.in_len += numlen;
    178 							last_newline = 0;
    179 							seq++;
    180 						}
    181 						if (tmp_buf[i] == '\\') {
    182 							bs++;
    183 							bs %= 2;
    184 						} else if (tmp_buf[i] == '"' && !bs) {
    185 							in_str = !in_str;
    186 						} else if (tmp_buf[i] == '\n' && !in_str) {
    187 							last_newline = 1;
    188 						} else {
    189 							bs = 0;
    190 						}
    191 						if (io_buffers.in_len == io_buffers.in_alloc) {
    192 							ltk_grow_string(
    193 							    &io_buffers.in_buffer,
    194 							    &io_buffers.in_alloc,
    195 							    io_buffers.in_len + 1
    196 							);
    197 						}
    198 						io_buffers.in_buffer[io_buffers.in_len++] = tmp_buf[i];
    199 					}
    200 				}
    201 			}
    202 		}
    203 
    204 		if (FD_ISSET(sockfd, &wfds)) {
    205 			while (io_buffers.in_len > 0) {
    206 				int maxwrite = BLK_SIZE > io_buffers.in_len ?
    207 					       io_buffers.in_len : BLK_SIZE;
    208 				int nwritten = write(sockfd, io_buffers.in_buffer, maxwrite);
    209 				if (nwritten <= 0) {
    210 					break;
    211 				} else {
    212 					memmove(io_buffers.in_buffer,
    213 						io_buffers.in_buffer + nwritten,
    214 						io_buffers.in_len - nwritten);
    215 					io_buffers.in_len -= nwritten;
    216 				}
    217 			}
    218 		}
    219 
    220 		if (FD_ISSET(outfd, &wfds)) {
    221 			while (io_buffers.out_len > 0) {
    222 				int maxwrite = BLK_SIZE > io_buffers.out_len ?
    223 					       io_buffers.out_len : BLK_SIZE;
    224 				int nwritten = write(outfd, io_buffers.out_buffer, maxwrite);
    225 				if (nwritten <= 0) {
    226 					break;
    227 				} else {
    228 					memmove(io_buffers.out_buffer,
    229 						io_buffers.out_buffer + nwritten,
    230 						io_buffers.out_len - nwritten);
    231 					io_buffers.out_len -= nwritten;
    232 				}
    233 			}
    234 		}
    235 		clock_gettime(CLOCK_MONOTONIC, &now);
    236 		ltk_timespecsub(&now, &last, &elapsed);
    237 		/* FIXME: configure framerate */
    238 		if (elapsed.tv_sec == 0 && elapsed.tv_nsec < 20000000LL) {
    239 			sleep_time.tv_nsec = 20000000LL - elapsed.tv_nsec;
    240 			nanosleep(&sleep_time, NULL);
    241 		}
    242 		last = now;
    243 	}
    244 
    245 	ltk_cleanup();
    246 
    247 	return 0;
    248 }
    249 
    250 void
    251 ltk_log_msg(const char *mode, const char *format, va_list args) {
    252 	fprintf(stderr, "ltkc %s: ", mode);
    253 	vfprintf(stderr, format, args);
    254 }
    255 
    256 void
    257 ltk_cleanup() {
    258 	if (sockfd >= 0)
    259 		close(sockfd);
    260 	if (ltk_dir)
    261 		ltk_free(ltk_dir);
    262 	if (sock_path)
    263 		ltk_free(sock_path);
    264 	if (io_buffers.in_buffer)
    265 		ltk_free(io_buffers.in_buffer);
    266 	if (io_buffers.out_buffer)
    267 		ltk_free(io_buffers.out_buffer);
    268 }