Take window shapes into account when processing drag and drop

* configure.ac: Test for the Nonrectangular Window Shape
extension.
* msdos/sed1v2.inp: Update.
* src/xterm.c (struct x_client_list_window): New fields for
shapes.
(x_dnd_free_toplevels): Free shapes.
(x_dnd_compute_toplevels): Populate window shapes.
(x_dnd_get_target_window_2): New function.
(x_dnd_get_target_window_1): Test WM state of window before
taking it into account.
(x_dnd_begin_drag_and_drop): Use outer window as the initial
last seen window.
(x_dnd_update_state): Small fixes to frame tracking.
(handle_one_xevent): Handle ShapeNotify events correctly.
(x_term_init): Test for the Nonrectangular Window Shape
extension.
* src/xterm.h (struct x_display_info): New atom `WM_STATE'.
This commit is contained in:
Po Lu 2022-03-25 16:15:57 +08:00
parent 9d2dcd1841
commit f38bdb0327
4 changed files with 410 additions and 22 deletions

View file

@ -4538,6 +4538,24 @@ fi
AC_SUBST(XDBE_CFLAGS)
AC_SUBST(XDBE_LIBS)
### Use the Nonrectangular Window Shape extension if available.
HAVE_XSHAPE=no
if test "${HAVE_X11}" = "yes"; then
AC_CHECK_HEADER(X11/extensions/shape.h,
[AC_CHECK_LIB(Xext, XShapeQueryVersion, HAVE_XSHAPE=yes)],
[],
[#include <X11/extensions/shape.h>
])
if test $HAVE_XSHAPE = yes; then
XSHAPE_LIBS=-lXext
fi
if test $HAVE_XSHAPE = yes; then
AC_DEFINE(HAVE_XSHAPE, 1, [Define to 1 if you have the Nonrectangular Window Shape extension.])
fi
fi
AC_SUBST(XSHAPE_CFLAGS)
AC_SUBST(XSHAPE_LIBS)
### Use Xcomposite (-lXcomposite) if available
HAVE_XCOMPOSITE=no
if test "${HAVE_X11}" = "yes"; then

View file

@ -119,6 +119,8 @@ s/ *@WEBP_LIBS@//
/^XDBE_CFLAGS *=/s/@XDBE_CFLAGS@//
/^XCOMPOSITE_LIBS *=/s/@XCOMPOSITE_LIBS@//
/^XCOMPOSITE_CFLAGS *=/s/@XCOMPOSITE_CFLAGS@//
/^XSHAPE_LIBS *=/s/@XSHAPE_LIBS@//
/^XSHAPE_CFLAGS *=/s/@XSHAPE_CFLAGS@//
/^XINPUT_LIBS *=/s/@XINPUT_LIBS@//
/^XINPUT_CFLAGS *=/s/@XINPUT_CFLAGS@//
/^XSYNC_LIBS *=/s/@XSYNC_LIBS@//

View file

@ -546,6 +546,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <X11/extensions/Xcomposite.h>
#endif
#ifdef HAVE_XSHAPE
#include <X11/extensions/shape.h>
#endif
/* Load sys/types.h if not already loaded.
In some systems loading it twice is suicidal. */
#ifndef makedev
@ -826,10 +830,21 @@ struct x_client_list_window
Display *dpy;
int x, y;
int width, height;
bool visible_p;
bool mapped_p;
long previous_event_mask;
unsigned long wm_state;
struct x_client_list_window *next;
#ifdef HAVE_XSHAPE
int border_width;
XRectangle *input_rects;
int n_input_rects;
XRectangle *bounding_rects;
int n_bounding_rects;
#endif
};
static struct x_client_list_window *x_dnd_toplevels = NULL;
@ -849,7 +864,18 @@ x_dnd_free_toplevels (void)
x_catch_errors (last->dpy);
XSelectInput (last->dpy, last->window,
last->previous_event_mask);
#ifdef HAVE_XSHAPE
XShapeSelectInput (last->dpy, last->window, None);
#endif
x_uncatch_errors ();
#ifdef HAVE_XSHAPE
if (last->n_input_rects != -1)
xfree (last->input_rects);
if (last->n_bounding_rects != -1)
xfree (last->bounding_rects);
#endif
xfree (last);
}
@ -862,11 +888,15 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
Atom type;
Window *toplevels, child;
int format, rc, dest_x, dest_y;
unsigned long nitems, bytes_after;
unsigned char *data = NULL;
unsigned long nitems, wmstate_items, bytes_after, *wmstate;
unsigned char *data = NULL, *wmstate_data = NULL;
unsigned long i;
XWindowAttributes attrs;
struct x_client_list_window *tem;
#ifdef HAVE_XSHAPE
int count, ordering;
XRectangle *rects;
#endif
rc = XGetWindowProperty (dpyinfo->display, dpyinfo->root_window,
dpyinfo->Xatom_net_client_list_stacking,
@ -899,10 +929,22 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
-attrs.border_width, &dest_x,
&dest_y, &child)
&& !x_had_errors_p (dpyinfo->display));
if (rc)
rc = ((XGetWindowProperty (dpyinfo->display,
toplevels[i],
dpyinfo->Xatom_wm_state,
0, 2, False, AnyPropertyType,
&type, &format, &wmstate_items,
&bytes_after, &wmstate_data)
== Success)
&& !x_had_errors_p (dpyinfo->display)
&& wmstate_data && wmstate_items == 2 && format == 32);
x_uncatch_errors_after_check ();
if (rc)
{
wmstate = (unsigned long *) wmstate_data;
tem = xmalloc (sizeof *tem);
tem->window = toplevels[i];
tem->dpy = dpyinfo->display;
@ -910,13 +952,93 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
tem->y = dest_y;
tem->width = attrs.width + attrs.border_width;
tem->height = attrs.height + attrs.border_width;
tem->visible_p = (attrs.map_state == IsViewable);
tem->mapped_p = (attrs.map_state != IsUnmapped);
tem->next = x_dnd_toplevels;
tem->previous_event_mask = attrs.your_event_mask;
tem->wm_state = wmstate[0];
XFree (wmstate_data);
#ifdef HAVE_XSHAPE
tem->border_width = attrs.border_width;
tem->n_bounding_rects = -1;
tem->n_input_rects = -1;
if (dpyinfo->xshape_supported_p)
{
x_catch_errors (dpyinfo->display);
XShapeSelectInput (dpyinfo->display,
toplevels[i],
ShapeNotifyMask);
x_uncatch_errors ();
x_catch_errors (dpyinfo->display);
rects = XShapeGetRectangles (dpyinfo->display,
toplevels[i],
ShapeBounding,
&count, &ordering);
rc = x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
/* Does XShapeGetRectangles allocate anything upon an
error? */
if (!rc)
{
tem->n_bounding_rects = count;
tem->bounding_rects
= xmalloc (sizeof *tem->bounding_rects * count);
memcpy (tem->bounding_rects, rects,
sizeof *tem->bounding_rects * count);
XFree (rects);
}
#ifdef ShapeInput
if (dpyinfo->xshape_major > 1
|| (dpyinfo->xshape_major == 1
&& dpyinfo->xshape_minor >= 1))
{
x_catch_errors (dpyinfo->display);
rects = XShapeGetRectangles (dpyinfo->display,
toplevels[i], ShapeInput,
&count, &ordering);
rc = x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
/* Does XShapeGetRectangles allocate anything upon
an error? */
if (!rc)
{
tem->n_input_rects = count;
tem->input_rects
= xmalloc (sizeof *tem->input_rects * count);
memcpy (tem->input_rects, rects,
sizeof *tem->input_rects * count);
XFree (rects);
}
}
#endif
}
/* Handle the common case where the input shape equals the
bounding shape. */
if (tem->n_input_rects != -1
&& tem->n_bounding_rects == tem->n_input_rects
&& !memcmp (tem->bounding_rects, tem->input_rects,
tem->n_input_rects * sizeof *tem->input_rects))
{
xfree (tem->input_rects);
tem->n_input_rects = -1;
}
#endif
x_catch_errors (dpyinfo->display);
XSelectInput (dpyinfo->display, toplevels[i],
attrs.your_event_mask | StructureNotifyMask);
(attrs.your_event_mask
| StructureNotifyMask
| PropertyChangeMask));
x_uncatch_errors ();
x_dnd_toplevels = tem;
@ -931,6 +1053,28 @@ x_dnd_compute_toplevels (struct x_display_info *dpyinfo)
static int x_dnd_get_window_proto (struct x_display_info *, Window);
static Window x_dnd_get_window_proxy (struct x_display_info *, Window);
#ifdef HAVE_XSHAPE
static bool
x_dnd_get_target_window_2 (XRectangle *rects, int nrects,
int x, int y)
{
int i;
XRectangle *tem;
for (i = 0; i < nrects; ++i)
{
tem = &rects[i];
if (x >= tem->x && y >= tem->y
&& x < tem->x + tem->width
&& y < tem->y + tem->height)
return true;
}
return false;
}
#endif
static Window
x_dnd_get_target_window_1 (struct x_display_info *dpyinfo,
int root_x, int root_y)
@ -942,13 +1086,33 @@ x_dnd_get_target_window_1 (struct x_display_info *dpyinfo,
for (tem = x_dnd_toplevels; tem; tem = tem->next)
{
if (!tem->visible_p)
if (!tem->mapped_p || tem->wm_state != NormalState)
continue;
if (root_x >= tem->x && root_y >= tem->y
&& root_x < tem->x + tem->width
&& root_y < tem->y + tem->height)
return tem->window;
{
#ifdef HAVE_XSHAPE
if (tem->n_bounding_rects == -1)
#endif
return tem->window;
#ifdef HAVE_XSHAPE
if (x_dnd_get_target_window_2 (tem->bounding_rects,
tem->n_bounding_rects,
tem->border_width + root_x - tem->x,
tem->border_width + root_y - tem->y))
{
if (tem->n_input_rects == -1
|| x_dnd_get_target_window_2 (tem->input_rects,
tem->n_input_rects,
tem->border_width + root_x - tem->x,
tem->border_width + root_y - tem->y))
return tem->window;
}
#endif
}
}
return None;
@ -7230,7 +7394,7 @@ x_dnd_begin_drag_and_drop (struct frame *f, Time time, Atom xaction,
x_dnd_in_progress = true;
x_dnd_frame = f;
x_dnd_last_seen_window = FRAME_X_WINDOW (f);
x_dnd_last_seen_window = FRAME_OUTER_WINDOW (f);
x_dnd_last_protocol_version = -1;
x_dnd_mouse_rect_target = None;
x_dnd_action = None;
@ -10983,10 +11147,10 @@ x_dnd_update_state (struct x_display_info *dpyinfo)
{
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1
&& x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
&& x_dnd_last_seen_window != FRAME_OUTER_WINDOW (x_dnd_frame))
x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
if (x_dnd_last_seen_window == FRAME_X_WINDOW (x_dnd_frame)
if (target != FRAME_OUTER_WINDOW (x_dnd_frame)
&& x_dnd_return_frame == 1)
x_dnd_return_frame = 2;
@ -10999,6 +11163,8 @@ x_dnd_update_state (struct x_display_info *dpyinfo)
x_dnd_return_frame_object
= x_any_window_to_frame (dpyinfo, target);
x_dnd_return_frame = 3;
x_dnd_waiting_for_finish = false;
target = None;
}
x_dnd_action = None;
@ -11018,7 +11184,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo)
x_dnd_wanted_action);
}
/* The pointer moved out of the screen. */
else if (x_dnd_last_protocol_version)
else if (x_dnd_last_protocol_version != -1)
{
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1)
@ -11028,6 +11194,7 @@ x_dnd_update_state (struct x_display_info *dpyinfo)
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_last_seen_window = None;
x_dnd_in_progress = false;
x_dnd_waiting_for_finish = false;
x_dnd_frame = NULL;
}
}
@ -11464,6 +11631,65 @@ handle_one_xevent (struct x_display_info *dpyinfo,
break;
case PropertyNotify:
if (x_dnd_in_progress && x_dnd_use_toplevels
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame)
&& event->xproperty.atom == dpyinfo->Xatom_wm_state)
{
struct x_client_list_window *tem, *last;
for (last = NULL, tem = x_dnd_toplevels; tem;
last = tem, tem = tem->next)
{
if (tem->window == event->xproperty.window)
{
Atom actual_type;
int actual_format, rc;
unsigned long nitems, bytesafter;
unsigned char *data = NULL;
if (event->xproperty.state == PropertyDelete)
{
if (!last)
x_dnd_toplevels = tem->next;
else
last->next = tem->next;
#ifdef HAVE_XSHAPE
if (tem->n_input_rects != -1)
xfree (tem->input_rects);
if (tem->n_bounding_rects != -1)
xfree (tem->bounding_rects);
#endif
xfree (tem);
}
else
{
x_catch_errors (dpyinfo->display);
rc = XGetWindowProperty (dpyinfo->display,
event->xproperty.window,
dpyinfo->Xatom_wm_state,
0, 2, False, AnyPropertyType,
&actual_type, &actual_format,
&nitems, &bytesafter, &data);
if (!x_had_errors_p (dpyinfo->display) && rc == Success && data
&& nitems == 2 && actual_format == 32)
{
tem->wm_state = ((unsigned long *) data)[0];
XFree (data);
}
else
tem->wm_state = WithdrawnState;
x_uncatch_errors_after_check ();
}
x_dnd_update_state (dpyinfo);
break;
}
}
}
f = x_top_window_to_frame (dpyinfo, event->xproperty.window);
if (f && event->xproperty.atom == dpyinfo->Xatom_net_wm_state)
{
@ -11697,14 +11923,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
break;
case UnmapNotify:
if (x_dnd_in_progress && x_dnd_use_toplevels)
if (x_dnd_in_progress && x_dnd_use_toplevels
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
{
for (struct x_client_list_window *tem = x_dnd_toplevels; tem;
tem = tem->next)
{
if (tem->window == event->xmap.window)
if (tem->window == event->xunmap.window)
{
tem->visible_p = false;
tem->mapped_p = false;
break;
}
}
@ -11758,14 +11985,15 @@ handle_one_xevent (struct x_display_info *dpyinfo,
if (x_dnd_in_progress)
x_dnd_update_state (dpyinfo);
if (x_dnd_in_progress && x_dnd_use_toplevels)
if (x_dnd_in_progress && x_dnd_use_toplevels
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
{
for (struct x_client_list_window *tem = x_dnd_toplevels; tem;
tem = tem->next)
{
if (tem->window == event->xmap.window)
{
tem->visible_p = true;
tem->mapped_p = true;
break;
}
}
@ -12389,10 +12617,10 @@ handle_one_xevent (struct x_display_info *dpyinfo,
{
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1
&& x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
&& x_dnd_last_seen_window != FRAME_OUTER_WINDOW (x_dnd_frame))
x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
if (x_dnd_last_seen_window == FRAME_X_WINDOW (x_dnd_frame)
if (target != FRAME_OUTER_WINDOW (x_dnd_frame)
&& x_dnd_return_frame == 1)
x_dnd_return_frame = 2;
@ -12405,6 +12633,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_dnd_return_frame_object
= x_any_window_to_frame (dpyinfo, target);
x_dnd_return_frame = 3;
x_dnd_waiting_for_finish = false;
target = None;
}
x_dnd_action = None;
@ -12547,7 +12777,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
configureEvent = next_event;
}
if (x_dnd_in_progress && x_dnd_use_toplevels)
if (x_dnd_in_progress && x_dnd_use_toplevels
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
{
int rc, dest_x, dest_y;
Window child;
@ -12589,6 +12820,12 @@ handle_one_xevent (struct x_display_info *dpyinfo,
else
last->next = tem->next;
#ifdef HAVE_XSHAPE
if (tem->n_input_rects != -1)
xfree (tem->input_rects);
if (tem->n_bounding_rects != -1)
xfree (tem->bounding_rects);
#endif
xfree (tem);
}
@ -13699,20 +13936,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
{
if (x_dnd_last_seen_window != None
&& x_dnd_last_protocol_version != -1
&& x_dnd_last_seen_window != FRAME_X_WINDOW (x_dnd_frame))
&& x_dnd_last_seen_window != FRAME_OUTER_WINDOW (x_dnd_frame))
x_dnd_send_leave (x_dnd_frame, x_dnd_last_seen_window);
if (x_dnd_last_seen_window == FRAME_X_WINDOW (x_dnd_frame)
if (target != FRAME_OUTER_WINDOW (x_dnd_frame)
&& x_dnd_return_frame == 1)
x_dnd_return_frame = 2;
if (x_dnd_return_frame == 2
&& x_any_window_to_frame (dpyinfo, target))
{
x_dnd_end_window = x_dnd_last_seen_window;
x_dnd_last_seen_window = None;
x_dnd_in_progress = false;
x_dnd_return_frame_object
= x_any_window_to_frame (dpyinfo, target);
x_dnd_return_frame = 3;
x_dnd_waiting_for_finish = false;
target = None;
}
x_dnd_action = None;
@ -15043,7 +15284,8 @@ handle_one_xevent (struct x_display_info *dpyinfo,
default:
#ifdef HAVE_XKB
if (event->type == dpyinfo->xkb_event_type)
if (dpyinfo->supports_xkb
&& event->type == dpyinfo->xkb_event_type)
{
XkbEvent *xkbevent = (XkbEvent *) event;
@ -15088,6 +15330,109 @@ handle_one_xevent (struct x_display_info *dpyinfo,
x_find_modifier_meanings (dpyinfo);
}
}
#endif
#ifdef HAVE_XSHAPE
if (dpyinfo->xshape_supported_p
&& event->type == dpyinfo->xshape_event_base + ShapeNotify
&& x_dnd_in_progress && x_dnd_use_toplevels
&& dpyinfo == FRAME_DISPLAY_INFO (x_dnd_frame))
{
XEvent xevent;
XShapeEvent *xse = (XShapeEvent *) event;
XRectangle *rects;
int rc, ordering;
while (XPending (dpyinfo->display))
{
XNextEvent (dpyinfo->display, &xevent);
if (xevent.type == dpyinfo->xshape_event_base + ShapeNotify
&& ((XShapeEvent *) &xevent)->window == xse->window)
xse = (XShapeEvent *) &xevent;
else
{
XPutBackEvent (dpyinfo->display, &xevent);
break;
}
}
for (struct x_client_list_window *tem = x_dnd_toplevels; tem;
tem = tem->next)
{
if (tem->window == xse->window)
{
if (tem->n_input_rects != -1)
xfree (tem->input_rects);
if (tem->n_bounding_rects != -1)
xfree (tem->bounding_rects);
tem->n_input_rects = -1;
tem->n_bounding_rects = -1;
x_catch_errors (dpyinfo->display);
rects = XShapeGetRectangles (dpyinfo->display,
xse->window,
ShapeBounding,
&count, &ordering);
rc = x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
/* Does XShapeGetRectangles allocate anything upon an
error? */
if (!rc)
{
tem->n_bounding_rects = count;
tem->bounding_rects
= xmalloc (sizeof *tem->bounding_rects * count);
memcpy (tem->bounding_rects, rects,
sizeof *tem->bounding_rects * count);
XFree (rects);
}
#ifdef ShapeInput
if (dpyinfo->xshape_major > 1
|| (dpyinfo->xshape_major == 1
&& dpyinfo->xshape_minor >= 1))
{
x_catch_errors (dpyinfo->display);
rects = XShapeGetRectangles (dpyinfo->display,
xse->window, ShapeInput,
&count, &ordering);
rc = x_had_errors_p (dpyinfo->display);
x_uncatch_errors_after_check ();
/* Does XShapeGetRectangles allocate anything upon
an error? */
if (!rc)
{
tem->n_input_rects = count;
tem->input_rects
= xmalloc (sizeof *tem->input_rects * count);
memcpy (tem->input_rects, rects,
sizeof *tem->input_rects * count);
XFree (rects);
}
}
#endif
/* Handle the common case where the input shape equals the
bounding shape. */
if (tem->n_input_rects != -1
&& tem->n_bounding_rects == tem->n_input_rects
&& !memcmp (tem->bounding_rects, tem->input_rects,
tem->n_input_rects * sizeof *tem->input_rects))
{
xfree (tem->input_rects);
tem->n_input_rects = -1;
}
break;
}
}
}
#endif
OTHER:
#ifdef USE_X_TOOLKIT
@ -19011,6 +19356,19 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
&dpyinfo->composite_minor);
#endif
#ifdef HAVE_XSHAPE
dpyinfo->xshape_supported_p
= XShapeQueryExtension (dpyinfo->display,
&dpyinfo->xshape_event_base,
&dpyinfo->xshape_error_base);
if (dpyinfo->xshape_supported_p)
dpyinfo->xshape_supported_p
= XShapeQueryVersion (dpyinfo->display,
&dpyinfo->xshape_major,
&dpyinfo->xshape_minor);
#endif
/* Put the rdb where we can find it in a way that works on
all versions. */
dpyinfo->rdb = xrdb;
@ -19391,6 +19749,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
ATOM_REFS_INIT ("WM_SAVE_YOURSELF", Xatom_wm_save_yourself)
ATOM_REFS_INIT ("WM_DELETE_WINDOW", Xatom_wm_delete_window)
ATOM_REFS_INIT ("WM_CHANGE_STATE", Xatom_wm_change_state)
ATOM_REFS_INIT ("WM_STATE", Xatom_wm_state)
ATOM_REFS_INIT ("WM_CONFIGURE_DENIED", Xatom_wm_configure_denied)
ATOM_REFS_INIT ("WM_MOVED", Xatom_wm_window_moved)
ATOM_REFS_INIT ("WM_CLIENT_LEADER", Xatom_wm_client_leader)

View file

@ -396,6 +396,7 @@ struct x_display_info
/* Atom for indicating window state to the window manager. */
Atom Xatom_wm_change_state;
Atom Xatom_wm_state;
/* Other WM communication */
Atom Xatom_wm_configure_denied; /* When our config request is denied */
@ -644,6 +645,14 @@ struct x_display_info
int composite_major;
int composite_minor;
#endif
#ifdef HAVE_XSHAPE
bool xshape_supported_p;
int xshape_major;
int xshape_minor;
int xshape_event_base;
int xshape_error_base;
#endif
};
#ifdef HAVE_X_I18N