Coverage Report

Created: 2026-08-31 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tmux/window-switch.c
Line
Count
Source
1
/* $OpenBSD: window-switch.c,v 1.3 2026/08/25 07:23:30 nicm Exp $ */
2
3
/*
4
 * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
5
 *
6
 * Permission to use, copy, modify, and distribute this software for any
7
 * purpose with or without fee is hereby granted, provided that the above
8
 * copyright notice and this permission notice appear in all copies.
9
 *
10
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
 */
18
19
#include <sys/types.h>
20
21
#include <ctype.h>
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include "tmux.h"
26
27
static struct screen  *window_switch_init(struct window_mode_entry *,
28
           struct cmdq_item *, struct cmd_find_state *,
29
           struct args *);
30
static void    window_switch_free(struct window_mode_entry *);
31
static void    window_switch_resize(struct window_mode_entry *, u_int,
32
           u_int);
33
static void    window_switch_key(struct window_mode_entry *,
34
           struct client *, struct session *,
35
           struct winlink *, key_code, struct mouse_event *);
36
static enum prompt_result window_switch_prompt_callback(void *, const char *,
37
           enum prompt_key_result);
38
39
0
#define WINDOW_SWITCH_DEFAULT_COMMAND "switch-client -Zt '%%'"
40
41
#define WINDOW_SWITCH_DEFAULT_FORMAT    \
42
0
  "#{?window_format," \
43
0
    "#{window_name} " \
44
0
    "#[dim]#{session_name}:#{window_index}#{window_flags}#[default] " \
45
0
    "#[dim]#{pane_current_command}#[default] " \
46
0
          "#[dim]#{?#{!=:#{pane_title},#{host_short}},#{pane_title},}#[default]" \
47
0
  "," \
48
0
    "#{session_name} " \
49
0
    "#[dim]#{session_windows} windows#[default] " \
50
0
    "#{?session_attached,attached,#[dim]detached#[default]} " \
51
0
    "#[dim]#{window_name}#[default]" \
52
0
  "}"
53
54
const struct window_mode window_switch_mode = {
55
  .name = "switch-mode",
56
  .default_format = WINDOW_SWITCH_DEFAULT_FORMAT,
57
58
  .init = window_switch_init,
59
  .free = window_switch_free,
60
  .resize = window_switch_resize,
61
  .key = window_switch_key,
62
};
63
64
enum window_switch_type {
65
  WINDOW_SWITCH_TYPE_SESSION,
66
  WINDOW_SWITCH_TYPE_WINDOW
67
};
68
69
struct window_switch_itemdata {
70
  enum window_switch_type  type;
71
  int      session;
72
  int      winlink;
73
74
  uint64_t     tag;
75
  char      *text;
76
  bitstr_t    *match;
77
78
  u_int      score;
79
  u_int      order;
80
};
81
82
struct window_switch_modedata {
83
  struct window_pane     *wp;
84
  struct screen       screen;
85
  int         zoomed;
86
87
  char         *format;
88
  char         *command;
89
90
  enum window_switch_type     type;
91
  char         *filter;
92
  struct prompt      *prompt;
93
  u_int         prompt_cx;
94
95
  struct window_switch_itemdata **item_list;
96
  u_int         item_size;
97
98
  struct window_switch_itemdata **matches;
99
  u_int         matches_size;
100
101
  u_int         current;
102
  u_int         offset;
103
};
104
105
static void
106
window_switch_free_item(struct window_switch_itemdata *item)
107
0
{
108
0
  free(item->match);
109
0
  free(item->text);
110
0
  free(item);
111
0
}
112
113
static struct window_switch_itemdata *
114
window_switch_add_item(struct window_switch_modedata *data)
115
0
{
116
0
  struct window_switch_itemdata *item;
117
118
0
  data->item_list = xreallocarray(data->item_list, data->item_size + 1,
119
0
      sizeof *data->item_list);
120
0
  item = data->item_list[data->item_size++] = xcalloc(1, sizeof *item);
121
0
  return (item);
122
0
}
123
124
static void
125
window_switch_add_session(struct window_switch_modedata *data,
126
    struct session *s, u_int *order)
127
0
{
128
0
  struct window_switch_itemdata *item;
129
0
  struct format_tree    *ft;
130
131
0
  ft = format_create(NULL, NULL, FORMAT_NONE, 0);
132
0
  format_defaults(ft, NULL, s, NULL, NULL);
133
134
0
  item = window_switch_add_item(data);
135
0
  item->type = WINDOW_SWITCH_TYPE_SESSION;
136
0
  item->session = s->id;
137
0
  item->winlink = -1;
138
0
  item->tag = (uint64_t)s;
139
0
  item->order = (*order)++;
140
0
  item->text = format_expand(ft, data->format);
141
142
0
  format_free(ft);
143
0
}
144
145
static void
146
window_switch_add_window(struct window_switch_modedata *data,
147
    struct winlink *wl, u_int *order)
148
0
{
149
0
  struct window_switch_itemdata *item;
150
0
  struct format_tree    *ft;
151
152
0
  ft = format_create(NULL, NULL, FORMAT_NONE, 0);
153
0
  format_defaults(ft, NULL, wl->session, wl, NULL);
154
155
0
  item = window_switch_add_item(data);
156
0
  item->type = WINDOW_SWITCH_TYPE_WINDOW;
157
0
  item->session = wl->session->id;
158
0
  item->winlink = wl->idx;
159
0
  item->tag = (uint64_t)wl;
160
0
  item->order = (*order)++;
161
0
  item->text = format_expand(ft, data->format);
162
163
0
  format_free(ft);
164
0
}
165
166
static int
167
window_switch_compare(const void *a0, const void *b0)
168
0
{
169
0
  struct window_switch_itemdata *const *a = a0;
170
0
  struct window_switch_itemdata *const *b = b0;
171
172
0
  if ((*a)->score > (*b)->score)
173
0
    return (-1);
174
0
  if ((*a)->score < (*b)->score)
175
0
    return (1);
176
0
  if ((*a)->order < (*b)->order)
177
0
    return (-1);
178
0
  if ((*a)->order > (*b)->order)
179
0
    return (1);
180
0
  return (0);
181
0
}
182
183
static void
184
window_switch_build(struct window_switch_modedata *data)
185
0
{
186
0
  struct window_switch_itemdata  *item, **m = NULL;
187
0
  const char       *f = data->filter;
188
0
  u_int         ns, nw, i, n = 0, order = 0;
189
0
  u_int         sx = screen_size_x(&data->screen);
190
0
  struct session      **sl;
191
0
  struct winlink      **wl;
192
0
  struct sort_criteria      sort_crit;
193
194
0
  sort_crit.order = SORT_NAME;
195
0
  sort_crit.reversed = 0;
196
197
0
  for (i = 0; i < data->item_size; i++)
198
0
    window_switch_free_item(data->item_list[i]);
199
0
  free(data->item_list);
200
0
  data->item_list = NULL;
201
0
  data->item_size = 0;
202
203
0
  switch (data->type) {
204
0
  case WINDOW_SWITCH_TYPE_SESSION:
205
0
    sl = sort_get_sessions(&ns, &sort_crit);
206
0
    for (i = 0; i < ns; i++)
207
0
      window_switch_add_session(data, sl[i], &order);
208
0
    break;
209
0
  case WINDOW_SWITCH_TYPE_WINDOW:
210
0
    wl = sort_get_winlinks(&nw, &sort_crit);
211
0
    for (i = 0; i < nw; i++)
212
0
      window_switch_add_window(data, wl[i], &order);
213
0
    break;
214
0
  }
215
216
0
  for (i = 0; i < data->item_size; i++) {
217
0
    item = data->item_list[i];
218
0
    if (*f == '\0') {
219
0
      m = xreallocarray(m, n + 1, sizeof *m);
220
0
      m[n++] = item;
221
0
      continue;
222
0
    }
223
224
0
    item->match = fuzzy_match(f, item->text, sx, &item->score);
225
0
    if (item->match == NULL)
226
0
      continue;
227
0
    m = xreallocarray(m, n + 1, sizeof *m);
228
0
    m[n++] = item;
229
0
  }
230
0
  if (n > 1)
231
0
    qsort(m, n, sizeof *m, window_switch_compare);
232
233
0
  free(data->matches);
234
0
  data->matches = m;
235
0
  data->matches_size = n;
236
0
}
237
238
static u_int
239
window_switch_visible(struct window_switch_modedata *data)
240
0
{
241
0
  u_int sy = screen_size_y(&data->screen);
242
243
0
  if (sy <= 1)
244
0
    return (0);
245
0
  return (sy - 1);
246
0
}
247
248
static void
249
window_switch_set_current(struct window_switch_modedata *data, u_int current)
250
0
{
251
0
  u_int visible = window_switch_visible(data);
252
253
0
  if (data->matches_size == 0) {
254
0
    data->current = 0;
255
0
    data->offset = 0;
256
0
    return;
257
0
  }
258
259
0
  if (current > data->matches_size - 1)
260
0
    current = data->matches_size - 1;
261
0
  data->current = current;
262
263
0
  if (data->current < data->offset)
264
0
    data->offset = data->current;
265
0
  else if (visible != 0 && data->current >= data->offset + visible)
266
0
    data->offset = data->current - visible + 1;
267
0
}
268
269
static void
270
window_switch_draw_screen(struct window_mode_entry *wme)
271
0
{
272
0
  struct window_pane    *wp = wme->wp;
273
0
  struct window_switch_modedata *data = wme->data;
274
0
  struct options      *oo = wp->options;
275
0
  struct screen_write_ctx    ctx;
276
0
  struct screen     *s = &data->screen;
277
0
  u_int        sx = screen_size_x(s), i, j;
278
0
  u_int        sy = screen_size_y(s), visible, idx;
279
0
  struct window_switch_itemdata *item;
280
0
  struct grid_cell     mgc, sgc, gc;
281
0
  const struct grid_cell    *dgc = &grid_default_cell;
282
0
  struct prompt_draw_data    pdd;
283
0
  screen_write_start(&ctx, s);
284
0
  screen_write_clearscreen(&ctx, 8);
285
286
0
  if (sy <= 1) {
287
0
    screen_write_stop(&ctx);
288
0
    return;
289
0
  }
290
291
0
  style_apply(&mgc, oo, "switch-mode-match-style", NULL);
292
0
  style_apply(&sgc, oo, "mode-style", NULL);
293
294
0
  visible = window_switch_visible(data);
295
0
  for (i = 0; i < visible; i++) {
296
0
    idx = data->offset + i;
297
0
    if (idx >= data->matches_size)
298
0
      break;
299
0
    item = data->matches[idx];
300
301
0
    screen_write_cursormove(&ctx, 0, i, 0);
302
0
    if (idx != data->current)
303
0
      format_draw(&ctx, dgc, sx, item->text, NULL, 0);
304
0
    else {
305
0
      screen_write_clearendofline(&ctx, sgc.bg);
306
0
      format_draw(&ctx, &sgc, sx, item->text, NULL, 0);
307
0
    }
308
309
0
    if (item->match == NULL)
310
0
      continue;
311
0
    for (j = 0; j < sx; j++) {
312
0
      if (!bit_test(item->match, j))
313
0
        continue;
314
0
      grid_get_cell(s->grid, j, i, &gc);
315
0
      gc.attr = mgc.attr;
316
0
      gc.fg = mgc.fg;
317
0
      gc.bg = mgc.bg;
318
0
      screen_write_cursormove(&ctx, j, i, 0);
319
0
      screen_write_cell(&ctx, &gc);
320
0
    }
321
0
  }
322
323
0
  if (data->prompt != NULL) {
324
0
    pdd.ctx = &ctx;
325
0
    pdd.cursor_x = &data->prompt_cx;
326
0
    pdd.area_x = 0;
327
0
    pdd.area_width = sx;
328
0
    pdd.prompt_line = sy - 1;
329
0
    s->mode |= MODE_CURSOR;
330
0
    prompt_draw(data->prompt, &pdd);
331
0
    screen_write_cursormove(&ctx, data->prompt_cx, sy - 1, 0);
332
0
  }
333
0
  screen_write_stop(&ctx);
334
0
}
335
336
static struct screen *
337
window_switch_init(struct window_mode_entry *wme,
338
    __unused struct cmdq_item *item, struct cmd_find_state *fs,
339
    struct args *args)
340
0
{
341
0
  struct window_pane    *wp = wme->wp;
342
0
  struct window_switch_modedata *data;
343
0
  struct screen     *s;
344
0
  struct prompt_create_data  pd;
345
346
0
  wme->data = data = xcalloc(1, sizeof *data);
347
0
  data->wp = wp;
348
349
0
  if (args_has(args, 'w'))
350
0
    data->type = WINDOW_SWITCH_TYPE_WINDOW;
351
0
  else
352
0
    data->type = WINDOW_SWITCH_TYPE_SESSION;
353
354
0
  data->filter = xstrdup("");
355
0
  if (args == NULL || !args_has(args, 'F'))
356
0
    data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT);
357
0
  else
358
0
    data->format = xstrdup(args_get(args, 'F'));
359
0
  if (args == NULL || args_count(args) == 0)
360
0
    data->command = xstrdup(WINDOW_SWITCH_DEFAULT_COMMAND);
361
0
  else
362
0
    data->command = xstrdup(args_string(args, 0));
363
364
0
  memset(&pd, 0, sizeof pd);
365
0
  prompt_set_options(&pd, fs->s);
366
0
  pd.fs = fs;
367
0
  pd.prompt = "(search) ";
368
0
  pd.input = "";
369
0
  pd.type = PROMPT_TYPE_SEARCH;
370
0
  pd.flags = PROMPT_INCREMENTAL|PROMPT_NOFORMAT|PROMPT_ISMODE|
371
0
      PROMPT_EDITARROWS;
372
0
  pd.inputcb = window_switch_prompt_callback;
373
0
  pd.data = data;
374
0
  data->prompt = prompt_create(&pd);
375
0
  prompt_update(data->prompt, "(search) ", data->filter);
376
377
0
  if (!args_has(args, 'Z'))
378
0
    data->zoomed = -1;
379
0
  else {
380
0
    data->zoomed = (wp->window->flags & WINDOW_ZOOMED);
381
0
    if (!data->zoomed && window_zoom(wp) == 0)
382
0
      server_redraw_window(wp->window);
383
0
  }
384
385
0
  s = &data->screen;
386
0
  screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0);
387
388
0
  window_switch_build(data);
389
0
  prompt_incremental_start(data->prompt);
390
0
  window_switch_draw_screen(wme);
391
392
0
  return (s);
393
0
}
394
395
static void
396
window_switch_free(struct window_mode_entry *wme)
397
0
{
398
0
  struct window_switch_modedata *data = wme->data;
399
0
  u_int        i;
400
401
0
  if (data->zoomed == 0)
402
0
    server_unzoom_window(wme->wp->window);
403
404
0
  for (i = 0; i < data->item_size; i++)
405
0
    window_switch_free_item(data->item_list[i]);
406
0
  free(data->item_list);
407
408
0
  free(data->matches);
409
0
  free(data->filter);
410
0
  prompt_free(data->prompt);
411
0
  free(data->format);
412
0
  free(data->command);
413
0
  screen_free(&data->screen);
414
415
0
  free(data);
416
0
}
417
418
static void
419
window_switch_resize(struct window_mode_entry *wme, u_int sx, u_int sy)
420
0
{
421
0
  struct window_switch_modedata *data = wme->data;
422
0
  struct screen     *s = &data->screen;
423
424
0
  screen_resize(s, sx, sy, 0);
425
0
  window_switch_build(data);
426
0
  window_switch_set_current(data, data->current);
427
0
  window_switch_draw_screen(wme);
428
0
}
429
430
static int
431
window_switch_run_command(struct window_switch_modedata *data, struct client *c)
432
0
{
433
0
  struct window_switch_itemdata *item;
434
0
  struct cmd_find_state    fs;
435
0
  struct session      *s;
436
0
  struct winlink      *wl;
437
0
  char        *target = NULL;
438
0
  struct cmdq_state   *state;
439
0
  char        *command, *error;
440
0
  enum cmd_parse_status    status;
441
442
0
  if (data->matches_size == 0)
443
0
    return (0);
444
0
  item = data->matches[data->current];
445
446
0
  cmd_find_clear_state(&fs, 0);
447
0
  switch (item->type) {
448
0
  case WINDOW_SWITCH_TYPE_SESSION:
449
0
    s = session_find_by_id(item->session);
450
0
    if (s != NULL) {
451
0
      xasprintf(&target, "=%s:", s->name);
452
0
      cmd_find_from_session(&fs, s, 0);
453
0
    }
454
0
    break;
455
0
  case WINDOW_SWITCH_TYPE_WINDOW:
456
0
    s = session_find_by_id(item->session);
457
0
    if (s != NULL) {
458
0
      wl = winlink_find_by_index(&s->windows, item->winlink);
459
0
      if (s != NULL && wl != NULL) {
460
0
        xasprintf(&target, "=%s:%u.", s->name, wl->idx);
461
0
        cmd_find_from_winlink(&fs, wl, 0);
462
0
      }
463
0
    }
464
0
    break;
465
0
  }
466
0
  if (target == NULL)
467
0
    return (0);
468
469
0
  command = cmd_template_replace(data->command, target, 1);
470
0
  if (command != NULL && *command != '\0') {
471
0
    state = cmdq_new_state(&fs, NULL, 0);
472
0
    status = cmd_parse_and_append(command, NULL, c, state, &error);
473
0
    if (status == CMD_PARSE_ERROR) {
474
0
      if (c != NULL) {
475
0
        *error = toupper((u_char)*error);
476
0
        status_message_set(c, -1, 1, 0, 0, "%s", error);
477
0
      }
478
0
      free(error);
479
0
    }
480
0
    cmdq_free_state(state);
481
0
  }
482
0
  free(command);
483
0
  free(target);
484
0
  return (1);
485
0
}
486
487
static enum prompt_result
488
window_switch_prompt_callback(void *arg, const char *s,
489
    enum prompt_key_result key)
490
0
{
491
0
  struct window_switch_modedata *data = arg;
492
493
0
  if (key != PROMPT_KEY_HANDLED)
494
0
    return (PROMPT_CONTINUE);
495
496
0
  if (s == NULL)
497
0
    s = "";
498
0
  else if (*s != '\0')
499
0
    s++;
500
501
0
  free(data->filter);
502
0
  data->filter = xstrdup(s);
503
0
  window_switch_build(data);
504
0
  data->current = 0;
505
0
  data->offset = 0;
506
507
0
  return (PROMPT_CONTINUE);
508
0
}
509
510
static void
511
window_switch_key(struct window_mode_entry *wme, struct client *c,
512
    __unused struct session *s, __unused struct winlink *wl, key_code key,
513
    struct mouse_event *m)
514
0
{
515
0
  struct window_pane    *wp = wme->wp;
516
0
  struct window_switch_modedata *data = wme->data;
517
0
  u_int        visible, current = data->current;
518
0
  u_int        x, y, size = data->matches_size;
519
0
  enum prompt_key_result     result;
520
0
  int        redraw = 0;
521
522
0
  if (KEYC_IS_MOUSE(key)) {
523
0
    if (m == NULL || cmd_mouse_at(wp, m, &x, &y, 0) != 0)
524
0
      return;
525
0
    if (data->prompt != NULL && screen_size_y(&data->screen) != 0 &&
526
0
        y == screen_size_y(&data->screen) - 1 &&
527
0
        MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_1 && !MOUSE_DRAG(m->b) &&
528
0
        !MOUSE_RELEASE(m->b)) {
529
0
      result = prompt_mouse(data->prompt, x, 0,
530
0
          screen_size_x(&data->screen), &redraw);
531
0
      if (redraw || result == PROMPT_KEY_HANDLED) {
532
0
        window_switch_draw_screen(wme);
533
0
        wp->flags |= PANE_REDRAW;
534
0
      }
535
0
      return;
536
0
    }
537
0
    switch (key) {
538
0
    case KEYC_WHEELUP_PANE:
539
0
      if (size != 0 && current != 0)
540
0
        window_switch_set_current(data, current - 1);
541
0
      goto moved;
542
0
    case KEYC_WHEELDOWN_PANE:
543
0
      if (size != 0 && current != size - 1)
544
0
        window_switch_set_current(data, current + 1);
545
0
      goto moved;
546
0
    case KEYC_MOUSEDOWN1_PANE:
547
0
    case KEYC_DOUBLECLICK1_PANE:
548
0
      if (y >= window_switch_visible(data) ||
549
0
          data->offset + y >= size)
550
0
        return;
551
0
      window_switch_set_current(data, data->offset + y);
552
0
      if (key == KEYC_DOUBLECLICK1_PANE) {
553
0
        if (window_switch_run_command(data, c))
554
0
          window_pane_reset_mode(wp);
555
0
        return;
556
0
      }
557
0
      goto moved;
558
0
    }
559
0
    return;
560
0
  }
561
562
0
  switch (key) {
563
0
  case 'p'|KEYC_CTRL:
564
0
  case 'k'|KEYC_CTRL:
565
0
    key = KEYC_UP;
566
0
    break;
567
0
  case 'n'|KEYC_CTRL:
568
0
  case 'j'|KEYC_CTRL:
569
0
    key = KEYC_DOWN;
570
0
    break;
571
0
  }
572
573
0
  switch (key) {
574
0
  case '\r':
575
0
    if (window_switch_run_command(data, c))
576
0
      window_pane_reset_mode(wp);
577
0
    return;
578
0
  case '\033': /* Escape */
579
0
  case '['|KEYC_CTRL:
580
0
  case 'c'|KEYC_CTRL:
581
0
  case 'g'|KEYC_CTRL:
582
0
    window_pane_reset_mode(wp);
583
0
    return;
584
0
  }
585
586
0
  if (data->prompt != NULL) {
587
0
    result = prompt_key(data->prompt, key, &redraw);
588
0
    if (redraw) {
589
0
      window_switch_draw_screen(wme);
590
0
      wp->flags |= PANE_REDRAW;
591
0
    }
592
0
    if (result == PROMPT_KEY_HANDLED ||
593
0
        result == PROMPT_KEY_NOT_HANDLED)
594
0
      return;
595
0
    current = data->current;
596
0
    size = data->matches_size;
597
0
  }
598
599
0
  switch (key) {
600
0
  case KEYC_UP:
601
0
    if (size == 0)
602
0
      goto moved;
603
0
    if (current == 0)
604
0
      window_switch_set_current(data, size - 1);
605
0
    else
606
0
      window_switch_set_current(data, current - 1);
607
0
    goto moved;
608
0
  case KEYC_DOWN:
609
0
    if (size == 0)
610
0
      goto moved;
611
0
    if (current == size - 1)
612
0
      window_switch_set_current(data, 0);
613
0
    else
614
0
      window_switch_set_current(data, current + 1);
615
0
    goto moved;
616
0
  case KEYC_PPAGE:
617
0
    visible = window_switch_visible(data);
618
0
    if (current >= visible)
619
0
      window_switch_set_current(data, current - visible);
620
0
    else
621
0
      window_switch_set_current(data, 0);
622
0
    goto moved;
623
0
  case KEYC_NPAGE:
624
0
    visible = window_switch_visible(data);
625
0
    window_switch_set_current(data, current + visible);
626
0
    goto moved;
627
0
  case KEYC_HOME:
628
0
    window_switch_set_current(data, 0);
629
0
    goto moved;
630
0
  case KEYC_END:
631
0
    if (size > 0)
632
0
      window_switch_set_current(data, size - 1);
633
0
    goto moved;
634
0
  }
635
636
0
moved:
637
0
  window_switch_draw_screen(wme);
638
0
  wp->flags |= PANE_REDRAW;
639
0
}