dvtm.c (40427B)
1 /* 2 * The initial "port" of dwm to curses was done by 3 * 4 * © 2007-2016 Marc André Tanner <mat at brain-dump dot org> 5 * 6 * It is highly inspired by the original X11 dwm and 7 * reuses some code of it which is mostly 8 * 9 * © 2006-2007 Anselm R. Garbe <garbeam at gmail dot com> 10 * 11 * See LICENSE for details. 12 */ 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include <stdint.h> 16 #include <wchar.h> 17 #include <limits.h> 18 #include <libgen.h> 19 #include <sys/select.h> 20 #include <sys/stat.h> 21 #include <sys/ioctl.h> 22 #include <sys/wait.h> 23 #include <sys/time.h> 24 #include <sys/types.h> 25 #include <fcntl.h> 26 #include <curses.h> 27 #include <stdio.h> 28 #include <stdarg.h> 29 #include <signal.h> 30 #include <locale.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <stdbool.h> 34 #include <errno.h> 35 #include <pwd.h> 36 #if defined __CYGWIN__ || defined __sun 37 # include <termios.h> 38 #endif 39 #include "vt.h" 40 41 #ifdef PDCURSES 42 int ESCDELAY; 43 #endif 44 45 #ifndef NCURSES_REENTRANT 46 # define set_escdelay(d) (ESCDELAY = (d)) 47 #endif 48 49 typedef struct { 50 float mfact; 51 unsigned int nmaster; 52 int history; 53 int w; 54 int h; 55 volatile sig_atomic_t need_resize; 56 } Screen; 57 58 typedef struct { 59 const char *symbol; 60 void (*arrange)(void); 61 } Layout; 62 63 typedef struct Client Client; 64 struct Client { 65 WINDOW *window; 66 Vt *term; 67 Vt *editor, *app; 68 int editor_fds[2]; 69 volatile sig_atomic_t editor_died; 70 const char *cmd; 71 char title[255]; 72 int order; 73 pid_t pid; 74 unsigned short int id; 75 unsigned short int x; 76 unsigned short int y; 77 unsigned short int w; 78 unsigned short int h; 79 bool has_title_line; 80 bool minimized; 81 bool urgent; 82 volatile sig_atomic_t died; 83 Client *next; 84 Client *prev; 85 Client *snext; 86 unsigned int tags; 87 }; 88 89 typedef struct { 90 short fg; 91 short bg; 92 short fg256; 93 short bg256; 94 short pair; 95 } Color; 96 97 typedef struct { 98 const char *title; 99 attr_t attrs; 100 Color *color; 101 } ColorRule; 102 103 #define ALT(k) ((k) + (161 - 'a')) 104 #if defined CTRL && defined _AIX 105 #undef CTRL 106 #endif 107 #ifndef CTRL 108 #define CTRL(k) ((k) & 0x1F) 109 #endif 110 #define CTRL_ALT(k) ((k) + (129 - 'a')) 111 112 #define MAX_ARGS 8 113 114 typedef struct { 115 void (*cmd)(const char *args[]); 116 const char *args[3]; 117 } Action; 118 119 #define MAX_KEYS 3 120 121 typedef unsigned int KeyCombo[MAX_KEYS]; 122 123 typedef struct { 124 KeyCombo keys; 125 Action action; 126 } KeyBinding; 127 128 typedef struct { 129 mmask_t mask; 130 Action action; 131 } Button; 132 133 typedef struct { 134 const char *name; 135 Action action; 136 } Cmd; 137 138 enum { BAR_TOP, BAR_BOTTOM, BAR_OFF }; 139 140 typedef struct { 141 int fd; 142 int pos, lastpos; 143 bool autohide; 144 unsigned short int h; 145 unsigned short int y; 146 char text[512]; 147 const char *file; 148 } StatusBar; 149 150 typedef struct { 151 int fd; 152 const char *file; 153 unsigned short int id; 154 } CmdFifo; 155 156 typedef struct { 157 char *data; 158 size_t len; 159 size_t size; 160 } Register; 161 162 typedef struct { 163 char *name; 164 const char *argv[4]; 165 bool filter; 166 bool color; 167 } Editor; 168 169 #define LENGTH(arr) (sizeof(arr) / sizeof((arr)[0])) 170 #define MAX(x, y) ((x) > (y) ? (x) : (y)) 171 #define MIN(x, y) ((x) < (y) ? (x) : (y)) 172 #define TAGMASK ((1 << LENGTH(tags)) - 1) 173 174 #ifdef NDEBUG 175 #define debug(format, args...) 176 #else 177 #define debug eprint 178 #endif 179 180 /* commands for use by keybindings */ 181 static void create(const char *args[]); 182 static void copymode(const char *args[]); 183 static void focusn(const char *args[]); 184 static void focusid(const char *args[]); 185 static void focusnext(const char *args[]); 186 static void focusnextnm(const char *args[]); 187 static void focusprev(const char *args[]); 188 static void focusprevnm(const char *args[]); 189 static void focuslast(const char *args[]); 190 static void focusup(const char *args[]); 191 static void focusdown(const char *args[]); 192 static void focusleft(const char *args[]); 193 static void focusright(const char *args[]); 194 static void killclient(const char *args[]); 195 static void paste(const char *args[]); 196 static void quit(const char *args[]); 197 static void redraw(const char *args[]); 198 static void scrollback(const char *args[]); 199 static void send(const char *args[]); 200 static void setlayout(const char *args[]); 201 static void incnmaster(const char *args[]); 202 static void setmfact(const char *args[]); 203 static void startup(const char *args[]); 204 static void tag(const char *args[]); 205 static void tagid(const char *args[]); 206 static void togglebar(const char *args[]); 207 static void togglebarpos(const char *args[]); 208 static void toggleminimize(const char *args[]); 209 static void togglemouse(const char *args[]); 210 static void togglerunall(const char *args[]); 211 static void toggletag(const char *args[]); 212 static void toggleview(const char *args[]); 213 static void viewprevtag(const char *args[]); 214 static void view(const char *args[]); 215 static void zoom(const char *args[]); 216 217 /* commands for use by mouse bindings */ 218 static void mouse_focus(const char *args[]); 219 static void mouse_fullscreen(const char *args[]); 220 static void mouse_minimize(const char *args[]); 221 static void mouse_zoom(const char *args[]); 222 223 /* functions and variables available to layouts via config.h */ 224 static Client* nextvisible(Client *c); 225 static void focus(Client *c); 226 static void resize(Client *c, int x, int y, int w, int h); 227 extern Screen screen; 228 static unsigned int waw, wah, wax, way; 229 static Client *clients = NULL; 230 static char *title; 231 static KeyCombo keys; 232 233 #include "config.h" 234 235 /* global variables */ 236 static const char *dvtm_name = "dvtm"; 237 Screen screen = { .mfact = MFACT, .nmaster = NMASTER, .history = SCROLL_HISTORY }; 238 static Client *stack = NULL; 239 static Client *sel = NULL; 240 static Client *lastsel = NULL; 241 static Client *msel = NULL; 242 static unsigned int seltags; 243 static unsigned int tagset[2] = { 1, 1 }; 244 static bool mouse_events_enabled = ENABLE_MOUSE; 245 static Layout *layout = layouts; 246 static StatusBar bar = { .fd = -1, .lastpos = BAR_POS, .pos = BAR_POS, .autohide = BAR_AUTOHIDE, .h = 1 }; 247 static CmdFifo cmdfifo = { .fd = -1 }; 248 static const char *shell; 249 static Register copyreg; 250 static volatile sig_atomic_t running = true; 251 static bool runinall = false; 252 253 static void 254 eprint(const char *errstr, ...) { 255 va_list ap; 256 va_start(ap, errstr); 257 vfprintf(stderr, errstr, ap); 258 va_end(ap); 259 } 260 261 static void 262 error(const char *errstr, ...) { 263 va_list ap; 264 va_start(ap, errstr); 265 vfprintf(stderr, errstr, ap); 266 va_end(ap); 267 exit(EXIT_FAILURE); 268 } 269 270 static bool 271 isarrange(void (*func)()) { 272 return func == layout->arrange; 273 } 274 275 static bool 276 isvisible(Client *c) { 277 return c->tags & tagset[seltags]; 278 } 279 280 static bool 281 is_content_visible(Client *c) { 282 if (!c) 283 return false; 284 if (isarrange(fullscreen)) 285 return sel == c; 286 return isvisible(c) && !c->minimized; 287 } 288 289 static Client* 290 nextvisible(Client *c) { 291 for (; c && !isvisible(c); c = c->next); 292 return c; 293 } 294 295 static void 296 updatebarpos(void) { 297 bar.y = 0; 298 wax = 0; 299 way = 0; 300 wah = screen.h; 301 waw = screen.w; 302 if (bar.pos == BAR_TOP) { 303 wah -= bar.h; 304 way += bar.h; 305 } else if (bar.pos == BAR_BOTTOM) { 306 wah -= bar.h; 307 bar.y = wah; 308 } 309 } 310 311 static void 312 hidebar(void) { 313 if (bar.pos != BAR_OFF) { 314 bar.lastpos = bar.pos; 315 bar.pos = BAR_OFF; 316 } 317 } 318 319 static void 320 showbar(void) { 321 if (bar.pos == BAR_OFF) 322 bar.pos = bar.lastpos; 323 } 324 325 static void 326 drawbar(void) { 327 int sx, sy, x, y, width; 328 unsigned int occupied = 0, urgent = 0; 329 if (bar.pos == BAR_OFF) 330 return; 331 332 for (Client *c = clients; c; c = c->next) { 333 occupied |= c->tags; 334 if (c->urgent) 335 urgent |= c->tags; 336 } 337 338 getyx(stdscr, sy, sx); 339 attrset(BAR_ATTR); 340 move(bar.y, 0); 341 342 for (unsigned int i = 0; i < LENGTH(tags); i++){ 343 if (tagset[seltags] & (1 << i)) 344 attrset(TAG_SEL); 345 else if (urgent & (1 << i)) 346 attrset(TAG_URGENT); 347 else if (occupied & (1 << i)) 348 attrset(TAG_OCCUPIED); 349 else 350 attrset(TAG_NORMAL); 351 printw(TAG_SYMBOL, tags[i]); 352 } 353 354 attrset(runinall ? TAG_SEL : TAG_NORMAL); 355 addstr(layout->symbol); 356 attrset(TAG_NORMAL); 357 358 for (unsigned int i = 0; i < MAX_KEYS && keys[i]; i++) { 359 if (keys[i] < ' ') 360 printw("^%c", 'A' - 1 + keys[i]); 361 else 362 printw("%c", keys[i]); 363 } 364 365 getyx(stdscr, y, x); 366 (void)y; 367 int maxwidth = screen.w - x - 2; 368 369 addch(BAR_BEGIN); 370 attrset(BAR_ATTR); 371 372 wchar_t wbuf[sizeof bar.text]; 373 size_t numchars = mbstowcs(wbuf, bar.text, sizeof bar.text); 374 375 if (numchars != (size_t)-1 && (width = wcswidth(wbuf, maxwidth)) != -1) { 376 int pos; 377 for (pos = 0; pos + width < maxwidth; pos++) 378 addch(' '); 379 380 for (size_t i = 0; i < numchars; i++) { 381 pos += wcwidth(wbuf[i]); 382 if (pos > maxwidth) 383 break; 384 addnwstr(wbuf+i, 1); 385 } 386 387 clrtoeol(); 388 } 389 390 attrset(TAG_NORMAL); 391 mvaddch(bar.y, screen.w - 1, BAR_END); 392 attrset(NORMAL_ATTR); 393 move(sy, sx); 394 wnoutrefresh(stdscr); 395 } 396 397 static int 398 show_border(void) { 399 return (bar.pos != BAR_OFF) || (clients && clients->next); 400 } 401 402 static void 403 draw_border(Client *c) { 404 char t = '\0'; 405 int x, y, maxlen, attrs = NORMAL_ATTR; 406 407 if (!show_border()) 408 return; 409 if (sel != c && c->urgent) 410 attrs = URGENT_ATTR; 411 if (sel == c || (runinall && !c->minimized)) 412 attrs = SELECTED_ATTR; 413 414 wattrset(c->window, attrs); 415 getyx(c->window, y, x); 416 mvwhline(c->window, 0, 0, ACS_HLINE, c->w); 417 maxlen = c->w - 10; 418 if (maxlen < 0) 419 maxlen = 0; 420 if ((size_t)maxlen < sizeof(c->title)) { 421 t = c->title[maxlen]; 422 c->title[maxlen] = '\0'; 423 } 424 425 mvwprintw(c->window, 0, 2, "[%s%s#%d]", 426 *c->title ? c->title : "", 427 *c->title ? " | " : "", 428 c->order); 429 if (t) 430 c->title[maxlen] = t; 431 wmove(c->window, y, x); 432 } 433 434 static void 435 draw_content(Client *c) { 436 vt_draw(c->term, c->window, c->has_title_line, 0); 437 } 438 439 static void 440 draw(Client *c) { 441 if (is_content_visible(c)) { 442 redrawwin(c->window); 443 draw_content(c); 444 } 445 if (!isarrange(fullscreen) || sel == c) 446 draw_border(c); 447 wnoutrefresh(c->window); 448 } 449 450 static void 451 draw_all(void) { 452 if (!nextvisible(clients)) { 453 sel = NULL; 454 curs_set(0); 455 erase(); 456 drawbar(); 457 doupdate(); 458 return; 459 } 460 461 if (!isarrange(fullscreen)) { 462 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { 463 if (c != sel) 464 draw(c); 465 } 466 } 467 /* as a last step the selected window is redrawn, 468 * this has the effect that the cursor position is 469 * accurate 470 */ 471 if (sel) 472 draw(sel); 473 } 474 475 static void 476 arrange(void) { 477 unsigned int m = 0, n = 0; 478 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { 479 c->order = ++n; 480 if (c->minimized) 481 m++; 482 } 483 erase(); 484 attrset(NORMAL_ATTR); 485 if (bar.fd == -1 && bar.autohide) { 486 if ((!clients || !clients->next) && n == 1) 487 hidebar(); 488 else 489 showbar(); 490 updatebarpos(); 491 } 492 if (m && !isarrange(fullscreen)) 493 wah--; 494 layout->arrange(); 495 if (m && !isarrange(fullscreen)) { 496 unsigned int i = 0, nw = waw / m, nx = wax; 497 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { 498 if (c->minimized) { 499 resize(c, nx, way+wah, ++i == m ? waw - nx : nw, 1); 500 nx += nw; 501 } 502 } 503 wah++; 504 } 505 focus(NULL); 506 wnoutrefresh(stdscr); 507 drawbar(); 508 draw_all(); 509 } 510 511 512 513 514 515 516 static void 517 attach(Client *c) { 518 if (clients) 519 clients->prev = c; 520 c->next = clients; 521 c->prev = NULL; 522 clients = c; 523 for (int o = 1; c; c = nextvisible(c->next), o++) 524 c->order = o; 525 } 526 527 static void 528 attachafter(Client *c, Client *a) { /* attach c after a */ 529 if (c == a) 530 return; 531 if (!a) 532 for (a = clients; a && a->next; a = a->next); 533 534 if (a) { 535 if (a->next) 536 a->next->prev = c; 537 c->next = a->next; 538 c->prev = a; 539 a->next = c; 540 for (int o = a->order; c; c = nextvisible(c->next)) 541 c->order = ++o; 542 } 543 } 544 545 void 546 attachbottom(Client *c) 547 { 548 // Find the last client in the clients list 549 Client *last = clients; 550 while (last && last->next) { 551 last = last->next; 552 } 553 554 // Attach the new client after the last client (if the list is not empty) 555 if (last) { 556 attachafter(c, last); 557 } else { 558 // If the list is empty, just attach the client normally 559 attach(c); 560 } 561 } 562 563 564 565 566 static void 567 attachstack(Client *c) { 568 c->snext = stack; 569 stack = c; 570 } 571 572 static void 573 detach(Client *c) { 574 Client *d; 575 if (c->prev) 576 c->prev->next = c->next; 577 if (c->next) { 578 c->next->prev = c->prev; 579 for (d = nextvisible(c->next); d; d = nextvisible(d->next)) 580 --d->order; 581 } 582 if (c == clients) 583 clients = c->next; 584 c->next = c->prev = NULL; 585 } 586 587 static void 588 settitle(Client *c) { 589 char *term, *t = title; 590 if (!t && sel == c && *c->title) 591 t = c->title; 592 if (t && (term = getenv("TERM")) && !strstr(term, "linux")) { 593 printf("\033]0;%s\007", t); 594 fflush(stdout); 595 wnoutrefresh(c->window); 596 } 597 } 598 599 static void 600 detachstack(Client *c) { 601 Client **tc; 602 for (tc = &stack; *tc && *tc != c; tc = &(*tc)->snext); 603 *tc = c->snext; 604 } 605 606 static void 607 focus(Client *c) { 608 if (!c) 609 for (c = stack; c && !isvisible(c); c = c->snext); 610 if (sel == c) 611 return; 612 lastsel = sel; 613 sel = c; 614 if (lastsel) { 615 lastsel->urgent = false; 616 if (!isarrange(fullscreen)) { 617 draw_border(lastsel); 618 wnoutrefresh(lastsel->window); 619 } 620 } 621 622 if (c) { 623 detachstack(c); 624 attachstack(c); 625 settitle(c); 626 c->urgent = false; 627 if (isarrange(fullscreen)) { 628 draw(c); 629 } else { 630 draw_border(c); 631 wnoutrefresh(c->window); 632 } 633 } 634 curs_set(c && !c->minimized && vt_cursor_visible(c->term)); 635 } 636 637 static void 638 applycolorrules(Client *c) { 639 const ColorRule *r = colorrules; 640 short fg = r->color->fg, bg = r->color->bg; 641 attr_t attrs = r->attrs; 642 643 for (unsigned int i = 1; i < LENGTH(colorrules); i++) { 644 r = &colorrules[i]; 645 if (strstr(c->title, r->title)) { 646 attrs = r->attrs; 647 fg = r->color->fg; 648 bg = r->color->bg; 649 break; 650 } 651 } 652 653 vt_default_colors_set(c->term, attrs, fg, bg); 654 } 655 656 static void 657 term_title_handler(Vt *term, const char *title) { 658 Client *c = (Client *)vt_data_get(term); 659 if (title) 660 strncpy(c->title, title, sizeof(c->title) - 1); 661 c->title[title ? sizeof(c->title) - 1 : 0] = '\0'; 662 settitle(c); 663 if (!isarrange(fullscreen) || sel == c) 664 draw_border(c); 665 applycolorrules(c); 666 } 667 668 static void 669 term_urgent_handler(Vt *term) { 670 Client *c = (Client *)vt_data_get(term); 671 c->urgent = true; 672 printf("\a"); 673 fflush(stdout); 674 drawbar(); 675 if (!isarrange(fullscreen) && sel != c && isvisible(c)) 676 draw_border(c); 677 } 678 679 static void 680 move_client(Client *c, int x, int y) { 681 if (c->x == x && c->y == y) 682 return; 683 debug("moving, x: %d y: %d\n", x, y); 684 if (mvwin(c->window, y, x) == ERR) { 685 eprint("error moving, x: %d y: %d\n", x, y); 686 } else { 687 c->x = x; 688 c->y = y; 689 } 690 } 691 692 static void 693 resize_client(Client *c, int w, int h) { 694 bool has_title_line = show_border(); 695 bool resize_window = c->w != w || c->h != h; 696 if (resize_window) { 697 debug("resizing, w: %d h: %d\n", w, h); 698 if (wresize(c->window, h, w) == ERR) { 699 eprint("error resizing, w: %d h: %d\n", w, h); 700 } else { 701 c->w = w; 702 c->h = h; 703 } 704 } 705 if (resize_window || c->has_title_line != has_title_line) { 706 c->has_title_line = has_title_line; 707 vt_resize(c->app, h - has_title_line, w); 708 if (c->editor) 709 vt_resize(c->editor, h - has_title_line, w); 710 } 711 } 712 713 static void 714 resize(Client *c, int x, int y, int w, int h) { 715 resize_client(c, w, h); 716 move_client(c, x, y); 717 } 718 719 static Client* 720 get_client_by_coord(unsigned int x, unsigned int y) { 721 if (y < way || y >= way+wah) 722 return NULL; 723 if (isarrange(fullscreen)) 724 return sel; 725 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { 726 if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) { 727 debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order); 728 return c; 729 } 730 } 731 return NULL; 732 } 733 734 static void 735 sigchld_handler(int sig) { 736 int errsv = errno; 737 int status; 738 pid_t pid; 739 740 while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { 741 if (pid == -1) { 742 if (errno == ECHILD) { 743 /* no more child processes */ 744 break; 745 } 746 eprint("waitpid: %s\n", strerror(errno)); 747 break; 748 } 749 750 debug("child with pid %d died\n", pid); 751 752 for (Client *c = clients; c; c = c->next) { 753 if (c->pid == pid) { 754 c->died = true; 755 break; 756 } 757 if (c->editor && vt_pid_get(c->editor) == pid) { 758 c->editor_died = true; 759 break; 760 } 761 } 762 } 763 764 errno = errsv; 765 } 766 767 static void 768 sigwinch_handler(int sig) { 769 screen.need_resize = true; 770 } 771 772 static void 773 sigterm_handler(int sig) { 774 running = false; 775 } 776 777 static void 778 resize_screen(void) { 779 struct winsize ws; 780 781 if (ioctl(0, TIOCGWINSZ, &ws) == -1) { 782 getmaxyx(stdscr, screen.h, screen.w); 783 } else { 784 screen.w = ws.ws_col; 785 screen.h = ws.ws_row; 786 } 787 788 debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h); 789 790 resizeterm(screen.h, screen.w); 791 wresize(stdscr, screen.h, screen.w); 792 updatebarpos(); 793 clear(); 794 arrange(); 795 } 796 797 static KeyBinding* 798 keybinding(KeyCombo keys, unsigned int keycount) { 799 for (unsigned int b = 0; b < LENGTH(bindings); b++) { 800 for (unsigned int k = 0; k < keycount; k++) { 801 if (keys[k] != bindings[b].keys[k]) 802 break; 803 if (k == keycount - 1) 804 return &bindings[b]; 805 } 806 } 807 return NULL; 808 } 809 810 static unsigned int 811 bitoftag(const char *tag) { 812 unsigned int i; 813 if (!tag) 814 return ~0; 815 for (i = 0; (i < LENGTH(tags)) && strcmp(tags[i], tag); i++); 816 return (i < LENGTH(tags)) ? (1 << i) : 0; 817 } 818 819 static void 820 tagschanged() { 821 bool allminimized = true; 822 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { 823 if (!c->minimized) { 824 allminimized = false; 825 break; 826 } 827 } 828 if (allminimized && nextvisible(clients)) { 829 focus(NULL); 830 toggleminimize(NULL); 831 } 832 arrange(); 833 } 834 835 static void 836 tag(const char *args[]) { 837 if (!sel) 838 return; 839 sel->tags = bitoftag(args[0]) & TAGMASK; 840 tagschanged(); 841 } 842 843 static void 844 tagid(const char *args[]) { 845 if (!args[0] || !args[1]) 846 return; 847 848 const int win_id = atoi(args[0]); 849 for (Client *c = clients; c; c = c->next) { 850 if (c->id == win_id) { 851 unsigned int ntags = c->tags; 852 for (unsigned int i = 1; i < MAX_ARGS && args[i]; i++) { 853 if (args[i][0] == '+') 854 ntags |= bitoftag(args[i]+1); 855 else if (args[i][0] == '-') 856 ntags &= ~bitoftag(args[i]+1); 857 else 858 ntags = bitoftag(args[i]); 859 } 860 ntags &= TAGMASK; 861 if (ntags) { 862 c->tags = ntags; 863 tagschanged(); 864 } 865 return; 866 } 867 } 868 } 869 870 static void 871 toggletag(const char *args[]) { 872 if (!sel) 873 return; 874 unsigned int newtags = sel->tags ^ (bitoftag(args[0]) & TAGMASK); 875 if (newtags) { 876 sel->tags = newtags; 877 tagschanged(); 878 } 879 } 880 881 static void 882 toggleview(const char *args[]) { 883 unsigned int newtagset = tagset[seltags] ^ (bitoftag(args[0]) & TAGMASK); 884 if (newtagset) { 885 tagset[seltags] = newtagset; 886 tagschanged(); 887 } 888 } 889 890 static void 891 view(const char *args[]) { 892 unsigned int newtagset = bitoftag(args[0]) & TAGMASK; 893 if (tagset[seltags] != newtagset && newtagset) { 894 seltags ^= 1; /* toggle sel tagset */ 895 tagset[seltags] = newtagset; 896 tagschanged(); 897 } 898 } 899 900 static void 901 viewprevtag(const char *args[]) { 902 seltags ^= 1; 903 tagschanged(); 904 } 905 906 static void 907 keypress(int code) { 908 int key = -1; 909 unsigned int len = 1; 910 char buf[8] = { '\e' }; 911 912 if (code == '\e') { 913 /* pass characters following escape to the underlying app */ 914 nodelay(stdscr, TRUE); 915 for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) { 916 if (t > 255) { 917 key = t; 918 break; 919 } 920 buf[len] = t; 921 } 922 nodelay(stdscr, FALSE); 923 } 924 925 for (Client *c = runinall ? nextvisible(clients) : sel; c; c = nextvisible(c->next)) { 926 if (is_content_visible(c)) { 927 c->urgent = false; 928 if (code == '\e') 929 vt_write(c->term, buf, len); 930 else 931 vt_keypress(c->term, code); 932 if (key != -1) 933 vt_keypress(c->term, key); 934 } 935 if (!runinall) 936 break; 937 } 938 } 939 940 static void 941 mouse_setup(void) { 942 #ifdef CONFIG_MOUSE 943 mmask_t mask = 0; 944 945 if (mouse_events_enabled) { 946 mask = BUTTON1_CLICKED | BUTTON2_CLICKED; 947 for (unsigned int i = 0; i < LENGTH(buttons); i++) 948 mask |= buttons[i].mask; 949 } 950 mousemask(mask, NULL); 951 #endif /* CONFIG_MOUSE */ 952 } 953 954 static bool 955 checkshell(const char *shell) { 956 if (shell == NULL || *shell == '\0' || *shell != '/') 957 return false; 958 if (!strcmp(strrchr(shell, '/')+1, dvtm_name)) 959 return false; 960 if (access(shell, X_OK)) 961 return false; 962 return true; 963 } 964 965 static const char * 966 getshell(void) { 967 const char *shell = getenv("SHELL"); 968 struct passwd *pw; 969 970 if (checkshell(shell)) 971 return shell; 972 if ((pw = getpwuid(getuid())) && checkshell(pw->pw_shell)) 973 return pw->pw_shell; 974 return "/bin/sh"; 975 } 976 977 static void 978 setup(void) { 979 shell = getshell(); 980 setlocale(LC_CTYPE, ""); 981 initscr(); 982 start_color(); 983 noecho(); 984 nonl(); 985 keypad(stdscr, TRUE); 986 mouse_setup(); 987 raw(); 988 vt_init(); 989 vt_keytable_set(keytable, LENGTH(keytable)); 990 for (unsigned int i = 0; i < LENGTH(colors); i++) { 991 if (COLORS == 256) { 992 if (colors[i].fg256) 993 colors[i].fg = colors[i].fg256; 994 if (colors[i].bg256) 995 colors[i].bg = colors[i].bg256; 996 } 997 colors[i].pair = vt_color_reserve(colors[i].fg, colors[i].bg); 998 } 999 resize_screen(); 1000 struct sigaction sa; 1001 memset(&sa, 0, sizeof sa); 1002 sa.sa_flags = 0; 1003 sigemptyset(&sa.sa_mask); 1004 sa.sa_handler = sigwinch_handler; 1005 sigaction(SIGWINCH, &sa, NULL); 1006 sa.sa_handler = sigchld_handler; 1007 sigaction(SIGCHLD, &sa, NULL); 1008 sa.sa_handler = sigterm_handler; 1009 sigaction(SIGTERM, &sa, NULL); 1010 sa.sa_handler = SIG_IGN; 1011 sigaction(SIGPIPE, &sa, NULL); 1012 } 1013 1014 static void 1015 destroy(Client *c) { 1016 if (sel == c) 1017 focusnextnm(NULL); 1018 detach(c); 1019 detachstack(c); 1020 if (sel == c) { 1021 Client *next = nextvisible(clients); 1022 if (next) { 1023 focus(next); 1024 toggleminimize(NULL); 1025 } else { 1026 sel = NULL; 1027 } 1028 } 1029 if (lastsel == c) 1030 lastsel = NULL; 1031 werase(c->window); 1032 wnoutrefresh(c->window); 1033 vt_destroy(c->term); 1034 delwin(c->window); 1035 if (!clients && LENGTH(actions)) { 1036 if (!strcmp(c->cmd, shell)) 1037 quit(NULL); 1038 else 1039 create(NULL); 1040 } 1041 free(c); 1042 arrange(); 1043 } 1044 1045 static void 1046 cleanup(void) { 1047 while (clients) 1048 destroy(clients); 1049 vt_shutdown(); 1050 endwin(); 1051 free(copyreg.data); 1052 if (bar.fd > 0) 1053 close(bar.fd); 1054 if (bar.file) 1055 unlink(bar.file); 1056 if (cmdfifo.fd > 0) 1057 close(cmdfifo.fd); 1058 if (cmdfifo.file) 1059 unlink(cmdfifo.file); 1060 } 1061 1062 static char *getcwd_by_pid(Client *c) { 1063 if (!c) 1064 return NULL; 1065 char buf[32]; 1066 snprintf(buf, sizeof buf, "/proc/%d/cwd", c->pid); 1067 return realpath(buf, NULL); 1068 } 1069 1070 static void 1071 create(const char *args[]) { 1072 const char *pargs[4] = { shell, NULL }; 1073 char buf[8], *cwd = NULL; 1074 const char *env[] = { 1075 "DVTM_WINDOW_ID", buf, 1076 NULL 1077 }; 1078 1079 if (args && args[0]) { 1080 pargs[1] = "-c"; 1081 pargs[2] = args[0]; 1082 pargs[3] = NULL; 1083 } 1084 Client *c = calloc(1, sizeof(Client)); 1085 if (!c) 1086 return; 1087 c->tags = tagset[seltags]; 1088 c->id = ++cmdfifo.id; 1089 snprintf(buf, sizeof buf, "%d", c->id); 1090 1091 if (!(c->window = newwin(wah, waw, way, wax))) { 1092 free(c); 1093 return; 1094 } 1095 1096 c->term = c->app = vt_create(screen.h, screen.w, screen.history); 1097 if (!c->term) { 1098 delwin(c->window); 1099 free(c); 1100 return; 1101 } 1102 1103 if (args && args[0]) { 1104 c->cmd = args[0]; 1105 char name[PATH_MAX]; 1106 strncpy(name, args[0], sizeof(name)); 1107 name[sizeof(name)-1] = '\0'; 1108 strncpy(c->title, basename(name), sizeof(c->title)); 1109 } else { 1110 c->cmd = shell; 1111 } 1112 1113 if (args && args[1]) 1114 strncpy(c->title, args[1], sizeof(c->title)); 1115 c->title[sizeof(c->title)-1] = '\0'; 1116 1117 if (args && args[2]) 1118 cwd = !strcmp(args[2], "$CWD") ? getcwd_by_pid(sel) : (char*)args[2]; 1119 c->pid = vt_forkpty(c->term, shell, pargs, cwd, env, NULL, NULL); 1120 if (args && args[2] && !strcmp(args[2], "$CWD")) 1121 free(cwd); 1122 vt_data_set(c->term, c); 1123 vt_title_handler_set(c->term, term_title_handler); 1124 vt_urgent_handler_set(c->term, term_urgent_handler); 1125 applycolorrules(c); 1126 c->x = wax; 1127 c->y = way; 1128 debug("client with pid %d forked\n", c->pid); 1129 //attach(c); 1130 1131 attachbottom(c); 1132 focus(c); 1133 arrange(); 1134 } 1135 1136 static void 1137 copymode(const char *args[]) { 1138 if (!args || !args[0] || !sel || sel->editor) 1139 return; 1140 1141 bool colored = strstr(args[0], "pager") != NULL; 1142 1143 if (!(sel->editor = vt_create(sel->h - sel->has_title_line, sel->w, 0))) 1144 return; 1145 1146 int *to = &sel->editor_fds[0]; 1147 int *from = strstr(args[0], "editor") ? &sel->editor_fds[1] : NULL; 1148 sel->editor_fds[0] = sel->editor_fds[1] = -1; 1149 1150 const char *argv[3] = { args[0], NULL, NULL }; 1151 char argline[32]; 1152 int line = vt_content_end(sel->app); 1153 snprintf(argline, sizeof(argline), "+%d", line); 1154 argv[1] = argline; 1155 1156 char *cwd = getcwd_by_pid(sel); 1157 if (vt_forkpty(sel->editor, args[0], argv, cwd, NULL, to, from) < 0) { 1158 vt_destroy(sel->editor); 1159 sel->editor = NULL; 1160 return; 1161 } 1162 1163 sel->term = sel->editor; 1164 1165 if (sel->editor_fds[0] != -1) { 1166 char *buf = NULL; 1167 size_t len = vt_content_get(sel->app, &buf, colored); 1168 char *cur = buf; 1169 while (len > 0) { 1170 ssize_t res = write(sel->editor_fds[0], cur, len); 1171 if (res < 0) { 1172 if (errno == EAGAIN || errno == EINTR) 1173 continue; 1174 break; 1175 } 1176 cur += res; 1177 len -= res; 1178 } 1179 free(buf); 1180 close(sel->editor_fds[0]); 1181 sel->editor_fds[0] = -1; 1182 } 1183 1184 if (args[1]) 1185 vt_write(sel->editor, args[1], strlen(args[1])); 1186 } 1187 1188 static void 1189 focusn(const char *args[]) { 1190 for (Client *c = nextvisible(clients); c; c = nextvisible(c->next)) { 1191 if (c->order == atoi(args[0])) { 1192 focus(c); 1193 if (c->minimized) 1194 toggleminimize(NULL); 1195 return; 1196 } 1197 } 1198 } 1199 1200 static void 1201 focusid(const char *args[]) { 1202 if (!args[0]) 1203 return; 1204 1205 const int win_id = atoi(args[0]); 1206 for (Client *c = clients; c; c = c->next) { 1207 if (c->id == win_id) { 1208 focus(c); 1209 if (c->minimized) 1210 toggleminimize(NULL); 1211 if (!isvisible(c)) { 1212 c->tags |= tagset[seltags]; 1213 tagschanged(); 1214 } 1215 return; 1216 } 1217 } 1218 } 1219 1220 static void 1221 focusnext(const char *args[]) { 1222 Client *c; 1223 if (!sel) 1224 return; 1225 for (c = sel->next; c && !isvisible(c); c = c->next); 1226 if (!c) 1227 for (c = clients; c && !isvisible(c); c = c->next); 1228 if (c) 1229 focus(c); 1230 } 1231 1232 static void 1233 focusnextnm(const char *args[]) { 1234 if (!sel) 1235 return; 1236 Client *c = sel; 1237 do { 1238 c = nextvisible(c->next); 1239 if (!c) 1240 c = nextvisible(clients); 1241 } while (c->minimized && c != sel); 1242 focus(c); 1243 } 1244 1245 static void 1246 focusprev(const char *args[]) { 1247 Client *c; 1248 if (!sel) 1249 return; 1250 for (c = sel->prev; c && !isvisible(c); c = c->prev); 1251 if (!c) { 1252 for (c = clients; c && c->next; c = c->next); 1253 for (; c && !isvisible(c); c = c->prev); 1254 } 1255 if (c) 1256 focus(c); 1257 } 1258 1259 static void 1260 focusprevnm(const char *args[]) { 1261 if (!sel) 1262 return; 1263 Client *c = sel; 1264 do { 1265 for (c = c->prev; c && !isvisible(c); c = c->prev); 1266 if (!c) { 1267 for (c = clients; c && c->next; c = c->next); 1268 for (; c && !isvisible(c); c = c->prev); 1269 } 1270 } while (c && c != sel && c->minimized); 1271 focus(c); 1272 } 1273 1274 static void 1275 focuslast(const char *args[]) { 1276 if (lastsel) 1277 focus(lastsel); 1278 } 1279 1280 static void 1281 focusup(const char *args[]) { 1282 if (!sel) 1283 return; 1284 /* avoid vertical separator, hence +1 in x direction */ 1285 Client *c = get_client_by_coord(sel->x + 1, sel->y - 1); 1286 if (c) 1287 focus(c); 1288 else 1289 focusprev(args); 1290 } 1291 1292 static void 1293 focusdown(const char *args[]) { 1294 if (!sel) 1295 return; 1296 Client *c = get_client_by_coord(sel->x, sel->y + sel->h); 1297 if (c) 1298 focus(c); 1299 else 1300 focusnext(args); 1301 } 1302 1303 static void 1304 focusleft(const char *args[]) { 1305 if (!sel) 1306 return; 1307 Client *c = get_client_by_coord(sel->x - 2, sel->y); 1308 if (c) 1309 focus(c); 1310 else 1311 focusprev(args); 1312 } 1313 1314 static void 1315 focusright(const char *args[]) { 1316 if (!sel) 1317 return; 1318 Client *c = get_client_by_coord(sel->x + sel->w + 1, sel->y); 1319 if (c) 1320 focus(c); 1321 else 1322 focusnext(args); 1323 } 1324 1325 static void 1326 killclient(const char *args[]) { 1327 if (!sel) 1328 return; 1329 debug("killing client with pid: %d\n", sel->pid); 1330 kill(-sel->pid, SIGKILL); 1331 } 1332 1333 static void 1334 paste(const char *args[]) { 1335 if (sel && copyreg.data) 1336 vt_write(sel->term, copyreg.data, copyreg.len); 1337 } 1338 1339 static void 1340 quit(const char *args[]) { 1341 cleanup(); 1342 exit(EXIT_SUCCESS); 1343 } 1344 1345 static void 1346 redraw(const char *args[]) { 1347 for (Client *c = clients; c; c = c->next) { 1348 if (!c->minimized) { 1349 vt_dirty(c->term); 1350 wclear(c->window); 1351 wnoutrefresh(c->window); 1352 } 1353 } 1354 resize_screen(); 1355 } 1356 1357 static void 1358 scrollback(const char *args[]) { 1359 if (!is_content_visible(sel)) 1360 return; 1361 1362 if (!args[0] || atoi(args[0]) < 0) 1363 vt_scroll(sel->term, -sel->h/2); 1364 else 1365 vt_scroll(sel->term, sel->h/2); 1366 1367 draw(sel); 1368 curs_set(vt_cursor_visible(sel->term)); 1369 } 1370 1371 static void 1372 send(const char *args[]) { 1373 if (sel && args && args[0]) 1374 vt_write(sel->term, args[0], strlen(args[0])); 1375 } 1376 1377 static void 1378 setlayout(const char *args[]) { 1379 unsigned int i; 1380 1381 if (!args || !args[0]) { 1382 if (++layout == &layouts[LENGTH(layouts)]) 1383 layout = &layouts[0]; 1384 } else { 1385 for (i = 0; i < LENGTH(layouts); i++) 1386 if (!strcmp(args[0], layouts[i].symbol)) 1387 break; 1388 if (i == LENGTH(layouts)) 1389 return; 1390 layout = &layouts[i]; 1391 } 1392 arrange(); 1393 } 1394 1395 static void 1396 incnmaster(const char *args[]) { 1397 int delta; 1398 1399 if (isarrange(fullscreen) || isarrange(grid)) 1400 return; 1401 /* arg handling, manipulate nmaster */ 1402 if (args[0] == NULL) { 1403 screen.nmaster = NMASTER; 1404 } else if (sscanf(args[0], "%d", &delta) == 1) { 1405 if (args[0][0] == '+' || args[0][0] == '-') 1406 screen.nmaster += delta; 1407 else 1408 screen.nmaster = delta; 1409 if (screen.nmaster < 1) 1410 screen.nmaster = 1; 1411 } 1412 arrange(); 1413 } 1414 1415 static void 1416 setmfact(const char *args[]) { 1417 float delta; 1418 1419 if (isarrange(fullscreen) || isarrange(grid)) 1420 return; 1421 /* arg handling, manipulate mfact */ 1422 if (args[0] == NULL) { 1423 screen.mfact = MFACT; 1424 } else if (sscanf(args[0], "%f", &delta) == 1) { 1425 if (args[0][0] == '+' || args[0][0] == '-') 1426 screen.mfact += delta; 1427 else 1428 screen.mfact = delta; 1429 if (screen.mfact < 0.1) 1430 screen.mfact = 0.1; 1431 else if (screen.mfact > 0.9) 1432 screen.mfact = 0.9; 1433 } 1434 arrange(); 1435 } 1436 1437 static void 1438 startup(const char *args[]) { 1439 for (unsigned int i = 0; i < LENGTH(actions); i++) 1440 actions[i].cmd(actions[i].args); 1441 } 1442 1443 static void 1444 togglebar(const char *args[]) { 1445 if (bar.pos == BAR_OFF) 1446 showbar(); 1447 else 1448 hidebar(); 1449 bar.autohide = false; 1450 updatebarpos(); 1451 redraw(NULL); 1452 } 1453 1454 static void 1455 togglebarpos(const char *args[]) { 1456 switch (bar.pos == BAR_OFF ? bar.lastpos : bar.pos) { 1457 case BAR_TOP: 1458 bar.pos = BAR_BOTTOM; 1459 break; 1460 case BAR_BOTTOM: 1461 bar.pos = BAR_TOP; 1462 break; 1463 } 1464 updatebarpos(); 1465 redraw(NULL); 1466 } 1467 1468 static void 1469 toggleminimize(const char *args[]) { 1470 Client *c, *m, *t; 1471 unsigned int n; 1472 if (!sel) 1473 return; 1474 /* the last window can't be minimized */ 1475 if (!sel->minimized) { 1476 for (n = 0, c = nextvisible(clients); c; c = nextvisible(c->next)) 1477 if (!c->minimized) 1478 n++; 1479 if (n == 1) 1480 return; 1481 } 1482 sel->minimized = !sel->minimized; 1483 m = sel; 1484 /* check whether the master client was minimized */ 1485 if (sel == nextvisible(clients) && sel->minimized) { 1486 c = nextvisible(sel->next); 1487 detach(c); 1488 attach(c); 1489 focus(c); 1490 detach(m); 1491 for (; c && (t = nextvisible(c->next)) && !t->minimized; c = t); 1492 attachafter(m, c); 1493 } else if (m->minimized) { 1494 /* non master window got minimized move it above all other 1495 * minimized ones */ 1496 focusnextnm(NULL); 1497 detach(m); 1498 for (c = nextvisible(clients); c && (t = nextvisible(c->next)) && !t->minimized; c = t); 1499 attachafter(m, c); 1500 } else { /* window is no longer minimized, move it to the master area */ 1501 vt_dirty(m->term); 1502 detach(m); 1503 attach(m); 1504 } 1505 arrange(); 1506 } 1507 1508 static void 1509 togglemouse(const char *args[]) { 1510 mouse_events_enabled = !mouse_events_enabled; 1511 mouse_setup(); 1512 } 1513 1514 static void 1515 togglerunall(const char *args[]) { 1516 runinall = !runinall; 1517 drawbar(); 1518 draw_all(); 1519 } 1520 1521 static void 1522 zoom(const char *args[]) { 1523 Client *c; 1524 1525 if (!sel) 1526 return; 1527 if (args && args[0]) 1528 focusn(args); 1529 if ((c = sel) == nextvisible(clients)) 1530 if (!(c = nextvisible(c->next))) 1531 return; 1532 detach(c); 1533 attach(c); 1534 focus(c); 1535 if (c->minimized) 1536 toggleminimize(NULL); 1537 arrange(); 1538 } 1539 1540 /* commands for use by mouse bindings */ 1541 static void 1542 mouse_focus(const char *args[]) { 1543 focus(msel); 1544 if (msel->minimized) 1545 toggleminimize(NULL); 1546 } 1547 1548 static void 1549 mouse_fullscreen(const char *args[]) { 1550 mouse_focus(NULL); 1551 setlayout(isarrange(fullscreen) ? NULL : args); 1552 } 1553 1554 static void 1555 mouse_minimize(const char *args[]) { 1556 focus(msel); 1557 toggleminimize(NULL); 1558 } 1559 1560 static void 1561 mouse_zoom(const char *args[]) { 1562 focus(msel); 1563 zoom(NULL); 1564 } 1565 1566 static Cmd * 1567 get_cmd_by_name(const char *name) { 1568 for (unsigned int i = 0; i < LENGTH(commands); i++) { 1569 if (!strcmp(name, commands[i].name)) 1570 return &commands[i]; 1571 } 1572 return NULL; 1573 } 1574 1575 static void 1576 handle_cmdfifo(void) { 1577 int r; 1578 char *p, *s, cmdbuf[512], c; 1579 Cmd *cmd; 1580 1581 r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1); 1582 if (r <= 0) { 1583 cmdfifo.fd = -1; 1584 return; 1585 } 1586 1587 cmdbuf[r] = '\0'; 1588 p = cmdbuf; 1589 while (*p) { 1590 /* find the command name */ 1591 for (; *p == ' ' || *p == '\n'; p++); 1592 for (s = p; *p && *p != ' ' && *p != '\n'; p++); 1593 if ((c = *p)) 1594 *p++ = '\0'; 1595 if (*s && (cmd = get_cmd_by_name(s)) != NULL) { 1596 bool quote = false; 1597 int argc = 0; 1598 const char *args[MAX_ARGS], *arg; 1599 memset(args, 0, sizeof(args)); 1600 /* if arguments were specified in config.h ignore the one given via 1601 * the named pipe and thus skip everything until we find a new line 1602 */ 1603 if (cmd->action.args[0] || c == '\n') { 1604 debug("execute %s", s); 1605 cmd->action.cmd(cmd->action.args); 1606 while (*p && *p != '\n') 1607 p++; 1608 continue; 1609 } 1610 /* no arguments were given in config.h so we parse the command line */ 1611 while (*p == ' ') 1612 p++; 1613 arg = p; 1614 for (; (c = *p); p++) { 1615 switch (*p) { 1616 case '\\': 1617 /* remove the escape character '\\' move every 1618 * following character to the left by one position 1619 */ 1620 switch (p[1]) { 1621 case '\\': 1622 case '\'': 1623 case '\"': { 1624 char *t = p+1; 1625 do { 1626 t[-1] = *t; 1627 } while (*t++); 1628 } 1629 } 1630 break; 1631 case '\'': 1632 case '\"': 1633 quote = !quote; 1634 break; 1635 case ' ': 1636 if (!quote) { 1637 case '\n': 1638 /* remove trailing quote if there is one */ 1639 if (*(p - 1) == '\'' || *(p - 1) == '\"') 1640 *(p - 1) = '\0'; 1641 *p++ = '\0'; 1642 /* remove leading quote if there is one */ 1643 if (*arg == '\'' || *arg == '\"') 1644 arg++; 1645 if (argc < MAX_ARGS) 1646 args[argc++] = arg; 1647 1648 while (*p == ' ') 1649 ++p; 1650 arg = p--; 1651 } 1652 break; 1653 } 1654 1655 if (c == '\n' || *p == '\n') { 1656 if (!*p) 1657 p++; 1658 debug("execute %s", s); 1659 for(int i = 0; i < argc; i++) 1660 debug(" %s", args[i]); 1661 debug("\n"); 1662 cmd->action.cmd(args); 1663 break; 1664 } 1665 } 1666 } 1667 } 1668 } 1669 1670 static void 1671 handle_mouse(void) { 1672 #ifdef CONFIG_MOUSE 1673 MEVENT event; 1674 unsigned int i; 1675 if (getmouse(&event) != OK) 1676 return; 1677 msel = get_client_by_coord(event.x, event.y); 1678 1679 if (!msel) 1680 return; 1681 1682 debug("mouse x:%d y:%d cx:%d cy:%d mask:%d\n", event.x, event.y, event.x - msel->x, event.y - msel->y, event.bstate); 1683 1684 vt_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate); 1685 1686 for (i = 0; i < LENGTH(buttons); i++) { 1687 if (event.bstate & buttons[i].mask) 1688 buttons[i].action.cmd(buttons[i].action.args); 1689 } 1690 1691 msel = NULL; 1692 #endif /* CONFIG_MOUSE */ 1693 } 1694 1695 static void 1696 handle_statusbar(void) { 1697 char *p; 1698 int r; 1699 switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) { 1700 case -1: 1701 strncpy(bar.text, strerror(errno), sizeof bar.text - 1); 1702 bar.text[sizeof bar.text - 1] = '\0'; 1703 bar.fd = -1; 1704 break; 1705 case 0: 1706 bar.fd = -1; 1707 break; 1708 default: 1709 bar.text[r] = '\0'; 1710 p = bar.text + r - 1; 1711 for (; p >= bar.text && *p == '\n'; *p-- = '\0'); 1712 for (; p >= bar.text && *p != '\n'; --p); 1713 if (p >= bar.text) 1714 memmove(bar.text, p + 1, strlen(p)); 1715 drawbar(); 1716 } 1717 } 1718 1719 static void 1720 handle_editor(Client *c) { 1721 if (!copyreg.data && (copyreg.data = malloc(screen.history))) 1722 copyreg.size = screen.history; 1723 copyreg.len = 0; 1724 while (c->editor_fds[1] != -1 && copyreg.len < copyreg.size) { 1725 ssize_t len = read(c->editor_fds[1], copyreg.data + copyreg.len, copyreg.size - copyreg.len); 1726 if (len == -1) { 1727 if (errno == EINTR) 1728 continue; 1729 break; 1730 } 1731 if (len == 0) 1732 break; 1733 copyreg.len += len; 1734 if (copyreg.len == copyreg.size) { 1735 copyreg.size *= 2; 1736 if (!(copyreg.data = realloc(copyreg.data, copyreg.size))) { 1737 copyreg.size = 0; 1738 copyreg.len = 0; 1739 } 1740 } 1741 } 1742 c->editor_died = false; 1743 c->editor_fds[1] = -1; 1744 vt_destroy(c->editor); 1745 c->editor = NULL; 1746 c->term = c->app; 1747 vt_dirty(c->term); 1748 draw_content(c); 1749 wnoutrefresh(c->window); 1750 } 1751 1752 static int 1753 open_or_create_fifo(const char *name, const char **name_created) { 1754 struct stat info; 1755 int fd; 1756 1757 do { 1758 if ((fd = open(name, O_RDWR|O_NONBLOCK)) == -1) { 1759 if (errno == ENOENT && !mkfifo(name, S_IRUSR|S_IWUSR)) { 1760 *name_created = name; 1761 continue; 1762 } 1763 error("%s\n", strerror(errno)); 1764 } 1765 } while (fd == -1); 1766 1767 if (fstat(fd, &info) == -1) 1768 error("%s\n", strerror(errno)); 1769 if (!S_ISFIFO(info.st_mode)) 1770 error("%s is not a named pipe\n", name); 1771 return fd; 1772 } 1773 1774 static void 1775 usage(void) { 1776 cleanup(); 1777 eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] " 1778 "[-s status-fifo] [-c cmd-fifo] [cmd...]\n"); 1779 exit(EXIT_FAILURE); 1780 } 1781 1782 static bool 1783 parse_args(int argc, char *argv[]) { 1784 bool init = false; 1785 const char *name = argv[0]; 1786 1787 if (name && (name = strrchr(name, '/'))) 1788 dvtm_name = name + 1; 1789 if (!getenv("ESCDELAY")) 1790 set_escdelay(100); 1791 for (int arg = 1; arg < argc; arg++) { 1792 if (argv[arg][0] != '-') { 1793 const char *args[] = { argv[arg], NULL, NULL }; 1794 if (!init) { 1795 setup(); 1796 init = true; 1797 } 1798 create(args); 1799 continue; 1800 } 1801 if (argv[arg][1] != 'v' && argv[arg][1] != 'M' && (arg + 1) >= argc) 1802 usage(); 1803 switch (argv[arg][1]) { 1804 case 'v': 1805 puts("dvtm-"VERSION" © 2007-2016 Marc André Tanner"); 1806 exit(EXIT_SUCCESS); 1807 case 'M': 1808 mouse_events_enabled = !mouse_events_enabled; 1809 break; 1810 case 'm': { 1811 char *mod = argv[++arg]; 1812 if (mod[0] == '^' && mod[1]) 1813 *mod = CTRL(mod[1]); 1814 for (unsigned int b = 0; b < LENGTH(bindings); b++) 1815 if (bindings[b].keys[0] == MOD) 1816 bindings[b].keys[0] = *mod; 1817 break; 1818 } 1819 case 'd': 1820 set_escdelay(atoi(argv[++arg])); 1821 if (ESCDELAY < 50) 1822 set_escdelay(50); 1823 else if (ESCDELAY > 1000) 1824 set_escdelay(1000); 1825 break; 1826 case 'h': 1827 screen.history = atoi(argv[++arg]); 1828 break; 1829 case 't': 1830 title = argv[++arg]; 1831 break; 1832 case 's': 1833 bar.fd = open_or_create_fifo(argv[++arg], &bar.file); 1834 updatebarpos(); 1835 break; 1836 case 'c': { 1837 char *fifo; 1838 cmdfifo.fd = open_or_create_fifo(argv[++arg], &cmdfifo.file); 1839 if (!(fifo = realpath(argv[arg], NULL))) 1840 error("%s\n", strerror(errno)); 1841 setenv("DVTM_CMD_FIFO", fifo, 1); 1842 free(fifo); 1843 break; 1844 } 1845 default: 1846 usage(); 1847 } 1848 } 1849 return init; 1850 } 1851 1852 int 1853 main(int argc, char *argv[]) { 1854 unsigned int key_index = 0; 1855 memset(keys, 0, sizeof(keys)); 1856 sigset_t emptyset, blockset; 1857 1858 setenv("DVTM", VERSION, 1); 1859 if (!parse_args(argc, argv)) { 1860 setup(); 1861 startup(NULL); 1862 } 1863 1864 sigemptyset(&emptyset); 1865 sigemptyset(&blockset); 1866 sigaddset(&blockset, SIGWINCH); 1867 sigaddset(&blockset, SIGCHLD); 1868 sigprocmask(SIG_BLOCK, &blockset, NULL); 1869 1870 while (running) { 1871 int r, nfds = 0; 1872 fd_set rd; 1873 1874 if (screen.need_resize) { 1875 resize_screen(); 1876 screen.need_resize = false; 1877 } 1878 1879 FD_ZERO(&rd); 1880 FD_SET(STDIN_FILENO, &rd); 1881 1882 if (cmdfifo.fd != -1) { 1883 FD_SET(cmdfifo.fd, &rd); 1884 nfds = cmdfifo.fd; 1885 } 1886 1887 if (bar.fd != -1) { 1888 FD_SET(bar.fd, &rd); 1889 nfds = MAX(nfds, bar.fd); 1890 } 1891 1892 for (Client *c = clients; c; ) { 1893 if (c->editor && c->editor_died) 1894 handle_editor(c); 1895 if (!c->editor && c->died) { 1896 Client *t = c->next; 1897 destroy(c); 1898 c = t; 1899 continue; 1900 } 1901 int pty = c->editor ? vt_pty_get(c->editor) : vt_pty_get(c->app); 1902 FD_SET(pty, &rd); 1903 nfds = MAX(nfds, pty); 1904 c = c->next; 1905 } 1906 1907 doupdate(); 1908 r = pselect(nfds + 1, &rd, NULL, NULL, NULL, &emptyset); 1909 1910 if (r < 0) { 1911 if (errno == EINTR) 1912 continue; 1913 perror("select()"); 1914 exit(EXIT_FAILURE); 1915 } 1916 1917 if (FD_ISSET(STDIN_FILENO, &rd)) { 1918 int code = getch(); 1919 if (code >= 0) { 1920 keys[key_index++] = code; 1921 KeyBinding *binding = NULL; 1922 if (code == KEY_MOUSE) { 1923 key_index = 0; 1924 handle_mouse(); 1925 } else if ((binding = keybinding(keys, key_index))) { 1926 unsigned int key_length = MAX_KEYS; 1927 while (key_length > 1 && !binding->keys[key_length-1]) 1928 key_length--; 1929 if (key_index == key_length) { 1930 binding->action.cmd(binding->action.args); 1931 key_index = 0; 1932 memset(keys, 0, sizeof(keys)); 1933 } 1934 } else { 1935 key_index = 0; 1936 memset(keys, 0, sizeof(keys)); 1937 keypress(code); 1938 } 1939 drawbar(); 1940 if (is_content_visible(sel)) 1941 wnoutrefresh(sel->window); 1942 } 1943 if (r == 1) /* no data available on pty's */ 1944 continue; 1945 } 1946 1947 if (cmdfifo.fd != -1 && FD_ISSET(cmdfifo.fd, &rd)) 1948 handle_cmdfifo(); 1949 1950 if (bar.fd != -1 && FD_ISSET(bar.fd, &rd)) 1951 handle_statusbar(); 1952 1953 for (Client *c = clients; c; c = c->next) { 1954 if (FD_ISSET(vt_pty_get(c->term), &rd)) { 1955 if (vt_process(c->term) < 0 && errno == EIO) { 1956 if (c->editor) 1957 c->editor_died = true; 1958 else 1959 c->died = true; 1960 continue; 1961 } 1962 } 1963 1964 if (c != sel && is_content_visible(c)) { 1965 draw_content(c); 1966 wnoutrefresh(c->window); 1967 } 1968 } 1969 1970 if (is_content_visible(sel)) { 1971 draw_content(sel); 1972 curs_set(vt_cursor_visible(sel->term)); 1973 wnoutrefresh(sel->window); 1974 } 1975 } 1976 1977 cleanup(); 1978 return 0; 1979 } 1980