dvtm

dynamic virtual terminal manager - with my changes
git clone https://pi.duncano.de/git/dvtm.git
Log | Files | Refs | README | LICENSE

commit 93d2f0015f2fed69a2e2ec2633a0bc6c80a3b483
parent 021a5bc0000c6e511dfbaab3d032bfd584357616
Author: Marc Andre Tanner <mat@brain-dump.org>
Date:   Sun, 12 Jun 2011 14:08:09 +0200

Code restructuring, no functional changes

Signed-off-by: Marc Andre Tanner <mat@brain-dump.org>

Diffstat:
Makefile | 3+--
cmdfifo.c | 125-------------------------------------------------------------------------------
config.h | 2+-
dvtm.c | 1310+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
mouse.c | 78------------------------------------------------------------------------------
statusbar.c | 80-------------------------------------------------------------------------------
6 files changed, 791 insertions(+), 807 deletions(-)

diff --git a/Makefile b/Makefile @@ -33,8 +33,7 @@ dist: clean @mkdir -p dvtm-${VERSION} @cp -R LICENSE Makefile README config.h config.mk \ ${SRC} tile.c bstack.c tstack.c grid.c fullscreen.c \ - madtty.h statusbar.c mouse.c cmdfifo.c \ - dvtm-status dvtm.1 dvtm-${VERSION} + madtty.h dvtm-status dvtm.1 dvtm-${VERSION} @tar -cf dvtm-${VERSION}.tar dvtm-${VERSION} @gzip dvtm-${VERSION}.tar @rm -rf dvtm-${VERSION} diff --git a/cmdfifo.c b/cmdfifo.c @@ -1,125 +0,0 @@ -/* glibc has a non-standard realpath(3) implementation which allocates - * the destination buffer, other C libraries may have a broken implementation - * which expect an already allocated destination buffer. - */ - -#ifndef __GLIBC__ -# include <limits.h> -# ifndef PATH_MAX -# define PATH_MAX 1024 -# endif -#endif - -static char *get_realpath(const char *path) { -#ifdef __GLIBC__ - return realpath(path, NULL); -#else - static char buf[PATH_MAX]; - return realpath(path, buf); -#endif -} - -static Cmd * -get_cmd_by_name(const char *name) { - for (unsigned int i = 0; i < countof(commands); i++) { - if (!strcmp(name, commands[i].name)) - return &commands[i]; - } - return NULL; -} - -static void -handle_cmdfifo() { - int r; - char *p, *s, cmdbuf[512], c; - Cmd *cmd; - switch (r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1)) { - case -1: - case 0: - cmdfifo.fd = -1; - break; - default: - cmdbuf[r] = '\0'; - p = cmdbuf; - while (*p) { - /* find the command name */ - for (; *p == ' ' || *p == '\n'; p++); - for (s = p; *p && *p != ' ' && *p != '\n'; p++); - if ((c = *p)) - *p++ = '\0'; - if (*s && (cmd = get_cmd_by_name(s)) != NULL) { - bool quote = false; - int argc = 0; - /* XXX: initializer assumes MAX_ARGS == 2 use a initialization loop? */ - const char *args[MAX_ARGS] = { NULL, NULL}, *arg; - /* if arguments were specified in config.h ignore the one given via - * the named pipe and thus skip everything until we find a new line - */ - if (cmd->action.args[0] || c == '\n') { - debug("execute %s", s); - cmd->action.cmd(cmd->action.args); - while (*p && *p != '\n') - p++; - continue; - } - /* no arguments were given in config.h so we parse the command line */ - while (*p == ' ') - p++; - arg = p; - for (; (c = *p); p++) { - switch (*p) { - case '\\': - /* remove the escape character '\\' move every - * following character to the left by one position - */ - switch (*(++p)) { - case '\\': - case '\'': - case '\"': { - char *t = p; - for (;;) { - *(t - 1) = *t; - if (*t++ == '\0') - break; - } - p -= 2; - } - } - break; - case '\'': - case '\"': - quote = !quote; - break; - case ' ': - if (!quote) { - case '\n': - /* remove trailing quote if there is one */ - if (*(p - 1) == '\'' || *(p - 1) == '\"') - *(p - 1) = '\0'; - *p++ = '\0'; - /* remove leading quote if there is one */ - if (*arg == '\'' || *arg == '\"') - arg++; - if (argc < MAX_ARGS) - args[argc++] = arg; - - while (*p == ' ') - ++p; - arg = p; - } - break; - } - - if (c == '\n' || *p == '\n') { - debug("execute %s", s); - for(int i = 0; i < argc; i++) - debug(" %s", args[i]); - debug("\n"); - cmd->action.cmd(args); - break; - } - } - } - } - } -} diff --git a/config.h b/config.h @@ -22,7 +22,7 @@ #define NORMAL_FG -1 #define NORMAL_BG -1 /* status bar (command line option -s) position */ -#define BARPOS BarTop /* BarBot, BarOff */ +#define BAR_POS BAR_TOP /* BAR_BOTTOM, BAR_OFF */ /* curses attributes for the status bar */ #define BAR_ATTR A_NORMAL #define BAR_FG BLUE diff --git a/dvtm.c b/dvtm.c @@ -106,7 +106,7 @@ typedef struct { Action action; } Cmd; -enum BarPos { BarTop, BarBot, BarOff }; +enum { BAR_TOP, BAR_BOTTOM, BAR_OFF }; enum { ALIGN_LEFT, ALIGN_RIGHT }; typedef struct { @@ -135,67 +135,54 @@ typedef struct { #endif /* commands for use by keybindings */ -static void quit(const char *args[]); static void create(const char *args[]); -static void startup(const char *args[]); static void escapekey(const char *args[]); -static void killclient(const char *args[]); static void focusn(const char *args[]); static void focusnext(const char *args[]); static void focusnextnm(const char *args[]); static void focusprev(const char *args[]); static void focusprevnm(const char *args[]); -static void togglebell(const char *key[]); -static void toggleminimize(const char *args[]); -static void setmfact(const char *args[]); -static void setlayout(const char *args[]); -static void scrollback(const char *args[]); -static void redraw(const char *args[]); -static void zoom(const char *args[]); +static void killclient(const char *args[]); static void lock(const char *key[]); -static void togglerunall(const char *args[]); - +static void quit(const char *args[]); +static void redraw(const char *args[]); +static void scrollback(const char *args[]); +static void setlayout(const char *args[]); +static void setmfact(const char *args[]); +static void startup(const char *args[]); static void togglebar(const char *args[]); +static void togglebell(const char *key[]); +static void toggleminimize(const char *args[]); static void togglemouse(const char *args[]); +static void togglerunall(const char *args[]); +static void zoom(const char *args[]); +/* commands for use by mouse bindings */ static void mouse_focus(const char *args[]); static void mouse_fullscreen(const char *args[]); static void mouse_minimize(const char *args[]); static void mouse_zoom(const char *args[]); -static void clear_workspace(); -static void draw(Client *c); -static void draw_all(bool border); -static void draw_border(Client *c); +/* functions and variables available to layouts via config.h */ static void resize(Client *c, int x, int y, int w, int h); -static void resize_screen(); -static void eprint(const char *errstr, ...); -static bool isarrange(void (*func)()); -static void arrange(); -static void focus(Client *c); -static void keypress(int code); - +extern Screen screen; static unsigned int waw, wah, wax, way; static Client *clients = NULL; -extern Screen screen; #include "config.h" +/* global variables */ Screen screen = { MFACT, SCROLL_HISTORY }; static Client *sel = NULL; static Client *msel = NULL; static bool mouse_events_enabled = ENABLE_MOUSE; static Layout *layout = layouts; -static StatusBar bar = { -1, BARPOS, 1 }; +static StatusBar bar = { -1, BAR_POS, 1 }; static CmdFifo cmdfifo = { -1 }; static const char *shell; static bool running = true; static bool runinall = false; -#include "mouse.c" -#include "cmdfifo.c" -#include "statusbar.c" - static void eprint(const char *errstr, ...) { va_list ap; @@ -213,6 +200,132 @@ error(const char *errstr, ...) { exit(EXIT_FAILURE); } +static bool +isarrange(void (*func)()) { + return func == layout->arrange; +} + +static void +clear_workspace() { + for (unsigned int y = 0; y < wah; y++) + mvhline(way + y, 0, ' ', waw); + wnoutrefresh(stdscr); +} + +static void +drawbar() { + wchar_t wbuf[sizeof bar.text]; + int w, maxwidth = screen.w - 2; + if (bar.pos == BAR_OFF || !bar.text[0]) + return; + curs_set(0); + attrset(BAR_ATTR); + wcolor_set(stdscr, madtty_color_get(BAR_FG, BAR_BG), NULL); + mvaddch(bar.y, 0, '['); + if (mbstowcs(wbuf, bar.text, sizeof bar.text) == (size_t)-1) + return; + if ((w = wcswidth(wbuf, maxwidth)) == -1) + return; + if (BAR_ALIGN == ALIGN_RIGHT) { + for (int i = 0; i + w < maxwidth; i++) + addch(' '); + } + addstr(bar.text); + if (BAR_ALIGN == ALIGN_LEFT) { + for (; w < maxwidth; w++) + addch(' '); + } + mvaddch(bar.y, screen.w - 1, ']'); + attrset(NORMAL_ATTR); + if (sel) + curs_set(madtty_cursor(sel->term)); + refresh(); +} + +static void +draw_border(Client *c) { + char *s, t = '\0'; + int x, y, o; + if (sel == c) { + wattrset(c->window, SELECTED_ATTR); + wcolor_set(c->window, madtty_color_get(SELECTED_FG, SELECTED_BG), NULL); + } else { + wattrset(c->window, NORMAL_ATTR); + wcolor_set(c->window, madtty_color_get(NORMAL_FG, NORMAL_BG), NULL); + } + getyx(c->window, y, x); + curs_set(0); + mvwhline(c->window, 0, 0, ACS_HLINE, c->w); + o = c->w - (4 + sstrlen(TITLE) - 5 + sstrlen(SEPARATOR)); + if (o < 0) + o = 0; + if ((size_t)o < sizeof(c->title)) { + t = *(s = &c->title[o]); + *s = '\0'; + } + mvwprintw(c->window, 0, 2, TITLE, + *c->title ? c->title : "", + *c->title ? SEPARATOR : "", + c->order); + if (t) + *s = t; + wmove(c->window, y, x); + if (!c->minimized) + curs_set(madtty_cursor(c->term)); +} + +static void +draw_content(Client *c) { + if (!c->minimized || isarrange(fullscreen)) { + madtty_draw(c->term, c->window, 1, 0); + if (c != sel) + curs_set(0); + } +} + +static void +draw(Client *c) { + draw_content(c); + draw_border(c); + wrefresh(c->window); +} + +static void +draw_all(bool border) { + Client *c; + curs_set(0); + for (c = clients; c; c = c->next) { + redrawwin(c->window); + if (c == sel) + continue; + draw_content(c); + if (border) + draw_border(c); + wnoutrefresh(c->window); + } + /* as a last step the selected window is redrawn, + * this has the effect that the cursor position is + * accurate + */ + refresh(); + if (sel) { + draw_content(sel); + if (border) + draw_border(sel); + wrefresh(sel->window); + } +} + +static void +arrange() { + clear_workspace(); + attrset(NORMAL_ATTR); + color_set(madtty_color_get(NORMAL_FG, NORMAL_BG), NULL); + layout->arrange(); + wnoutrefresh(stdscr); + draw_all(true); +} + static void attach(Client *c) { uint8_t order; @@ -260,21 +373,6 @@ detach(Client *c) { } static void -arrange() { - clear_workspace(); - attrset(NORMAL_ATTR); - color_set(madtty_color_get(NORMAL_FG, NORMAL_BG), NULL); - layout->arrange(); - wnoutrefresh(stdscr); - draw_all(true); -} - -static bool -isarrange(void (*func)()) { - return func == layout->arrange; -} - -static void focus(Client *c) { Client *tmp = sel; if (sel == c) @@ -291,298 +389,421 @@ focus(Client *c) { } static void -focusn(const char *args[]) { - Client *c; +applycolorrules(madtty_t *term, char *title) { + unsigned int i; + unsigned attrs = A_NORMAL; + short fg = -1, bg = -1; + const ColorRule *r; - for (c = clients; c; c = c->next) { - if (c->order == atoi(args[0])) { - focus(c); - if (c->minimized) - toggleminimize(NULL); - return; + for (i = 0; i < countof(colorrules); i++) { + r = &colorrules[i]; + if (strstr(title, r->title)) { + attrs = r->attrs; + fg = r->fg; + bg = r->bg; + break; } } + madtty_set_default_colors(term, attrs, fg, bg); } -static void -focusnext(const char *args[]) { +static int +title_escape_seq_handler(madtty_t *term, char *es) { Client *c; - - if (!sel) - return; - - c = sel->next; - if (!c) - c = clients; - if (c) - focus(c); + unsigned int l; + if (es[0] != ']' || (es[1] && (es[1] < '0' || es[1] > '9')) || (es[2] && es[2] != ';')) + return MADTTY_HANDLER_NOWAY; + if ((l = strlen(es)) < 3 || es[l - 1] != '\07') + return MADTTY_HANDLER_NOTYET; + es[l - 1] = '\0'; + c = (Client *)madtty_get_data(term); + strncpy(c->title, es + 3, sizeof(c->title)); + draw_border(c); + debug("window title: %s\n", c->title); + applycolorrules(term, c->title); + return MADTTY_HANDLER_OK; } static void -focusnextnm(const char *args[]) { - Client *c; - - if (!sel) +move_client(Client *c, int x, int y) { + if (c->x == x && c->y == y) return; - c = sel; - do { - c = c->next; - if (!c) - c = clients; - } while (c->minimized && c != sel); - focus(c); + debug("moving, x: %d y: %d\n", x, y); + if (mvwin(c->window, y, x) == ERR) + eprint("error moving, x: %d y: %d\n", x, y); + else { + c->x = x; + c->y = y; + } } static void -focusprev(const char *args[]) { - Client *c; - - if (!sel) +resize_client(Client *c, int w, int h) { + if (c->w == w && c->h == h) return; - c = sel->prev; - if (!c) - for (c = clients; c && c->next; c = c->next); - if (c) - focus(c); + debug("resizing, w: %d h: %d\n", w, h); + if (wresize(c->window, h, w) == ERR) + eprint("error resizing, w: %d h: %d\n", w, h); + else { + c->w = w; + c->h = h; + } + madtty_resize(c->term, h - 1, w); } static void -focusprevnm(const char *args[]) { - Client *c; - - if (!sel) - return; - c = sel; - do { - c = c->prev; - if (!c) - for (c = clients; c && c->next; c = c->next); - } while (c->minimized && c != sel); - focus(c); +resize(Client *c, int x, int y, int w, int h) { + resize_client(c, w, h); + move_client(c, x, y); } -static void -zoom(const char *args[]) { +static Client* +get_client_by_pid(pid_t pid) { Client *c; - - if (!sel) - return; - if ((c = sel) == clients) - if (!(c = c->next)) - return; - detach(c); - attach(c); - focus(c); - if (c->minimized) - toggleminimize(NULL); - arrange(); + for (c = clients; c; c = c->next) { + if (c->pid == pid) + return c; + } + return NULL; } -static void -togglebell(const char *args[]) { - madtty_togglebell(sel->term); +static Client* +get_client_by_coord(unsigned int x, unsigned int y) { + Client *c; + if (y < way || y >= wah) + return NULL; + if (isarrange(fullscreen)) + return sel; + for (c = clients; c; c = c->next) { + if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) { + debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order); + return c; + } + } + return NULL; } static void -toggleminimize(const char *args[]) { - Client *c, *m; - unsigned int n; - if (!sel) - return; - /* the last window can't be minimized */ - if (!sel->minimized) { - for (n = 0, c = clients; c; c = c->next) - if (!c->minimized) - n++; - if (n == 1) - return; - } - sel->minimized = !sel->minimized; - m = sel; - /* check whether the master client was minimized */ - if (sel == clients && sel->minimized) { - c = sel->next; - detach(c); - attach(c); - focus(c); - detach(m); - for (; c && c->next && !c->next->minimized; c = c->next); - attachafter(m, c); - } else if (m->minimized) { - /* non master window got minimized move it above all other - * minimized ones */ - focusnextnm(NULL); - detach(m); - for (c = clients; c && c->next && !c->next->minimized; c = c->next); - attachafter(m, c); - } else { /* window is no longer minimized, move it to the master area */ - madtty_dirty(m->term); - detach(m); - attach(m); +sigchld_handler(int sig) { + int errsv = errno; + int status; + pid_t pid; + Client *c; + + while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { + if (pid == -1) { + if (errno == ECHILD) { + /* no more child processes */ + break; + } + eprint("waitpid: %s\n", strerror(errno)); + break; + } + debug("child with pid %d died\n", pid); + if ((c = get_client_by_pid(pid))) + c->died = true; } - arrange(); + + signal(SIGCHLD, sigchld_handler); + + errno = errsv; } static void -setlayout(const char *args[]) { - unsigned int i; +sigwinch_handler(int sig) { + signal(SIGWINCH, sigwinch_handler); + screen.need_resize = true; +} - if (!args || !args[0]) { - if (++layout == &layouts[countof(layouts)]) - layout = &layouts[0]; - } else { - for (i = 0; i < countof(layouts); i++) - if (!strcmp(args[0], layouts[i].symbol)) - break; - if (i == countof(layouts)) - return; - layout = &layouts[i]; +static void +sigterm_handler(int sig) { + running = false; +} + +static void +updatebarpos(void) { + bar.y = 0; + wax = 0; + way = 0; + wah = screen.h; + if (bar.fd == -1) + return; + if (bar.pos == BAR_TOP) { + wah -= bar.h; + way += bar.h; + } else if (bar.pos == BAR_BOTTOM) { + wah -= bar.h; + bar.y = wah; } - arrange(); } static void -setmfact(const char *args[]) { - double delta; +resize_screen() { + struct winsize ws; - if (isarrange(fullscreen) || isarrange(grid)) + if (ioctl(0, TIOCGWINSZ, &ws) == -1) return; - /* arg handling, manipulate mfact */ - if (args[0] == NULL) - screen.mfact = MFACT; - else if (1 == sscanf(args[0], "%lf", &delta)) { - if (args[0][0] == '+' || args[0][0] == '-') - screen.mfact += delta; - else - screen.mfact = delta; - if (screen.mfact < 0.1) - screen.mfact = 0.1; - else if (screen.mfact > 0.9) - screen.mfact = 0.9; - } + + screen.w = ws.ws_col; + screen.h = ws.ws_row; + + debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h); + + resizeterm(screen.h, screen.w); + wresize(stdscr, screen.h, screen.w); + wrefresh(curscr); + refresh(); + + waw = screen.w; + wah = screen.h; + updatebarpos(); + drawbar(); arrange(); } +static bool +is_modifier(unsigned int mod) { + unsigned int i; + for (i = 0; i < countof(keys); i++) { + if (keys[i].mod == mod) + return true; + } + return false; +} + +static Key* +keybinding(unsigned int mod, unsigned int code) { + unsigned int i; + for (i = 0; i < countof(keys); i++) { + if (keys[i].mod == mod && keys[i].code == code) + return &keys[i]; + } + return NULL; +} + static void -scrollback(const char *args[]) { - if (!sel) return; +keypress(int code) { + Client *c; + unsigned int len = 1; + char buf[8] = { '\e' }; - if (!args[0] || atoi(args[0]) < 0) - madtty_scroll(sel->term, -sel->h/2); - else - madtty_scroll(sel->term, sel->h/2); + if (code == '\e') { + /* pass characters following escape to the underlying app */ + nodelay(stdscr, TRUE); + for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) + buf[len] = t; + nodelay(stdscr, FALSE); + } - draw(sel); + for (c = runinall ? clients : sel; c; c = c->next) { + if (!c->minimized || isarrange(fullscreen)) { + if (code == '\e') + madtty_write(c->term, buf, len); + else + madtty_keypress(c->term, code); + } + if (!runinall) + break; + } } static void -redraw(const char *args[]) { - wrefresh(curscr); +mouse_setup() { +#ifdef CONFIG_MOUSE + mmask_t mask = 0; + + if (mouse_events_enabled) { + mask = BUTTON1_CLICKED | BUTTON2_CLICKED; + for (unsigned int i = 0; i < countof(buttons); i++) + mask |= buttons[i].mask; + } + mousemask(mask, NULL); +#endif /* CONFIG_MOUSE */ +} + +static void +setup() { + if (!(shell = getenv("SHELL"))) + shell = "/bin/sh"; + setlocale(LC_CTYPE, ""); + initscr(); + start_color(); + noecho(); + keypad(stdscr, TRUE); + mouse_setup(); + raw(); + madtty_init(); + getmaxyx(stdscr, screen.h, screen.w); resize_screen(); - draw_all(true); + signal(SIGWINCH, sigwinch_handler); + signal(SIGCHLD, sigchld_handler); + signal(SIGTERM, sigterm_handler); } static void -draw_border(Client *c) { - char *s, t = '\0'; - int x, y, o; +destroy(Client *c) { + if (sel == c) + focusnextnm(NULL); + detach(c); if (sel == c) { - wattrset(c->window, SELECTED_ATTR); - wcolor_set(c->window, madtty_color_get(SELECTED_FG, SELECTED_BG), NULL); - } else { - wattrset(c->window, NORMAL_ATTR); - wcolor_set(c->window, madtty_color_get(NORMAL_FG, NORMAL_BG), NULL); + if (clients) { + focus(clients); + toggleminimize(NULL); + } else + sel = NULL; } - getyx(c->window, y, x); - curs_set(0); - mvwhline(c->window, 0, 0, ACS_HLINE, c->w); - o = c->w - (4 + sstrlen(TITLE) - 5 + sstrlen(SEPARATOR)); - if (o < 0) - o = 0; - if ((size_t)o < sizeof(c->title)) { - t = *(s = &c->title[o]); - *s = '\0'; + werase(c->window); + wrefresh(c->window); + madtty_destroy(c->term); + delwin(c->window); + if (!clients && countof(actions)) { + if (!strcmp(c->cmd, shell)) + quit(NULL); + else + create(NULL); + } + free(c); + arrange(); +} + +static void +cleanup() { + endwin(); + if (bar.fd > 0) + close(bar.fd); + if (bar.file) + unlink(bar.file); + if (cmdfifo.fd > 0) + close(cmdfifo.fd); + if (cmdfifo.file) + unlink(cmdfifo.file); +} + +/* commands for use by keybindings */ +static void +create(const char *args[]) { + Client *c = calloc(sizeof(Client), 1); + if (!c) + return; + const char *cmd = (args && args[0]) ? args[0] : shell; + const char *pargs[] = { "/bin/sh", "-c", cmd, NULL }; + c->id = ++cmdfifo.id; + char buf[8]; + snprintf(buf, sizeof buf, "%d", c->id); + const char *env[] = { + "DVTM", VERSION, + "DVTM_WINDOW_ID", buf, + NULL + }; + + c->window = newwin(wah, waw, way, wax); + c->term = madtty_create(screen.h - 1, screen.w, screen.history); + c->cmd = cmd; + if (args && args[1]) + strncpy(c->title, args[1], sizeof(c->title)); + c->pid = madtty_forkpty(c->term, "/bin/sh", pargs, env, &c->pty); + madtty_set_data(c->term, c); + madtty_set_handler(c->term, title_escape_seq_handler); + c->w = screen.w; + c->h = screen.h; + c->x = wax; + c->y = way; + c->order = 0; + c->minimized = false; + debug("client with pid %d forked\n", c->pid); + attach(c); + focus(c); + arrange(); +} + +static void +escapekey(const char *args[]) { + int key; + if ((key = getch()) >= 0) { + debug("escaping key `%c'\n", key); + keypress(CTRL(key)); } - mvwprintw(c->window, 0, 2, TITLE, - *c->title ? c->title : "", - *c->title ? SEPARATOR : "", - c->order); - if (t) - *s = t; - wmove(c->window, y, x); - if (!c->minimized) - curs_set(madtty_cursor(c->term)); } static void -draw_content(Client *c) { - if (!c->minimized || isarrange(fullscreen)) { - madtty_draw(c->term, c->window, 1, 0); - if (c != sel) - curs_set(0); +focusn(const char *args[]) { + Client *c; + + for (c = clients; c; c = c->next) { + if (c->order == atoi(args[0])) { + focus(c); + if (c->minimized) + toggleminimize(NULL); + return; + } } } static void -draw(Client *c) { - draw_content(c); - draw_border(c); - wrefresh(c->window); +focusnext(const char *args[]) { + Client *c; + + if (!sel) + return; + + c = sel->next; + if (!c) + c = clients; + if (c) + focus(c); } static void -clear_workspace() { - for (unsigned int y = 0; y < wah; y++) - mvhline(way + y, 0, ' ', waw); - wnoutrefresh(stdscr); +focusnextnm(const char *args[]) { + Client *c; + + if (!sel) + return; + c = sel; + do { + c = c->next; + if (!c) + c = clients; + } while (c->minimized && c != sel); + focus(c); } static void -draw_all(bool border) { +focusprev(const char *args[]) { Client *c; - curs_set(0); - for (c = clients; c; c = c->next) { - redrawwin(c->window); - if (c == sel) - continue; - draw_content(c); - if (border) - draw_border(c); - wnoutrefresh(c->window); - } - /* as a last step the selected window is redrawn, - * this has the effect that the cursor position is - * accurate - */ - refresh(); - if (sel) { - draw_content(sel); - if (border) - draw_border(sel); - wrefresh(sel->window); - } + + if (!sel) + return; + c = sel->prev; + if (!c) + for (c = clients; c && c->next; c = c->next); + if (c) + focus(c); } static void -escapekey(const char *args[]) { - int key; - if ((key = getch()) >= 0) { - debug("escaping key `%c'\n", key); - keypress(CTRL(key)); - } +focusprevnm(const char *args[]) { + Client *c; + + if (!sel) + return; + c = sel; + do { + c = c->prev; + if (!c) + for (c = clients; c && c->next; c = c->next); + } while (c->minimized && c != sel); + focus(c); +} + +static void +killclient(const char *args[]) { + if (!sel) + return; + debug("killing client with pid: %d\n", sel->pid); + kill(-sel->pid, SIGKILL); } -/* - * Lock the screen until the correct password is entered. - * The password can either be specified in config.h which is - * not recommended because `strings dvtm` will contain it. If - * no password is specified in the configuration file it is read - * from the keyboard before the screen is locked. - * - * NOTE: this function doesn't handle the input from clients. All - * foreground operations are temporarily suspended since the - * function doesn't return. - */ static void lock(const char *args[]) { size_t len = 0, i = 0; @@ -615,298 +836,340 @@ lock(const char *args[]) { } static void -togglemouse(const char *args[]) { - mouse_events_enabled = !mouse_events_enabled; - mouse_setup(); -} - -static void -togglerunall(const char *args[]) { - runinall = !runinall; +quit(const char *args[]) { + cleanup(); + exit(EXIT_SUCCESS); } static void -killclient(const char *args[]) { - if (!sel) - return; - debug("killing client with pid: %d\n", sel->pid); - kill(-sel->pid, SIGKILL); +redraw(const char *args[]) { + wrefresh(curscr); + resize_screen(); + draw_all(true); } static void -applycolorrules(madtty_t *term, char *title) { - unsigned int i; - unsigned attrs = A_NORMAL; - short fg = -1, bg = -1; - const ColorRule *r; +scrollback(const char *args[]) { + if (!sel) return; - for (i = 0; i < countof(colorrules); i++) { - r = &colorrules[i]; - if (strstr(title, r->title)) { - attrs = r->attrs; - fg = r->fg; - bg = r->bg; - break; - } - } - madtty_set_default_colors(term, attrs, fg, bg); -} + if (!args[0] || atoi(args[0]) < 0) + madtty_scroll(sel->term, -sel->h/2); + else + madtty_scroll(sel->term, sel->h/2); -static int -title_escape_seq_handler(madtty_t *term, char *es) { - Client *c; - unsigned int l; - if (es[0] != ']' || (es[1] && (es[1] < '0' || es[1] > '9')) || (es[2] && es[2] != ';')) - return MADTTY_HANDLER_NOWAY; - if ((l = strlen(es)) < 3 || es[l - 1] != '\07') - return MADTTY_HANDLER_NOTYET; - es[l - 1] = '\0'; - c = (Client *)madtty_get_data(term); - strncpy(c->title, es + 3, sizeof(c->title)); - draw_border(c); - debug("window title: %s\n", c->title); - applycolorrules(term, c->title); - return MADTTY_HANDLER_OK; + draw(sel); } static void -create(const char *args[]) { - Client *c = calloc(sizeof(Client), 1); - if (!c) - return; - const char *cmd = (args && args[0]) ? args[0] : shell; - const char *pargs[] = { "/bin/sh", "-c", cmd, NULL }; - c->id = ++cmdfifo.id; - char buf[8]; - snprintf(buf, sizeof buf, "%d", c->id); - const char *env[] = { - "DVTM", VERSION, - "DVTM_WINDOW_ID", buf, - NULL - }; +setlayout(const char *args[]) { + unsigned int i; - c->window = newwin(wah, waw, way, wax); - c->term = madtty_create(screen.h - 1, screen.w, screen.history); - c->cmd = cmd; - if (args && args[1]) - strncpy(c->title, args[1], sizeof(c->title)); - c->pid = madtty_forkpty(c->term, "/bin/sh", pargs, env, &c->pty); - madtty_set_data(c->term, c); - madtty_set_handler(c->term, title_escape_seq_handler); - c->w = screen.w; - c->h = screen.h; - c->x = wax; - c->y = way; - c->order = 0; - c->minimized = false; - debug("client with pid %d forked\n", c->pid); - attach(c); - focus(c); + if (!args || !args[0]) { + if (++layout == &layouts[countof(layouts)]) + layout = &layouts[0]; + } else { + for (i = 0; i < countof(layouts); i++) + if (!strcmp(args[0], layouts[i].symbol)) + break; + if (i == countof(layouts)) + return; + layout = &layouts[i]; + } arrange(); } static void -destroy(Client *c) { - if (sel == c) - focusnextnm(NULL); - detach(c); - if (sel == c) { - if (clients) { - focus(clients); - toggleminimize(NULL); - } else - sel = NULL; - } - werase(c->window); - wrefresh(c->window); - madtty_destroy(c->term); - delwin(c->window); - if (!clients && countof(actions)) { - if (!strcmp(c->cmd, shell)) - quit(NULL); +setmfact(const char *args[]) { + double delta; + + if (isarrange(fullscreen) || isarrange(grid)) + return; + /* arg handling, manipulate mfact */ + if (args[0] == NULL) + screen.mfact = MFACT; + else if (1 == sscanf(args[0], "%lf", &delta)) { + if (args[0][0] == '+' || args[0][0] == '-') + screen.mfact += delta; else - create(NULL); + screen.mfact = delta; + if (screen.mfact < 0.1) + screen.mfact = 0.1; + else if (screen.mfact > 0.9) + screen.mfact = 0.9; } - free(c); arrange(); } static void -move_client(Client *c, int x, int y) { - if (c->x == x && c->y == y) - return; - debug("moving, x: %d y: %d\n", x, y); - if (mvwin(c->window, y, x) == ERR) - eprint("error moving, x: %d y: %d\n", x, y); - else { - c->x = x; - c->y = y; - } +startup(const char *args[]) { + for (unsigned int i = 0; i < countof(actions); i++) + actions[i].cmd(actions[i].args); } static void -resize_client(Client *c, int w, int h) { - if (c->w == w && c->h == h) - return; - debug("resizing, w: %d h: %d\n", w, h); - if (wresize(c->window, h, w) == ERR) - eprint("error resizing, w: %d h: %d\n", w, h); - else { - c->w = w; - c->h = h; - } - madtty_resize(c->term, h - 1, w); +togglebar(const char *args[]) { + if (bar.pos == BAR_OFF) + bar.pos = (BAR_POS == BAR_OFF) ? BAR_TOP : BAR_POS; + else + bar.pos = BAR_OFF; + updatebarpos(); + arrange(); + drawbar(); } static void -resize(Client *c, int x, int y, int w, int h) { - resize_client(c, w, h); - move_client(c, x, y); +togglebell(const char *args[]) { + madtty_togglebell(sel->term); } -static bool -is_modifier(unsigned int mod) { - unsigned int i; - for (i = 0; i < countof(keys); i++) { - if (keys[i].mod == mod) - return true; +static void +toggleminimize(const char *args[]) { + Client *c, *m; + unsigned int n; + if (!sel) + return; + /* the last window can't be minimized */ + if (!sel->minimized) { + for (n = 0, c = clients; c; c = c->next) + if (!c->minimized) + n++; + if (n == 1) + return; } - return false; + sel->minimized = !sel->minimized; + m = sel; + /* check whether the master client was minimized */ + if (sel == clients && sel->minimized) { + c = sel->next; + detach(c); + attach(c); + focus(c); + detach(m); + for (; c && c->next && !c->next->minimized; c = c->next); + attachafter(m, c); + } else if (m->minimized) { + /* non master window got minimized move it above all other + * minimized ones */ + focusnextnm(NULL); + detach(m); + for (c = clients; c && c->next && !c->next->minimized; c = c->next); + attachafter(m, c); + } else { /* window is no longer minimized, move it to the master area */ + madtty_dirty(m->term); + detach(m); + attach(m); + } + arrange(); } -static Key* -keybinding(unsigned int mod, unsigned int code) { - unsigned int i; - for (i = 0; i < countof(keys); i++) { - if (keys[i].mod == mod && keys[i].code == code) - return &keys[i]; - } - return NULL; +static void +togglemouse(const char *args[]) { + mouse_events_enabled = !mouse_events_enabled; + mouse_setup(); } -static Client* -get_client_by_pid(pid_t pid) { - Client *c; - for (c = clients; c; c = c->next) { - if (c->pid == pid) - return c; - } - return NULL; +static void +togglerunall(const char *args[]) { + runinall = !runinall; } static void -sigchld_handler(int sig) { - int errsv = errno; - int status; - pid_t pid; +zoom(const char *args[]) { Client *c; - while ((pid = waitpid(-1, &status, WNOHANG)) != 0) { - if (pid == -1) { - if (errno == ECHILD) { - /* no more child processes */ - break; - } - eprint("waitpid: %s\n", strerror(errno)); - break; - } - debug("child with pid %d died\n", pid); - if ((c = get_client_by_pid(pid))) - c->died = true; - } - - signal(SIGCHLD, sigchld_handler); - - errno = errsv; + if (!sel) + return; + if ((c = sel) == clients) + if (!(c = c->next)) + return; + detach(c); + attach(c); + focus(c); + if (c->minimized) + toggleminimize(NULL); + arrange(); } +/* commands for use by mouse bindings */ static void -sigwinch_handler(int sig) { - signal(SIGWINCH, sigwinch_handler); - screen.need_resize = true; +mouse_focus(const char *args[]) { + focus(msel); + if (msel->minimized) + toggleminimize(NULL); } static void -sigterm_handler(int sig) { - running = false; +mouse_fullscreen(const char *args[]) { + mouse_focus(NULL); + if (isarrange(fullscreen)) + setlayout(NULL); + else + setlayout(args); } static void -resize_screen() { - struct winsize ws; - - if (ioctl(0, TIOCGWINSZ, &ws) == -1) - return; - - screen.w = ws.ws_col; - screen.h = ws.ws_row; - - debug("resize_screen(), w: %d h: %d\n", screen.w, screen.h); - - resizeterm(screen.h, screen.w); - wresize(stdscr, screen.h, screen.w); - wrefresh(curscr); - refresh(); - - waw = screen.w; - wah = screen.h; - updatebarpos(); - drawbar(); - arrange(); +mouse_minimize(const char *args[]) { + focus(msel); + toggleminimize(NULL); } static void -startup(const char *args[]) { - for (unsigned int i = 0; i < countof(actions); i++) - actions[i].cmd(actions[i].args); +mouse_zoom(const char *args[]) { + focus(msel); + zoom(NULL); } -static void -setup() { - if (!(shell = getenv("SHELL"))) - shell = "/bin/sh"; - setlocale(LC_CTYPE, ""); - initscr(); - start_color(); - noecho(); - keypad(stdscr, TRUE); - mouse_setup(); - raw(); - madtty_init(); - getmaxyx(stdscr, screen.h, screen.w); - resize_screen(); - signal(SIGWINCH, sigwinch_handler); - signal(SIGCHLD, sigchld_handler); - signal(SIGTERM, sigterm_handler); +static Cmd * +get_cmd_by_name(const char *name) { + for (unsigned int i = 0; i < countof(commands); i++) { + if (!strcmp(name, commands[i].name)) + return &commands[i]; + } + return NULL; } static void -cleanup() { - endwin(); - if (bar.fd > 0) - close(bar.fd); - if (bar.file) - unlink(bar.file); - if (cmdfifo.fd > 0) - close(cmdfifo.fd); - if (cmdfifo.file) - unlink(cmdfifo.file); +handle_cmdfifo() { + int r; + char *p, *s, cmdbuf[512], c; + Cmd *cmd; + switch (r = read(cmdfifo.fd, cmdbuf, sizeof cmdbuf - 1)) { + case -1: + case 0: + cmdfifo.fd = -1; + break; + default: + cmdbuf[r] = '\0'; + p = cmdbuf; + while (*p) { + /* find the command name */ + for (; *p == ' ' || *p == '\n'; p++); + for (s = p; *p && *p != ' ' && *p != '\n'; p++); + if ((c = *p)) + *p++ = '\0'; + if (*s && (cmd = get_cmd_by_name(s)) != NULL) { + bool quote = false; + int argc = 0; + /* XXX: initializer assumes MAX_ARGS == 2 use a initialization loop? */ + const char *args[MAX_ARGS] = { NULL, NULL}, *arg; + /* if arguments were specified in config.h ignore the one given via + * the named pipe and thus skip everything until we find a new line + */ + if (cmd->action.args[0] || c == '\n') { + debug("execute %s", s); + cmd->action.cmd(cmd->action.args); + while (*p && *p != '\n') + p++; + continue; + } + /* no arguments were given in config.h so we parse the command line */ + while (*p == ' ') + p++; + arg = p; + for (; (c = *p); p++) { + switch (*p) { + case '\\': + /* remove the escape character '\\' move every + * following character to the left by one position + */ + switch (*(++p)) { + case '\\': + case '\'': + case '\"': { + char *t = p; + for (;;) { + *(t - 1) = *t; + if (*t++ == '\0') + break; + } + p -= 2; + } + } + break; + case '\'': + case '\"': + quote = !quote; + break; + case ' ': + if (!quote) { + case '\n': + /* remove trailing quote if there is one */ + if (*(p - 1) == '\'' || *(p - 1) == '\"') + *(p - 1) = '\0'; + *p++ = '\0'; + /* remove leading quote if there is one */ + if (*arg == '\'' || *arg == '\"') + arg++; + if (argc < MAX_ARGS) + args[argc++] = arg; + + while (*p == ' ') + ++p; + arg = p; + } + break; + } + + if (c == '\n' || *p == '\n') { + debug("execute %s", s); + for(int i = 0; i < argc; i++) + debug(" %s", args[i]); + debug("\n"); + cmd->action.cmd(args); + break; + } + } + } + } + } } static void -quit(const char *args[]) { - cleanup(); - exit(EXIT_SUCCESS); +handle_mouse() { +#ifdef CONFIG_MOUSE + MEVENT event; + unsigned int i; + if (getmouse(&event) != OK) + return; + msel = get_client_by_coord(event.x, event.y); + + if (!msel) + return; + + 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); + + madtty_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate); + + for (i = 0; i < countof(buttons); i++) { + if (event.bstate & buttons[i].mask) + buttons[i].action.cmd(buttons[i].action.args); + } + + msel = NULL; +#endif /* CONFIG_MOUSE */ } static void -usage() { - cleanup(); - eprint("usage: dvtm [-v] [-m mod] [-d escdelay] [-h n] " - "[-s status-fifo] " - "[-c cmd-fifo] " - "[cmd...]\n"); - exit(EXIT_FAILURE); +handle_statusbar() { + char *p; + int r; + switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) { + case -1: + strncpy(bar.text, strerror(errno), sizeof bar.text - 1); + bar.text[sizeof bar.text - 1] = '\0'; + bar.fd = -1; + break; + case 0: + bar.fd = -1; + break; + default: + bar.text[r] = '\0'; p = bar.text + strlen(bar.text) - 1; + for (; p >= bar.text && *p == '\n'; *p-- = '\0'); + for (; p >= bar.text && *p != '\n'; --p); + if (p > bar.text) + strncpy(bar.text, p + 1, sizeof bar.text); + drawbar(); + } } static int @@ -931,6 +1194,37 @@ open_or_create_fifo(const char *name, const char **name_created) { return fd; } +static void +usage() { + cleanup(); + eprint("usage: dvtm [-v] [-m mod] [-d escdelay] [-h n] " + "[-s status-fifo] " + "[-c cmd-fifo] " + "[cmd...]\n"); + exit(EXIT_FAILURE); +} + +/* glibc has a non-standard realpath(3) implementation which allocates + * the destination buffer, other C libraries may have a broken implementation + * which expect an already allocated destination buffer. + */ + +#ifndef __GLIBC__ +# include <limits.h> +# ifndef PATH_MAX +# define PATH_MAX 1024 +# endif +#endif + +static char *get_realpath(const char *path) { +#ifdef __GLIBC__ + return realpath(path, NULL); +#else + static char buf[PATH_MAX]; + return realpath(path, buf); +#endif +} + static bool parse_args(int argc, char *argv[]) { int arg; @@ -991,32 +1285,6 @@ parse_args(int argc, char *argv[]) { return init; } -void -keypress(int code) { - Client *c; - unsigned int len = 1; - char buf[8] = { '\e' }; - - if (code == '\e') { - /* pass characters following escape to the underlying app */ - nodelay(stdscr, TRUE); - for (int t; len < sizeof(buf) && (t = getch()) != ERR; len++) - buf[len] = t; - nodelay(stdscr, FALSE); - } - - for (c = runinall ? clients : sel; c; c = c->next) { - if (!c->minimized || isarrange(fullscreen)) { - if (code == '\e') - madtty_write(c->term, buf, len); - else - madtty_keypress(c->term, code); - } - if (!runinall) - break; - } -} - int main(int argc, char *argv[]) { if (!parse_args(argc, argv)) { diff --git a/mouse.c b/mouse.c @@ -1,78 +0,0 @@ -static void -mouse_focus(const char *args[]) { - focus(msel); - if (msel->minimized) - toggleminimize(NULL); -} - -static void -mouse_fullscreen(const char *args[]) { - mouse_focus(NULL); - if (isarrange(fullscreen)) - setlayout(NULL); - else - setlayout(args); -} - -static void -mouse_minimize(const char *args[]) { - focus(msel); - toggleminimize(NULL); -} - -static void -mouse_zoom(const char *args[]) { - focus(msel); - zoom(NULL); -} - -static Client* -get_client_by_coord(unsigned int x, unsigned int y) { - Client *c; - if (y < way || y >= wah) - return NULL; - if (isarrange(fullscreen)) - return sel; - for (c = clients; c; c = c->next) { - if (x >= c->x && x < c->x + c->w && y >= c->y && y < c->y + c->h) { - debug("mouse event, x: %d y: %d client: %d\n", x, y, c->order); - return c; - } - } - return NULL; -} - -static void -handle_mouse() { - MEVENT event; - unsigned int i; - if (getmouse(&event) != OK) - return; - msel = get_client_by_coord(event.x, event.y); - - if (!msel) - return; - - 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); - - madtty_mouse(msel->term, event.x - msel->x, event.y - msel->y, event.bstate); - - for (i = 0; i < countof(buttons); i++) { - if (event.bstate & buttons[i].mask) - buttons[i].action.cmd(buttons[i].action.args); - } - - msel = NULL; -} - -static void -mouse_setup() { - mmask_t mask = 0; - - if (mouse_events_enabled) { - mask = BUTTON1_CLICKED | BUTTON2_CLICKED; - for (unsigned int i = 0; i < countof(buttons); i++) - mask |= buttons[i].mask; - } - mousemask(mask, NULL); -} diff --git a/statusbar.c b/statusbar.c @@ -1,80 +0,0 @@ -static void -updatebarpos(void) { - bar.y = 0; - wax = 0; - way = 0; - wah = screen.h; - if (bar.fd == -1) - return; - if (bar.pos == BarTop) { - wah -= bar.h; - way += bar.h; - } else if (bar.pos == BarBot) { - wah -= bar.h; - bar.y = wah; - } -} - -static void -drawbar() { - wchar_t wbuf[sizeof bar.text]; - int w, maxwidth = screen.w - 2; - if (bar.pos == BarOff || !bar.text[0]) - return; - curs_set(0); - attrset(BAR_ATTR); - wcolor_set(stdscr, madtty_color_get(BAR_FG, BAR_BG), NULL); - mvaddch(bar.y, 0, '['); - if (mbstowcs(wbuf, bar.text, sizeof bar.text) == (size_t)-1) - return; - if ((w = wcswidth(wbuf, maxwidth)) == -1) - return; - if (BAR_ALIGN == ALIGN_RIGHT) { - for (int i = 0; i + w < maxwidth; i++) - addch(' '); - } - addstr(bar.text); - if (BAR_ALIGN == ALIGN_LEFT) { - for (; w < maxwidth; w++) - addch(' '); - } - mvaddch(bar.y, screen.w - 1, ']'); - attrset(NORMAL_ATTR); - if (sel) - curs_set(madtty_cursor(sel->term)); - refresh(); -} - -static void -togglebar(const char *args[]) { - if (bar.pos == BarOff) - bar.pos = (BARPOS == BarOff) ? BarTop : BARPOS; - else - bar.pos = BarOff; - updatebarpos(); - arrange(); - drawbar(); -} - -static void -handle_statusbar() { - char *p; - int r; - switch (r = read(bar.fd, bar.text, sizeof bar.text - 1)) { - case -1: - strncpy(bar.text, strerror(errno), sizeof bar.text - 1); - bar.text[sizeof bar.text - 1] = '\0'; - bar.fd = -1; - break; - case 0: - bar.fd = -1; - break; - default: - bar.text[r] = '\0'; p = bar.text + strlen(bar.text) - 1; - for (; p >= bar.text && *p == '\n'; *p-- = '\0'); - for (; p >= bar.text && *p != '\n'; --p); - if (p > bar.text) - strncpy(bar.text, p + 1, sizeof bar.text); - drawbar(); - } -}