dvtm

dynamic virtual terminal manager - with my changes
git clone https://pi.duncano.de/git/dvtm.git

dvtm.c (39801B)


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