Improve xdg_popup error checking and remove redundant code

* compositor.h: Remove some unused prototypes.
* seat.c (DispatchButton): Remove redundant popup code as
owner-events is now handled correctly when a surface is not
found.
* xdg_popup.c (Detach, Dismiss, RecordGrabPending, CanDestroyPopup)
(Destroy, MaybeDismissPopup): Keep track of which grabbed popup
is topmost, and don't allow deleting non-topmost ones.
(HandleOneGenericEvent, XLHandleButtonForXdgPopups): Delete
functions.
This commit is contained in:
hujianwei 2022-10-23 05:11:11 +00:00
parent a220f29723
commit a0d2d34f0f
3 changed files with 78 additions and 92 deletions

View file

@ -1354,7 +1354,6 @@ extern void XLGetXdgPopup (struct wl_client *, struct wl_resource *,
uint32_t, struct wl_resource *, uint32_t, struct wl_resource *,
struct wl_resource *); struct wl_resource *);
extern Bool XLHandleXEventForXdgPopups (XEvent *); extern Bool XLHandleXEventForXdgPopups (XEvent *);
extern Bool XLHandleButtonForXdgPopups (Seat *, Surface *);
extern void XLInitPopups (void); extern void XLInitPopups (void);
/* Defined in xerror.c. */ /* Defined in xerror.c. */

7
seat.c
View file

@ -4268,13 +4268,6 @@ DispatchButton (Subcompositor *subcompositor, XIDeviceEvent *xev)
return; return;
} }
/* Allow popups to be dismissed when the mouse button is released on
some other client's window. */
if (XLHandleButtonForXdgPopups (seat, dispatch))
/* Ignore the button event that resulted in popup(s) being
dismissed. */
return;
/* If dispatching during an active grab, and the event is for the /* If dispatching during an active grab, and the event is for the
wrong client, translate the coordinates to the grab window. */ wrong client, translate the coordinates to the grab window. */
if (!CanDeliverEvents (seat, dispatch)) if (!CanDeliverEvents (seat, dispatch))

View file

@ -36,6 +36,7 @@ enum
StatePendingGrab = (1 << 2), StatePendingGrab = (1 << 2),
StatePendingPosition = (1 << 3), StatePendingPosition = (1 << 3),
StateAckPosition = (1 << 4), StateAckPosition = (1 << 4),
StateIsTopmost = (1 << 5),
}; };
struct _PropMotifWmHints struct _PropMotifWmHints
@ -223,6 +224,40 @@ RevertGrabTo (XdgPopup *popup, Role *parent_role)
popup->current_grab_serial); popup->current_grab_serial);
} }
static void
RevertTopmostTo (Role *parent_role)
{
XdgPopup *parent;
XdgRoleImplementation *impl;
impl = XLImplementationOfXdgRole (parent_role);
if (!impl || XLTypeOfXdgRole (parent_role) != TypePopup)
return;
parent = PopupFromRoleImpl (impl);
/* Now, make the parent the topmost popup again. This is done
outside RevertGrabTo because it is valid to destroy an unmapped
topmost popup. */
parent->state |= StateIsTopmost;
}
static void
ClearTopmostOf (Role *parent_role)
{
XdgPopup *parent;
XdgRoleImplementation *impl;
impl = XLImplementationOfXdgRole (parent_role);
if (!impl || XLTypeOfXdgRole (parent_role) != TypePopup)
return;
parent = PopupFromRoleImpl (impl);
parent->state &= ~StateIsTopmost;
}
static void static void
Detach (Role *role, XdgRoleImplementation *impl) Detach (Role *role, XdgRoleImplementation *impl)
{ {
@ -231,11 +266,18 @@ Detach (Role *role, XdgRoleImplementation *impl)
popup = PopupFromRoleImpl (impl); popup = PopupFromRoleImpl (impl);
/* Detaching the popup means that it will be destroyed soon. Revert if (popup->parent)
the grab to the parent and unmap it. */ {
if (popup->state & StateIsGrabbed
|| popup->state & StatePendingGrab)
RevertTopmostTo (popup->parent);
if (popup->state & StateIsGrabbed) /* Detaching the popup means that it will be destroyed soon.
RevertGrabTo (popup, popup->parent); Revert the grab to the parent and unmap it. */
if (popup->state & StateIsGrabbed)
RevertGrabTo (popup, popup->parent);
}
if (popup->state & StateIsMapped) if (popup->state & StateIsMapped)
Unmap (popup); Unmap (popup);
@ -537,7 +579,8 @@ Dismiss (XdgPopup *popup, Bool do_parents)
XdgRoleImplementation *impl; XdgRoleImplementation *impl;
XdgPopup *parent; XdgPopup *parent;
if (popup->state & StateIsGrabbed) if (popup->state & StateIsGrabbed
&& popup->parent)
RevertGrabTo (popup, popup->parent); RevertGrabTo (popup, popup->parent);
if (popup->state & StateIsMapped) if (popup->state & StateIsMapped)
@ -591,6 +634,14 @@ RecordGrabPending (XdgPopup *popup, Seat *seat, uint32_t serial)
popup->pending_grab_seat = seat; popup->pending_grab_seat = seat;
popup->pending_grab_serial = serial; popup->pending_grab_serial = serial;
/* This popup is now the topmost popup. */
popup->state |= StateIsTopmost;
/* If the parent is also a popup, then it is no longer the
topmost popup. */
if (popup->parent)
ClearTopmostOf (popup->parent);
popup->state |= StatePendingGrab; popup->state |= StatePendingGrab;
} }
} }
@ -635,6 +686,23 @@ Reposition (struct wl_client *client, struct wl_resource *resource,
InternalReposition (popup); InternalReposition (popup);
} }
static Bool
CanDestroyPopup (XdgPopup *popup)
{
if (popup->state & StateIsTopmost)
/* This is the topmost popup and can be destroyed. */
return True;
if (!(popup->state & StateIsGrabbed)
&& !(popup->state & StatePendingGrab))
/* This popup is not grabbed. */
return True;
/* Otherwise, this popup cannot be destroyed; it is grabbed, but not
the topmost popup. */
return False;
}
static void static void
Destroy (struct wl_client *client, struct wl_resource *resource) Destroy (struct wl_client *client, struct wl_resource *resource)
{ {
@ -642,6 +710,11 @@ Destroy (struct wl_client *client, struct wl_resource *resource)
popup = wl_resource_get_user_data (resource); popup = wl_resource_get_user_data (resource);
if (!CanDestroyPopup (popup))
wl_resource_post_error (resource,
XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP,
"trying to destroy non-topmost popup");
if (popup->role) if (popup->role)
XLXdgRoleDetachImplementation (popup->role, XLXdgRoleDetachImplementation (popup->role,
&popup->impl); &popup->impl);
@ -649,51 +722,6 @@ Destroy (struct wl_client *client, struct wl_resource *resource)
wl_resource_destroy (resource); wl_resource_destroy (resource);
} }
static Bool
MaybeDismissPopup (XIDeviceEvent *xev)
{
XdgRoleImplementation *impl;
XdgPopup *popup;
impl = XLLookUpXdgPopup (xev->event);
if (!impl)
return False;
popup = PopupFromRoleImpl (impl);
if (popup->state & StateIsGrabbed)
{
/* If the popup is grabbed and the click is outside the input
region of the popup, this means a click has happened outside
the client, and the popup should be dismissed. */
if (!XLXdgRoleInputRegionContains (popup->role,
lrint (xev->event_x),
lrint (xev->event_y)))
{
Dismiss (popup, True);
return True;
}
}
return False;
}
static Bool
HandleOneGenericEvent (XIEvent *event)
{
switch (event->evtype)
{
case XI_ButtonRelease:
case XI_ButtonPress:
return MaybeDismissPopup ((XIDeviceEvent *) event);
default:
return False;
}
}
static Bool static Bool
HandleOneConfigureNotify (XEvent *event) HandleOneConfigureNotify (XEvent *event)
{ {
@ -832,10 +860,6 @@ XLGetXdgPopup (struct wl_client *client, struct wl_resource *resource,
Bool Bool
XLHandleXEventForXdgPopups (XEvent *event) XLHandleXEventForXdgPopups (XEvent *event)
{ {
if (event->type == GenericEvent
&& event->xgeneric.extension == xi2_opcode)
return HandleOneGenericEvent (event->xcookie.data);
if (event->type == ConfigureNotify) if (event->type == ConfigureNotify)
return HandleOneConfigureNotify (event); return HandleOneConfigureNotify (event);
@ -848,33 +872,3 @@ XLInitPopups (void)
live_popups.next = &live_popups; live_popups.next = &live_popups;
live_popups.last = &live_popups; live_popups.last = &live_popups;
} }
Bool
XLHandleButtonForXdgPopups (Seat *seat, Surface *dispatch)
{
XdgPopup *popup;
Bool rc;
/* This means a button press happened on dispatch, on seat. Loop
through all grabbed popups. Dismiss each one whose client is not
the same as dispatch. */
popup = live_popups.next;
rc = False;
while (popup != &live_popups)
{
if (popup->state & StateIsGrabbed
&& seat == popup->grab_holder
&& (wl_resource_get_client (popup->resource)
!= wl_resource_get_client (dispatch->resource)))
{
Dismiss (popup, True);
rc = True;
}
popup = popup->next;
}
return rc;
}