tltkc.c - ltk - Socket-based GUI for X11 (WIP)
HTML git clone git://lumidify.org/ltk.git (fast, but not encrypted)
HTML git clone https://lumidify.org/git/ltk.git (encrypted, but very slow)
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tltkc.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 }