💾 Archived View for gemini.rmf-dev.com › repo › Vaati › Vgmi › files › c8e215950955db231e7549ac22a87… captured on 2023-11-04 at 12:17:56. Gemini links have been rewritten to link to archived content
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
0 #include <string.h>
1 #include <strings.h>
2 #include <unistd.h>
3 #define TB_IMPL
4 #include "wcwidth.h"
5 #undef wcwidth
6 #define wcwidth(x) mk_wcwidth(x)
7 #ifdef sun
8 #include <termios.h>
9 void cfmakeraw(struct termios *t);
10 #endif
11 #include <termbox.h>
12 #include "gemini.h"
13 #include "display.h"
14 #include "cert.h"
15 #include "str.h"
16 #include "url.h"
17 #include "sandbox.h"
18 #include "util.h"
19 #include <stdio.h>
20
21 int get_cursor_pos() {
22 int pos = 0;
23 for (int i = 0; i < client.input.cursor; i++) {
24 pos += tb_utf8_char_length(client.input.field[pos]);
25 if (!client.input.field[pos]) break;
26 }
27 return pos;
28 }
29
30 int vim_counter() {
31 int counter = atoi(client.vim.counter);
32 bzero(client.vim.counter, sizeof(client.vim.counter));
33 return counter?counter:1;
34 }
35
36 void fix_scroll(struct gmi_tab* tab) {
37 int h = tab->page.lines - tb_height() + 2 + (client.tabs_count > 1);
38 if (tab->scroll > h)
39 tab->scroll = h;
40 if (tab->scroll < 0)
41 tab->scroll = -1;
42 }
43
44 int command() {
45 struct gmi_tab* tab = client.tab;
46 struct gmi_page* page = &tab->page;
47
48 // Trim
49 for (int i = STRNLEN(client.input.field) - 1;
50 client.input.field[i] == ' ' ||
51 client.input.field[i] == '\t'; i--)
52 client.input.field[i] = '\0';
53
54 if (!STRCMP(client.input.field, ":q")) {
55 gmi_freetab(tab);
56 client.input.field[0] = '\0';
57 if (client.tabs_count < 1)
58 return 1;
59 if (client.tabs_count == 1)
60 fix_scroll(client.tab);
61 return 0;
62 }
63 if (!STRCMP(client.input.field, ":qa"))
64 return 1;
65 if ((client.input.field[1] == 'n' && client.input.field[2] == 't'
66 && client.input.field[3] == '\0')
67 || !STRCMP(client.input.field, ":tabnew")) {
68 client.tab = gmi_newtab();
69 client.input.field[0] = '\0';
70 return 0;
71 }
72 if (STARTWITH(client.input.field, ":nt ") ||
73 STARTWITH(client.input.field, ":tabnew ")) {
74 int i = client.input.field[1] == 'n' ? 4 : 8;
75 client.input.cursor = 0;
76 int id = atoi(&client.input.field[i]);
77 if (id != 0 ||
78 (client.input.field[i] == '0' &&
79 client.input.field[i + 1] == '\0')) {
80 gmi_goto_new(tab, id);
81 client.input.field[0] = '\0';
82 } else {
83 client.tab = gmi_newtab_url(&client.input.field[i]);
84 client.input.field[0] = '\0';
85 }
86 return 0;
87 }
88 if (STARTWITH(client.input.field, ":o ")) {
89 char urlbuf[MAX_URL];
90 if (STRLCPY(urlbuf, &client.input.field[3]) >= sizeof(urlbuf)) {
91 tab->show_error = 1;
92 snprintf(tab->error, sizeof(tab->error),
93 "Url too long");
94 return 0;
95 }
96 client.input.field[0] = '\0';
97 gmi_cleanforward(tab);
98 int bytes = gmi_request(tab, urlbuf, 1);
99 if (bytes > 0) {
100 tab->scroll = -1;
101 }
102 if (page->code == 11 || page->code == 10) {
103 client.input.mode = 1;
104 client.input.cursor = 0;
105 }
106 tab->selected = 0;
107 return 0;
108 }
109 if (STARTWITH(client.input.field, ":s")) {
110 char urlbuf[MAX_URL];
111 snprintf(urlbuf, sizeof(urlbuf),
112 "gemini://geminispace.info/search?%s",
113 &client.input.field[3]);
114 client.input.field[0] = '\0';
115 gmi_cleanforward(tab);
116 int bytes = gmi_request(tab, urlbuf, 1);
117 if (bytes > 0) {
118 tab->scroll = -1;
119 }
120 if (page->code == 11 || page->code == 10) {
121 client.input.mode = 1;
122 client.input.cursor = 0;
123 }
124 tab->selected = 0;
125 return 0;
126 }
127 if (STARTWITH(client.input.field, ":add")) {
128 char* title = client.input.field[4] == '\0'?
129 NULL:&client.input.field[5];
130 gmi_addbookmark(tab, tab->url, title);
131 client.input.field[0] = '\0';
132 tab->selected = 0;
133 if (!strcmp("about:home", tab->url)) {
134 gmi_freetab(tab);
135 gmi_gohome(tab, 1);
136 }
137 return 0;
138 }
139 int ignore = STARTWITH(client.input.field, ":ignore");
140 int forget = STARTWITH(client.input.field, ":forget");
141 if (forget || ignore) {
142 char* ptr = client.input.field + sizeof(":forget") - 1;
143 int space = 0;
144 for (; *ptr; ptr++) {
145 if (*ptr != ' ') break;
146 space++;
147 }
148 if (space == 0 || !*ptr) goto unknown;
149 if ((ignore && cert_ignore_expiration(ptr)) ||
150 (forget && cert_forget(ptr))) {
151 tab->show_error = 1;
152 if (forget) {
153 snprintf(tab->error, sizeof(tab->error),
154 "Unknown %s certificate", ptr);
155 } else {
156 strerror_r(errno, tab->error,
157 sizeof(tab->error));
158 }
159 return 0;
160 }
161 tab->show_info = 1;
162 snprintf(tab->info, sizeof(tab->info),
163 "%s %s", ptr, (forget ?
164 "certificate removed from known hosts" :
165 "certificate expiration will be ignored"));
166 client.input.field[0] = '\0';
167 return 0;
168 }
169 if (!STRCMP(client.input.field, ":gencert")) {
170 client.input.field[0] = '\0';
171 tab->selected = 0;
172 if (!STRCMP(tab->url, "about:home")) {
173 tab->show_error = 1;
174 snprintf(tab->error, sizeof(tab->error),
175 "Cannot create a certificate for this page");
176 return 0;
177 }
178 char host[256];
179 parse_url(tab->url, host, sizeof(host), NULL, 0, NULL);
180 if (cert_create(host, tab->error, sizeof(tab->error))) {
181 tab->show_error = 1;
182 return 0;
183 }
184 cert_getcert(host, 1);
185 tab->show_info = 1;
186 snprintf(tab->info, sizeof(tab->info),
187 "Certificate generated for %s", host);
188 return 0;
189 }
190 if (!STRCMP(client.input.field, ":exec")) {
191 #ifdef DISABLE_XDG
192 tab->show_error = 1;
193 snprintf(tab->error, sizeof(tab->error),
194 "xdg is disabled");
195 return 0;
196 #else
197 if (!*client.input.download) {
198 snprintf(tab->error, sizeof(tab->error),
199 "No file was downloaded");
200 tab->show_error = 1;
201 client.input.field[0] = '\0';
202 return 0;
203 }
204 xdg_open(client.input.download);
205 client.input.field[0] = '\0';
206 client.input.download[0] = '\0';
207 return 0;
208 #endif
209 }
210 if (STARTWITH(client.input.field, ":download")) {
211 char* ptr = client.input.field + sizeof(":download") - 1;
212 int space = 0;
213 for (; *ptr; ptr++) {
214 if (*ptr != ' ') break;
215 space++;
216 }
217 if (space == 0 && *ptr) goto unknown;
218 char urlbuf[1024];
219 char* url = strrchr(tab->history->url, '/');
220 if (url && (*(url+1) == '\0')) {
221 while (*url == '/' && url > tab->history->url)
222 url--;
223 char* ptr = url + 1;
224 while (*url != '/' && url > tab->history->url)
225 url--;
226 if (*url == '/') url++;
227 if (strlcpy(urlbuf, url, ptr - url + 1) >=
228 sizeof(urlbuf))
229 return fatalI();
230 url = urlbuf;
231 } else if (url) url++;
232 else url = tab->history->url;
233 char* download = *ptr ? ptr : url;
234 #ifdef SANDBOX_SUN
235 if (sandbox_download(tab, download))
236 return -1;
237 int fd = wr_pair[1];
238 #else
239 int fd = openat(getdownloadfd(), download,
240 O_CREAT|O_EXCL|O_WRONLY, 0600);
241 char buf[1024];
242 if (fd < 0 && errno == EEXIST) {
243 #ifdef __OpenBSD__
244 snprintf(buf, sizeof(buf), "%lld_%s",
245 #else
246 snprintf(buf, sizeof(buf), "%ld_%s",
247 #endif
248 time(NULL), download);
249 fd = openat(getdownloadfd(), buf,
250 O_CREAT|O_EXCL|O_WRONLY, 0600);
251 download = buf;
252 }
253 if (fd < 0) {
254 tab->show_error = -1;
255 snprintf(tab->error, sizeof(tab->error),
256 "Failed to write file : %s", strerror(errno));
257 return 0;
258 }
259 #endif
260 char* data = strnstr(tab->page.data, "\r\n",
261 tab->page.data_len);
262 uint64_t data_len = tab->page.data_len;
263 if (!data) data = tab->page.data;
264 else {
265 data += 2;
266 data_len -= (data - tab->page.data);
267 }
268 #ifdef SANDBOX_SUN
269 sandbox_dl_length(data_len);
270 #endif
271 write(fd, data, data_len);
272 #ifndef SANDBOX_SUN
273 close(fd);
274 #endif
275 tab->show_info = 1;
276 snprintf(tab->info, sizeof(tab->info),
277 "File downloaded : %s", download);
278 STRLCPY(client.input.download, download);
279 client.input.field[0] = '\0';
280 tab->selected = 0;
281 return 0;
282 }
283 if (client.input.field[0] == ':' && (atoi(&client.input.field[1]) ||
284 (client.input.field[1] == '0' && client.input.field[2] == '\0'))) {
285 client.tab->scroll = atoi(&client.input.field[1]) - 1 -
286 !!client.tabs_count;
287 if (client.tab->scroll < 0)
288 client.tab->scroll = -!!client.tabs_count;
289 fix_scroll(client.tab);
290 client.input.field[0] = '\0';
291 tab->selected = 0;
292 return 0;
293 }
294 if (client.input.field[0] == '/') {
295 STRLCPY(tab->search.entry, &client.input.field[1]);
296 client.input.field[0] = '\0';
297 } else if (client.input.field[1] == '\0') client.input.field[0] = '\0';
298 else {
299 unknown:
300 tab->show_error = -1;
301 snprintf(tab->error, sizeof(tab->error),
302 "Unknown input: %s", &client.input.field[1]);
303 }
304 return 0;
305 }
306
307 int input_page(struct tb_event ev) {
308 struct gmi_tab* tab = client.tab;
309 struct gmi_page* page = &tab->page;
310 int counter;
311 switch (ev.key) {
312 case TB_KEY_ESC:
313 tab->selected = 0;
314 bzero(client.vim.counter, sizeof(client.vim.counter));
315 client.vim.g = 0;
316 return 0;
317 case TB_KEY_DELETE:
318 if (strcmp(tab->url, "about:home")) return 0;
319 if (tab->selected && tab->selected > 0 &&
320 tab->selected <= page->links_count) {
321 gmi_removebookmark(tab->selected);
322 tab->selected = 0;
323 gmi_request(tab, tab->url, 0);
324 }
325 return 0;
326 case TB_KEY_BACK_TAB:
327 if (tab->selected && tab->selected > 0 &&
328 tab->selected <= page->links_count) {
329 int linkid = tab->selected;
330 tab->selected = 0;
331 gmi_goto_new(tab, linkid);
332 }
333 return 0;
334 case TB_KEY_ARROW_LEFT:
335 if (ev.mod == TB_MOD_SHIFT)
336 goto go_back;
337 goto tab_prev;
338 case TB_KEY_ARROW_RIGHT:
339 if (ev.mod == TB_MOD_SHIFT)
340 goto go_forward;
341 goto tab_next;
342 case TB_KEY_ARROW_UP:
343 goto move_up;
344 case TB_KEY_ARROW_DOWN:
345 goto move_down;
346 case TB_KEY_TAB:
347 client.vim.g = 0;
348 if (client.vim.counter[0] == '\0' ||
349 !atoi(client.vim.counter)) {
350 if (!tab->selected)
351 return 0;
352 gmi_goto(tab, tab->selected);
353 tab->selected = 0;
354 bzero(client.vim.counter, sizeof(client.vim.counter));
355 return 0;
356 }
357 tab->selected = atoi(client.vim.counter);
358 bzero(client.vim.counter, sizeof(client.vim.counter));
359 if (tab->selected > page->links_count) {
360 snprintf(tab->error, sizeof(tab->error),
361 "Invalid link number");
362 tab->selected = 0;
363 tab->show_error = 1;
364 return 0;
365 }
366 size_t len = STRLCPY(tab->selected_url,
367 page->links[tab->selected - 1]);
368 if (len >= sizeof(tab->selected_url)) {
369 snprintf(tab->error, sizeof(tab->error),
370 "Invalid link, above %lu characters",
371 sizeof(tab->selected_url));
372 tab->selected = 0;
373 tab->show_error = 1;
374 }
375 return 0;
376 case TB_KEY_ENTER:
377 if (client.vim.counter[0] != '\0' &&
378 atoi(client.vim.counter)) {
379 tab->scroll += atoi(client.vim.counter);
380 bzero(client.vim.counter, sizeof(client.vim.counter));
381 }
382 else tab->scroll++;
383 fix_scroll(tab);
384 client.vim.g = 0;
385 return 0;
386 case TB_KEY_PGUP:
387 tab->scroll -= vim_counter() * tb_height() -
388 2 - (client.tabs_count>1);
389 fix_scroll(tab);
390 client.vim.g = 0;
391 return 0;
392 case TB_KEY_PGDN:
393 tab->scroll += vim_counter() * tb_height() -
394 2 - (client.tabs_count>1);
395 fix_scroll(tab);
396 client.vim.g = 0;
397 return 0;
398 }
399 switch (ev.ch) {
400 case 'u':
401 tab->show_error = 0;
402 client.input.mode = 1;
403 snprintf(client.input.field,
404 sizeof(client.input.field),
405 ":o %s", tab->url);
406 client.input.cursor = utf8_len(client.input.field,
407 sizeof(client.input.field));
408 break;
409 case ':':
410 tab->show_error = 0;
411 client.input.mode = 1;
412 client.input.cursor = 1;
413 client.input.field[0] = ':';
414 client.input.field[1] = '\0';
415 break;
416 case '/':
417 tab->show_error = 0;
418 client.input.mode = 1;
419 client.input.cursor = 1;
420 client.input.field[0] = '/';
421 client.input.field[1] = '\0';
422 break;
423 case 'r': // Reload
424 if (!tab->history) break;
425 gmi_request(tab, tab->history->url, 0);
426 break;
427 case 'T': // Tab left
428 tab_prev:
429 if (!client.vim.g) break;
430 client.vim.g = 0;
431 counter = vim_counter();
432 if (!client.tab->next && !client.tab->prev) break;
433 for (int i = counter; i > 0; i--) {
434 if (client.tab->prev)
435 client.tab = client.tab->prev;
436 else
437 while (client.tab->next)
438 client.tab = client.tab->next;
439 fix_scroll(client.tab);
440 }
441 break;
442 case 't': // Tab right
443 tab_next:
444 if (!client.vim.g) break;
445 client.vim.g = 0;
446 counter = vim_counter() % client.tabs_count;
447 if (!client.tab->next && !client.tab->prev) break;
448 for (int i = counter; i > 0; i--) {
449 if (client.tab->next)
450 client.tab = client.tab->next;
451 else
452 while (client.tab->prev)
453 client.tab = client.tab->prev;
454 fix_scroll(client.tab);
455 }
456 break;
457 case 'H': // Back
458 go_back:
459 if (!tab->history || !tab->history->prev) break;
460 tab->selected = 0;
461 if (page->code == 20 || page->code == 10 || page->code == 11) {
462 tab->history->scroll = tab->scroll;
463 if (!tab->history->next) {
464 tab->history->page = tab->page;
465 tab->history->cached = 1;
466 }
467 tab->history = tab->history->prev;
468 tab->scroll = tab->history->scroll;
469 if (tab->history->cached)
470 tab->page = tab->history->page;
471 }
472 if (tab->history->cached) {
473 tab->page = tab->history->page;
474 STRLCPY(tab->url, tab->history->url);
475 } else if (gmi_request(tab, tab->history->url, 1) < 0) break;
476 fix_scroll(client.tab);
477 break;
478 case 'L': // Forward
479 go_forward:
480 if (!tab->history || !tab->history->next) break;
481 tab->selected = 0;
482 if (!tab->history->cached) {
483 tab->history->page = tab->page;
484 tab->history->cached = 1;
485 }
486 if (tab->history->next->cached)
487 tab->page = tab->history->next->page;
488 else if (gmi_request(tab, tab->history->next->url, 1) < 0)
489 break;
490 tab->history->scroll = tab->scroll;
491 tab->history = tab->history->next;
492 tab->scroll = tab->history->scroll;
493 STRLCPY(tab->url, tab->history->url);
494 fix_scroll(client.tab);
495 break;
496 case 'k': // UP
497 move_up:
498 tab->scroll -= vim_counter();
499 fix_scroll(tab);
500 client.vim.g = 0;
501 break;
502 case 'j': // DOWN
503 move_down:
504 tab->scroll += vim_counter();
505 fix_scroll(tab);
506 client.vim.g = 0;
507 break;
508 case 'f': // Show history
509 display_history();
510 break;
511 case 'g': // Start of file
512 if (client.vim.g) {
513 tab->scroll = -1;
514 client.vim.g = 0;
515 } else client.vim.g++;
516 break;
517 case 'G': // End of file
518 tab->scroll = page->lines-tb_height()+2;
519 if (client.tabs_count != 1)
520 tab->scroll++;
521 if (tb_height()-2-(client.tabs_count>1) > page->lines)
522 tab->scroll = -1;
523 break;
524 case 'n': // Next occurence
525 tab->search.cursor++;
526 tab->scroll = tab->search.pos[1] - tb_height()/2;
527 fix_scroll(tab);
528 break;
529 case 'N': // Previous occurence
530 tab->search.cursor--;
531 tab->scroll = tab->search.pos[0] - tb_height()/2;
532 fix_scroll(tab);
533 break;
534 default:
535 if (!(ev.ch >= '0' && ev.ch <= '9'))
536 break;
537 tab->show_error = 0;
538 tab->show_info = 0;
539 unsigned int len = STRNLEN(client.vim.counter);
540 if (len == 0 && ev.ch == '0') break;
541 if (len >= sizeof(client.vim.counter)) break;
542 client.vim.counter[len] = ev.ch;
543 client.vim.counter[len+1] = '\0';
544 }
545 return 0;
546 }
547
548 int input_field(struct tb_event ev) {
549 struct gmi_tab* tab = client.tab;
550 struct gmi_page* page = &tab->page;
551 int pos = 0;
552 switch (ev.key) {
553 case TB_KEY_ESC:
554 client.input.mode = 0;
555 client.input.cursor = 0;
556 if (page->code == 11 || page->code == 10)
557 page->code = 20;
558 client.input.field[0] = '\0';
559 tb_hide_cursor();
560 return 0;
561 case TB_KEY_BACKSPACE:
562 case TB_KEY_BACKSPACE2:
563 if (client.input.cursor >
564 ((page->code == 10 || page->code == 11)?0:1)) {
565 if (client.input.field[0] == '/' &&
566 page->code == 20)
567 tab->search.scroll = 1;
568 char* ptr = client.input.field;
569 int l = 1;
570 int pos = 0;
571 while (*ptr) {
572 l = tb_utf8_char_length(*ptr);
573 pos++;
574 if (pos == client.input.cursor)
575 break;
576 ptr += l;
577 }
578
579 strlcpy(ptr, ptr + l,
580 sizeof(client.input.field) -
581 (ptr - client.input.field));
582 client.input.cursor--;
583 }
584 return 0;
585 case TB_KEY_ENTER:
586 client.input.mode = 0;
587 tb_hide_cursor();
588 if (page->code == 10 || page->code == 11) {
589 char urlbuf[MAX_URL];
590 char* start = strstr(tab->url, "gemini://");
591 char* request = strrchr(tab->url, '?');
592 if (!(start?
593 strchr(&start[GMI], '/'):strchr(tab->url, '/')))
594 snprintf(urlbuf, sizeof(urlbuf),
595 "%s/?%s", tab->url,
596 client.input.field);
597 else if (request && request > strrchr(tab->url, '/')) {
598 *request = '\0';
599 snprintf(urlbuf, sizeof(urlbuf),
600 "%s?%s", tab->url,
601 client.input.field);
602 *request = '?';
603 } else
604 snprintf(urlbuf, sizeof(urlbuf),
605 "%s?%s", tab->url,
606 client.input.field);
607 int bytes = gmi_request(tab, urlbuf, 1);
608 if (bytes>0) {
609 tab = client.tab;
610 tab->scroll = -1;
611 }
612 client.input.field[0] = '\0';
613 return 0;
614 }
615 return command();
616 case TB_KEY_ARROW_LEFT:
617 {
618 int min = 1;
619 if (tab->page.code == 10 || tab->page.code == 11)
620 min = 0;
621 if (ev.mod != TB_MOD_CTRL) {
622 if (client.input.cursor > min)
623 client.input.cursor--;
624 return 0;
625 }
626 pos = get_cursor_pos();
627 while (client.input.cursor > min) {
628 pos -= tb_utf8_char_length(client.input.field[pos]);
629 client.input.cursor--;
630 if (client.input.field[pos] == ' ' ||
631 client.input.field[pos] == '.')
632 break;
633 }
634 return 0;
635 }
636 case TB_KEY_ARROW_RIGHT:
637 pos = get_cursor_pos();
638 if (ev.mod != TB_MOD_CTRL) {
639 if (client.input.field[pos])
640 client.input.cursor++;
641 return 0;
642 }
643 while (client.input.field[pos]) {
644 pos += tb_utf8_char_length(client.input.field[pos]);
645 client.input.cursor++;
646 if (!client.input.field[pos]) break;
647 if (client.input.field[pos] == ' ' ||
648 client.input.field[pos] == '.')
649 break;
650 }
651 return 0;
652 }
653 if (!ev.ch) {
654 tb_hide_cursor();
655 return 0;
656 }
657 char* end = client.input.field;
658 while (*end)
659 end += tb_utf8_char_length(*end);
660 if ((size_t)(end - client.input.field) >=
661 sizeof(client.input.field) - 1)
662 return 0;
663
664 char insert[16];
665 int insert_len = tb_utf8_unicode_to_char(insert, ev.ch);
666 char* start = client.input.field;
667 for (int i = 0; *start && i < client.input.cursor; i++)
668 start += tb_utf8_char_length(*start);
669 for (char* ptr = end; start <= ptr; ptr--)
670 ptr[insert_len] = *ptr;
671
672 memcpy(start, insert, insert_len);
673 client.input.cursor++;
674 end += insert_len;
675 *end = '\0';
676 if (client.input.field[0] != '/' || page->code != 20)
677 return 0;
678 int lines = 0;
679 int posx = 0;
680 int w = tb_width();
681 int found = 0;
682 for (int i = 0; i < tab->page.data_len - 1; i++) {
683 if (posx == 0 && tab->page.data[i] == '=' &&
684 tab->page.data[i + 1] == '>') {
685 int ignore = 0;
686 int firstspace = 0;
687 while (i + ignore < tab->page.data_len) {
688 if (tab->page.data[i + ignore] != '\n') {
689 i += 2 + firstspace;
690 break;
691 }
692 if (tab->page.data[i + ignore] == ' ') {
693 ignore++;
694 if (firstspace) continue;
695 i += ignore;
696 break;
697 }
698 ignore++;
699 firstspace = 1;
700 }
701 if (i > tab->page.data_len)
702 break;
703 }
704 if (lines &&
705 !strncasecmp(&client.input.field[1],
706 &tab->page.data[i],
707 strnlen(&client.input.field[1],
708 sizeof(client.input.field) - 1))) {
709 found = 1;
710 break;
711 }
712 if (posx == w || tab->page.data[i] == '\n') {
713 lines++;
714 posx = 0;
715 continue;
716 }
717 posx++;
718 }
719 if (found) {
720 tab->scroll = lines - tb_height()/2;
721 fix_scroll(tab);
722 }
723 return 0;
724 }
725
726 int input(struct tb_event ev) {
727 struct gmi_tab* tab = client.tab;
728 struct gmi_page* page = &tab->page;
729 if (page->code == 11 || page->code == 10) {
730 if (!client.input.mode) client.input.cursor = 0;
731 client.input.mode = 1;
732 }
733 if (ev.type == TB_EVENT_RESIZE) {
734 fix_scroll(tab);
735 return 0;
736 }
737 if (ev.type != TB_EVENT_KEY) return 0;
738 if (client.input.mode)
739 return input_field(ev);
740 return input_page(ev);
741 }
742
743 int tb_interupt() {
744 int sig = 0;
745 return write(global.resize_pipefd[1], &sig, sizeof(sig)) ==
746 sizeof(sig) ? 0 : -1;
747 }
748