1*104e139dSnicm /* $OpenBSD: server-fn.c,v 1.138 2024/11/15 14:09:04 nicm Exp $ */ 2311827fbSnicm 3311827fbSnicm /* 498ca8272Snicm * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5311827fbSnicm * 6311827fbSnicm * Permission to use, copy, modify, and distribute this software for any 7311827fbSnicm * purpose with or without fee is hereby granted, provided that the above 8311827fbSnicm * copyright notice and this permission notice appear in all copies. 9311827fbSnicm * 10311827fbSnicm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11311827fbSnicm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12311827fbSnicm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13311827fbSnicm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14311827fbSnicm * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15311827fbSnicm * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16311827fbSnicm * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17311827fbSnicm */ 18311827fbSnicm 19311827fbSnicm #include <sys/types.h> 205b8ac713Snicm #include <sys/queue.h> 21514fd64dSnicm #include <sys/wait.h> 225b8ac713Snicm #include <sys/uio.h> 23311827fbSnicm 245b8ac713Snicm #include <imsg.h> 257d053cf9Snicm #include <stdlib.h> 26311827fbSnicm #include <string.h> 27b04643ffSnicm #include <time.h> 28311827fbSnicm #include <unistd.h> 29311827fbSnicm 30311827fbSnicm #include "tmux.h" 31311827fbSnicm 32ced21769Snicm static void server_destroy_session_group(struct session *); 33c5e332b7Snicm 346f7d62ebSnicm void 35311827fbSnicm server_redraw_client(struct client *c) 36311827fbSnicm { 37e7808201Snicm c->flags |= CLIENT_ALLREDRAWFLAGS; 38311827fbSnicm } 39311827fbSnicm 40311827fbSnicm void 41311827fbSnicm server_status_client(struct client *c) 42311827fbSnicm { 43e7808201Snicm c->flags |= CLIENT_REDRAWSTATUS; 44311827fbSnicm } 45311827fbSnicm 46311827fbSnicm void 47311827fbSnicm server_redraw_session(struct session *s) 48311827fbSnicm { 49311827fbSnicm struct client *c; 50311827fbSnicm 5182873134Snicm TAILQ_FOREACH(c, &clients, entry) { 52311827fbSnicm if (c->session == s) 53311827fbSnicm server_redraw_client(c); 54311827fbSnicm } 55311827fbSnicm } 56311827fbSnicm 57311827fbSnicm void 5801b2421eSnicm server_redraw_session_group(struct session *s) 5901b2421eSnicm { 6001b2421eSnicm struct session_group *sg; 6101b2421eSnicm 6206440b28Snicm if ((sg = session_group_contains(s)) == NULL) 6301b2421eSnicm server_redraw_session(s); 6401b2421eSnicm else { 6501b2421eSnicm TAILQ_FOREACH(s, &sg->sessions, gentry) 6601b2421eSnicm server_redraw_session(s); 6701b2421eSnicm } 6801b2421eSnicm } 6901b2421eSnicm 7001b2421eSnicm void 71311827fbSnicm server_status_session(struct session *s) 72311827fbSnicm { 73311827fbSnicm struct client *c; 74311827fbSnicm 7582873134Snicm TAILQ_FOREACH(c, &clients, entry) { 76311827fbSnicm if (c->session == s) 77311827fbSnicm server_status_client(c); 78311827fbSnicm } 79311827fbSnicm } 80311827fbSnicm 81311827fbSnicm void 8201b2421eSnicm server_status_session_group(struct session *s) 8301b2421eSnicm { 8401b2421eSnicm struct session_group *sg; 8501b2421eSnicm 8606440b28Snicm if ((sg = session_group_contains(s)) == NULL) 8701b2421eSnicm server_status_session(s); 8801b2421eSnicm else { 8901b2421eSnicm TAILQ_FOREACH(s, &sg->sessions, gentry) 9001b2421eSnicm server_status_session(s); 9101b2421eSnicm } 9201b2421eSnicm } 9301b2421eSnicm 9401b2421eSnicm void 95311827fbSnicm server_redraw_window(struct window *w) 96311827fbSnicm { 97311827fbSnicm struct client *c; 98311827fbSnicm 9982873134Snicm TAILQ_FOREACH(c, &clients, entry) { 10082873134Snicm if (c->session != NULL && c->session->curw->window == w) 101311827fbSnicm server_redraw_client(c); 102311827fbSnicm } 103311827fbSnicm } 104311827fbSnicm 105311827fbSnicm void 106b9b22aa8Snicm server_redraw_window_borders(struct window *w) 107b9b22aa8Snicm { 108b9b22aa8Snicm struct client *c; 109b9b22aa8Snicm 11082873134Snicm TAILQ_FOREACH(c, &clients, entry) { 11182873134Snicm if (c->session != NULL && c->session->curw->window == w) 112e7808201Snicm c->flags |= CLIENT_REDRAWBORDERS; 113b9b22aa8Snicm } 114b9b22aa8Snicm } 115b9b22aa8Snicm 116b9b22aa8Snicm void 117311827fbSnicm server_status_window(struct window *w) 118311827fbSnicm { 119311827fbSnicm struct session *s; 120311827fbSnicm 121311827fbSnicm /* 122311827fbSnicm * This is slightly different. We want to redraw the status line of any 123311827fbSnicm * clients containing this window rather than anywhere it is the 124311827fbSnicm * current window. 125311827fbSnicm */ 126311827fbSnicm 12759996dc3Snicm RB_FOREACH(s, sessions, &sessions) { 12852e9404eSnicm if (session_has(s, w)) 129311827fbSnicm server_status_session(s); 130311827fbSnicm } 131311827fbSnicm } 132311827fbSnicm 133311827fbSnicm void 134311827fbSnicm server_lock(void) 135311827fbSnicm { 136311827fbSnicm struct client *c; 137311827fbSnicm 13882873134Snicm TAILQ_FOREACH(c, &clients, entry) { 13982873134Snicm if (c->session != NULL) 140168ee6d3Snicm server_lock_client(c); 141168ee6d3Snicm } 142168ee6d3Snicm } 143168ee6d3Snicm 144168ee6d3Snicm void 145168ee6d3Snicm server_lock_session(struct session *s) 146168ee6d3Snicm { 147168ee6d3Snicm struct client *c; 148168ee6d3Snicm 14982873134Snicm TAILQ_FOREACH(c, &clients, entry) { 15082873134Snicm if (c->session == s) 151168ee6d3Snicm server_lock_client(c); 152168ee6d3Snicm } 153168ee6d3Snicm } 154168ee6d3Snicm 155168ee6d3Snicm void 156168ee6d3Snicm server_lock_client(struct client *c) 157168ee6d3Snicm { 158168ee6d3Snicm const char *cmd; 159311827fbSnicm 16043a67eb2Snicm if (c->flags & CLIENT_CONTROL) 161721350bcSnicm return; 162721350bcSnicm 1635d8d7431Snicm if (c->flags & CLIENT_SUSPENDED) 1645d8d7431Snicm return; 1655d8d7431Snicm 166d89252e5Snicm cmd = options_get_string(c->session->options, "lock-command"); 167b70f7e17Snicm if (*cmd == '\0' || strlen(cmd) + 1 > MAX_IMSGSIZE - IMSG_HEADER_SIZE) 168168ee6d3Snicm return; 169311827fbSnicm 1701c5425bdSnicm tty_stop_tty(&c->tty); 1711c5425bdSnicm tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_SMCUP)); 1721c5425bdSnicm tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_CLEAR)); 173786c44dcSnicm tty_raw(&c->tty, tty_term_string(c->tty.term, TTYC_E3)); 174311827fbSnicm 1751c5425bdSnicm c->flags |= CLIENT_SUSPENDED; 176f7772d13Snicm proc_send(c->peer, MSG_LOCK, -1, cmd, strlen(cmd) + 1); 177ced36c44Snicm } 178f1d65372Snicm 179f1d65372Snicm void 180d0ca3a30Snicm server_kill_pane(struct window_pane *wp) 181d0ca3a30Snicm { 182d0ca3a30Snicm struct window *w = wp->window; 183d0ca3a30Snicm 184d0ca3a30Snicm if (window_count_panes(w) == 1) { 185fedaf9c8Snicm server_kill_window(w, 1); 186d0ca3a30Snicm recalculate_sizes(); 187d0ca3a30Snicm } else { 188d0ca3a30Snicm server_unzoom_window(w); 189249e1654Snicm server_client_remove_pane(wp); 190d0ca3a30Snicm layout_close_pane(wp); 191d0ca3a30Snicm window_remove_pane(w, wp); 192d0ca3a30Snicm server_redraw_window(w); 193d0ca3a30Snicm } 194d0ca3a30Snicm } 195d0ca3a30Snicm 196d0ca3a30Snicm void 197fedaf9c8Snicm server_kill_window(struct window *w, int renumber) 198f1d65372Snicm { 199fedaf9c8Snicm struct session *s, *s1; 200f1d65372Snicm struct winlink *wl; 201f1d65372Snicm 202fedaf9c8Snicm RB_FOREACH_SAFE(s, sessions, &sessions, s1) { 20352e9404eSnicm if (!session_has(s, w)) 204f1d65372Snicm continue; 205fedaf9c8Snicm 20657ca3c78Snicm server_unzoom_window(w); 207f6cdf7ccSnicm while ((wl = winlink_find_by_window(&s->windows, w)) != NULL) { 208f6cdf7ccSnicm if (session_detach(s, wl)) { 20901b2421eSnicm server_destroy_session_group(s); 210f6cdf7ccSnicm break; 2116a041561Snicm } 212d2c17161Snicm server_redraw_session_group(s); 213f1d65372Snicm } 21453c15224Snicm 215fedaf9c8Snicm if (renumber) 216fedaf9c8Snicm server_renumber_session(s); 217fedaf9c8Snicm } 218fedaf9c8Snicm recalculate_sizes(); 219fedaf9c8Snicm } 220fedaf9c8Snicm 221fedaf9c8Snicm void 222fedaf9c8Snicm server_renumber_session(struct session *s) 223fedaf9c8Snicm { 224fedaf9c8Snicm struct session_group *sg; 225fedaf9c8Snicm 226d89252e5Snicm if (options_get_number(s->options, "renumber-windows")) { 22706440b28Snicm if ((sg = session_group_contains(s)) != NULL) { 228fedaf9c8Snicm TAILQ_FOREACH(s, &sg->sessions, gentry) 229fedaf9c8Snicm session_renumber_windows(s); 23087d95c92Snicm } else 23153c15224Snicm session_renumber_windows(s); 23221ced74dSnicm } 23387d95c92Snicm } 234fedaf9c8Snicm 235fedaf9c8Snicm void 236fedaf9c8Snicm server_renumber_all(void) 237fedaf9c8Snicm { 238fedaf9c8Snicm struct session *s; 239fedaf9c8Snicm 240fedaf9c8Snicm RB_FOREACH(s, sessions, &sessions) 241fedaf9c8Snicm server_renumber_session(s); 242f6cdf7ccSnicm } 24321ced74dSnicm 24421ced74dSnicm int 24501b2421eSnicm server_link_window(struct session *src, struct winlink *srcwl, 246bfcd10e2Snicm struct session *dst, int dstidx, int killflag, int selectflag, 247bfcd10e2Snicm char **cause) 24821ced74dSnicm { 24921ced74dSnicm struct winlink *dstwl; 25001b2421eSnicm struct session_group *srcsg, *dstsg; 25101b2421eSnicm 25206440b28Snicm srcsg = session_group_contains(src); 25306440b28Snicm dstsg = session_group_contains(dst); 25401b2421eSnicm if (src != dst && srcsg != NULL && dstsg != NULL && srcsg == dstsg) { 25501b2421eSnicm xasprintf(cause, "sessions are grouped"); 25601b2421eSnicm return (-1); 25701b2421eSnicm } 25821ced74dSnicm 25921ced74dSnicm dstwl = NULL; 26021ced74dSnicm if (dstidx != -1) 26121ced74dSnicm dstwl = winlink_find_by_index(&dst->windows, dstidx); 26221ced74dSnicm if (dstwl != NULL) { 263795597afSnicm if (dstwl->window == srcwl->window) { 264795597afSnicm xasprintf(cause, "same index: %d", dstidx); 26502b834e4Snicm return (-1); 266795597afSnicm } 26721ced74dSnicm if (killflag) { 26821ced74dSnicm /* 26921ced74dSnicm * Can't use session_detach as it will destroy session 27021ced74dSnicm * if this makes it empty. 27121ced74dSnicm */ 2722ae124feSnicm notify_session_window("window-unlinked", dst, 2732ae124feSnicm dstwl->window); 2744336fb18Snicm dstwl->flags &= ~WINLINK_ALERTFLAGS; 27521ced74dSnicm winlink_stack_remove(&dst->lastw, dstwl); 27621ced74dSnicm winlink_remove(&dst->windows, dstwl); 27721ced74dSnicm 27821ced74dSnicm /* Force select/redraw if current. */ 279cc715dcaSnicm if (dstwl == dst->curw) { 28021ced74dSnicm selectflag = 1; 281cc715dcaSnicm dst->curw = NULL; 282cc715dcaSnicm } 28321ced74dSnicm } 28421ced74dSnicm } 28521ced74dSnicm 28621ced74dSnicm if (dstidx == -1) 287d89252e5Snicm dstidx = -1 - options_get_number(dst->options, "base-index"); 28821ced74dSnicm dstwl = session_attach(dst, srcwl->window, dstidx, cause); 28921ced74dSnicm if (dstwl == NULL) 29021ced74dSnicm return (-1); 29121ced74dSnicm 29201b2421eSnicm if (selectflag) 29321ced74dSnicm session_select(dst, dstwl->idx); 29401b2421eSnicm server_redraw_session_group(dst); 29521ced74dSnicm 29621ced74dSnicm return (0); 29721ced74dSnicm } 29821ced74dSnicm 29921ced74dSnicm void 30021ced74dSnicm server_unlink_window(struct session *s, struct winlink *wl) 30121ced74dSnicm { 30221ced74dSnicm if (session_detach(s, wl)) 30301b2421eSnicm server_destroy_session_group(s); 30421ced74dSnicm else 30501b2421eSnicm server_redraw_session_group(s); 30601b2421eSnicm } 30701b2421eSnicm 30801b2421eSnicm void 3092ae124feSnicm server_destroy_pane(struct window_pane *wp, int notify) 3107c6e570cSnicm { 3117c6e570cSnicm struct window *w = wp->window; 31284604cd7Snicm struct screen_write_ctx ctx; 31384604cd7Snicm struct grid_cell gc; 314c93e2546Snicm int remain_on_exit; 3158d518d48Snicm const char *s; 3168d518d48Snicm char *expanded; 3178d518d48Snicm u_int sx = screen_size_x(&wp->base); 3188d518d48Snicm u_int sy = screen_size_y(&wp->base); 3197c6e570cSnicm 320ca33238aSnicm if (wp->fd != -1) { 3217c6e570cSnicm bufferevent_free(wp->event); 3228ba21c53Snicm wp->event = NULL; 32384045c9aSnicm close(wp->fd); 3247c6e570cSnicm wp->fd = -1; 325ca33238aSnicm } 3267c6e570cSnicm 327c93e2546Snicm remain_on_exit = options_get_number(wp->options, "remain-on-exit"); 328c93e2546Snicm if (remain_on_exit != 0 && (~wp->flags & PANE_STATUSREADY)) 3297c6e570cSnicm return; 330c93e2546Snicm switch (remain_on_exit) { 331c93e2546Snicm case 0: 332c93e2546Snicm break; 333c93e2546Snicm case 2: 334c93e2546Snicm if (WIFEXITED(wp->status) && WEXITSTATUS(wp->status) == 0) 335c93e2546Snicm break; 336c93e2546Snicm /* FALLTHROUGH */ 337c93e2546Snicm case 1: 338514fd64dSnicm if (wp->flags & PANE_STATUSDRAWN) 339514fd64dSnicm return; 340514fd64dSnicm wp->flags |= PANE_STATUSDRAWN; 341514fd64dSnicm 3428d518d48Snicm gettimeofday(&wp->dead_time, NULL); 3432ae124feSnicm if (notify) 3442ae124feSnicm notify_pane("pane-died", wp); 3452ae124feSnicm 3468d518d48Snicm s = options_get_string(wp->options, "remain-on-exit-format"); 3478d518d48Snicm if (*s != '\0') { 34883e83a91Snicm screen_write_start_pane(&ctx, wp, &wp->base); 3498d518d48Snicm screen_write_scrollregion(&ctx, 0, sy - 1); 3508d518d48Snicm screen_write_cursormove(&ctx, 0, sy - 1, 0); 3516d5c64a0Snicm screen_write_linefeed(&ctx, 1, 8); 35284604cd7Snicm memcpy(&gc, &grid_default_cell, sizeof gc); 353514fd64dSnicm 3548d518d48Snicm expanded = format_single(NULL, s, NULL, NULL, NULL, wp); 3558d518d48Snicm format_draw(&ctx, &gc, sx, expanded, NULL, 0); 3568d518d48Snicm free(expanded); 357514fd64dSnicm 35884604cd7Snicm screen_write_stop(&ctx); 3598d518d48Snicm } 3608d518d48Snicm wp->base.mode &= ~MODE_CURSOR; 3618d518d48Snicm 36284604cd7Snicm wp->flags |= PANE_REDRAW; 36384604cd7Snicm return; 36484604cd7Snicm } 3657c6e570cSnicm 3662ae124feSnicm if (notify) 3672ae124feSnicm notify_pane("pane-exited", wp); 3682ae124feSnicm 369b4a3311eSnicm server_unzoom_window(w); 370249e1654Snicm server_client_remove_pane(wp); 3717c6e570cSnicm layout_close_pane(wp); 3727c6e570cSnicm window_remove_pane(w, wp); 3737c6e570cSnicm 3747c6e570cSnicm if (TAILQ_EMPTY(&w->panes)) 375fedaf9c8Snicm server_kill_window(w, 1); 3767c6e570cSnicm else 3777c6e570cSnicm server_redraw_window(w); 3787c6e570cSnicm } 3797c6e570cSnicm 380ced21769Snicm static void 38101b2421eSnicm server_destroy_session_group(struct session *s) 38201b2421eSnicm { 38301b2421eSnicm struct session_group *sg; 384a8dbd3acSnicm struct session *s1; 38501b2421eSnicm 3866a041561Snicm if ((sg = session_group_contains(s)) == NULL) { 38701b2421eSnicm server_destroy_session(s); 3886a041561Snicm session_destroy(s, 1, __func__); 3896a041561Snicm } else { 390a8dbd3acSnicm TAILQ_FOREACH_SAFE(s, &sg->sessions, gentry, s1) { 39101b2421eSnicm server_destroy_session(s); 392c26c4f79Snicm session_destroy(s, 1, __func__); 393a8dbd3acSnicm } 39401b2421eSnicm } 395f1d65372Snicm } 396669c539cSnicm 3979883b791Snicm static struct session * 3986a041561Snicm server_find_session(struct session *s, 3996a041561Snicm int (*f)(struct session *, struct session *)) 400abc89c60Snicm { 401579115bdSnicm struct session *s_loop, *s_out = NULL; 402abc89c60Snicm 40359996dc3Snicm RB_FOREACH(s_loop, sessions, &sessions) { 4046a041561Snicm if (s_loop != s && (s_out == NULL || f(s_loop, s_out))) 405abc89c60Snicm s_out = s_loop; 406abc89c60Snicm } 407abc89c60Snicm return (s_out); 408abc89c60Snicm } 409abc89c60Snicm 4106a041561Snicm static int 4116a041561Snicm server_newer_session(struct session *s_loop, struct session *s_out) 412579115bdSnicm { 4135d6bcbf0Snicm return (timercmp(&s_loop->activity_time, &s_out->activity_time, >)); 414579115bdSnicm } 4156a041561Snicm 4166a041561Snicm static int 4176a041561Snicm server_newer_detached_session(struct session *s_loop, struct session *s_out) 4186a041561Snicm { 4196a041561Snicm if (s_loop->attached) 4206a041561Snicm return (0); 4216a041561Snicm return (server_newer_session(s_loop, s_out)); 422579115bdSnicm } 423579115bdSnicm 424669c539cSnicm void 4252d9a0fc7Snicm server_destroy_session(struct session *s) 4262d9a0fc7Snicm { 4272d9a0fc7Snicm struct client *c; 428*104e139dSnicm struct session *s_new = NULL, *cs_new, *use_s; 429579115bdSnicm int detach_on_destroy; 4302d9a0fc7Snicm 431579115bdSnicm detach_on_destroy = options_get_number(s->options, "detach-on-destroy"); 432579115bdSnicm if (detach_on_destroy == 0) 4336a041561Snicm s_new = server_find_session(s, server_newer_session); 434579115bdSnicm else if (detach_on_destroy == 2) 4356a041561Snicm s_new = server_find_session(s, server_newer_detached_session); 4366a041561Snicm else if (detach_on_destroy == 3) 4376a041561Snicm s_new = session_previous_session(s); 4386a041561Snicm else if (detach_on_destroy == 4) 4396a041561Snicm s_new = session_next_session(s); 440*104e139dSnicm 441*104e139dSnicm /* 442*104e139dSnicm * If no suitable new session was found above, then look for any 443*104e139dSnicm * session as an alternative in case a client needs it. 444*104e139dSnicm */ 445*104e139dSnicm if (s_new == NULL && 446*104e139dSnicm (detach_on_destroy == 1 || detach_on_destroy == 2)) 447*104e139dSnicm cs_new = server_find_session(s, server_newer_session); 448*104e139dSnicm 44982873134Snicm TAILQ_FOREACH(c, &clients, entry) { 45082873134Snicm if (c->session != s) 4512d9a0fc7Snicm continue; 452*104e139dSnicm use_s = s_new; 453*104e139dSnicm if (use_s == NULL && (c->flags & CLIENT_NO_DETACH_ON_DESTROY)) 454*104e139dSnicm use_s = cs_new; 455*104e139dSnicm 4566a041561Snicm c->session = NULL; 4576a041561Snicm c->last_session = NULL; 458*104e139dSnicm server_client_set_session(c, use_s); 459*104e139dSnicm if (use_s == NULL) 460636a2c4aSnicm c->flags |= CLIENT_EXIT; 4612d9a0fc7Snicm } 462f61b6cb0Snicm recalculate_sizes(); 4632d9a0fc7Snicm } 4642d9a0fc7Snicm 4652d9a0fc7Snicm void 46635776674Snicm server_check_unattached(void) 46735776674Snicm { 46835776674Snicm struct session *s; 4699461a728Snicm struct session_group *sg; 47035776674Snicm 47135776674Snicm /* 47235776674Snicm * If any sessions are no longer attached and have destroy-unattached 47335776674Snicm * set, collect them. 47435776674Snicm */ 47559996dc3Snicm RB_FOREACH(s, sessions, &sessions) { 476647c5c18Snicm if (s->attached != 0) 47735776674Snicm continue; 4789461a728Snicm switch (options_get_number(s->options, "destroy-unattached")) { 4799461a728Snicm case 0: /* off */ 4809461a728Snicm continue; 4819461a728Snicm case 1: /* on */ 4829461a728Snicm break; 4839461a728Snicm case 2: /* keep-last */ 4849461a728Snicm sg = session_group_contains(s); 4859461a728Snicm if (sg == NULL || session_group_count(sg) <= 1) 4869461a728Snicm continue; 4879461a728Snicm break; 4889461a728Snicm case 3: /* keep-group */ 4899461a728Snicm sg = session_group_contains(s); 4909461a728Snicm if (sg != NULL && session_group_count(sg) == 1) 4919461a728Snicm continue; 4929461a728Snicm break; 4939461a728Snicm } 494c26c4f79Snicm session_destroy(s, 1, __func__); 49535776674Snicm } 49635776674Snicm } 49735776674Snicm 498b4a3311eSnicm void 499b4a3311eSnicm server_unzoom_window(struct window *w) 500b4a3311eSnicm { 5010ad0daf4Snicm if (window_unzoom(w, 1) == 0) 502b4a3311eSnicm server_redraw_window(w); 503e048bb79Snicm } 504