forked from 12to11/12to11

This is in preparation for a migration to the Present extension on systems that do not support frame synchronization. * 12to11.man: Remove option that is no longer useful. * Imakefile (SRCS, OBJS): Add `sync_source.c.'. * compositor.h (enum _RenderMode): New enum. (struct _RenderFuncs): Add some new functions. (enum _FrameMode): Remove ModeNotifyDisablePresent. * frame_clock.c (struct _FrameClockCallback): Add draw time to frame callback. (struct _FrameClock): Rearrange for alignment and remove many unused members. (BumpFrame): New function. (FreezeForValue): Remove code that is no longer useful, as clock->frozen no longer exists. (StartFrame, EndFrame): Remove clock "freezing" logic. Only `need_configure' remains. (RunFrameCallbacks, NoteFakeFrame): Add frame drawn time. (XLFrameClockAfterFrame): Adjust for new type of callback. (XLFrameClockFreeze): Remove function. (XLFrameClockFrameInProgress): Remove test for `frozen_until_end_frame'. (XLFrameClockHandleFrameEvent): Improve code in accordance with above changes. (XLFrameClockUnfreeze, XLFrameClockNeedConfigure): Remove functions. (XLFrameClockIsFrozen): Remove function. (XLFrameClockSetFreezeCallback): Accept new callback `fast_forward_callback'. (XLFrameClockGetFrameTime): Remove unused function. * icon_surface.c (struct _IconSurface, ReleaseBacking) (ReleaseBuffer, RunFrameCallbacks, AfterFrame, Commit) (SubsurfaceUpdate, XLGetIconSurface) (XLHandleOneXEventForIconSurfaces): Switch the icon surface to the sync helper abstraction, and allow asynch buffer release from inside. * picture_renderer.c (struct _PictureTarget): New field `next_msc' and `render_mode'. (SwapBackBuffers): Respect the render mode. (InitSynchronizedPresentation): Delete function. (InitAdditionalModifiers): Remove incorrect comment. (InitRenderFuncs): Stop initializing obsolete option. (SetRenderMode): New function. (PresentToWindow): Respect the render mode. (NotifyMsc): New function. (picture_render_funcs): Add notify_msc. (HandlePresentCompleteNotify): Call completion callback with the fraame counter. * renderer.c (RenderSetRenderMode): (RenderNotifyMsc): New functions. * subcompositor.c (struct _Subcompositor) (SubcompositorSetNoteFrameCallback): Add msc and ust to note frame callback. (PresentCompletedCallback, RenderCompletedCallback): Call with msc and ust. (BeginFrame): When presentation is being synchronized and there is no existing presentation callback, ask for an event to be sent on the next frame. (EndFrame): Do not clear the presentation callbacks, as the update might not touch anything. * test.c (NoteFrame): Update prototype. * xdg_popup.c (InternalReposition): Stop "freezing" the frame clock. * xdg_surface.c (struct _XdgRole): Replace the frame clock with the sync helper abstraction. (RunFrameCallbacks): Run with the frame time. (RunFrameCallbacksConditionally): Save the pending frame time. (UpdateFrameRefreshPrediction): Delete function. (XLHandleXEventForXdgSurfaces): Give events to the sync helper instead. (Unfreeze, IsRoleMapped, CheckFrame): Remove functions. (Commit, SubsurfaceUpdate): Update using the sync helper instead, only if not pending ack commit. (MaybeRunLateFrame, AfterFrame): Delete functions. (NoteConfigure, NoteBounds): Update for the sync helper. (WriteRedirectProperty): Always require redirection for now. Disabling redirection requires sorting out some Present problems on the X server side. (WasFrameQueued, NoteFrame): Delete functions. (HandleFreeze): Delete function. (HandleResize, CheckFastForward, HandleFrameCallback): New functions. (XLGetXdgSurface): Use the sync helper for most things. (XLXdgRoleSendConfigure, XLXdgRoleReconstrain) (XLXdgRoleGetFrameClock): Delete function. (XLXdgRoleNoteRejectedConfigure): Clean up for using the sync clock instead.
866 lines
20 KiB
C
866 lines
20 KiB
C
/* Wayland compositor running on top of an X server.
|
||
|
||
Copyright (C) 2022 to various contributors.
|
||
|
||
This file is part of 12to11.
|
||
|
||
12to11 is free software: you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by the
|
||
Free Software Foundation, either version 3 of the License, or (at your
|
||
option) any later version.
|
||
|
||
12to11 is distributed in the hope that it will be useful, but WITHOUT
|
||
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||
for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with 12to11. If not, see <https://www.gnu.org/licenses/>. */
|
||
|
||
#include <string.h>
|
||
|
||
#include "compositor.h"
|
||
#include "xdg-shell.h"
|
||
|
||
#include <X11/extensions/XInput2.h>
|
||
|
||
#define PopupFromRoleImpl(impl) ((XdgPopup *) (impl))
|
||
|
||
typedef struct _XdgPopup XdgPopup;
|
||
typedef struct _PropMotifWmHints PropMotifWmHints;
|
||
|
||
enum
|
||
{
|
||
StateIsMapped = 1,
|
||
StateIsGrabbed = (1 << 1),
|
||
StatePendingGrab = (1 << 2),
|
||
StatePendingPosition = (1 << 3),
|
||
StateAckPosition = (1 << 4),
|
||
StateIsTopmost = (1 << 5),
|
||
};
|
||
|
||
struct _PropMotifWmHints
|
||
{
|
||
unsigned long flags;
|
||
unsigned long functions;
|
||
unsigned long decorations;
|
||
long input_mode;
|
||
unsigned long status;
|
||
};
|
||
|
||
struct _XdgPopup
|
||
{
|
||
/* The parent role implementation. */
|
||
XdgRoleImplementation impl;
|
||
|
||
/* The role associated with this popup. */
|
||
Role *role;
|
||
|
||
/* The parent xdg_surface object. */
|
||
Role *parent;
|
||
|
||
/* The wl_resource associated with this popup. */
|
||
struct wl_resource *resource;
|
||
|
||
/* The number of references to this popup. */
|
||
int refcount;
|
||
|
||
/* Some state associated with this popup. */
|
||
int state;
|
||
|
||
/* Whether or not we are waiting for a reply to a configure
|
||
event. */
|
||
Bool conf_reply;
|
||
|
||
/* The serial of the last configure event sent, and the last
|
||
position event sent. */
|
||
uint32_t conf_serial, position_serial;
|
||
|
||
/* The associated positioner. */
|
||
Positioner positioner;
|
||
|
||
/* Any pending seat on which a grab should be asserted. */
|
||
Seat *pending_grab_seat;
|
||
|
||
/* The serial to use for that grab. */
|
||
uint32_t pending_grab_serial;
|
||
|
||
/* The seat that currently holds the grab. */
|
||
Seat *grab_holder;
|
||
|
||
/* The current grab serial. */
|
||
uint32_t current_grab_serial;
|
||
|
||
/* Its destroy callback key. */
|
||
void *seat_callback_key, *pending_callback_key;
|
||
|
||
/* The current position. */
|
||
int x, y;
|
||
|
||
/* The pending coordinates. */
|
||
int pending_x, pending_y;
|
||
|
||
/* The current width and height. */
|
||
int width, height;
|
||
|
||
/* Reconstrain callback associated with the parent. */
|
||
void *reconstrain_callback_key;
|
||
};
|
||
|
||
|
||
|
||
/* Forward declarations. */
|
||
|
||
static void DoGrab (XdgPopup *, Seat *, uint32_t);
|
||
static void Dismiss (XdgPopup *, Bool);
|
||
|
||
static void
|
||
DestroyBacking (XdgPopup *popup)
|
||
{
|
||
void *key;
|
||
|
||
if (--popup->refcount)
|
||
return;
|
||
|
||
key = popup->reconstrain_callback_key;
|
||
|
||
if (key)
|
||
XLXdgRoleCancelReconstrainCallback (key);
|
||
|
||
/* Release the parent if it exists. */
|
||
if (popup->parent)
|
||
XLReleaseXdgRole (popup->parent);
|
||
|
||
/* Release seat callbacks if they exist. */
|
||
if (popup->seat_callback_key)
|
||
XLSeatCancelDestroyListener (popup->seat_callback_key);
|
||
|
||
if (popup->pending_callback_key)
|
||
XLSeatCancelDestroyListener (popup->pending_callback_key);
|
||
|
||
/* Free the popup. */
|
||
XLFree (popup);
|
||
}
|
||
|
||
static void
|
||
HandleResourceDestroy (struct wl_resource *resource)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = wl_resource_get_user_data (resource);
|
||
popup->resource = NULL;
|
||
|
||
DestroyBacking (popup);
|
||
}
|
||
|
||
static void
|
||
Attach (Role *role, XdgRoleImplementation *impl)
|
||
{
|
||
XdgPopup *popup;
|
||
XSetWindowAttributes attrs;
|
||
PropMotifWmHints hints;
|
||
Window window;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
popup->refcount++;
|
||
popup->role = role;
|
||
|
||
window = XLWindowFromXdgRole (role);
|
||
|
||
/* Make the popup override-redirect. */
|
||
attrs.override_redirect = True;
|
||
XChangeWindowAttributes (compositor.display, window,
|
||
CWOverrideRedirect, &attrs);
|
||
|
||
/* It turns out that Mutter still draws drop shadows for popups, so
|
||
turn them off. */
|
||
hints.flags = 0;
|
||
hints.decorations = 0;
|
||
|
||
/* Add _NET_WM_SYNC_REQUEST to the list of supported protocols. */
|
||
XSetWMProtocols (compositor.display, XLWindowFromXdgRole (role),
|
||
&_NET_WM_SYNC_REQUEST, 1);
|
||
|
||
XChangeProperty (compositor.display, window,
|
||
_MOTIF_WM_HINTS, _MOTIF_WM_HINTS, 32,
|
||
PropModeReplace,
|
||
(unsigned char *) &hints, 5);
|
||
}
|
||
|
||
static void
|
||
Unmap (XdgPopup *popup)
|
||
{
|
||
popup->state &= ~StateIsMapped;
|
||
|
||
XUnmapWindow (compositor.display,
|
||
XLWindowFromXdgRole (popup->role));
|
||
}
|
||
|
||
static void
|
||
RevertGrabTo (XdgPopup *popup, Role *parent_role)
|
||
{
|
||
XdgPopup *parent;
|
||
XdgRoleImplementation *impl;
|
||
|
||
impl = XLImplementationOfXdgRole (parent_role);
|
||
|
||
if (!impl || XLTypeOfXdgRole (parent_role) != TypePopup)
|
||
return;
|
||
|
||
parent = PopupFromRoleImpl (impl);
|
||
|
||
DoGrab (parent, popup->grab_holder,
|
||
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
|
||
Detach (Role *role, XdgRoleImplementation *impl)
|
||
{
|
||
XdgPopup *popup;
|
||
XSetWindowAttributes attrs;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
|
||
if (popup->parent)
|
||
{
|
||
if (popup->state & StateIsGrabbed
|
||
|| popup->state & StatePendingGrab)
|
||
RevertTopmostTo (popup->parent);
|
||
|
||
/* Detaching the popup means that it will be destroyed soon.
|
||
Revert the grab to the parent and unmap it. */
|
||
|
||
if (popup->state & StateIsGrabbed)
|
||
RevertGrabTo (popup, popup->parent);
|
||
}
|
||
|
||
if (popup->state & StateIsMapped)
|
||
Unmap (popup);
|
||
|
||
popup->role = NULL;
|
||
|
||
DestroyBacking (popup);
|
||
|
||
/* Make the window non-override-redirect. */
|
||
attrs.override_redirect = False;
|
||
XChangeWindowAttributes (compositor.display,
|
||
XLWindowFromXdgRole (role),
|
||
CWOverrideRedirect, &attrs);
|
||
}
|
||
|
||
static void
|
||
SendConfigure (XdgPopup *popup, int x, int y, int width, int height)
|
||
{
|
||
uint32_t serial;
|
||
|
||
serial = wl_display_next_serial (compositor.wl_display);
|
||
|
||
if (width != -1 && height != -1)
|
||
{
|
||
xdg_popup_send_configure (popup->resource,
|
||
x, y, width, height);
|
||
popup->state |= StateAckPosition;
|
||
}
|
||
|
||
XLXdgRoleSendConfigure (popup->role, serial);
|
||
|
||
popup->conf_reply = True;
|
||
popup->conf_serial = serial;
|
||
popup->position_serial = serial;
|
||
}
|
||
|
||
static void
|
||
MoveWindow (XdgPopup *popup)
|
||
{
|
||
int root_x, root_y, parent_gx, parent_gy;
|
||
int geometry_x, geometry_y, x, y;
|
||
Window window;
|
||
|
||
/* No parent was specified or the role is detached. */
|
||
if (!popup->role || !popup->parent)
|
||
return;
|
||
|
||
if (!popup->role->surface || !popup->parent->surface)
|
||
/* No surface being available means we cannot obtain the window
|
||
scale. */
|
||
return;
|
||
|
||
window = XLWindowFromXdgRole (popup->role);
|
||
|
||
XLXdgRoleGetCurrentGeometry (popup->parent, &parent_gx,
|
||
&parent_gy, NULL, NULL);
|
||
XLXdgRoleGetCurrentGeometry (popup->role, &geometry_x,
|
||
&geometry_y, NULL, NULL);
|
||
XLXdgRoleCurrentRootPosition (popup->parent, &root_x,
|
||
&root_y);
|
||
|
||
/* Parent geometry is relative to the parent coordinate system. */
|
||
TruncateSurfaceToWindow (popup->parent->surface, parent_gx, parent_gy,
|
||
&parent_gx, &parent_gy);
|
||
|
||
/* geometry_x and geometry_y are relative to the local coordinate
|
||
system. */
|
||
TruncateSurfaceToWindow (popup->role->surface, geometry_x,
|
||
geometry_y, &geometry_x, &geometry_y);
|
||
|
||
/* X and Y are relative to the parent coordinate system. */
|
||
TruncateSurfaceToWindow (popup->parent->surface, popup->x,
|
||
popup->y, &x, &y);
|
||
|
||
XMoveWindow (compositor.display, window,
|
||
x + root_x + parent_gx - geometry_x,
|
||
y + root_y + parent_gy - geometry_y);
|
||
}
|
||
|
||
static void
|
||
Map (XdgPopup *popup)
|
||
{
|
||
/* We can't guarantee that the toplevel contents will be preserved
|
||
at this point. */
|
||
SubcompositorGarbage (XLSubcompositorFromXdgRole (popup->role));
|
||
|
||
/* Update the state. */
|
||
popup->state |= StateIsMapped;
|
||
|
||
/* Move the window to the correct position. */
|
||
MoveWindow (popup);
|
||
|
||
/* And map the window. */
|
||
XMapRaised (compositor.display, XLWindowFromXdgRole (popup->role));
|
||
|
||
/* Do any pending grab if the seat is still there. */
|
||
if (popup->state & StatePendingGrab)
|
||
{
|
||
if (popup->pending_grab_seat)
|
||
DoGrab (popup, popup->pending_grab_seat,
|
||
popup->pending_grab_serial);
|
||
else
|
||
Dismiss (popup, False);
|
||
|
||
/* Now free the callback belonging to the pending grab seat. */
|
||
if (popup->pending_callback_key)
|
||
XLSeatCancelDestroyListener (popup->pending_callback_key);
|
||
|
||
popup->pending_grab_seat = NULL;
|
||
popup->pending_callback_key = NULL;
|
||
popup->state &= ~StatePendingGrab;
|
||
}
|
||
}
|
||
|
||
static void
|
||
Commit (Role *role, Surface *surface, XdgRoleImplementation *impl)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
|
||
if (popup->state & StatePendingPosition)
|
||
MoveWindow (popup);
|
||
|
||
popup->state &= ~StatePendingPosition;
|
||
|
||
if (!surface->current_state.buffer)
|
||
{
|
||
/* No buffer was attached, unmap the window. */
|
||
|
||
if (popup->state & StateIsMapped)
|
||
Unmap (popup);
|
||
}
|
||
else if (!popup->conf_reply)
|
||
{
|
||
/* Map the window if a reply was received. */
|
||
|
||
if (!(popup->state & StateIsMapped))
|
||
Map (popup);
|
||
}
|
||
}
|
||
|
||
static void
|
||
AckConfigure (Role *role, XdgRoleImplementation *impl, uint32_t serial)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
|
||
if (serial == popup->conf_serial)
|
||
{
|
||
popup->conf_reply = False;
|
||
popup->conf_serial = 0;
|
||
}
|
||
|
||
if (serial == popup->position_serial
|
||
&& popup->state & StateAckPosition)
|
||
{
|
||
/* Now apply the position of the popup. */
|
||
popup->x = popup->pending_x;
|
||
popup->y = popup->pending_y;
|
||
|
||
/* The position has been acked. Clear that flag. */
|
||
popup->state &= ~StateAckPosition;
|
||
|
||
/* Set a new flag which tells commit to move the popup. */
|
||
popup->state |= StatePendingPosition;
|
||
popup->position_serial = 0;
|
||
}
|
||
}
|
||
|
||
static void
|
||
InternalReposition (XdgPopup *popup)
|
||
{
|
||
int x, y, width, height;
|
||
|
||
/* No parent was specified or the role is detached. */
|
||
if (!popup->role || !popup->parent)
|
||
return;
|
||
|
||
XLPositionerCalculateGeometry (&popup->positioner,
|
||
popup->parent, &x, &y,
|
||
&width, &height);
|
||
|
||
popup->pending_x = x;
|
||
popup->pending_y = y;
|
||
|
||
SendConfigure (popup, popup->pending_x, popup->pending_y,
|
||
width, height);
|
||
|
||
popup->state |= StateAckPosition;
|
||
}
|
||
|
||
static void
|
||
HandleGeometryChange (Role *role, XdgRoleImplementation *impl)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
|
||
MoveWindow (popup);
|
||
}
|
||
|
||
static Bool
|
||
CheckCanGrab (Role *parent, Seat *seat)
|
||
{
|
||
XdgRoleImplementationType type;
|
||
XdgRoleImplementation *parent_impl;
|
||
XdgPopup *popup;
|
||
|
||
if (!parent->surface)
|
||
return False;
|
||
|
||
parent_impl = XLImplementationOfXdgRole (parent);
|
||
|
||
if (!parent_impl)
|
||
return False;
|
||
|
||
type = XLTypeOfXdgRole (parent);
|
||
|
||
if (type == TypeToplevel)
|
||
return True;
|
||
|
||
if (type == TypePopup)
|
||
{
|
||
popup = PopupFromRoleImpl (parent_impl);
|
||
|
||
return (popup->state & StateIsGrabbed
|
||
&& popup->grab_holder == seat);
|
||
}
|
||
|
||
return False;
|
||
}
|
||
|
||
static void
|
||
HandleGrabHolderDestroy (void *data)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = data;
|
||
popup->grab_holder = NULL;
|
||
popup->seat_callback_key = NULL;
|
||
|
||
Dismiss (popup, False);
|
||
}
|
||
|
||
static void
|
||
SaveGrabHolder (XdgPopup *popup, Seat *seat)
|
||
{
|
||
if (popup->grab_holder == seat)
|
||
return;
|
||
|
||
if (popup->grab_holder)
|
||
{
|
||
XLSeatCancelDestroyListener (popup->seat_callback_key);
|
||
|
||
popup->seat_callback_key = NULL;
|
||
popup->grab_holder = NULL;
|
||
}
|
||
|
||
if (seat)
|
||
{
|
||
popup->grab_holder = seat;
|
||
popup->seat_callback_key
|
||
= XLSeatRunOnDestroy (seat, HandleGrabHolderDestroy,
|
||
popup);
|
||
}
|
||
}
|
||
|
||
static void
|
||
DoGrab (XdgPopup *popup, Seat *seat, uint32_t serial)
|
||
{
|
||
if (popup->resource
|
||
&& popup->role && popup->role->surface
|
||
&& CheckCanGrab (popup->parent, seat)
|
||
&& XLSeatExplicitlyGrabSurface (seat,
|
||
popup->role->surface,
|
||
serial))
|
||
{
|
||
popup->current_grab_serial = serial;
|
||
SaveGrabHolder (popup, seat);
|
||
|
||
popup->state |= StateIsGrabbed;
|
||
}
|
||
else
|
||
Dismiss (popup, False);
|
||
}
|
||
|
||
static void
|
||
Dismiss (XdgPopup *popup, Bool do_parents)
|
||
{
|
||
Role *role;
|
||
XdgRoleImplementation *impl;
|
||
XdgPopup *parent;
|
||
|
||
if (popup->state & StateIsGrabbed
|
||
&& popup->parent)
|
||
RevertGrabTo (popup, popup->parent);
|
||
|
||
if (popup->state & StateIsMapped)
|
||
Unmap (popup);
|
||
|
||
popup->state &= ~StateIsGrabbed;
|
||
|
||
if (popup->resource)
|
||
xdg_popup_send_popup_done (popup->resource);
|
||
|
||
if (do_parents && popup->parent)
|
||
{
|
||
role = popup->parent;
|
||
impl = XLImplementationOfXdgRole (role);
|
||
|
||
if (impl && XLTypeOfXdgRole (role) == TypePopup)
|
||
{
|
||
parent = PopupFromRoleImpl (impl);
|
||
Dismiss (parent, True);
|
||
}
|
||
}
|
||
}
|
||
|
||
static void
|
||
HandleSeatDestroy (void *data)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = data;
|
||
popup->pending_callback_key = NULL;
|
||
popup->pending_grab_seat = NULL;
|
||
|
||
/* The popup will later be dismissed upon mapping. */
|
||
}
|
||
|
||
static void
|
||
RecordGrabPending (XdgPopup *popup, Seat *seat, uint32_t serial)
|
||
{
|
||
void *key;
|
||
|
||
if (popup->seat_callback_key || popup->pending_callback_key)
|
||
return;
|
||
|
||
key = XLSeatRunOnDestroy (seat, HandleSeatDestroy, popup);
|
||
|
||
if (!key)
|
||
Dismiss (popup, False);
|
||
else
|
||
{
|
||
popup->pending_callback_key = key;
|
||
popup->pending_grab_seat = seat;
|
||
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;
|
||
}
|
||
}
|
||
|
||
static void
|
||
Grab (struct wl_client *client, struct wl_resource *resource,
|
||
struct wl_resource *seat_resource, uint32_t serial)
|
||
{
|
||
Seat *seat;
|
||
XdgPopup *popup;
|
||
|
||
seat = wl_resource_get_user_data (seat_resource);
|
||
popup = wl_resource_get_user_data (resource);
|
||
|
||
if (!popup->role || !popup->role->surface)
|
||
return;
|
||
|
||
if (popup->state & StateIsGrabbed)
|
||
return;
|
||
|
||
if (!(popup->state & StateIsMapped))
|
||
RecordGrabPending (popup, seat, serial);
|
||
else
|
||
wl_resource_post_error (resource, XDG_POPUP_ERROR_INVALID_GRAB,
|
||
"trying to grab mapped popup");
|
||
}
|
||
|
||
static void
|
||
Reposition (struct wl_client *client, struct wl_resource *resource,
|
||
struct wl_resource *positioner_resource, uint32_t token)
|
||
{
|
||
XdgPopup *popup;
|
||
Positioner *positioner;
|
||
|
||
popup = wl_resource_get_user_data (resource);
|
||
|
||
/* Make sure that the positioner is complete. */
|
||
positioner = wl_resource_get_user_data (positioner_resource);
|
||
XLCheckPositionerComplete (positioner);
|
||
|
||
/* Copy the positioner to the popup. */
|
||
popup->positioner = *positioner;
|
||
popup->positioner.resource = NULL;
|
||
|
||
xdg_popup_send_repositioned (resource, token);
|
||
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
|
||
Destroy (struct wl_client *client, struct wl_resource *resource)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
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)
|
||
XLXdgRoleDetachImplementation (popup->role,
|
||
&popup->impl);
|
||
|
||
wl_resource_destroy (resource);
|
||
}
|
||
|
||
static Bool
|
||
HandleOneConfigureNotify (XEvent *event)
|
||
{
|
||
XdgPopup *popup;
|
||
XdgRoleImplementation *impl;
|
||
|
||
impl = XLLookUpXdgPopup (event->xconfigure.window);
|
||
|
||
if (!impl)
|
||
return False;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
XLXdgRoleNoteConfigure (popup->role, event);
|
||
|
||
return False;
|
||
}
|
||
|
||
static void
|
||
NoteSize (Role *role, XdgRoleImplementation *impl,
|
||
int width, int height)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
popup->width = width;
|
||
popup->height = height;
|
||
}
|
||
|
||
static void
|
||
HandleParentConfigure (void *data, XEvent *xevent)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = data;
|
||
|
||
if (popup->positioner.reactive)
|
||
InternalReposition (popup);
|
||
}
|
||
|
||
static void
|
||
HandleParentResize (void *data)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = data;
|
||
|
||
if (popup->positioner.reactive)
|
||
InternalReposition (popup);
|
||
}
|
||
|
||
static Bool
|
||
IsWindowMapped (Role *role, XdgRoleImplementation *impl)
|
||
{
|
||
XdgPopup *popup;
|
||
|
||
popup = PopupFromRoleImpl (impl);
|
||
return popup->state & StateIsMapped;
|
||
}
|
||
|
||
static const struct xdg_popup_interface xdg_popup_impl =
|
||
{
|
||
.destroy = Destroy,
|
||
.grab = Grab,
|
||
.reposition = Reposition,
|
||
};
|
||
|
||
void
|
||
XLGetXdgPopup (struct wl_client *client, struct wl_resource *resource,
|
||
uint32_t id, struct wl_resource *parent_resource,
|
||
struct wl_resource *positioner_resource)
|
||
{
|
||
XdgPopup *popup;
|
||
Role *role, *parent;
|
||
void *key;
|
||
Positioner *positioner;
|
||
|
||
popup = XLSafeMalloc (sizeof *popup);
|
||
role = wl_resource_get_user_data (resource);
|
||
|
||
if (!popup)
|
||
{
|
||
wl_client_post_no_memory (client);
|
||
return;
|
||
}
|
||
|
||
memset (popup, 0, sizeof *popup);
|
||
popup->resource = wl_resource_create (client, &xdg_popup_interface,
|
||
wl_resource_get_version (resource),
|
||
id);
|
||
|
||
if (!popup->resource)
|
||
{
|
||
wl_resource_post_no_memory (resource);
|
||
XLFree (popup);
|
||
return;
|
||
}
|
||
|
||
popup->impl.funcs.attach = Attach;
|
||
popup->impl.funcs.commit = Commit;
|
||
popup->impl.funcs.detach = Detach;
|
||
|
||
popup->impl.funcs.ack_configure = AckConfigure;
|
||
popup->impl.funcs.note_size = NoteSize;
|
||
popup->impl.funcs.handle_geometry_change = HandleGeometryChange;
|
||
popup->impl.funcs.is_window_mapped = IsWindowMapped;
|
||
|
||
if (parent_resource)
|
||
{
|
||
parent = wl_resource_get_user_data (parent_resource);
|
||
key = XLXdgRoleRunOnReconstrain (parent, HandleParentConfigure,
|
||
HandleParentResize, popup);
|
||
XLRetainXdgRole (parent);
|
||
|
||
popup->parent = parent;
|
||
popup->reconstrain_callback_key = key;
|
||
}
|
||
|
||
/* Make sure that the positioner is complete. */
|
||
positioner = wl_resource_get_user_data (positioner_resource);
|
||
XLCheckPositionerComplete (positioner);
|
||
|
||
/* Save the positioner into the popup. The spec says:
|
||
|
||
At the time of the request, the compositor makes a copy of the
|
||
rules specified by the xdg_positioner. Thus, after the request
|
||
is complete the xdg_positioner object can be destroyed or
|
||
reused; further changes to the object will have no effect on
|
||
previous usages. */
|
||
popup->positioner = *positioner;
|
||
popup->positioner.resource = NULL;
|
||
|
||
wl_resource_set_implementation (popup->resource, &xdg_popup_impl,
|
||
popup, HandleResourceDestroy);
|
||
popup->refcount++;
|
||
|
||
XLXdgRoleAttachImplementation (role, &popup->impl);
|
||
|
||
/* Send the initial configure event. */
|
||
InternalReposition (popup);
|
||
}
|
||
|
||
Bool
|
||
XLHandleXEventForXdgPopups (XEvent *event)
|
||
{
|
||
if (event->type == ConfigureNotify)
|
||
return HandleOneConfigureNotify (event);
|
||
|
||
return False;
|
||
}
|
||
|
||
void
|
||
XLInitPopups (void)
|
||
{
|
||
/* Nothing to do here. */
|
||
}
|