dvtm

Fork of dvtm, a minimal terminal multiplexer
git clone git://git.laack.co/dvtm.git
Log | Files | Refs | README | LICENSE

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