window.c 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483
  1. /* $OpenBSD$ */
  2. /*
  3. * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
  14. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  15. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #include <sys/types.h>
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <fnmatch.h>
  21. #include <signal.h>
  22. #include <stdint.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <termios.h>
  26. #include <unistd.h>
  27. #include "tmux.h"
  28. #include "tmate.h"
  29. /*
  30. * Each window is attached to a number of panes, each of which is a pty. This
  31. * file contains code to handle them.
  32. *
  33. * A pane has two buffers attached, these are filled and emptied by the main
  34. * server poll loop. Output data is received from pty's in screen format,
  35. * translated and returned as a series of escape sequences and strings via
  36. * input_parse (in input.c). Input data is received as key codes and written
  37. * directly via input_key.
  38. *
  39. * Each pane also has a "virtual" screen (screen.c) which contains the current
  40. * state and is redisplayed when the window is reattached to a client.
  41. *
  42. * Windows are stored directly on a global array and wrapped in any number of
  43. * winlink structs to be linked onto local session RB trees. A reference count
  44. * is maintained and a window removed from the global list and destroyed when
  45. * it reaches zero.
  46. */
  47. /* Global window list. */
  48. struct windows windows;
  49. /* Global panes tree. */
  50. struct window_pane_tree all_window_panes;
  51. u_int next_window_pane_id;
  52. u_int next_window_id;
  53. u_int next_active_point;
  54. void window_pane_timer_callback(int, short, void *);
  55. void window_pane_read_callback(struct bufferevent *, void *);
  56. void window_pane_error_callback(struct bufferevent *, short, void *);
  57. struct window_pane *window_pane_choose_best(struct window_pane **, u_int);
  58. RB_GENERATE(windows, window, entry, window_cmp);
  59. int
  60. window_cmp(struct window *w1, struct window *w2)
  61. {
  62. return (w1->id - w2->id);
  63. }
  64. RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
  65. int
  66. winlink_cmp(struct winlink *wl1, struct winlink *wl2)
  67. {
  68. return (wl1->idx - wl2->idx);
  69. }
  70. RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
  71. int
  72. window_pane_cmp(struct window_pane *wp1, struct window_pane *wp2)
  73. {
  74. return (wp1->id - wp2->id);
  75. }
  76. struct winlink *
  77. winlink_find_by_window(struct winlinks *wwl, struct window *w)
  78. {
  79. struct winlink *wl;
  80. RB_FOREACH(wl, winlinks, wwl) {
  81. if (wl->window == w)
  82. return (wl);
  83. }
  84. return (NULL);
  85. }
  86. struct winlink *
  87. winlink_find_by_index(struct winlinks *wwl, int idx)
  88. {
  89. struct winlink wl;
  90. if (idx < 0)
  91. fatalx("bad index");
  92. wl.idx = idx;
  93. return (RB_FIND(winlinks, wwl, &wl));
  94. }
  95. struct winlink *
  96. winlink_find_by_window_id(struct winlinks *wwl, u_int id)
  97. {
  98. struct winlink *wl;
  99. RB_FOREACH(wl, winlinks, wwl) {
  100. if (wl->window->id == id)
  101. return (wl);
  102. }
  103. return (NULL);
  104. }
  105. int
  106. winlink_next_index(struct winlinks *wwl, int idx)
  107. {
  108. int i;
  109. i = idx;
  110. do {
  111. if (winlink_find_by_index(wwl, i) == NULL)
  112. return (i);
  113. if (i == INT_MAX)
  114. i = 0;
  115. else
  116. i++;
  117. } while (i != idx);
  118. return (-1);
  119. }
  120. u_int
  121. winlink_count(struct winlinks *wwl)
  122. {
  123. struct winlink *wl;
  124. u_int n;
  125. n = 0;
  126. RB_FOREACH(wl, winlinks, wwl)
  127. n++;
  128. return (n);
  129. }
  130. struct winlink *
  131. winlink_add(struct winlinks *wwl, int idx)
  132. {
  133. struct winlink *wl;
  134. if (idx < 0) {
  135. if ((idx = winlink_next_index(wwl, -idx - 1)) == -1)
  136. return (NULL);
  137. } else if (winlink_find_by_index(wwl, idx) != NULL)
  138. return (NULL);
  139. wl = xcalloc(1, sizeof *wl);
  140. wl->idx = idx;
  141. RB_INSERT(winlinks, wwl, wl);
  142. return (wl);
  143. }
  144. void
  145. winlink_set_window(struct winlink *wl, struct window *w)
  146. {
  147. wl->window = w;
  148. w->references++;
  149. }
  150. void
  151. winlink_remove(struct winlinks *wwl, struct winlink *wl)
  152. {
  153. struct window *w = wl->window;
  154. RB_REMOVE(winlinks, wwl, wl);
  155. free(wl->status_text);
  156. free(wl);
  157. if (w != NULL)
  158. window_remove_ref(w);
  159. }
  160. struct winlink *
  161. winlink_next(struct winlink *wl)
  162. {
  163. return (RB_NEXT(winlinks, wwl, wl));
  164. }
  165. struct winlink *
  166. winlink_previous(struct winlink *wl)
  167. {
  168. return (RB_PREV(winlinks, wwl, wl));
  169. }
  170. struct winlink *
  171. winlink_next_by_number(struct winlink *wl, struct session *s, int n)
  172. {
  173. for (; n > 0; n--) {
  174. if ((wl = RB_NEXT(winlinks, wwl, wl)) == NULL)
  175. wl = RB_MIN(winlinks, &s->windows);
  176. }
  177. return (wl);
  178. }
  179. struct winlink *
  180. winlink_previous_by_number(struct winlink *wl, struct session *s, int n)
  181. {
  182. for (; n > 0; n--) {
  183. if ((wl = RB_PREV(winlinks, wwl, wl)) == NULL)
  184. wl = RB_MAX(winlinks, &s->windows);
  185. }
  186. return (wl);
  187. }
  188. void
  189. winlink_stack_push(struct winlink_stack *stack, struct winlink *wl)
  190. {
  191. if (wl == NULL)
  192. return;
  193. winlink_stack_remove(stack, wl);
  194. TAILQ_INSERT_HEAD(stack, wl, sentry);
  195. }
  196. void
  197. winlink_stack_remove(struct winlink_stack *stack, struct winlink *wl)
  198. {
  199. struct winlink *wl2;
  200. if (wl == NULL)
  201. return;
  202. TAILQ_FOREACH(wl2, stack, sentry) {
  203. if (wl2 == wl) {
  204. TAILQ_REMOVE(stack, wl, sentry);
  205. return;
  206. }
  207. }
  208. }
  209. struct window *
  210. window_find_by_id_str(const char *s)
  211. {
  212. const char *errstr;
  213. u_int id;
  214. if (*s != '@')
  215. return (NULL);
  216. id = strtonum(s + 1, 0, UINT_MAX, &errstr);
  217. if (errstr != NULL)
  218. return (NULL);
  219. return (window_find_by_id(id));
  220. }
  221. struct window *
  222. window_find_by_id(u_int id)
  223. {
  224. struct window w;
  225. w.id = id;
  226. return (RB_FIND(windows, &windows, &w));
  227. }
  228. void
  229. window_update_activity(struct window *w)
  230. {
  231. gettimeofday(&w->activity_time, NULL);
  232. alerts_queue(w, WINDOW_ACTIVITY);
  233. }
  234. struct window *
  235. window_create1(u_int sx, u_int sy)
  236. {
  237. struct window *w;
  238. w = xcalloc(1, sizeof *w);
  239. w->name = NULL;
  240. w->flags = 0;
  241. TAILQ_INIT(&w->panes);
  242. w->active = NULL;
  243. #ifdef TMATE
  244. w->tmate_last_sync_active_pane = NULL;
  245. #endif
  246. w->lastlayout = -1;
  247. w->layout_root = NULL;
  248. w->sx = sx;
  249. w->sy = sy;
  250. w->options = options_create(global_w_options);
  251. w->references = 0;
  252. w->id = next_window_id++;
  253. RB_INSERT(windows, &windows, w);
  254. window_update_activity(w);
  255. return (w);
  256. }
  257. struct window *
  258. window_create(const char *name, int argc, char **argv, const char *path,
  259. const char *shell, const char *cwd, struct environ *env,
  260. struct termios *tio, u_int sx, u_int sy, u_int hlimit, char **cause)
  261. {
  262. struct window *w;
  263. struct window_pane *wp;
  264. w = window_create1(sx, sy);
  265. wp = window_add_pane(w, hlimit);
  266. layout_init(w, wp);
  267. if (window_pane_spawn(wp, argc, argv, path, shell, cwd, env, tio,
  268. cause) != 0) {
  269. window_destroy(w);
  270. return (NULL);
  271. }
  272. w->active = TAILQ_FIRST(&w->panes);
  273. if (name != NULL) {
  274. w->name = xstrdup(name);
  275. options_set_number(w->options, "automatic-rename", 0);
  276. } else
  277. w->name = default_window_name(w);
  278. return (w);
  279. }
  280. void
  281. window_destroy(struct window *w)
  282. {
  283. RB_REMOVE(windows, &windows, w);
  284. if (w->layout_root != NULL)
  285. layout_free_cell(w->layout_root);
  286. if (w->saved_layout_root != NULL)
  287. layout_free_cell(w->saved_layout_root);
  288. free(w->old_layout);
  289. if (event_initialized(&w->name_event))
  290. evtimer_del(&w->name_event);
  291. if (event_initialized(&w->alerts_timer))
  292. evtimer_del(&w->alerts_timer);
  293. options_free(w->options);
  294. window_destroy_panes(w);
  295. free(w->name);
  296. free(w);
  297. }
  298. void
  299. window_remove_ref(struct window *w)
  300. {
  301. if (w->references == 0)
  302. fatal("bad reference count");
  303. w->references--;
  304. if (w->references == 0)
  305. window_destroy(w);
  306. }
  307. void
  308. window_set_name(struct window *w, const char *new_name)
  309. {
  310. #ifdef TMATE
  311. /*
  312. * We don't want to sync the layout too much.
  313. * We might want to have some sort of timer for when to
  314. * sync the layout.
  315. */
  316. if (!strcmp(w->name, new_name))
  317. return;
  318. #endif
  319. free(w->name);
  320. w->name = xstrdup(new_name);
  321. notify_window_renamed(w);
  322. #ifdef TMATE
  323. tmate_sync_layout();
  324. #endif
  325. }
  326. void
  327. window_resize(struct window *w, u_int sx, u_int sy)
  328. {
  329. w->sx = sx;
  330. w->sy = sy;
  331. }
  332. int
  333. window_has_pane(struct window *w, struct window_pane *wp)
  334. {
  335. struct window_pane *wp1;
  336. TAILQ_FOREACH(wp1, &w->panes, entry) {
  337. if (wp1 == wp)
  338. return (1);
  339. }
  340. return (0);
  341. }
  342. int
  343. window_set_active_pane(struct window *w, struct window_pane *wp)
  344. {
  345. if (wp == w->active)
  346. return (0);
  347. w->last = w->active;
  348. w->active = wp;
  349. while (!window_pane_visible(w->active)) {
  350. w->active = TAILQ_PREV(w->active, window_panes, entry);
  351. if (w->active == NULL)
  352. w->active = TAILQ_LAST(&w->panes, window_panes);
  353. if (w->active == wp)
  354. return (1);
  355. }
  356. w->active->active_point = next_active_point++;
  357. w->active->flags |= PANE_CHANGED;
  358. return (1);
  359. }
  360. void
  361. window_redraw_active_switch(struct window *w, struct window_pane *wp)
  362. {
  363. const struct grid_cell *agc, *wgc;
  364. if (wp == w->active)
  365. return;
  366. /*
  367. * If window-style and window-active-style are the same, we don't need
  368. * to redraw panes when switching active panes. Otherwise, if the
  369. * active or inactive pane do not have a custom style, they will need
  370. * to be redrawn.
  371. */
  372. agc = options_get_style(w->options, "window-active-style");
  373. wgc = options_get_style(w->options, "window-style");
  374. if (style_equal(agc, wgc))
  375. return;
  376. if (style_equal(&grid_default_cell, &w->active->colgc))
  377. w->active->flags |= PANE_REDRAW;
  378. if (style_equal(&grid_default_cell, &wp->colgc))
  379. wp->flags |= PANE_REDRAW;
  380. }
  381. struct window_pane *
  382. window_get_active_at(struct window *w, u_int x, u_int y)
  383. {
  384. struct window_pane *wp;
  385. TAILQ_FOREACH(wp, &w->panes, entry) {
  386. if (!window_pane_visible(wp))
  387. continue;
  388. if (x < wp->xoff || x > wp->xoff + wp->sx)
  389. continue;
  390. if (y < wp->yoff || y > wp->yoff + wp->sy)
  391. continue;
  392. return (wp);
  393. }
  394. return (NULL);
  395. }
  396. struct window_pane *
  397. window_find_string(struct window *w, const char *s)
  398. {
  399. u_int x, y;
  400. x = w->sx / 2;
  401. y = w->sy / 2;
  402. if (strcasecmp(s, "top") == 0)
  403. y = 0;
  404. else if (strcasecmp(s, "bottom") == 0)
  405. y = w->sy - 1;
  406. else if (strcasecmp(s, "left") == 0)
  407. x = 0;
  408. else if (strcasecmp(s, "right") == 0)
  409. x = w->sx - 1;
  410. else if (strcasecmp(s, "top-left") == 0) {
  411. x = 0;
  412. y = 0;
  413. } else if (strcasecmp(s, "top-right") == 0) {
  414. x = w->sx - 1;
  415. y = 0;
  416. } else if (strcasecmp(s, "bottom-left") == 0) {
  417. x = 0;
  418. y = w->sy - 1;
  419. } else if (strcasecmp(s, "bottom-right") == 0) {
  420. x = w->sx - 1;
  421. y = w->sy - 1;
  422. } else
  423. return (NULL);
  424. return (window_get_active_at(w, x, y));
  425. }
  426. int
  427. window_zoom(struct window_pane *wp)
  428. {
  429. struct window *w = wp->window;
  430. struct window_pane *wp1;
  431. if (w->flags & WINDOW_ZOOMED)
  432. return (-1);
  433. if (!window_pane_visible(wp))
  434. return (-1);
  435. if (window_count_panes(w) == 1)
  436. return (-1);
  437. if (w->active != wp)
  438. window_set_active_pane(w, wp);
  439. TAILQ_FOREACH(wp1, &w->panes, entry) {
  440. wp1->saved_layout_cell = wp1->layout_cell;
  441. wp1->layout_cell = NULL;
  442. }
  443. w->saved_layout_root = w->layout_root;
  444. layout_init(w, wp);
  445. w->flags |= WINDOW_ZOOMED;
  446. notify_window_layout_changed(w);
  447. return (0);
  448. }
  449. int
  450. window_unzoom(struct window *w)
  451. {
  452. struct window_pane *wp;
  453. if (!(w->flags & WINDOW_ZOOMED))
  454. return (-1);
  455. w->flags &= ~WINDOW_ZOOMED;
  456. layout_free(w);
  457. w->layout_root = w->saved_layout_root;
  458. w->saved_layout_root = NULL;
  459. TAILQ_FOREACH(wp, &w->panes, entry) {
  460. wp->layout_cell = wp->saved_layout_cell;
  461. wp->saved_layout_cell = NULL;
  462. }
  463. layout_fix_panes(w, w->sx, w->sy);
  464. notify_window_layout_changed(w);
  465. return (0);
  466. }
  467. struct window_pane *
  468. window_add_pane(struct window *w, u_int hlimit)
  469. {
  470. struct window_pane *wp;
  471. wp = window_pane_create(w, w->sx, w->sy, hlimit);
  472. if (TAILQ_EMPTY(&w->panes))
  473. TAILQ_INSERT_HEAD(&w->panes, wp, entry);
  474. else
  475. TAILQ_INSERT_AFTER(&w->panes, w->active, wp, entry);
  476. return (wp);
  477. }
  478. void
  479. window_lost_pane(struct window *w, struct window_pane *wp)
  480. {
  481. if (wp == marked_pane.wp)
  482. server_clear_marked();
  483. if (wp == w->active) {
  484. w->active = w->last;
  485. w->last = NULL;
  486. if (w->active == NULL) {
  487. w->active = TAILQ_PREV(wp, window_panes, entry);
  488. if (w->active == NULL)
  489. w->active = TAILQ_NEXT(wp, entry);
  490. }
  491. if (w->active != NULL)
  492. w->active->flags |= PANE_CHANGED;
  493. } else if (wp == w->last)
  494. w->last = NULL;
  495. }
  496. void
  497. window_remove_pane(struct window *w, struct window_pane *wp)
  498. {
  499. window_lost_pane(w, wp);
  500. TAILQ_REMOVE(&w->panes, wp, entry);
  501. window_pane_destroy(wp);
  502. }
  503. struct window_pane *
  504. window_pane_at_index(struct window *w, u_int idx)
  505. {
  506. struct window_pane *wp;
  507. u_int n;
  508. n = options_get_number(w->options, "pane-base-index");
  509. TAILQ_FOREACH(wp, &w->panes, entry) {
  510. if (n == idx)
  511. return (wp);
  512. n++;
  513. }
  514. return (NULL);
  515. }
  516. struct window_pane *
  517. window_pane_next_by_number(struct window *w, struct window_pane *wp, u_int n)
  518. {
  519. for (; n > 0; n--) {
  520. if ((wp = TAILQ_NEXT(wp, entry)) == NULL)
  521. wp = TAILQ_FIRST(&w->panes);
  522. }
  523. return (wp);
  524. }
  525. struct window_pane *
  526. window_pane_previous_by_number(struct window *w, struct window_pane *wp,
  527. u_int n)
  528. {
  529. for (; n > 0; n--) {
  530. if ((wp = TAILQ_PREV(wp, window_panes, entry)) == NULL)
  531. wp = TAILQ_LAST(&w->panes, window_panes);
  532. }
  533. return (wp);
  534. }
  535. int
  536. window_pane_index(struct window_pane *wp, u_int *i)
  537. {
  538. struct window_pane *wq;
  539. struct window *w = wp->window;
  540. *i = options_get_number(w->options, "pane-base-index");
  541. TAILQ_FOREACH(wq, &w->panes, entry) {
  542. if (wp == wq) {
  543. return (0);
  544. }
  545. (*i)++;
  546. }
  547. return (-1);
  548. }
  549. u_int
  550. window_count_panes(struct window *w)
  551. {
  552. struct window_pane *wp;
  553. u_int n;
  554. n = 0;
  555. TAILQ_FOREACH(wp, &w->panes, entry)
  556. n++;
  557. return (n);
  558. }
  559. void
  560. window_destroy_panes(struct window *w)
  561. {
  562. struct window_pane *wp;
  563. while (!TAILQ_EMPTY(&w->panes)) {
  564. wp = TAILQ_FIRST(&w->panes);
  565. TAILQ_REMOVE(&w->panes, wp, entry);
  566. window_pane_destroy(wp);
  567. }
  568. }
  569. /* Retuns the printable flags on a window, empty string if no flags set. */
  570. char *
  571. window_printable_flags(struct session *s, struct winlink *wl)
  572. {
  573. char flags[32];
  574. int pos;
  575. pos = 0;
  576. if (wl->flags & WINLINK_ACTIVITY)
  577. flags[pos++] = '#';
  578. if (wl->flags & WINLINK_BELL)
  579. flags[pos++] = '!';
  580. if (wl->flags & WINLINK_SILENCE)
  581. flags[pos++] = '~';
  582. if (wl == s->curw)
  583. flags[pos++] = '*';
  584. if (wl == TAILQ_FIRST(&s->lastw))
  585. flags[pos++] = '-';
  586. if (server_check_marked() && wl == marked_pane.wl)
  587. flags[pos++] = 'M';
  588. if (wl->window->flags & WINDOW_ZOOMED)
  589. flags[pos++] = 'Z';
  590. flags[pos] = '\0';
  591. return (xstrdup(flags));
  592. }
  593. struct window_pane *
  594. window_pane_find_by_id_str(const char *s)
  595. {
  596. const char *errstr;
  597. u_int id;
  598. if (*s != '%')
  599. return (NULL);
  600. id = strtonum(s + 1, 0, UINT_MAX, &errstr);
  601. if (errstr != NULL)
  602. return (NULL);
  603. return (window_pane_find_by_id(id));
  604. }
  605. struct window_pane *
  606. window_pane_find_by_id(u_int id)
  607. {
  608. struct window_pane wp;
  609. wp.id = id;
  610. return (RB_FIND(window_pane_tree, &all_window_panes, &wp));
  611. }
  612. struct window_pane *
  613. window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
  614. {
  615. struct window_pane *wp;
  616. char host[HOST_NAME_MAX + 1];
  617. wp = xcalloc(1, sizeof *wp);
  618. wp->window = w;
  619. wp->id = next_window_pane_id++;
  620. RB_INSERT(window_pane_tree, &all_window_panes, wp);
  621. wp->argc = 0;
  622. wp->argv = NULL;
  623. wp->shell = NULL;
  624. wp->cwd = NULL;
  625. wp->fd = -1;
  626. wp->event = NULL;
  627. wp->mode = NULL;
  628. wp->layout_cell = NULL;
  629. wp->xoff = 0;
  630. wp->yoff = 0;
  631. wp->sx = sx;
  632. wp->sy = sy;
  633. wp->pipe_fd = -1;
  634. wp->pipe_off = 0;
  635. wp->pipe_event = NULL;
  636. #ifdef TMATE
  637. wp->tmate_off = 0;
  638. #endif
  639. wp->saved_grid = NULL;
  640. memcpy(&wp->colgc, &grid_default_cell, sizeof wp->colgc);
  641. screen_init(&wp->base, sx, sy, hlimit);
  642. wp->screen = &wp->base;
  643. if (gethostname(host, sizeof host) == 0)
  644. screen_set_title(&wp->base, host);
  645. input_init(wp);
  646. return (wp);
  647. }
  648. void
  649. window_pane_destroy(struct window_pane *wp)
  650. {
  651. window_pane_reset_mode(wp);
  652. if (event_initialized(&wp->timer))
  653. evtimer_del(&wp->timer);
  654. if (wp->fd != -1) {
  655. #ifdef HAVE_UTEMPTER
  656. utempter_remove_record(wp->fd);
  657. #endif
  658. bufferevent_free(wp->event);
  659. close(wp->fd);
  660. }
  661. input_free(wp);
  662. screen_free(&wp->base);
  663. if (wp->saved_grid != NULL)
  664. grid_destroy(wp->saved_grid);
  665. if (wp->pipe_fd != -1) {
  666. bufferevent_free(wp->pipe_event);
  667. close(wp->pipe_fd);
  668. }
  669. RB_REMOVE(window_pane_tree, &all_window_panes, wp);
  670. free((void *)wp->cwd);
  671. free(wp->shell);
  672. cmd_free_argv(wp->argc, wp->argv);
  673. free(wp);
  674. }
  675. int
  676. window_pane_spawn(struct window_pane *wp, int argc, char **argv,
  677. const char *path, const char *shell, const char *cwd, struct environ *env,
  678. struct termios *tio, char **cause)
  679. {
  680. struct winsize ws;
  681. char *argv0, *cmd, **argvp;
  682. const char *ptr, *first, *home;
  683. struct termios tio2;
  684. #ifdef HAVE_UTEMPTER
  685. char s[32];
  686. #endif
  687. int i;
  688. if (wp->fd != -1) {
  689. bufferevent_free(wp->event);
  690. close(wp->fd);
  691. }
  692. if (argc > 0) {
  693. cmd_free_argv(wp->argc, wp->argv);
  694. wp->argc = argc;
  695. wp->argv = cmd_copy_argv(argc, argv);
  696. }
  697. if (shell != NULL) {
  698. free(wp->shell);
  699. wp->shell = xstrdup(shell);
  700. }
  701. if (cwd != NULL) {
  702. free((void *)wp->cwd);
  703. wp->cwd = xstrdup(cwd);
  704. }
  705. cmd = cmd_stringify_argv(wp->argc, wp->argv);
  706. log_debug("spawn: %s -- %s", wp->shell, cmd);
  707. for (i = 0; i < wp->argc; i++)
  708. log_debug("spawn: argv[%d] = %s", i, wp->argv[i]);
  709. memset(&ws, 0, sizeof ws);
  710. ws.ws_col = screen_size_x(&wp->base);
  711. ws.ws_row = screen_size_y(&wp->base);
  712. switch (wp->pid = forkpty(&wp->fd, wp->tty, NULL, &ws)) {
  713. case -1:
  714. wp->fd = -1;
  715. xasprintf(cause, "%s: %s", cmd, strerror(errno));
  716. free(cmd);
  717. return (-1);
  718. case 0:
  719. if (chdir(wp->cwd) != 0) {
  720. if ((home = find_home()) == NULL || chdir(home) != 0)
  721. chdir("/");
  722. }
  723. if (tcgetattr(STDIN_FILENO, &tio2) != 0)
  724. fatal("tcgetattr failed");
  725. if (tio != NULL)
  726. memcpy(tio2.c_cc, tio->c_cc, sizeof tio2.c_cc);
  727. tio2.c_cc[VERASE] = '\177';
  728. #ifdef IUTF8
  729. tio2.c_iflag |= IUTF8;
  730. #endif
  731. if (tcsetattr(STDIN_FILENO, TCSANOW, &tio2) != 0)
  732. fatal("tcgetattr failed");
  733. closefrom(STDERR_FILENO + 1);
  734. if (path != NULL)
  735. environ_set(env, "PATH", "%s", path);
  736. environ_set(env, "TMUX_PANE", "%%%u", wp->id);
  737. environ_push(env);
  738. clear_signals(1);
  739. log_close();
  740. setenv("SHELL", wp->shell, 1);
  741. ptr = strrchr(wp->shell, '/');
  742. /*
  743. * If given one argument, assume it should be passed to sh -c;
  744. * with more than one argument, use execvp(). If there is no
  745. * arguments, create a login shell.
  746. */
  747. if (wp->argc > 0) {
  748. if (wp->argc != 1) {
  749. /* Copy to ensure argv ends in NULL. */
  750. argvp = cmd_copy_argv(wp->argc, wp->argv);
  751. execvp(argvp[0], argvp);
  752. fatal("execvp failed");
  753. }
  754. first = wp->argv[0];
  755. if (ptr != NULL && *(ptr + 1) != '\0')
  756. xasprintf(&argv0, "%s", ptr + 1);
  757. else
  758. xasprintf(&argv0, "%s", wp->shell);
  759. execl(wp->shell, argv0, "-c", first, (char *)NULL);
  760. fatal("execl failed");
  761. }
  762. if (ptr != NULL && *(ptr + 1) != '\0')
  763. xasprintf(&argv0, "-%s", ptr + 1);
  764. else
  765. xasprintf(&argv0, "-%s", wp->shell);
  766. execl(wp->shell, argv0, (char *)NULL);
  767. fatal("execl failed");
  768. }
  769. #ifdef HAVE_UTEMPTER
  770. xsnprintf(s, sizeof s, "tmux(%lu).%%%u", (long) getpid(), wp->id);
  771. utempter_add_record(wp->fd, s);
  772. kill(getpid(), SIGCHLD);
  773. #endif
  774. setblocking(wp->fd, 0);
  775. wp->event = bufferevent_new(wp->fd, window_pane_read_callback, NULL,
  776. window_pane_error_callback, wp);
  777. bufferevent_setwatermark(wp->event, EV_READ, 0, READ_SIZE);
  778. bufferevent_enable(wp->event, EV_READ|EV_WRITE);
  779. free(cmd);
  780. return (0);
  781. }
  782. void
  783. window_pane_timer_callback(__unused int fd, __unused short events, void *data)
  784. {
  785. window_pane_read_callback(NULL, data);
  786. }
  787. void
  788. window_pane_read_callback(__unused struct bufferevent *bufev, void *data)
  789. {
  790. struct window_pane *wp = data;
  791. struct evbuffer *evb = wp->event->input;
  792. char *new_data;
  793. size_t new_size, available;
  794. struct client *c;
  795. struct timeval tv;
  796. if (event_initialized(&wp->timer))
  797. evtimer_del(&wp->timer);
  798. log_debug("%%%u has %zu bytes", wp->id, EVBUFFER_LENGTH(evb));
  799. TAILQ_FOREACH(c, &clients, entry) {
  800. if (!tty_client_ready(c, wp))
  801. continue;
  802. available = EVBUFFER_LENGTH(c->tty.event->output);
  803. if (available > READ_BACKOFF)
  804. goto start_timer;
  805. }
  806. new_size = EVBUFFER_LENGTH(evb) - wp->pipe_off;
  807. if (wp->pipe_fd != -1 && new_size > 0) {
  808. new_data = EVBUFFER_DATA(evb) + wp->pipe_off;
  809. bufferevent_write(wp->pipe_event, new_data, new_size);
  810. }
  811. #ifdef TMATE
  812. new_size = EVBUFFER_LENGTH(wp->event->input) - wp->tmate_off;
  813. new_data = EVBUFFER_DATA(wp->event->input) + wp->tmate_off;
  814. if (new_size > 0)
  815. tmate_pty_data(wp, new_data, new_size);
  816. #endif
  817. input_parse(wp);
  818. wp->pipe_off = EVBUFFER_LENGTH(evb);
  819. #ifdef TMATE
  820. wp->tmate_off = EVBUFFER_LENGTH(evb);
  821. #endif
  822. return;
  823. start_timer:
  824. log_debug("%%%u backing off (%s %zu > %d)", wp->id, c->ttyname,
  825. available, READ_BACKOFF);
  826. tv.tv_sec = 0;
  827. tv.tv_usec = READ_TIME;
  828. evtimer_set(&wp->timer, window_pane_timer_callback, wp);
  829. evtimer_add(&wp->timer, &tv);
  830. }
  831. void
  832. window_pane_error_callback(__unused struct bufferevent *bufev,
  833. __unused short what, void *data)
  834. {
  835. struct window_pane *wp = data;
  836. server_destroy_pane(wp, 1);
  837. }
  838. void
  839. window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
  840. {
  841. if (sx == wp->sx && sy == wp->sy)
  842. return;
  843. wp->sx = sx;
  844. wp->sy = sy;
  845. screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL);
  846. if (wp->mode != NULL)
  847. wp->mode->resize(wp, sx, sy);
  848. wp->flags |= PANE_RESIZE;
  849. }
  850. /*
  851. * Enter alternative screen mode. A copy of the visible screen is saved and the
  852. * history is not updated
  853. */
  854. void
  855. window_pane_alternate_on(struct window_pane *wp, struct grid_cell *gc,
  856. int cursor)
  857. {
  858. struct screen *s = &wp->base;
  859. u_int sx, sy;
  860. if (wp->saved_grid != NULL)
  861. return;
  862. if (!options_get_number(wp->window->options, "alternate-screen"))
  863. return;
  864. sx = screen_size_x(s);
  865. sy = screen_size_y(s);
  866. wp->saved_grid = grid_create(sx, sy, 0);
  867. grid_duplicate_lines(wp->saved_grid, 0, s->grid, screen_hsize(s), sy);
  868. if (cursor) {
  869. wp->saved_cx = s->cx;
  870. wp->saved_cy = s->cy;
  871. }
  872. memcpy(&wp->saved_cell, gc, sizeof wp->saved_cell);
  873. grid_view_clear(s->grid, 0, 0, sx, sy);
  874. wp->base.grid->flags &= ~GRID_HISTORY;
  875. wp->flags |= PANE_REDRAW;
  876. }
  877. /* Exit alternate screen mode and restore the copied grid. */
  878. void
  879. window_pane_alternate_off(struct window_pane *wp, struct grid_cell *gc,
  880. int cursor)
  881. {
  882. struct screen *s = &wp->base;
  883. u_int sx, sy;
  884. if (wp->saved_grid == NULL)
  885. return;
  886. if (!options_get_number(wp->window->options, "alternate-screen"))
  887. return;
  888. sx = screen_size_x(s);
  889. sy = screen_size_y(s);
  890. /*
  891. * If the current size is bigger, temporarily resize to the old size
  892. * before copying back.
  893. */
  894. if (sy > wp->saved_grid->sy)
  895. screen_resize(s, sx, wp->saved_grid->sy, 1);
  896. /* Restore the grid, cursor position and cell. */
  897. grid_duplicate_lines(s->grid, screen_hsize(s), wp->saved_grid, 0, sy);
  898. if (cursor)
  899. s->cx = wp->saved_cx;
  900. if (s->cx > screen_size_x(s) - 1)
  901. s->cx = screen_size_x(s) - 1;
  902. if (cursor)
  903. s->cy = wp->saved_cy;
  904. if (s->cy > screen_size_y(s) - 1)
  905. s->cy = screen_size_y(s) - 1;
  906. memcpy(gc, &wp->saved_cell, sizeof *gc);
  907. /*
  908. * Turn history back on (so resize can use it) and then resize back to
  909. * the current size.
  910. */
  911. wp->base.grid->flags |= GRID_HISTORY;
  912. if (sy > wp->saved_grid->sy || sx != wp->saved_grid->sx)
  913. screen_resize(s, sx, sy, 1);
  914. grid_destroy(wp->saved_grid);
  915. wp->saved_grid = NULL;
  916. wp->flags |= PANE_REDRAW;
  917. }
  918. int
  919. window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode)
  920. {
  921. struct screen *s;
  922. if (wp->mode != NULL)
  923. return (1);
  924. wp->mode = mode;
  925. if ((s = wp->mode->init(wp)) != NULL)
  926. wp->screen = s;
  927. wp->flags |= (PANE_REDRAW|PANE_CHANGED);
  928. server_status_window(wp->window);
  929. return (0);
  930. }
  931. void
  932. window_pane_reset_mode(struct window_pane *wp)
  933. {
  934. if (wp->mode == NULL)
  935. return;
  936. wp->mode->free(wp);
  937. wp->mode = NULL;
  938. wp->screen = &wp->base;
  939. wp->flags |= (PANE_REDRAW|PANE_CHANGED);
  940. server_status_window(wp->window);
  941. #ifdef TMATE
  942. tmate_sync_copy_mode(wp);
  943. #endif
  944. }
  945. void
  946. window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
  947. key_code key, struct mouse_event *m)
  948. {
  949. struct window_pane *wp2;
  950. if (KEYC_IS_MOUSE(key) && m == NULL)
  951. return;
  952. if (wp->mode != NULL) {
  953. if (wp->mode->key != NULL)
  954. wp->mode->key(wp, c, s, key, m);
  955. return;
  956. }
  957. if (wp->fd == -1 || wp->flags & PANE_INPUTOFF)
  958. return;
  959. input_key(wp, key, m);
  960. if (KEYC_IS_MOUSE(key))
  961. return;
  962. if (options_get_number(wp->window->options, "synchronize-panes")) {
  963. TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
  964. if (wp2 == wp || wp2->mode != NULL)
  965. continue;
  966. if (wp2->fd == -1 || wp2->flags & PANE_INPUTOFF)
  967. continue;
  968. if (window_pane_visible(wp2))
  969. input_key(wp2, key, NULL);
  970. }
  971. }
  972. }
  973. int
  974. window_pane_visible(struct window_pane *wp)
  975. {
  976. struct window *w = wp->window;
  977. if (wp->layout_cell == NULL)
  978. return (0);
  979. if (wp->xoff >= w->sx || wp->yoff >= w->sy)
  980. return (0);
  981. if (wp->xoff + wp->sx > w->sx || wp->yoff + wp->sy > w->sy)
  982. return (0);
  983. return (1);
  984. }
  985. char *
  986. window_pane_search(struct window_pane *wp, const char *searchstr,
  987. u_int *lineno)
  988. {
  989. struct screen *s = &wp->base;
  990. char *newsearchstr, *line, *msg;
  991. u_int i;
  992. msg = NULL;
  993. xasprintf(&newsearchstr, "*%s*", searchstr);
  994. for (i = 0; i < screen_size_y(s); i++) {
  995. line = grid_view_string_cells(s->grid, 0, i, screen_size_x(s));
  996. if (fnmatch(newsearchstr, line, 0) == 0) {
  997. msg = line;
  998. if (lineno != NULL)
  999. *lineno = i;
  1000. break;
  1001. }
  1002. free(line);
  1003. }
  1004. free(newsearchstr);
  1005. return (msg);
  1006. }
  1007. /* Get MRU pane from a list. */
  1008. struct window_pane *
  1009. window_pane_choose_best(struct window_pane **list, u_int size)
  1010. {
  1011. struct window_pane *next, *best;
  1012. u_int i;
  1013. if (size == 0)
  1014. return (NULL);
  1015. best = list[0];
  1016. for (i = 1; i < size; i++) {
  1017. next = list[i];
  1018. if (next->active_point > best->active_point)
  1019. best = next;
  1020. }
  1021. return (best);
  1022. }
  1023. /*
  1024. * Find the pane directly above another. We build a list of those adjacent to
  1025. * top edge and then choose the best.
  1026. */
  1027. struct window_pane *
  1028. window_pane_find_up(struct window_pane *wp)
  1029. {
  1030. struct window_pane *next, *best, **list;
  1031. u_int edge, left, right, end, size;
  1032. int found;
  1033. if (wp == NULL || !window_pane_visible(wp))
  1034. return (NULL);
  1035. list = NULL;
  1036. size = 0;
  1037. edge = wp->yoff;
  1038. if (edge == 0)
  1039. edge = wp->window->sy + 1;
  1040. left = wp->xoff;
  1041. right = wp->xoff + wp->sx;
  1042. TAILQ_FOREACH(next, &wp->window->panes, entry) {
  1043. if (next == wp || !window_pane_visible(next))
  1044. continue;
  1045. if (next->yoff + next->sy + 1 != edge)
  1046. continue;
  1047. end = next->xoff + next->sx - 1;
  1048. found = 0;
  1049. if (next->xoff < left && end > right)
  1050. found = 1;
  1051. else if (next->xoff >= left && next->xoff <= right)
  1052. found = 1;
  1053. else if (end >= left && end <= right)
  1054. found = 1;
  1055. if (!found)
  1056. continue;
  1057. list = xreallocarray(list, size + 1, sizeof *list);
  1058. list[size++] = next;
  1059. }
  1060. best = window_pane_choose_best(list, size);
  1061. free(list);
  1062. return (best);
  1063. }
  1064. /* Find the pane directly below another. */
  1065. struct window_pane *
  1066. window_pane_find_down(struct window_pane *wp)
  1067. {
  1068. struct window_pane *next, *best, **list;
  1069. u_int edge, left, right, end, size;
  1070. int found;
  1071. if (wp == NULL || !window_pane_visible(wp))
  1072. return (NULL);
  1073. list = NULL;
  1074. size = 0;
  1075. edge = wp->yoff + wp->sy + 1;
  1076. if (edge >= wp->window->sy)
  1077. edge = 0;
  1078. left = wp->xoff;
  1079. right = wp->xoff + wp->sx;
  1080. TAILQ_FOREACH(next, &wp->window->panes, entry) {
  1081. if (next == wp || !window_pane_visible(next))
  1082. continue;
  1083. if (next->yoff != edge)
  1084. continue;
  1085. end = next->xoff + next->sx - 1;
  1086. found = 0;
  1087. if (next->xoff < left && end > right)
  1088. found = 1;
  1089. else if (next->xoff >= left && next->xoff <= right)
  1090. found = 1;
  1091. else if (end >= left && end <= right)
  1092. found = 1;
  1093. if (!found)
  1094. continue;
  1095. list = xreallocarray(list, size + 1, sizeof *list);
  1096. list[size++] = next;
  1097. }
  1098. best = window_pane_choose_best(list, size);
  1099. free(list);
  1100. return (best);
  1101. }
  1102. /* Find the pane directly to the left of another. */
  1103. struct window_pane *
  1104. window_pane_find_left(struct window_pane *wp)
  1105. {
  1106. struct window_pane *next, *best, **list;
  1107. u_int edge, top, bottom, end, size;
  1108. int found;
  1109. if (wp == NULL || !window_pane_visible(wp))
  1110. return (NULL);
  1111. list = NULL;
  1112. size = 0;
  1113. edge = wp->xoff;
  1114. if (edge == 0)
  1115. edge = wp->window->sx + 1;
  1116. top = wp->yoff;
  1117. bottom = wp->yoff + wp->sy;
  1118. TAILQ_FOREACH(next, &wp->window->panes, entry) {
  1119. if (next == wp || !window_pane_visible(next))
  1120. continue;
  1121. if (next->xoff + next->sx + 1 != edge)
  1122. continue;
  1123. end = next->yoff + next->sy - 1;
  1124. found = 0;
  1125. if (next->yoff < top && end > bottom)
  1126. found = 1;
  1127. else if (next->yoff >= top && next->yoff <= bottom)
  1128. found = 1;
  1129. else if (end >= top && end <= bottom)
  1130. found = 1;
  1131. if (!found)
  1132. continue;
  1133. list = xreallocarray(list, size + 1, sizeof *list);
  1134. list[size++] = next;
  1135. }
  1136. best = window_pane_choose_best(list, size);
  1137. free(list);
  1138. return (best);
  1139. }
  1140. /* Find the pane directly to the right of another. */
  1141. struct window_pane *
  1142. window_pane_find_right(struct window_pane *wp)
  1143. {
  1144. struct window_pane *next, *best, **list;
  1145. u_int edge, top, bottom, end, size;
  1146. int found;
  1147. if (wp == NULL || !window_pane_visible(wp))
  1148. return (NULL);
  1149. list = NULL;
  1150. size = 0;
  1151. edge = wp->xoff + wp->sx + 1;
  1152. if (edge >= wp->window->sx)
  1153. edge = 0;
  1154. top = wp->yoff;
  1155. bottom = wp->yoff + wp->sy;
  1156. TAILQ_FOREACH(next, &wp->window->panes, entry) {
  1157. if (next == wp || !window_pane_visible(next))
  1158. continue;
  1159. if (next->xoff != edge)
  1160. continue;
  1161. end = next->yoff + next->sy - 1;
  1162. found = 0;
  1163. if (next->yoff < top && end > bottom)
  1164. found = 1;
  1165. else if (next->yoff >= top && next->yoff <= bottom)
  1166. found = 1;
  1167. else if (end >= top && end <= bottom)
  1168. found = 1;
  1169. if (!found)
  1170. continue;
  1171. list = xreallocarray(list, size + 1, sizeof *list);
  1172. list[size++] = next;
  1173. }
  1174. best = window_pane_choose_best(list, size);
  1175. free(list);
  1176. return (best);
  1177. }
  1178. /* Clear alert flags for a winlink */
  1179. void
  1180. winlink_clear_flags(struct winlink *wl)
  1181. {
  1182. struct session *s;
  1183. struct winlink *wl_loop;
  1184. RB_FOREACH(s, sessions, &sessions) {
  1185. RB_FOREACH(wl_loop, winlinks, &s->windows) {
  1186. if (wl_loop->window != wl->window)
  1187. continue;
  1188. if ((wl_loop->flags & WINLINK_ALERTFLAGS) == 0)
  1189. continue;
  1190. wl_loop->flags &= ~WINLINK_ALERTFLAGS;
  1191. wl_loop->window->flags &= ~WINDOW_ALERTFLAGS;
  1192. server_status_session(s);
  1193. }
  1194. }
  1195. }
  1196. int
  1197. winlink_shuffle_up(struct session *s, struct winlink *wl)
  1198. {
  1199. int idx, last;
  1200. idx = wl->idx + 1;
  1201. /* Find the next free index. */
  1202. for (last = idx; last < INT_MAX; last++) {
  1203. if (winlink_find_by_index(&s->windows, last) == NULL)
  1204. break;
  1205. }
  1206. if (last == INT_MAX)
  1207. return (-1);
  1208. /* Move everything from last - 1 to idx up a bit. */
  1209. for (; last > idx; last--) {
  1210. wl = winlink_find_by_index(&s->windows, last - 1);
  1211. server_link_window(s, wl, s, last, 0, 0, NULL);
  1212. server_unlink_window(s, wl);
  1213. }
  1214. return (idx);
  1215. }