Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-05-21 12:01:06 +01:00
3 changed files with 253 additions and 58 deletions

View File

@@ -29,7 +29,11 @@
static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_resize_pane_exec(struct cmd *, struct cmdq_item *);
static void cmd_resize_pane_mouse_update(struct client *, static enum cmd_retval cmd_resize_pane_mouse_update(struct cmd *,
struct cmdq_item *);
static void cmd_resize_pane_mouse_update_floating(struct client *,
struct mouse_event *);
static void cmd_resize_pane_mouse_update_tiled(struct client *,
struct mouse_event *); struct mouse_event *);
const struct cmd_entry cmd_resize_pane_entry = { const struct cmd_entry cmd_resize_pane_entry = {
@@ -51,12 +55,9 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
struct winlink *wl = target->wl; struct winlink *wl = target->wl;
struct window *w = wl->window; struct window *w = wl->window;
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
const char *errstr; const char *errstr;
char *cause; char *cause;
u_int adjust; u_int adjust;
@@ -75,15 +76,8 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
if (args_has(args, 'M')) { if (args_has(args, 'M'))
if (!event->m.valid || cmd_mouse_window(&event->m, &s) == NULL) return (cmd_resize_pane_mouse_update(self, item));
return (CMD_RETURN_NORMAL);
if (c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update;
cmd_resize_pane_mouse_update(c, &event->m);
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'Z')) { if (args_has(args, 'Z')) {
if (w->flags & WINDOW_ZOOMED) if (w->flags & WINDOW_ZOOMED)
@@ -148,8 +142,153 @@ cmd_resize_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static enum cmd_retval
cmd_resize_pane_mouse_update(__unused struct cmd *self, struct cmdq_item *item)
{
struct cmd_find_state *target = cmdq_get_target(item);
struct key_event *event = cmdq_get_event(item);
struct window_pane *wp = target->wp;
struct winlink *wl = target->wl;
struct window *w = wl->window;
struct client *c = cmdq_get_client(item);
struct session *s = target->s;
if (!event->m.valid)
return (CMD_RETURN_NORMAL);
wp = cmd_mouse_pane(&event->m, &s, NULL);
if (wp == NULL || c == NULL || c->session != s)
return (CMD_RETURN_NORMAL);
if (~wp->flags & PANE_FLOATING) {
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_tiled;
cmd_resize_pane_mouse_update_tiled(c, &event->m);
return (CMD_RETURN_NORMAL);
}
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1);
c->tty.mouse_drag_update = cmd_resize_pane_mouse_update_floating;
cmd_resize_pane_mouse_update_floating(c, &event->m);
return (CMD_RETURN_NORMAL);
}
static void static void
cmd_resize_pane_mouse_update(struct client *c, struct mouse_event *m) cmd_resize_pane_mouse_update_floating(struct client *c, struct mouse_event *m)
{
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct layout_cell *lc;
int y, ly, x, lx, sx, sy, new_sx, new_sy;
int new_xoff, new_yoff, resizes = 0;
wp = cmd_mouse_pane(m, NULL, &wl);
if (wp == NULL) {
c->tty.mouse_drag_update = NULL;
return;
}
w = wl->window;
lc = wp->layout_cell;
sx = wp->sx;
sy = wp->sy;
y = m->y + m->oy; x = m->x + m->ox;
if (m->statusat == 0 && y >= (int)m->statuslines)
y -= m->statuslines;
else if (m->statusat > 0 && y >= m->statusat)
y = m->statusat - 1;
ly = m->ly + m->oy; lx = m->lx + m->ox;
if (m->statusat == 0 && ly >= (int)m->statuslines)
ly -= m->statuslines;
else if (m->statusat > 0 && ly >= m->statusat)
ly = m->statusat - 1;
if ((lx == wp->xoff - 1 || lx == wp->xoff) && ly == wp->yoff - 1) {
/* Top left corner. */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_xoff = x + 1; /* because mouse is on border at xoff - 1 */
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, new_yoff);
resizes++;
} else if ((lx == wp->xoff + sx + 1 || lx == wp->xoff + sx) &&
ly == wp->yoff - 1) {
/* Top right corner. */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = lc->sy + (ly - y);
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
new_yoff = y + 1;
layout_set_size(lc, new_sx, new_sy, lc->xoff, new_yoff);
resizes++;
} else if ((lx == wp->xoff - 1 || lx == wp->xoff) &&
ly == wp->yoff + sy) {
/* Bottom left corner. */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, new_sy, new_xoff, lc->yoff);
resizes++;
} else if ((lx == wp->xoff + sx + 1 || lx == wp->xoff + sx) &&
ly == wp->yoff + sy) {
/* Bottom right corner. */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
new_sx = PANE_MINIMUM;
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
new_sy = PANE_MINIMUM;
layout_set_size(lc, new_sx, new_sy, lc->xoff, lc->yoff);
resizes++;
} else if (lx == wp->xoff + sx + 1) {
/* Right border. */
new_sx = x - lc->xoff;
if (new_sx < PANE_MINIMUM)
return;
layout_set_size(lc, new_sx, lc->sy, lc->xoff, lc->yoff);
resizes++;
} else if (lx == wp->xoff - 1) {
/* Left border. */
new_sx = lc->sx + (lx - x);
if (new_sx < PANE_MINIMUM)
return;
new_xoff = x + 1;
layout_set_size(lc, new_sx, lc->sy, new_xoff, lc->yoff);
resizes++;
} else if (ly == wp->yoff + sy) {
/* Bottom border. */
new_sy = y - lc->yoff;
if (new_sy < PANE_MINIMUM)
return;
layout_set_size(lc, lc->sx, new_sy, lc->xoff, lc->yoff);
resizes++;
} else if (ly == wp->yoff - 1) {
/* Top border (move instead of resize). */
new_xoff = lc->xoff + (x - lx);
new_yoff = y + 1;
layout_set_size(lc, lc->sx, lc->sy, new_xoff, new_yoff);
resizes++;
}
if (resizes != 0) {
layout_fix_panes(w, NULL);
server_redraw_window(w);
server_redraw_window_borders(w);
}
}
static void
cmd_resize_pane_mouse_update_tiled(struct client *c, struct mouse_event *m)
{ {
struct winlink *wl; struct winlink *wl;
struct window *w; struct window *w;

View File

@@ -599,7 +599,7 @@ server_client_exec(struct client *c, const char *cmd)
} }
static enum key_code_mouse_location static enum key_code_mouse_location
server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py, server_client_check_mouse_in_pane(struct window_pane *wp, int px, int py,
u_int *sl_mpos) u_int *sl_mpos)
{ {
struct window *w = wp->window; struct window *w = wp->window;
@@ -607,7 +607,7 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
struct window_pane *fwp; struct window_pane *fwp;
int pane_status, sb, sb_pos, sb_w, sb_pad; int pane_status, sb, sb_pos, sb_w, sb_pad;
int pane_status_line, sl_top, sl_bottom; int pane_status_line, sl_top, sl_bottom;
int bdr_bottom, bdr_top, bdr_right; int bdr_bottom, bdr_top, bdr_left, bdr_right;
sb = options_get_number(wo, "pane-scrollbars"); sb = options_get_number(wo, "pane-scrollbars");
sb_pos = options_get_number(wo, "pane-scrollbars-position"); sb_pos = options_get_number(wo, "pane-scrollbars-position");
@@ -630,32 +630,37 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
/* Check if point is within the pane or scrollbar. */ /* Check if point is within the pane or scrollbar. */
if (((pane_status != PANE_STATUS_OFF && if (((pane_status != PANE_STATUS_OFF &&
(int)py != pane_status_line && py != wp->yoff + wp->sy) || py != pane_status_line && py != wp->yoff + (int)wp->sy) ||
(wp->yoff == 0 && py < wp->sy) || (wp->yoff == 0 && py < (int)wp->sy) ||
((int)py >= wp->yoff && py < wp->yoff + wp->sy)) && (py >= wp->yoff && py < wp->yoff + (int)wp->sy)) &&
((sb_pos == PANE_SCROLLBARS_RIGHT && ((sb_pos == PANE_SCROLLBARS_RIGHT &&
(int)px < (int)wp->xoff + (int)wp->sx + sb_pad + sb_w) || px < wp->xoff + (int)wp->sx + sb_pad + sb_w) ||
(sb_pos == PANE_SCROLLBARS_LEFT && (sb_pos == PANE_SCROLLBARS_LEFT &&
(int)px < (int)wp->xoff + (int)wp->sx - sb_pad - sb_w))) { px < wp->xoff + (int)wp->sx - sb_pad - sb_w))) {
/* Check if in the scrollbar. */ /* Check if in the scrollbar. */
if ((sb_pos == PANE_SCROLLBARS_RIGHT && if ((sb_pos == PANE_SCROLLBARS_RIGHT &&
((int)px >= (int)wp->xoff + (int)wp->sx + sb_pad && (px >= wp->xoff + (int)wp->sx + sb_pad &&
(int)px < (int)wp->xoff + (int)wp->sx + sb_pad + sb_w)) || px < wp->xoff + (int)wp->sx + sb_pad + sb_w)) ||
(sb_pos == PANE_SCROLLBARS_LEFT && (sb_pos == PANE_SCROLLBARS_LEFT &&
((int)px >= (int)wp->xoff - sb_pad - sb_w && (px >= wp->xoff - sb_pad - sb_w &&
(int)px < (int)wp->xoff - sb_pad))) { px < wp->xoff - sb_pad))) {
/* Check where inside the scrollbar. */ /* Check where inside the scrollbar. */
sl_top = wp->yoff + wp->sb_slider_y; sl_top = wp->yoff + wp->sb_slider_y;
sl_bottom = (wp->yoff + wp->sb_slider_y + sl_bottom = (wp->yoff + wp->sb_slider_y +
wp->sb_slider_h - 1); wp->sb_slider_h - 1);
if ((int)py < sl_top) if (py < sl_top)
return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP); return (KEYC_MOUSE_LOCATION_SCROLLBAR_UP);
else if ((int)py >= sl_top && else if (py >= sl_top && py <= sl_bottom) {
(int)py <= sl_bottom) {
*sl_mpos = (py - wp->sb_slider_y - wp->yoff); *sl_mpos = (py - wp->sb_slider_y - wp->yoff);
return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER); return (KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER);
} else /* py > sl_bottom */ } else /* py > sl_bottom */
return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN); return (KEYC_MOUSE_LOCATION_SCROLLBAR_DOWN);
} else if (wp->flags & PANE_FLOATING &&
(px == wp->xoff - 1 ||
py == wp->yoff - 1 ||
py == wp->yoff + (int)wp->sy)) {
/* Floating pane left, bottom or top border. */
return (KEYC_MOUSE_LOCATION_BORDER);
} else { } else {
/* Must be inside the pane. */ /* Must be inside the pane. */
return (KEYC_MOUSE_LOCATION_PANE); return (KEYC_MOUSE_LOCATION_PANE);
@@ -667,22 +672,35 @@ server_client_check_mouse_in_pane(struct window_pane *wp, u_int px, u_int py,
(~fwp->flags & PANE_ZOOMED)) (~fwp->flags & PANE_ZOOMED))
continue; continue;
bdr_top = fwp->yoff - 1; bdr_top = fwp->yoff - 1;
bdr_bottom = fwp->yoff + (int)fwp->sy; bdr_bottom = fwp->yoff + fwp->sy;
if (sb_pos == PANE_SCROLLBARS_LEFT) if (sb_pos == PANE_SCROLLBARS_LEFT)
bdr_right = fwp->xoff + (int)fwp->sx; bdr_right = fwp->xoff + fwp->sx;
else else {
/* PANE_SCROLLBARS_RIGHT or none. */ /* PANE_SCROLLBARS_RIGHT or none. */
bdr_right = fwp->xoff + (int)fwp->sx + bdr_right = fwp->xoff + fwp->sx + sb_pad + sb_w;
sb_pad + sb_w; }
if ((int)py >= (int)fwp->yoff - 1 && if (py >= fwp->yoff - 1 &&
py <= fwp->yoff + fwp->sy) { py <= fwp->yoff + (int)fwp->sy) {
if ((int)px == bdr_right) if (px == bdr_right)
break;
if (wp->flags & PANE_FLOATING) {
/* Floating pane, check left border. */
bdr_left = fwp->xoff - 1;
if (px == bdr_left)
break; break;
} }
if ((int)px >= (int)fwp->xoff - 1 && }
px <= fwp->xoff + fwp->sx) { if (px >= fwp->xoff - 1 &&
if ((int)py == bdr_bottom || (int)py == bdr_top) px <= fwp->xoff + (int)fwp->sx) {
bdr_bottom = fwp->yoff + fwp->sy;
if (py == bdr_bottom)
break; break;
if (wp->flags & PANE_FLOATING) {
/* Floating pane, check top border. */
bdr_top = fwp->yoff - 1;
if (py == bdr_top)
break;
}
} }
} }
if (fwp != NULL) if (fwp != NULL)
@@ -699,7 +717,7 @@ server_client_check_mouse(struct client *c, struct key_event *event)
struct session *s = c->session, *fs; struct session *s = c->session, *fs;
struct window *w = s->curw->window; struct window *w = s->curw->window;
struct winlink *fwl; struct winlink *fwl;
struct window_pane *wp, *fwp; struct window_pane *wp, *fwp, *lwp = NULL;
u_int x, y, sx, sy, px, py, n, sl_mpos = 0; u_int x, y, sx, sy, px, py, n, sl_mpos = 0;
u_int b, bn; u_int b, bn;
int ignore = 0; int ignore = 0;
@@ -712,6 +730,13 @@ server_client_check_mouse(struct client *c, struct key_event *event)
log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b, log_debug("%s mouse %02x at %u,%u (last %u,%u) (%d)", c->name, m->b,
m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag); m->x, m->y, m->lx, m->ly, c->tty.mouse_drag_flag);
/* Find last pane, if any. */
if (c->tty.mouse_last_pane != -1) {
lwp = window_pane_find_by_id(c->tty.mouse_last_pane);
if (lwp != NULL)
log_debug("%s mouse last pane %%%u", c->name, lwp->id);
}
/* What type of event is this? */ /* What type of event is this? */
if (event->key == KEYC_DOUBLECLICK) { if (event->key == KEYC_DOUBLECLICK) {
type = KEYC_TYPE_DOUBLECLICK; type = KEYC_TYPE_DOUBLECLICK;
@@ -855,9 +880,13 @@ have_event:
* scrollbar. * scrollbar.
*/ */
if (loc == KEYC_MOUSE_LOCATION_NOWHERE) { if (loc == KEYC_MOUSE_LOCATION_NOWHERE) {
if (c->tty.mouse_scrolling_flag) if (c->tty.mouse_scrolling_flag) {
if (lwp != NULL) {
loc = KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER; loc = KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER;
else { m->wp = lwp->id;
m->w = lwp->window->id;
}
} else {
px = x; px = x;
if (m->statusat == 0 && y >= m->statuslines) if (m->statusat == 0 && y >= m->statuslines)
py = y - m->statuslines; py = y - m->statuslines;
@@ -874,8 +903,13 @@ have_event:
px = px + m->ox; px = px + m->ox;
py = py + m->oy; py = py + m->oy;
if (type == KEYC_TYPE_MOUSEDRAG && lwp != NULL) {
/* Use pane from last mouse event. */
wp = lwp;
} else {
/* Try inside the pane. */ /* Try inside the pane. */
wp = window_get_active_at(w, px, py); wp = window_get_active_at(w, px, py);
}
if (wp == NULL) if (wp == NULL)
return (KEYC_UNKNOWN); return (KEYC_UNKNOWN);
loc = server_client_check_mouse_in_pane(wp, px, py, loc = server_client_check_mouse_in_pane(wp, px, py,
@@ -954,6 +988,7 @@ have_event:
type = KEYC_TYPE_MOUSEDRAGEND; type = KEYC_TYPE_MOUSEDRAGEND;
c->tty.mouse_drag_flag = 0; c->tty.mouse_drag_flag = 0;
c->tty.mouse_slider_mpos = -1; c->tty.mouse_slider_mpos = -1;
c->tty.mouse_last_pane = -1;
} }
/* Convert to a key binding. */ /* Convert to a key binding. */
@@ -962,6 +997,7 @@ have_event:
if (wp != NULL && if (wp != NULL &&
wp != w->active && wp != w->active &&
options_get_number(s->options, "focus-follows-mouse")) { options_get_number(s->options, "focus-follows-mouse")) {
window_redraw_active_switch(w, wp);
window_set_active_pane(w, wp, 1); window_set_active_pane(w, wp, 1);
server_redraw_window_borders(w); server_redraw_window_borders(w);
server_status_window(w); server_status_window(w);
@@ -978,6 +1014,12 @@ have_event:
* where the user grabbed. * where the user grabbed.
*/ */
c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1; c->tty.mouse_drag_flag = MOUSE_BUTTONS(b) + 1;
/* Only change pane if not already dragging a pane border. */
if (lwp == NULL) {
lwp = wp = window_get_active_at(w, px, py);
c->tty.mouse_last_pane = wp->id;
}
if (c->tty.mouse_scrolling_flag == 0 && if (c->tty.mouse_scrolling_flag == 0 &&
loc == KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER) { loc == KEYC_MOUSE_LOCATION_SCROLLBAR_SLIDER) {
c->tty.mouse_scrolling_flag = 1; c->tty.mouse_scrolling_flag = 1;
@@ -1719,6 +1761,7 @@ server_client_reset_state(struct client *c)
struct options *oo = c->session->options; struct options *oo = c->session->options;
int mode = 0, cursor, flags; int mode = 0, cursor, flags;
u_int cx = 0, cy = 0, ox, oy, sx, sy, n; u_int cx = 0, cy = 0, ox, oy, sx, sy, n;
struct visible_ranges *r;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
return; return;
@@ -1731,7 +1774,7 @@ server_client_reset_state(struct client *c)
if (c->overlay_draw != NULL) { if (c->overlay_draw != NULL) {
if (c->overlay_mode != NULL) if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy); s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (c->prompt_string == NULL) } else if (wp != NULL && c->prompt_string == NULL)
s = wp->screen; s = wp->screen;
else else
s = c->status.active; s = c->status.active;
@@ -1759,22 +1802,30 @@ server_client_reset_state(struct client *c)
cy = tty->sy - 1; cy = tty->sy - 1;
} }
cx = c->prompt_cursor; cx = c->prompt_cursor;
} else if (c->overlay_draw == NULL) { } else if (wp != NULL && c->overlay_draw == NULL) {
cursor = 0; cursor = 0;
tty_window_offset(tty, &ox, &oy, &sx, &sy); tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + s->cx >= ox && wp->xoff + s->cx <= ox + sx && if (wp->xoff + (int)s->cx >= (int)ox &&
wp->yoff + s->cy >= oy && wp->yoff + s->cy <= oy + sy) { wp->xoff + (int)s->cx <= (int)ox + (int)sx &&
wp->yoff + (int)s->cy >= (int)oy &&
wp->yoff + (int)s->cy <= (int)oy + (int)sy) {
cursor = 1; cursor = 1;
cx = wp->xoff + s->cx - ox; cx = wp->xoff + (int)s->cx - (int)ox;
cy = wp->yoff + s->cy - oy; cy = wp->yoff + (int)s->cy - (int)oy;
if (status_at_line(c) == 0) if (status_at_line(c) == 0)
cy += status_line_size(c); cy += status_line_size(c);
} }
r = screen_redraw_get_visible_ranges(wp, cx, cy, 1, NULL);
if (!screen_redraw_is_visible(r, cx))
cursor = 0;
if (!cursor) if (!cursor)
mode &= ~MODE_CURSOR; mode &= ~MODE_CURSOR;
} } else if (c->overlay_mode == NULL || s == NULL)
mode &= ~MODE_CURSOR;
log_debug("%s: cursor to %u,%u", __func__, cx, cy); log_debug("%s: cursor to %u,%u", __func__, cx, cy);
tty_cursor(tty, cx, cy); tty_cursor(tty, cx, cy);
@@ -2115,7 +2166,7 @@ server_client_set_path(struct client *c)
struct session *s = c->session; struct session *s = c->session;
const char *path; const char *path;
if (s->curw == NULL) if (s->curw == NULL || s->curw->window->active == NULL)
return; return;
if (s->curw->window->active->base.path == NULL) if (s->curw->window->active->base.path == NULL)
path = ""; path = "";
@@ -2734,6 +2785,11 @@ server_client_remove_pane(struct window_pane *wp)
RB_REMOVE(client_windows, &c->windows, cw); RB_REMOVE(client_windows, &c->windows, cw);
free(cw); free(cw);
} }
if (c->tty.mouse_last_pane == (int)wp->id) {
c->tty.mouse_last_pane = -1;
c->tty.mouse_drag_update = NULL;
c->tty.mouse_scrolling_flag = 0;
}
} }
} }

2
tmux.h
View File

@@ -1711,7 +1711,7 @@ struct tty {
int mouse_drag_flag; int mouse_drag_flag;
int mouse_scrolling_flag; int mouse_scrolling_flag;
int mouse_slider_mpos; int mouse_slider_mpos;
int mouse_last_pane;
void (*mouse_drag_update)(struct client *, void (*mouse_drag_update)(struct client *,
struct mouse_event *); struct mouse_event *);
void (*mouse_drag_release)(struct client *, void (*mouse_drag_release)(struct client *,