dvtm.c 37 KB

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