2 * The initial "port" of dwm to curses was done by
4 * © 2007-2014 Marc André Tanner <mat at brain-dump dot org>
6 * It is highly inspired by the original X11 dwm and
7 * reuses some code of it which is mostly
9 * © 2006-2007 Anselm R. Garbe <garbeam at gmail dot com>
11 * See LICENSE for details.
14 #include <sys/select.h>
16 #include <sys/ioctl.h>
19 #include <sys/types.h>
31 #if defined __CYGWIN__ || defined __sun
40 #ifndef NCURSES_REENTRANT
41 # define set_escdelay(d) (ESCDELAY = (d))
54 void (*arrange
)(void);
57 typedef struct Client Client
;
63 volatile sig_atomic_t editor_died
;
68 unsigned short int id
;
75 volatile sig_atomic_t died
;
94 #define ALT(k) ((k) + (161 - 'a'))
95 #if defined CTRL && defined _AIX
99 #define CTRL(k) ((k) & 0x1F)
101 #define CTRL_ALT(k) ((k) + (129 - 'a'))
106 void (*cmd
)(const char *args
[]);
107 /* needed to avoid an error about initialization
108 * of nested flexible array members */
109 const char *args
[MAX_ARGS
];
114 typedef unsigned int KeyCombo
[MAX_KEYS
];
131 enum { BAR_TOP
, BAR_BOTTOM
, BAR_OFF
};
132 enum { ALIGN_LEFT
, ALIGN_RIGHT
};
137 unsigned short int h
;
138 unsigned short int y
;
146 unsigned short int id
;
155 #define countof(arr) (sizeof(arr) / sizeof((arr)[0]))
156 #define sstrlen(str) (sizeof(str) - 1)
157 #define max(x, y) ((x) > (y) ? (x) : (y))
160 #define debug(format, args...)
165 /* commands for use by keybindings */
166 static void create(const char *args
[]);
167 static void copymode(const char *args
[]);
168 static void focusn(const char *args
[]);
169 static void focusnext(const char *args
[]);
170 static void focusnextnm(const char *args
[]);
171 static void focusprev(const char *args
[]);
172 static void focusprevnm(const char *args
[]);
173 static void focuslast(const char *args
[]);
174 static void killclient(const char *args
[]);
175 static void paste(const char *args
[]);
176 static void quit(const char *args
[]);
177 static void redraw(const char *args
[]);
178 static void scrollback(const char *args
[]);
179 static void send(const char *args
[]);
180 static void setlayout(const char *args
[]);
181 static void setmfact(const char *args
[]);
182 static void startup(const char *args
[]);
183 static void togglebar(const char *args
[]);
184 static void togglebell(const char *key
[]);
185 static void toggleminimize(const char *args
[]);
186 static void togglemouse(const char *args
[]);
187 static void togglerunall(const char *args
[]);
188 static void zoom(const char *args
[]);
190 /* commands for use by mouse bindings */
191 static void mouse_focus(const char *args
[]);
192 static void mouse_fullscreen(const char *args
[]);
193 static void mouse_minimize(const char *args
[]);
194 static void mouse_zoom(const char *args
[]);
196 /* functions and variables available to layouts via config.h */
197 static void resize(Client
*c
, int x
, int y
, int w
, int h
);
198 extern Screen screen
;
199 static unsigned int waw
, wah
, wax
, way
;
200 static Client
*clients
= NULL
;
202 #define COLOR(c) COLOR_PAIR(colors[c].pair)
207 /* global variables */
208 static const char *dvtm_name
= "dvtm";
209 Screen screen
= { MFACT
, SCROLL_HISTORY
};
210 static Client
*sel
= NULL
;
211 static Client
*lastsel
= NULL
;
212 static Client
*msel
= NULL
;
213 static bool mouse_events_enabled
= ENABLE_MOUSE
;
214 static Layout
*layout
= layouts
;
215 static StatusBar bar
= { -1, BAR_POS
, 1 };
216 static CmdFifo cmdfifo
= { -1 };
217 static const char *shell
;
218 static Register copyreg
;
219 static volatile sig_atomic_t running
= true;
220 static bool runinall
= false;
223 eprint(const char *errstr
, ...) {
225 va_start(ap
, errstr
);
226 vfprintf(stderr
, errstr
, ap
);
231 error(const char *errstr
, ...) {
233 va_start(ap
, errstr
);
234 vfprintf(stderr
, errstr
, ap
);
240 isarrange(void (*func
)()) {
241 return func
== layout
->arrange
;
245 is_content_visible(Client
*c
) {
248 if (isarrange(fullscreen
))
250 return !c
->minimized
;
255 wchar_t wbuf
[sizeof bar
.text
];
256 int x
, y
, w
, maxwidth
= screen
.w
- 2;
257 if (bar
.pos
== BAR_OFF
)
261 mvaddch(bar
.y
, 0, '[');
262 if (mbstowcs(wbuf
, bar
.text
, sizeof bar
.text
) == (size_t)-1)
264 if ((w
= wcswidth(wbuf
, maxwidth
)) == -1)
266 if (BAR_ALIGN
== ALIGN_RIGHT
) {
267 for (int i
= 0; i
+ w
< maxwidth
; i
++)
271 if (BAR_ALIGN
== ALIGN_LEFT
) {
272 for (; w
< maxwidth
; w
++)
275 mvaddch(bar
.y
, screen
.w
- 1, ']');
276 attrset(NORMAL_ATTR
);
278 wnoutrefresh(stdscr
);
283 return (bar
.fd
!= -1 && bar
.pos
!= BAR_OFF
) || (clients
&& clients
->next
);
287 draw_border(Client
*c
) {
293 wattrset(c
->window
, (sel
== c
|| (runinall
&& !c
->minimized
)) ? SELECTED_ATTR
: NORMAL_ATTR
);
294 getyx(c
->window
, y
, x
);
295 mvwhline(c
->window
, 0, 0, ACS_HLINE
, c
->w
);
296 maxlen
= c
->w
- (2 + sstrlen(TITLE
) - sstrlen("%s%sd") + sstrlen(SEPARATOR
) + 2);
299 if ((size_t)maxlen
< sizeof(c
->title
)) {
300 t
= c
->title
[maxlen
];
301 c
->title
[maxlen
] = '\0';
304 mvwprintw(c
->window
, 0, 2, TITLE
,
305 *c
->title
? c
->title
: "",
306 *c
->title
? SEPARATOR
: "",
309 c
->title
[maxlen
] = t
;
310 wmove(c
->window
, y
, x
);
314 draw_content(Client
*c
) {
315 vt_draw(c
->term
, c
->window
, c
->has_title_line
, 0);
320 if (is_content_visible(c
)) {
321 redrawwin(c
->window
);
324 if (!isarrange(fullscreen
) || sel
== c
)
326 wnoutrefresh(c
->window
);
331 if (!isarrange(fullscreen
)) {
332 for (Client
*c
= clients
; c
; c
= c
->next
) {
338 /* as a last step the selected window is redrawn,
339 * this has the effect that the cursor position is
349 for (Client
*c
= clients
; c
; c
= c
->next
)
354 attrset(NORMAL_ATTR
);
355 if (m
&& !isarrange(fullscreen
))
358 if (m
&& !isarrange(fullscreen
)) {
359 int nw
= waw
/ m
, nx
= wax
;
360 for (Client
*c
= clients
; c
; c
= c
->next
) {
362 resize(c
, nx
, way
+wah
, nw
, 1);
368 wnoutrefresh(stdscr
);
379 for (int o
= 1; c
; c
= c
->next
, o
++)
384 attachafter(Client
*c
, Client
*a
) { /* attach c after a */
388 for (a
= clients
; a
&& a
->next
; a
= a
->next
);
396 for (int o
= a
->order
; c
; c
= c
->next
)
405 c
->prev
->next
= c
->next
;
407 c
->next
->prev
= c
->prev
;
408 for (d
= c
->next
; d
; d
= d
->next
)
413 c
->next
= c
->prev
= NULL
;
417 settitle(Client
*c
) {
418 char *term
, *t
= title
;
419 if (!t
&& sel
== c
&& *c
->title
)
421 if (t
&& (term
= getenv("TERM")) && !strstr(term
, "linux"))
422 printf("\033]0;%s\007", t
);
433 if (tmp
&& !isarrange(fullscreen
)) {
435 wnoutrefresh(tmp
->window
);
437 if (isarrange(fullscreen
)) {
441 wnoutrefresh(c
->window
);
443 curs_set(!c
->minimized
&& vt_cursor(c
->term
));
447 applycolorrules(Client
*c
) {
448 const ColorRule
*r
= colorrules
;
449 short fg
= r
->color
->fg
, bg
= r
->color
->bg
;
450 attr_t attrs
= r
->attrs
;
452 for (unsigned int i
= 1; i
< countof(colorrules
); i
++) {
454 if (strstr(c
->title
, r
->title
)) {
462 vt_set_default_colors(c
->term
, attrs
, fg
, bg
);
466 term_event_handler(Vt
*term
, int event
, void *event_data
) {
467 Client
*c
= (Client
*)vt_get_data(term
);
471 strncpy(c
->title
, event_data
, sizeof(c
->title
) - 1);
472 c
->title
[event_data
? sizeof(c
->title
) - 1 : 0] = '\0';
474 if (!isarrange(fullscreen
) || sel
== c
)
482 move_client(Client
*c
, int x
, int y
) {
483 if (c
->x
== x
&& c
->y
== y
)
485 debug("moving, x: %d y: %d\n", x
, y
);
486 if (mvwin(c
->window
, y
, x
) == ERR
) {
487 eprint("error moving, x: %d y: %d\n", x
, y
);
495 resize_client(Client
*c
, int w
, int h
) {
496 bool has_title_line
= show_border();
497 bool resize_window
= c
->w
!= w
|| c
->h
!= h
;
499 debug("resizing, w: %d h: %d\n", w
, h
);
500 if (wresize(c
->window
, h
, w
) == ERR
) {
501 eprint("error resizing, w: %d h: %d\n", w
, h
);
507 if (resize_window
|| c
->has_title_line
!= has_title_line
) {
508 c
->has_title_line
= has_title_line
;
509 vt_resize(c
->app
, h
- has_title_line
, w
);
511 vt_resize(c
->editor
, h
- has_title_line
, w
);
516 resize(Client
*c
, int x
, int y
, int w
, int h
) {
517 resize_client(c
, w
, h
);
518 move_client(c
, x
, y
);
522 get_client_by_coord(unsigned int x
, unsigned int y
) {
523 if (y
< way
|| y
>= wah
)
525 if (isarrange(fullscreen
))
527 for (Client
*c
= clients
; c
; c
= c
->next
) {
528 if (x
>= c
->x
&& x
< c
->x
+ c
->w
&& y
>= c
->y
&& y
< c
->y
+ c
->h
) {
529 debug("mouse event, x: %d y: %d client: %d\n", x
, y
, c
->order
);
537 sigchld_handler(int sig
) {
542 while ((pid
= waitpid(-1, &status
, WNOHANG
)) != 0) {
544 if (errno
== ECHILD
) {
545 /* no more child processes */
548 eprint("waitpid: %s\n", strerror(errno
));
552 debug("child with pid %d died\n", pid
);
554 for (Client
*c
= clients
; c
; c
= c
->next
) {
559 if (c
->editor
&& vt_pid_get(c
->editor
) == pid
) {
560 c
->editor_died
= true;
570 sigwinch_handler(int sig
) {
571 screen
.need_resize
= true;
575 sigterm_handler(int sig
) {
588 if (bar
.pos
== BAR_TOP
) {
591 } else if (bar
.pos
== BAR_BOTTOM
) {
598 resize_screen(void) {
601 if (ioctl(0, TIOCGWINSZ
, &ws
) == -1) {
602 getmaxyx(stdscr
, screen
.h
, screen
.w
);
604 screen
.w
= ws
.ws_col
;
605 screen
.h
= ws
.ws_row
;
608 debug("resize_screen(), w: %d h: %d\n", screen
.w
, screen
.h
);
610 resizeterm(screen
.h
, screen
.w
);
611 wresize(stdscr
, screen
.h
, screen
.w
);
618 keybinding(KeyCombo keys
) {
619 unsigned int keycount
= 0;
620 while (keycount
< MAX_KEYS
&& keys
[keycount
])
622 for (unsigned int b
= 0; b
< countof(bindings
); b
++) {
623 for (unsigned int k
= 0; k
< keycount
; k
++) {
624 if (keys
[k
] != bindings
[b
].keys
[k
])
626 if (k
== keycount
- 1)
635 unsigned int len
= 1;
636 char buf
[8] = { '\e' };
639 /* pass characters following escape to the underlying app */
640 nodelay(stdscr
, TRUE
);
641 for (int t
; len
< sizeof(buf
) && (t
= getch()) != ERR
; len
++)
643 nodelay(stdscr
, FALSE
);
646 for (Client
*c
= runinall
? clients
: sel
; c
; c
= c
->next
) {
647 if (is_content_visible(c
)) {
649 vt_write(c
->term
, buf
, len
);
651 vt_keypress(c
->term
, code
);
663 if (mouse_events_enabled
) {
664 mask
= BUTTON1_CLICKED
| BUTTON2_CLICKED
;
665 for (unsigned int i
= 0; i
< countof(buttons
); i
++)
666 mask
|= buttons
[i
].mask
;
668 mousemask(mask
, NULL
);
669 #endif /* CONFIG_MOUSE */
673 checkshell(const char *shell
) {
674 if (shell
== NULL
|| *shell
== '\0' || *shell
!= '/')
676 fprintf(stderr
, "%s == %s\n", strrchr(shell
, '/')+1, dvtm_name
);
677 if (!strcmp(strrchr(shell
, '/')+1, dvtm_name
))
679 if (access(shell
, X_OK
))
686 const char *shell
= getenv("SHELL");
689 if (checkshell(shell
))
691 if ((pw
= getpwuid(getuid())) && checkshell(pw
->pw_shell
))
699 setlocale(LC_CTYPE
, "");
703 keypad(stdscr
, TRUE
);
707 vt_set_keytable(keytable
, countof(keytable
));
708 for (unsigned int i
= 0; i
< countof(colors
); i
++) {
711 colors
[i
].fg
= colors
[i
].fg256
;
713 colors
[i
].bg
= colors
[i
].bg256
;
715 colors
[i
].pair
= vt_color_reserve(colors
[i
].fg
, colors
[i
].bg
);
720 sigemptyset(&sa
.sa_mask
);
721 sa
.sa_handler
= sigwinch_handler
;
722 sigaction(SIGWINCH
, &sa
, NULL
);
723 sa
.sa_handler
= sigchld_handler
;
724 sigaction(SIGCHLD
, &sa
, NULL
);
725 sa
.sa_handler
= sigterm_handler
;
726 sigaction(SIGTERM
, &sa
, NULL
);
727 sa
.sa_handler
= SIG_IGN
;
728 sigaction(SIGPIPE
, &sa
, NULL
);
739 toggleminimize(NULL
);
747 wnoutrefresh(c
->window
);
750 if (!clients
&& countof(actions
)) {
751 if (!strcmp(c
->cmd
, shell
))
774 unlink(cmdfifo
.file
);
777 static char *getcwd_by_pid(Client
*c
) {
781 snprintf(buf
, sizeof buf
, "/proc/%d/cwd", c
->pid
);
782 return realpath(buf
, NULL
);
785 /* commands for use by keybindings */
787 create(const char *args
[]) {
788 Client
*c
= calloc(1, sizeof(Client
));
791 const char *cmd
= (args
&& args
[0]) ? args
[0] : shell
;
792 const char *pargs
[] = { "/bin/sh", "-c", cmd
, NULL
};
793 c
->id
= ++cmdfifo
.id
;
794 char buf
[8], *cwd
= NULL
;
795 snprintf(buf
, sizeof buf
, "%d", c
->id
);
796 const char *env
[] = {
798 "DVTM_WINDOW_ID", buf
,
802 if (!(c
->window
= newwin(wah
, waw
, way
, wax
))) {
807 c
->has_title_line
= show_border();
808 c
->term
= c
->app
= vt_create(screen
.h
- c
->has_title_line
, screen
.w
, screen
.history
);
816 if (args
&& args
[1]) {
817 strncpy(c
->title
, args
[1], sizeof(c
->title
) - 1);
818 c
->title
[sizeof(c
->title
) - 1] = '\0';
821 cwd
= !strcmp(args
[2], "$CWD") ? getcwd_by_pid(sel
) : (char*)args
[2];
822 c
->pid
= vt_forkpty(c
->term
, "/bin/sh", pargs
, cwd
, env
, NULL
, NULL
);
823 if (args
&& args
[2] && !strcmp(args
[2], "$CWD"))
825 vt_set_data(c
->term
, c
);
826 vt_set_event_handler(c
->term
, term_event_handler
);
832 c
->minimized
= false;
833 debug("client with pid %d forked\n", c
->pid
);
840 copymode(const char *args
[]) {
841 if (!sel
|| sel
->editor
)
843 if (!(sel
->editor
= vt_create(sel
->h
, sel
->w
, 0)))
846 char *ed
= getenv("DVTM_EDITOR");
847 const char **argv
= NULL
;
848 if (!ed
&& !(ed
= getenv("EDITOR"))) {
853 argv
= (const char*[]){ ed
, "-", NULL
};
855 const char *cwd
= NULL
;
856 const char *env
[] = { "DVTM", VERSION
, NULL
};
857 int *to
= &sel
->editor_fds
[0], *from
= &sel
->editor_fds
[1];
859 if (vt_forkpty(sel
->editor
, ed
, argv
, cwd
, env
, to
, from
) < 0) {
860 vt_destroy(sel
->editor
);
865 sel
->term
= sel
->editor
;
867 if (sel
->editor_fds
[0] != -1) {
869 size_t len
= vt_content_get(sel
->app
, &buf
);
872 ssize_t res
= write(sel
->editor_fds
[0], cur
, len
);
874 if (errno
== EAGAIN
|| errno
== EINTR
)
882 close(sel
->editor_fds
[0]);
886 vt_write(sel
->editor
, args
[0], strlen(args
[0]));
890 focusn(const char *args
[]) {
891 for (Client
*c
= clients
; c
; c
= c
->next
) {
892 if (c
->order
== atoi(args
[0])) {
895 toggleminimize(NULL
);
902 focusnext(const char *args
[]) {
905 Client
*c
= sel
->next
;
913 focusnextnm(const char *args
[]) {
921 } while (c
->minimized
&& c
!= sel
);
926 focusprev(const char *args
[]) {
929 Client
*c
= sel
->prev
;
931 for (c
= clients
; c
&& c
->next
; c
= c
->next
);
937 focusprevnm(const char *args
[]) {
944 for (c
= clients
; c
&& c
->next
; c
= c
->next
);
945 } while (c
->minimized
&& c
!= sel
);
950 focuslast(const char *args
[]) {
956 killclient(const char *args
[]) {
959 debug("killing client with pid: %d\n", sel
->pid
);
960 kill(-sel
->pid
, SIGKILL
);
964 paste(const char *args
[]) {
965 if (sel
&& copyreg
.data
)
966 vt_write(sel
->term
, copyreg
.data
, copyreg
.len
);
970 quit(const char *args
[]) {
976 redraw(const char *args
[]) {
977 for (Client
*c
= clients
; c
; c
= c
->next
) {
981 wnoutrefresh(c
->window
);
988 scrollback(const char *args
[]) {
989 if (!is_content_visible(sel
))
992 if (!args
[0] || atoi(args
[0]) < 0)
993 vt_scroll(sel
->term
, -sel
->h
/2);
995 vt_scroll(sel
->term
, sel
->h
/2);
998 curs_set(vt_cursor(sel
->term
));
1002 send(const char *args
[]) {
1003 if (sel
&& args
&& args
[0])
1004 vt_write(sel
->term
, args
[0], strlen(args
[0]));
1008 setlayout(const char *args
[]) {
1011 if (!args
|| !args
[0]) {
1012 if (++layout
== &layouts
[countof(layouts
)])
1013 layout
= &layouts
[0];
1015 for (i
= 0; i
< countof(layouts
); i
++)
1016 if (!strcmp(args
[0], layouts
[i
].symbol
))
1018 if (i
== countof(layouts
))
1020 layout
= &layouts
[i
];
1026 setmfact(const char *args
[]) {
1029 if (isarrange(fullscreen
) || isarrange(grid
))
1031 /* arg handling, manipulate mfact */
1032 if (args
[0] == NULL
) {
1033 screen
.mfact
= MFACT
;
1034 } else if (1 == sscanf(args
[0], "%f", &delta
)) {
1035 if (args
[0][0] == '+' || args
[0][0] == '-')
1036 screen
.mfact
+= delta
;
1038 screen
.mfact
= delta
;
1039 if (screen
.mfact
< 0.1)
1041 else if (screen
.mfact
> 0.9)
1048 startup(const char *args
[]) {
1049 for (unsigned int i
= 0; i
< countof(actions
); i
++)
1050 actions
[i
].cmd(actions
[i
].args
);
1054 togglebar(const char *args
[]) {
1055 if (bar
.pos
== BAR_OFF
)
1056 bar
.pos
= (BAR_POS
== BAR_OFF
) ? BAR_TOP
: BAR_POS
;
1064 togglebell(const char *args
[]) {
1065 vt_togglebell(sel
->term
);
1069 toggleminimize(const char *args
[]) {
1074 /* the last window can't be minimized */
1075 if (!sel
->minimized
) {
1076 for (n
= 0, c
= clients
; c
; c
= c
->next
)
1082 sel
->minimized
= !sel
->minimized
;
1084 /* check whether the master client was minimized */
1085 if (sel
== clients
&& sel
->minimized
) {
1091 for (; c
&& c
->next
&& !c
->next
->minimized
; c
= c
->next
);
1093 } else if (m
->minimized
) {
1094 /* non master window got minimized move it above all other
1098 for (c
= clients
; c
&& c
->next
&& !c
->next
->minimized
; c
= c
->next
);
1100 } else { /* window is no longer minimized, move it to the master area */
1109 togglemouse(const char *args
[]) {
1110 mouse_events_enabled
= !mouse_events_enabled
;
1115 togglerunall(const char *args
[]) {
1116 runinall
= !runinall
;
1121 zoom(const char *args
[]) {
1126 if (args
&& args
[0])
1128 if ((c
= sel
) == clients
)
1135 toggleminimize(NULL
);
1139 /* commands for use by mouse bindings */
1141 mouse_focus(const char *args
[]) {
1143 if (msel
->minimized
)
1144 toggleminimize(NULL
);
1148 mouse_fullscreen(const char *args
[]) {
1150 if (isarrange(fullscreen
))
1157 mouse_minimize(const char *args
[]) {
1159 toggleminimize(NULL
);
1163 mouse_zoom(const char *args
[]) {
1169 get_cmd_by_name(const char *name
) {
1170 for (unsigned int i
= 0; i
< countof(commands
); i
++) {
1171 if (!strcmp(name
, commands
[i
].name
))
1172 return &commands
[i
];
1178 handle_cmdfifo(void) {
1180 char *p
, *s
, cmdbuf
[512], c
;
1182 switch (r
= read(cmdfifo
.fd
, cmdbuf
, sizeof cmdbuf
- 1)) {
1191 /* find the command name */
1192 for (; *p
== ' ' || *p
== '\n'; p
++);
1193 for (s
= p
; *p
&& *p
!= ' ' && *p
!= '\n'; p
++);
1196 if (*s
&& (cmd
= get_cmd_by_name(s
)) != NULL
) {
1199 const char *args
[MAX_ARGS
], *arg
;
1200 memset(args
, 0, sizeof(args
));
1201 /* if arguments were specified in config.h ignore the one given via
1202 * the named pipe and thus skip everything until we find a new line
1204 if (cmd
->action
.args
[0] || c
== '\n') {
1205 debug("execute %s", s
);
1206 cmd
->action
.cmd(cmd
->action
.args
);
1207 while (*p
&& *p
!= '\n')
1211 /* no arguments were given in config.h so we parse the command line */
1215 for (; (c
= *p
); p
++) {
1218 /* remove the escape character '\\' move every
1219 * following character to the left by one position
1239 /* remove trailing quote if there is one */
1240 if (*(p
- 1) == '\'' || *(p
- 1) == '\"')
1243 /* remove leading quote if there is one */
1244 if (*arg
== '\'' || *arg
== '\"')
1246 if (argc
< MAX_ARGS
)
1256 if (c
== '\n' || *p
== '\n') {
1259 debug("execute %s", s
);
1260 for(int i
= 0; i
< argc
; i
++)
1261 debug(" %s", args
[i
]);
1263 cmd
->action
.cmd(args
);
1273 handle_mouse(void) {
1277 if (getmouse(&event
) != OK
)
1279 msel
= get_client_by_coord(event
.x
, event
.y
);
1284 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
);
1286 vt_mouse(msel
->term
, event
.x
- msel
->x
, event
.y
- msel
->y
, event
.bstate
);
1288 for (i
= 0; i
< countof(buttons
); i
++) {
1289 if (event
.bstate
& buttons
[i
].mask
)
1290 buttons
[i
].action
.cmd(buttons
[i
].action
.args
);
1294 #endif /* CONFIG_MOUSE */
1298 handle_statusbar(void) {
1301 switch (r
= read(bar
.fd
, bar
.text
, sizeof bar
.text
- 1)) {
1303 strncpy(bar
.text
, strerror(errno
), sizeof bar
.text
- 1);
1304 bar
.text
[sizeof bar
.text
- 1] = '\0';
1312 p
= bar
.text
+ r
- 1;
1313 for (; p
>= bar
.text
&& *p
== '\n'; *p
-- = '\0');
1314 for (; p
>= bar
.text
&& *p
!= '\n'; --p
);
1316 memmove(bar
.text
, p
+ 1, strlen(p
));
1322 handle_editor(Client
*c
) {
1323 if (!copyreg
.data
&& (copyreg
.data
= malloc(screen
.history
)))
1324 copyreg
.size
= screen
.history
;
1326 while (copyreg
.len
< copyreg
.size
) {
1327 ssize_t len
= read(c
->editor_fds
[1], copyreg
.data
+ copyreg
.len
, copyreg
.size
- copyreg
.len
);
1328 if (len
== -1 && errno
== EINTR
)
1333 if (copyreg
.len
== copyreg
.size
) {
1335 if (!(copyreg
.data
= realloc(copyreg
.data
, copyreg
.size
))) {
1341 c
->editor_died
= false;
1342 vt_destroy(c
->editor
);
1347 wnoutrefresh(c
->window
);
1351 open_or_create_fifo(const char *name
, const char **name_created
) {
1356 if ((fd
= open(name
, O_RDWR
|O_NONBLOCK
)) == -1) {
1357 if (errno
== ENOENT
&& !mkfifo(name
, S_IRUSR
|S_IWUSR
)) {
1358 *name_created
= name
;
1361 error("%s\n", strerror(errno
));
1365 if (fstat(fd
, &info
) == -1)
1366 error("%s\n", strerror(errno
));
1367 if (!S_ISFIFO(info
.st_mode
))
1368 error("%s is not a named pipe\n", name
);
1375 eprint("usage: dvtm [-v] [-M] [-m mod] [-d delay] [-h lines] [-t title] "
1376 "[-s status-fifo] [-c cmd-fifo] [cmd...]\n");
1381 parse_args(int argc
, char *argv
[]) {
1383 const char *name
= argv
[0];
1385 if (name
&& (name
= strrchr(name
, '/')))
1386 dvtm_name
= name
+ 1;
1387 if (!getenv("ESCDELAY"))
1389 for (int arg
= 1; arg
< argc
; arg
++) {
1390 if (argv
[arg
][0] != '-') {
1391 const char *args
[] = { argv
[arg
], NULL
, NULL
};
1399 if (argv
[arg
][1] != 'v' && argv
[arg
][1] != 'M' && (arg
+ 1) >= argc
)
1401 switch (argv
[arg
][1]) {
1403 puts("dvtm-"VERSION
" © 2007-2014 Marc André Tanner");
1406 mouse_events_enabled
= !mouse_events_enabled
;
1409 char *mod
= argv
[++arg
];
1410 if (mod
[0] == '^' && mod
[1])
1411 *mod
= CTRL(mod
[1]);
1412 for (unsigned int b
= 0; b
< countof(bindings
); b
++)
1413 if (bindings
[b
].keys
[0] == MOD
)
1414 bindings
[b
].keys
[0] = *mod
;
1418 set_escdelay(atoi(argv
[++arg
]));
1421 else if (ESCDELAY
> 1000)
1425 screen
.history
= atoi(argv
[++arg
]);
1428 title
= argv
[++arg
];
1431 bar
.fd
= open_or_create_fifo(argv
[++arg
], &bar
.file
);
1436 cmdfifo
.fd
= open_or_create_fifo(argv
[++arg
], &cmdfifo
.file
);
1437 if (!(fifo
= realpath(argv
[arg
], NULL
)))
1438 error("%s\n", strerror(errno
));
1439 setenv("DVTM_CMD_FIFO", fifo
, 1);
1450 main(int argc
, char *argv
[]) {
1452 unsigned int key_index
= 0;
1453 memset(keys
, 0, sizeof(keys
));
1454 sigset_t emptyset
, blockset
;
1456 if (!parse_args(argc
, argv
)) {
1461 sigemptyset(&emptyset
);
1462 sigemptyset(&blockset
);
1463 sigaddset(&blockset
, SIGWINCH
);
1464 sigaddset(&blockset
, SIGCHLD
);
1465 sigprocmask(SIG_BLOCK
, &blockset
, NULL
);
1471 if (screen
.need_resize
) {
1473 screen
.need_resize
= false;
1477 FD_SET(STDIN_FILENO
, &rd
);
1479 if (cmdfifo
.fd
!= -1) {
1480 FD_SET(cmdfifo
.fd
, &rd
);
1485 FD_SET(bar
.fd
, &rd
);
1486 nfds
= max(nfds
, bar
.fd
);
1489 for (Client
*c
= clients
; c
; ) {
1490 if (c
->editor
&& c
->editor_died
)
1492 if (!c
->editor
&& c
->died
) {
1493 Client
*t
= c
->next
;
1498 int pty
= c
->editor
? vt_getpty(c
->editor
) : vt_getpty(c
->app
);
1500 nfds
= max(nfds
, pty
);
1505 r
= pselect(nfds
+ 1, &rd
, NULL
, NULL
, NULL
, &emptyset
);
1507 if (r
== -1 && errno
== EINTR
)
1515 if (FD_ISSET(STDIN_FILENO
, &rd
)) {
1518 keys
[key_index
++] = code
;
1519 KeyBinding
*binding
= NULL
;
1520 if (code
== KEY_MOUSE
) {
1522 } else if ((binding
= keybinding(keys
))) {
1523 unsigned int key_length
= 0;
1524 while (key_length
< MAX_KEYS
&& binding
->keys
[key_length
])
1526 if (key_index
== key_length
) {
1527 binding
->action
.cmd(binding
->action
.args
);
1529 memset(keys
, 0, sizeof(keys
));
1533 memset(keys
, 0, sizeof(keys
));
1537 if (r
== 1) /* no data available on pty's */
1541 if (cmdfifo
.fd
!= -1 && FD_ISSET(cmdfifo
.fd
, &rd
))
1544 if (bar
.fd
!= -1 && FD_ISSET(bar
.fd
, &rd
))
1547 for (Client
*c
= clients
; c
; c
= c
->next
) {
1548 bool ed
= c
->editor
&& FD_ISSET(vt_getpty(c
->editor
), &rd
);
1549 bool vt
= FD_ISSET(vt_getpty(c
->app
), &rd
);
1551 if (ed
&& vt_process(c
->editor
) < 0 && errno
== EIO
) {
1552 c
->editor_died
= true;
1554 } else if (vt
&& vt_process(c
->term
) < 0 && errno
== EIO
) {
1559 if ((ed
|| vt
) && c
!= sel
&& is_content_visible(c
)) {
1561 wnoutrefresh(c
->window
);
1565 if (is_content_visible(sel
)) {
1567 curs_set(vt_cursor(sel
->term
));
1568 wnoutrefresh(sel
->window
);