forked from 12to11/12to11
Implement _NET_WM_PING and some WIP Present code
* 12to11.c (XLMain): Initialize timestamp tracking. * 12to11.man: Document *.useDirectPresentation resource. * atoms.c (names): Add _NET_WM_PID and _NET_WM_PING. (XLInitAtoms): Likewise. * compositor.h (struct _RenderFuncs): Update doc of cancel_presentation. (enum _FrameMode): Add ModeNotifyDisablePresent. (struct _XdgRoleList, struct _XdgWmBase): New structs. * frame_clock.c (StartFrame): Return if the clock was really started. (XLFrameClockStartFrame): Likewise. (XLInitFrameClock): Move Sync extension initialization to InitTime. * picture_renderer.c (struct _PresentCompletionCallback) (struct _PictureTarget, InitSynchronizedPresentation) (InitRenderFuncs, TargetFromDrawable, DestroyRenderTarget) (PresentToWindow, CancelPresentationCallback, AddRenderFlag) (HandlePresentCompleteNotify): Link structure on target. When synchronized presentation is enabled, use the approriate msc to present, and record the last known msc. * run.c (HandleOneXEvent): Handle timestamp events. * subcompositor.c (SubcompositorUpdate): Note frame with ModeNotifyDisablePresent. (FindSystemCounter, ValueToScalar, ScalarToValue, StartAlarms) (HandleAlarmNotify, HandleOneXEventForTime, InitTime): New functions. * xdg_surface.c (struct _XdgRole): New fields `link' and `wm_base'. (struct _PingEvent): New structure. (ReleaseBacking): Release all ping events. (NoteFrame, WriteRedirectProperty): Allow unredirection under some circumstances. (XLGetXdgSurface): Attach wm base. (XLXdgRoleHandlePing, ReplyPingEvent, XLXdgRoleReplyPing): New function. * xdg_toplevel.c (GetClientMachine): New function. (WriteCredentialProperties): New function. (Attach): Write credential properties. (ReplyToPing): New function. (XLHandleXEventForXdgToplevels): Handle ping events. * xdg_wm.c (Pong): Reply to ping events once pong is received from the client. (HandleResourceDestroy): Detach all surfaces and run their ping events. (HandleBind): Allocate memory for XdgWmBase. (XLXdgWmBaseSendPing): New function.
This commit is contained in:
parent
17571b6f97
commit
d143b45496
12 changed files with 817 additions and 97 deletions
3
12to11.c
3
12to11.c
|
@ -200,6 +200,9 @@ XLMain (int argc, char **argv)
|
|||
compositor.wl_event_loop
|
||||
= wl_display_get_event_loop (wl_display);
|
||||
|
||||
/* Initialize server time tracking very early. */
|
||||
InitTime ();
|
||||
|
||||
InitXErrors ();
|
||||
SubcompositorInit ();
|
||||
InitSelections ();
|
||||
|
|
|
@ -110,6 +110,12 @@ Specifies the rendering backend the protocol translator uses to
|
|||
composite the contents of Wayland surfaces onto X windows. This can
|
||||
either be \fBpicture\fP (the XRender based compositor) or \fBegl\fP
|
||||
(the OpenGL ES 2.0 based compositor).
|
||||
.TP
|
||||
.B useDirectPresentation \fP (class \fBUseDirectPresentation\fP)
|
||||
If ``True'' or ``true'', this resource enables the use of direct
|
||||
presentation on unredirected windows. This is not yet complete
|
||||
pending the installation of required functionality in the X server.
|
||||
.IP
|
||||
.SH ENVIRONMENT
|
||||
Several environment variables exist that modify the behavior of the
|
||||
protocol translator in one way or another. Most of these are used for
|
||||
|
|
8
atoms.c
8
atoms.c
|
@ -109,6 +109,8 @@ static const char *names[] =
|
|||
"_NET_WM_WINDOW_TYPE_MENU",
|
||||
"_NET_WM_WINDOW_TYPE_DND",
|
||||
"CONNECTOR_ID",
|
||||
"_NET_WM_PID",
|
||||
"_NET_WM_PING",
|
||||
|
||||
/* These are automatically generated from mime.txt. */
|
||||
DirectTransferAtomNames
|
||||
|
@ -131,7 +133,7 @@ Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE, _NET_WM_SYNC_REQUEST_COUNTER,
|
|||
XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop, XdndFinished,
|
||||
_NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
|
||||
_NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND,
|
||||
CONNECTOR_ID;
|
||||
CONNECTOR_ID, _NET_WM_PID, _NET_WM_PING;
|
||||
|
||||
XrmQuark resource_quark, app_quark, QString;
|
||||
|
||||
|
@ -286,9 +288,11 @@ XLInitAtoms (void)
|
|||
_NET_WM_WINDOW_TYPE_MENU = atoms[59];
|
||||
_NET_WM_WINDOW_TYPE_DND = atoms[60];
|
||||
CONNECTOR_ID = atoms[61];
|
||||
_NET_WM_PID = atoms[62];
|
||||
_NET_WM_PING = atoms[63];
|
||||
|
||||
/* This is automatically generated. */
|
||||
DirectTransferAtomInit (atoms, 62);
|
||||
DirectTransferAtomInit (atoms, 64);
|
||||
|
||||
/* Now, initialize quarks. */
|
||||
resource_quark = XrmPermStringToQuark (compositor.resource_name);
|
||||
|
|
48
compositor.h
48
compositor.h
|
@ -135,6 +135,9 @@ extern TimestampDifference CompareTimeWith (Time, Timestamp);
|
|||
#define TimestampIs(a, op, b) (CompareTimestamps ((a), (b)) == (op))
|
||||
#define TimeIs(a, op, b) (CompareTimeWith ((a), (b)) == (op))
|
||||
|
||||
extern Bool HandleOneXEventForTime (XEvent *);
|
||||
extern void InitTime (void);
|
||||
|
||||
/* Defined in renderer.c. */
|
||||
|
||||
typedef struct _RenderFuncs RenderFuncs;
|
||||
|
@ -276,11 +279,14 @@ enum
|
|||
{
|
||||
/* The render target always preserves previously drawn contents;
|
||||
IOW, target_age always returns 0. */
|
||||
NeverAges = 1,
|
||||
NeverAges = 1,
|
||||
/* Buffers attached can always be immediately released. */
|
||||
ImmediateRelease = 1 << 2,
|
||||
ImmediateRelease = 1 << 2,
|
||||
/* The render target supports explicit synchronization. */
|
||||
SupportsExplicitSync = 1 << 3,
|
||||
SupportsExplicitSync = 1 << 3,
|
||||
/* The render target supports direct presentation, so it is okay
|
||||
for surfaces to be unredirected by the compositing manager. */
|
||||
SupportsDirectPresent = 1 << 4,
|
||||
};
|
||||
|
||||
struct _RenderFuncs
|
||||
|
@ -382,7 +388,8 @@ struct _RenderFuncs
|
|||
void (*cancel_presentation_callback) (PresentCompletionKey);
|
||||
|
||||
/* Cancel any presentation that might have happened to the window
|
||||
backing the given target. */
|
||||
backing the given target. This must be called before any normal
|
||||
drawing operations. */
|
||||
void (*cancel_presentation) (RenderTarget);
|
||||
|
||||
/* Some flags. NeverAges means targets always preserve contents
|
||||
|
@ -707,6 +714,7 @@ typedef enum _FrameMode FrameMode;
|
|||
enum _FrameMode
|
||||
{
|
||||
ModeStarted,
|
||||
ModeNotifyDisablePresent,
|
||||
ModeComplete,
|
||||
ModePresented,
|
||||
};
|
||||
|
@ -1150,7 +1158,7 @@ extern Atom _NET_WM_OPAQUE_REGION, _XL_BUFFER_RELEASE,
|
|||
XdndProxy, XdndEnter, XdndPosition, XdndStatus, XdndLeave, XdndDrop,
|
||||
XdndFinished, _NET_WM_FRAME_TIMINGS, _NET_WM_BYPASS_COMPOSITOR, WM_STATE,
|
||||
_NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_MENU, _NET_WM_WINDOW_TYPE_DND,
|
||||
CONNECTOR_ID;
|
||||
CONNECTOR_ID, _NET_WM_PID, _NET_WM_PING;
|
||||
|
||||
extern XrmQuark resource_quark, app_quark, QString;
|
||||
|
||||
|
@ -1164,7 +1172,32 @@ extern void XLInitAtoms (void);
|
|||
|
||||
/* Defined in xdg_wm.c. */
|
||||
|
||||
typedef struct _XdgWmBase XdgWmBase;
|
||||
typedef struct _XdgRoleList XdgRoleList;
|
||||
|
||||
struct _XdgRoleList
|
||||
{
|
||||
/* The next and last elements in this list. */
|
||||
XdgRoleList *next, *last;
|
||||
|
||||
/* The role. */
|
||||
Role *role;
|
||||
};
|
||||
|
||||
struct _XdgWmBase
|
||||
{
|
||||
/* The associated struct wl_resource. */
|
||||
struct wl_resource *resource;
|
||||
|
||||
/* The latest ping sent. */
|
||||
uint32_t last_ping;
|
||||
|
||||
/* List of all surfaces attached. */
|
||||
XdgRoleList list;
|
||||
};
|
||||
|
||||
extern void XLInitXdgWM (void);
|
||||
extern void XLXdgWmBaseSendPing (XdgWmBase *);
|
||||
|
||||
/* Defined in frame_clock.c. */
|
||||
|
||||
|
@ -1176,7 +1209,7 @@ extern void XLFrameClockAfterFrame (FrameClock *, void (*) (FrameClock *,
|
|||
void *),
|
||||
void *);
|
||||
extern void XLFrameClockHandleFrameEvent (FrameClock *, XEvent *);
|
||||
extern void XLFrameClockStartFrame (FrameClock *, Bool);
|
||||
extern Bool XLFrameClockStartFrame (FrameClock *, Bool);
|
||||
extern void XLFrameClockEndFrame (FrameClock *);
|
||||
extern Bool XLFrameClockIsFrozen (FrameClock *);
|
||||
extern Bool XLFrameClockCanBatch (FrameClock *);
|
||||
|
@ -1269,6 +1302,9 @@ extern FrameClock *XLXdgRoleGetFrameClock (Role *);
|
|||
extern XdgRoleImplementation *XLLookUpXdgToplevel (Window);
|
||||
extern XdgRoleImplementation *XLLookUpXdgPopup (Window);
|
||||
|
||||
extern void XLXdgRoleHandlePing (Role *, XEvent *, void (*) (XEvent *));
|
||||
extern void XLXdgRoleReplyPing (Role *);
|
||||
|
||||
/* Defined in positioner.c. */
|
||||
|
||||
typedef struct _Positioner Positioner;
|
||||
|
|
|
@ -32,10 +32,6 @@ enum
|
|||
MaxPresentationAge = 150000,
|
||||
};
|
||||
|
||||
/* Major and minor versions of the XSync extension. */
|
||||
|
||||
static int xsync_major, xsync_minor;
|
||||
|
||||
/* Whether or not the compositor supports frame synchronization. */
|
||||
|
||||
static Bool frame_sync_supported;
|
||||
|
@ -361,17 +357,17 @@ PostEndFrame (FrameClock *clock)
|
|||
timespec);
|
||||
}
|
||||
|
||||
static void
|
||||
static Bool
|
||||
StartFrame (FrameClock *clock, Bool urgent, Bool predict)
|
||||
{
|
||||
if (clock->frozen)
|
||||
return;
|
||||
return False;
|
||||
|
||||
if (clock->frozen_until_end_frame)
|
||||
return;
|
||||
return False;
|
||||
|
||||
if (clock->in_frame)
|
||||
return;
|
||||
return False;
|
||||
|
||||
if (clock->need_configure)
|
||||
{
|
||||
|
@ -406,7 +402,7 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
|
|||
counter itself isn't necessary; the values are used as a flag to
|
||||
tell us whether or not a frame has been completely drawn. */
|
||||
if (!frame_sync_supported)
|
||||
return;
|
||||
return True;
|
||||
|
||||
SetSyncCounter (clock->secondary_counter,
|
||||
clock->next_frame_id);
|
||||
|
@ -415,6 +411,7 @@ StartFrame (FrameClock *clock, Bool urgent, Bool predict)
|
|||
PostEndFrame (clock);
|
||||
|
||||
clock->need_configure = False;
|
||||
return True;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -527,10 +524,10 @@ XLFrameClockAfterFrame (FrameClock *clock,
|
|||
callback->frame = frame_func;
|
||||
}
|
||||
|
||||
void
|
||||
Bool
|
||||
XLFrameClockStartFrame (FrameClock *clock, Bool urgent)
|
||||
{
|
||||
StartFrame (clock, urgent, True);
|
||||
return StartFrame (clock, urgent, True);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -871,24 +868,6 @@ XLStopCursorClock (void)
|
|||
void
|
||||
XLInitFrameClock (void)
|
||||
{
|
||||
Bool supported;
|
||||
int xsync_event_base, xsync_error_base;
|
||||
|
||||
supported = XSyncQueryExtension (compositor.display,
|
||||
&xsync_event_base,
|
||||
&xsync_error_base);
|
||||
|
||||
if (supported)
|
||||
supported = XSyncInitialize (compositor.display,
|
||||
&xsync_major, &xsync_minor);
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
fprintf (stderr, "A compatible version of the Xsync extension"
|
||||
" was not found\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
if (!getenv ("DISABLE_FRAME_SYNCHRONIZATION"))
|
||||
frame_sync_supported = XLWmSupportsHint (_NET_WM_FRAME_DRAWN);
|
||||
|
||||
|
|
|
@ -146,8 +146,39 @@ enum
|
|||
NoPresentation = 2,
|
||||
};
|
||||
|
||||
/* Structure describing presentation callback. The callback is run
|
||||
upon a given presentation being completed. */
|
||||
|
||||
struct _PresentCompletionCallback
|
||||
{
|
||||
/* The next and last presentation callbacks. */
|
||||
PresentCompletionCallback *next, *last;
|
||||
|
||||
/* The next and last presentation completion callbacks on the
|
||||
target. */
|
||||
PresentCompletionCallback *target_next, *target_last;
|
||||
|
||||
/* The picture target. */
|
||||
PictureTarget *target;
|
||||
|
||||
/* The callback itself. */
|
||||
PresentCompletionFunc callback;
|
||||
|
||||
/* Data the callback will be called with. */
|
||||
void *data;
|
||||
|
||||
/* The function. */
|
||||
PresentCompletionFunc function;
|
||||
|
||||
/* The presentation ID. */
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
struct _PictureTarget
|
||||
{
|
||||
/* The last media stamp counter for the presentation window. */
|
||||
uint64_t last_msc;
|
||||
|
||||
/* The XID of the picture. */
|
||||
Picture picture;
|
||||
|
||||
|
@ -178,6 +209,9 @@ struct _PictureTarget
|
|||
|
||||
/* Ongoing buffer activity. */
|
||||
BufferActivityRecord activity;
|
||||
|
||||
/* List of present completion callbacks. */
|
||||
PresentCompletionCallback completion_callbacks;
|
||||
};
|
||||
|
||||
struct _DrmFormatInfo
|
||||
|
@ -229,27 +263,6 @@ struct _DmaBufRecord
|
|||
int depth;
|
||||
};
|
||||
|
||||
/* Structure describing presentation callback. The callback is run
|
||||
upon a given presentation being completed. */
|
||||
|
||||
struct _PresentCompletionCallback
|
||||
{
|
||||
/* The next and last presentation callbacks. */
|
||||
PresentCompletionCallback *next, *last;
|
||||
|
||||
/* The callback itself. */
|
||||
PresentCompletionFunc callback;
|
||||
|
||||
/* Data the callback will be called with. */
|
||||
void *data;
|
||||
|
||||
/* The function. */
|
||||
PresentCompletionFunc function;
|
||||
|
||||
/* The presentation ID. */
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
/* Hash table mapping between presentation windows and targets. */
|
||||
static XLAssocTable *xid_table;
|
||||
|
||||
|
@ -369,6 +382,9 @@ static BufferActivityRecord all_activity;
|
|||
/* List of all presentations that have not yet been completed. */
|
||||
static PresentCompletionCallback all_completion_callbacks;
|
||||
|
||||
/* Whether or not direct presentation should be used. */
|
||||
static Bool use_direct_presentation;
|
||||
|
||||
/* XRender, DRI3 and XPresent-based renderer. A RenderTarget is just
|
||||
a Picture. Here is a rough explanation of how the buffer release
|
||||
machinery works.
|
||||
|
@ -657,6 +673,45 @@ PickVisual (int *depth)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
InitSynchronizedPresentation (void)
|
||||
{
|
||||
XrmDatabase rdb;
|
||||
XrmName namelist[3];
|
||||
XrmClass classlist[3];
|
||||
XrmValue value;
|
||||
XrmRepresentation type;
|
||||
|
||||
rdb = XrmGetDatabase (compositor.display);
|
||||
|
||||
if (!rdb)
|
||||
return;
|
||||
|
||||
namelist[1] = XrmStringToQuark ("useDirectPresentation");
|
||||
namelist[0] = app_quark;
|
||||
namelist[2] = NULLQUARK;
|
||||
|
||||
classlist[1] = XrmStringToQuark ("UseDirectPresentation");
|
||||
classlist[0] = resource_quark;
|
||||
classlist[2] = NULLQUARK;
|
||||
|
||||
/* Enable the use of direct presentation if
|
||||
*.UseDirectPresentation.*.useDirectPresentation is true. This is
|
||||
still incomplete, as the features necessary for it to play nice
|
||||
with frame synchronization have not yet been implemented in the X
|
||||
server. */
|
||||
|
||||
if (XrmQGetResource (rdb, namelist, classlist,
|
||||
&type, &value)
|
||||
&& type == QString
|
||||
&& (!strcmp (value.addr, "True")
|
||||
|| !strcmp (value.addr, "true")))
|
||||
use_direct_presentation = True;
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static void AddRenderFlag (int);
|
||||
|
||||
static Bool
|
||||
InitRenderFuncs (void)
|
||||
{
|
||||
|
@ -678,6 +733,13 @@ InitRenderFuncs (void)
|
|||
return False;
|
||||
}
|
||||
|
||||
/* Figure out whether or not the user wants synchronized
|
||||
presentation. */
|
||||
InitSynchronizedPresentation ();
|
||||
|
||||
if (use_direct_presentation)
|
||||
AddRenderFlag (SupportsDirectPresent);
|
||||
|
||||
/* Create an unmapped, InputOnly window, that is used to receive
|
||||
roundtrip events. */
|
||||
attrs.override_redirect = True;
|
||||
|
@ -727,6 +789,10 @@ TargetFromDrawable (Drawable drawable, Window window,
|
|||
/* And the event mask. */
|
||||
target->standard_event_mask = standard_event_mask;
|
||||
|
||||
/* Initialize the list of present completion callbacks. */
|
||||
target->completion_callbacks.target_next = &target->completion_callbacks;
|
||||
target->completion_callbacks.target_last = &target->completion_callbacks;
|
||||
|
||||
return (RenderTarget) (void *) target;
|
||||
}
|
||||
|
||||
|
@ -846,6 +912,7 @@ DestroyRenderTarget (RenderTarget target)
|
|||
PresentRecord *record, *last;
|
||||
BufferActivityRecord *activity_record, *activity_last;
|
||||
IdleCallback *idle, *idle_last;
|
||||
PresentCompletionCallback *callback, *callback_last;
|
||||
|
||||
pict_target = target.pointer;
|
||||
|
||||
|
@ -892,6 +959,20 @@ DestroyRenderTarget (RenderTarget target)
|
|||
XLFree (idle_last);
|
||||
}
|
||||
|
||||
/* Detach the target from each present completion callback. */
|
||||
callback = pict_target->completion_callbacks.target_next;
|
||||
while (callback != &pict_target->completion_callbacks)
|
||||
{
|
||||
callback_last = callback;
|
||||
callback = callback->target_next;
|
||||
|
||||
/* Clear callback->target and the target_next/target_last
|
||||
fields. */
|
||||
callback_last->target = NULL;
|
||||
callback_last->target_next = NULL;
|
||||
callback_last->target_last = NULL;
|
||||
}
|
||||
|
||||
XRenderFreePicture (compositor.display,
|
||||
pict_target->picture);
|
||||
XFree (pict_target);
|
||||
|
@ -1203,6 +1284,7 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
|
|||
XserverRegion region;
|
||||
PresentRecord *record;
|
||||
PresentCompletionCallback *callback_rec;
|
||||
uint64_t target_msc;
|
||||
|
||||
/* Present SOURCE onto TARGET. Return False if the presentation is
|
||||
not supported. */
|
||||
|
@ -1236,12 +1318,27 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
|
|||
else
|
||||
region = None;
|
||||
|
||||
/* Present the pixmap now from the damage, immediately. */
|
||||
XPresentPixmap (compositor.display,
|
||||
pict_target->presentation_window, buffer->pixmap,
|
||||
++present_serial, None, region, 0, 0, None,
|
||||
None, None, PresentOptionAsync, 0, 0, 0,
|
||||
NULL, 0);
|
||||
if (use_direct_presentation)
|
||||
{
|
||||
/* Determine the target msc. Force the next frame after this
|
||||
one by increasing last_msc. */
|
||||
target_msc = (pict_target->last_msc ? ++pict_target->last_msc : 0);
|
||||
|
||||
/* Present the pixmap now from the damage; it will complete upon
|
||||
the next vblank if direct presentation is enabled, or
|
||||
immediately if it is not. */
|
||||
XPresentPixmap (compositor.display, pict_target->presentation_window,
|
||||
buffer->pixmap, ++present_serial, None, region, 0, 0,
|
||||
None, None, None, PresentOptionNone, target_msc, 1, 0,
|
||||
NULL, 0);
|
||||
}
|
||||
else
|
||||
/* Direct presentation is off; present the pixmap asynchronously
|
||||
at an msc of 0. */
|
||||
XPresentPixmap (compositor.display, pict_target->presentation_window,
|
||||
buffer->pixmap, ++present_serial, None, region, 0, 0,
|
||||
None, None, None, PresentOptionAsync, 0, 0, 0, NULL,
|
||||
0);
|
||||
|
||||
if (region)
|
||||
XFixesDestroyRegion (compositor.display, region);
|
||||
|
@ -1270,6 +1367,11 @@ PresentToWindow (RenderTarget target, RenderBuffer source,
|
|||
callback_rec->function = callback;
|
||||
callback_rec->data = data;
|
||||
callback_rec->id = present_serial;
|
||||
callback_rec->target_next = pict_target->completion_callbacks.target_next;
|
||||
callback_rec->target_last = &pict_target->completion_callbacks;
|
||||
pict_target->completion_callbacks.target_next->target_last = callback_rec;
|
||||
pict_target->completion_callbacks.target_next = callback_rec;
|
||||
callback_rec->target = pict_target;
|
||||
|
||||
return callback_rec;
|
||||
}
|
||||
|
@ -1285,6 +1387,12 @@ CancelPresentationCallback (PresentCompletionKey key)
|
|||
callback->next->last = callback->last;
|
||||
callback->last->next = callback->next;
|
||||
|
||||
if (callback->target_last)
|
||||
{
|
||||
callback->target_last->target_next = callback->target_next;
|
||||
callback->target_next->target_last = callback->target_last;
|
||||
}
|
||||
|
||||
XLFree (callback);
|
||||
}
|
||||
|
||||
|
@ -1352,6 +1460,12 @@ static RenderFuncs picture_render_funcs =
|
|||
.flags = NeverAges,
|
||||
};
|
||||
|
||||
static void
|
||||
AddRenderFlag (int flag)
|
||||
{
|
||||
picture_render_funcs.flags |= flag;
|
||||
}
|
||||
|
||||
static DrmFormatInfo *
|
||||
FindFormatMatching (XRenderPictFormat *format)
|
||||
{
|
||||
|
@ -2574,6 +2688,9 @@ static Bool
|
|||
HandlePresentCompleteNotify (XPresentCompleteNotifyEvent *complete)
|
||||
{
|
||||
PresentCompletionCallback *callback, *last;
|
||||
#ifdef DEBUG_PRESENT_TIME
|
||||
static uint64_t last_ust;
|
||||
#endif
|
||||
|
||||
callback = all_completion_callbacks.next;
|
||||
while (callback != &all_completion_callbacks)
|
||||
|
@ -2588,6 +2705,26 @@ HandlePresentCompleteNotify (XPresentCompleteNotifyEvent *complete)
|
|||
last->function (last->data);
|
||||
last->next->last = last->last;
|
||||
last->last->next = last->next;
|
||||
|
||||
if (last->target && last->target->last_msc < complete->msc)
|
||||
/* Set the last known msc of the target. */
|
||||
last->target->last_msc = complete->msc;
|
||||
|
||||
if (last->target_next)
|
||||
{
|
||||
/* Unlink the callback from the target as well. */
|
||||
last->target_next->target_last = last->target_last;
|
||||
last->target_last->target_next = last->target_next;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_PRESENT_TIME
|
||||
fprintf (stderr, "Time taken: %lu us (%g ms) (= 1/%g s)\n",
|
||||
complete->ust - last_ust,
|
||||
(complete->ust - last_ust) / 1000.0,
|
||||
1000.0 / ((complete->ust - last_ust) / 1000.0));
|
||||
last_ust = complete->ust;
|
||||
#endif
|
||||
|
||||
XLFree (last);
|
||||
|
||||
return True;
|
||||
|
|
3
run.c
3
run.c
|
@ -167,6 +167,9 @@ HandleOneXEvent (XEvent *event)
|
|||
|
||||
if (XLHandleOneXEventForXSettings (event))
|
||||
return;
|
||||
|
||||
if (HandleOneXEventForTime (event))
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
@ -2577,7 +2577,16 @@ SubcompositorUpdate (Subcompositor *subcompositor)
|
|||
}
|
||||
}
|
||||
else
|
||||
RenderCancelPresentation (subcompositor->target);
|
||||
{
|
||||
RenderCancelPresentation (subcompositor->target);
|
||||
|
||||
/* Tell the surface to make the compositor redirect the
|
||||
window again. */
|
||||
if (subcompositor->note_frame)
|
||||
subcompositor->note_frame (ModeNotifyDisablePresent,
|
||||
subcompositor->frame_counter,
|
||||
subcompositor->note_frame_data);
|
||||
}
|
||||
|
||||
/* The first view with an attached buffer should be drawn
|
||||
with PictOpSrc so that transparency is applied correctly,
|
||||
|
|
237
time.c
237
time.c
|
@ -17,14 +17,31 @@ 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 <stdio.h>
|
||||
|
||||
#include "compositor.h"
|
||||
|
||||
#include <X11/extensions/sync.h>
|
||||
|
||||
/* The latest known time. */
|
||||
static Timestamp current_time;
|
||||
|
||||
/* Alarms used to keep track of the server time. */
|
||||
static XSyncAlarm alarm_a, alarm_b;
|
||||
|
||||
/* The server time counter. */
|
||||
static XSyncCounter counter;
|
||||
|
||||
/* The event and error bases of the synchronization extension. */
|
||||
static int xsync_event_base, xsync_error_base;
|
||||
|
||||
/* Half a month; used as a threshold in various places. */
|
||||
#define HalfMonth (1U << 31)
|
||||
|
||||
/* The max value of Time. */
|
||||
#define MaxTime 0xffffffff
|
||||
|
||||
/* The protocol translator can run for more than 48 days. That makes
|
||||
normal X timestamp handling unsafe, as the X server wraps around
|
||||
timestamps after that much time. This function creates a Timestamp
|
||||
|
@ -92,3 +109,223 @@ CompareTimeWith (Time a, Timestamp b)
|
|||
{
|
||||
return CompareTimestamps (TimestampFromClientTime (a), b);
|
||||
}
|
||||
|
||||
|
||||
/* Timestamp tracking. The code below treats INT64 as unsigned, since
|
||||
that won't overflow until long in the far future. */
|
||||
|
||||
static XSyncCounter
|
||||
FindSystemCounter (const char *name)
|
||||
{
|
||||
XSyncSystemCounter *system_counters;
|
||||
int i, num_counters;
|
||||
XSyncCounter counter;
|
||||
|
||||
num_counters = 0;
|
||||
system_counters = XSyncListSystemCounters (compositor.display,
|
||||
&num_counters);
|
||||
counter = None;
|
||||
|
||||
for (i = 0; i < num_counters; ++i)
|
||||
{
|
||||
if (!strcmp (system_counters[i].name, name))
|
||||
{
|
||||
counter = system_counters[i].counter;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Continue looking at the next counter. */
|
||||
}
|
||||
|
||||
if (system_counters)
|
||||
XSyncFreeSystemCounterList (system_counters);
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
ValueToScalar (XSyncValue value)
|
||||
{
|
||||
uint64_t low, high;
|
||||
|
||||
low = XSyncValueLow32 (value);
|
||||
high = XSyncValueHigh32 (value);
|
||||
|
||||
return low | (high << 32);
|
||||
}
|
||||
|
||||
static void
|
||||
ScalarToValue (uint64_t scalar, XSyncValue *value)
|
||||
{
|
||||
XSyncIntsToValue (value, scalar & 0xffffffff, scalar >> 32);
|
||||
}
|
||||
|
||||
static void
|
||||
StartAlarms (XSyncCounter counter, XSyncValue current_value)
|
||||
{
|
||||
uint64_t scalar_value, target;
|
||||
XSyncTrigger trigger;
|
||||
XSyncAlarmAttributes attributes;
|
||||
unsigned long value_mask;
|
||||
|
||||
scalar_value = ValueToScalar (current_value);
|
||||
|
||||
/* Delete existing alarms. */
|
||||
|
||||
if (alarm_a)
|
||||
XSyncDestroyAlarm (compositor.display, alarm_a);
|
||||
|
||||
if (alarm_b)
|
||||
XSyncDestroyAlarm (compositor.display, alarm_b);
|
||||
|
||||
value_mask = (XSyncCACounter | XSyncCATestType
|
||||
| XSyncCAValue | XSyncCAEvents);
|
||||
|
||||
/* Start the first kind of alarm. This alarm assumes that the
|
||||
counter does not wrap around along with the server time.
|
||||
|
||||
The protocol allows for more kinds of server behavior, but all
|
||||
servers either implement the counter as one that wraps around
|
||||
after Time ends, as defined here... */
|
||||
|
||||
if (scalar_value >= HalfMonth)
|
||||
{
|
||||
/* value exceeds HalfMonth. Wait for value to overflow back to
|
||||
0. */
|
||||
trigger.counter = counter;
|
||||
trigger.test_type = XSyncNegativeComparison;
|
||||
trigger.wait_value = current_value;
|
||||
|
||||
/* Set the trigger and ask for events. */
|
||||
attributes.trigger = trigger;
|
||||
attributes.events = True;
|
||||
|
||||
/* Create the alarm. */
|
||||
alarm_a = XSyncCreateAlarm (compositor.display,
|
||||
value_mask, &attributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* value is not yet HalfMonth. Wait for value to exceed
|
||||
HalfMonth - 1. */
|
||||
trigger.counter = counter;
|
||||
trigger.test_type = XSyncPositiveComparison;
|
||||
ScalarToValue (HalfMonth, &trigger.wait_value);
|
||||
|
||||
/* Set the trigger and ask for events. */
|
||||
attributes.trigger = trigger;
|
||||
attributes.events = True;
|
||||
|
||||
/* Create the alarm. */
|
||||
alarm_a = XSyncCreateAlarm (compositor.display,
|
||||
value_mask, &attributes);
|
||||
}
|
||||
|
||||
/* ...or the counter increases indefinitely, with its lower 32 bits
|
||||
representing the server time, which this counter takes into
|
||||
account. */
|
||||
if ((scalar_value & MaxTime) >= HalfMonth)
|
||||
{
|
||||
/* The time exceeds HalfMonth. Wait for the value to overflow
|
||||
time again. */
|
||||
target = (scalar_value & ~MaxTime) + MaxTime + 1;
|
||||
trigger.counter = counter;
|
||||
trigger.test_type = XSyncPositiveComparison;
|
||||
ScalarToValue (target, &trigger.wait_value);
|
||||
|
||||
/* Set the trigger and ask for events. */
|
||||
attributes.trigger = trigger;
|
||||
attributes.events = True;
|
||||
|
||||
/* Create the alarm. */
|
||||
alarm_b = XSyncCreateAlarm (compositor.display,
|
||||
value_mask, &attributes);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The time is not yet HalfMonth. Wait for the time to exceed
|
||||
HalfMonth - 1. */
|
||||
target = (scalar_value & ~MaxTime) + HalfMonth;
|
||||
trigger.counter = counter;
|
||||
trigger.test_type = XSyncPositiveComparison;
|
||||
ScalarToValue (target, &trigger.wait_value);
|
||||
|
||||
/* Set the trigger and ask for events. */
|
||||
attributes.trigger = trigger;
|
||||
attributes.events = True;
|
||||
|
||||
/* Create the alarm. */
|
||||
alarm_b = XSyncCreateAlarm (compositor.display,
|
||||
value_mask, &attributes);
|
||||
}
|
||||
|
||||
/* Now wait for alarm notifications to arrive. */
|
||||
}
|
||||
|
||||
static Bool
|
||||
HandleAlarmNotify (XSyncAlarmNotifyEvent *notify)
|
||||
{
|
||||
if (notify->alarm != alarm_a
|
||||
|| notify->alarm != alarm_b)
|
||||
/* We are not interested in this outdated or irrelevant alarm. */
|
||||
return False;
|
||||
|
||||
/* First, synchronize our local time with the server time in the
|
||||
notification. */
|
||||
TimestampFromServerTime (notify->time);
|
||||
|
||||
/* Next, recreate the alarms for the new time. */
|
||||
StartAlarms (counter, notify->counter_value);
|
||||
return True;
|
||||
}
|
||||
|
||||
Bool
|
||||
HandleOneXEventForTime (XEvent *event)
|
||||
{
|
||||
if (event->type == xsync_event_base + XSyncAlarmNotify)
|
||||
return HandleAlarmNotify ((XSyncAlarmNotifyEvent *) event);
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
void
|
||||
InitTime (void)
|
||||
{
|
||||
XSyncValue value;
|
||||
Bool supported;
|
||||
int xsync_major, xsync_minor;
|
||||
|
||||
supported = XSyncQueryExtension (compositor.display,
|
||||
&xsync_event_base,
|
||||
&xsync_error_base);
|
||||
|
||||
if (supported)
|
||||
supported = XSyncInitialize (compositor.display,
|
||||
&xsync_major, &xsync_minor);
|
||||
|
||||
if (!supported)
|
||||
{
|
||||
fprintf (stderr, "A compatible version of the synchronization"
|
||||
" extension was not found\n");
|
||||
exit (1);
|
||||
}
|
||||
|
||||
/* Initialize server timestamp tracking. In order for server time
|
||||
accounting to be absolutely reliable, we must receive an event
|
||||
detailing each change every time it reaches HalfMonth and 0. Set
|
||||
up multiple counter alarms to do that. */
|
||||
|
||||
counter = FindSystemCounter ("SERVERTIME");
|
||||
|
||||
if (!counter)
|
||||
fprintf (stderr, "Server missing required system counter SERVERTIME\n");
|
||||
else
|
||||
{
|
||||
/* Now, obtain the current value of the counter. This cannot
|
||||
fail without calling the error (or IO error) handler. */
|
||||
XSyncQueryCounter (compositor.display, counter, &value);
|
||||
|
||||
/* Start the alarms. */
|
||||
StartAlarms (counter, value);
|
||||
}
|
||||
}
|
||||
|
|
161
xdg_surface.c
161
xdg_surface.c
|
@ -45,12 +45,14 @@ enum
|
|||
StateDirtyFrameExtents = (1 << 7),
|
||||
StateTemporaryBounds = (1 << 8),
|
||||
StateFrameStarted = (1 << 9),
|
||||
StateAllowUnredirection = (1 << 10),
|
||||
};
|
||||
|
||||
typedef struct _XdgRole XdgRole;
|
||||
typedef struct _XdgState XdgState;
|
||||
typedef struct _ReleaseLaterRecord ReleaseLaterRecord;
|
||||
typedef struct _ReconstrainCallback ReconstrainCallback;
|
||||
typedef struct _PingEvent PingEvent;
|
||||
|
||||
/* Association between XIDs and surfaces. */
|
||||
|
||||
|
@ -90,6 +92,12 @@ struct _XdgRole
|
|||
/* The role object. */
|
||||
Role role;
|
||||
|
||||
/* The link to the wm_base's list of surfaces. */
|
||||
XdgRoleList link;
|
||||
|
||||
/* The attached XdgWmBase. Not valid if link->next is NULL. */
|
||||
XdgWmBase *wm_base;
|
||||
|
||||
/* The window backing this role. */
|
||||
Window window;
|
||||
|
||||
|
@ -105,6 +113,9 @@ struct _XdgRole
|
|||
/* The pending frame ID. */
|
||||
uint64_t pending_frame;
|
||||
|
||||
/* List of pending ping events. */
|
||||
XLList *ping_events;
|
||||
|
||||
/* Number of references to this role. Used when the client
|
||||
terminates and the Wayland library destroys objects out of
|
||||
order. */
|
||||
|
@ -174,6 +185,15 @@ struct _ReleaseLaterRecord
|
|||
ReleaseLaterRecord *next, *last;
|
||||
};
|
||||
|
||||
struct _PingEvent
|
||||
{
|
||||
/* Function called to reply to this event. */
|
||||
void (*reply_func) (XEvent *);
|
||||
|
||||
/* The event. */
|
||||
XEvent event;
|
||||
};
|
||||
|
||||
/* Event base of the XShape extension. */
|
||||
int shape_base;
|
||||
|
||||
|
@ -741,9 +761,18 @@ ReleaseBacking (XdgRole *role)
|
|||
if (--role->refcount)
|
||||
return;
|
||||
|
||||
/* Sync, and then release all buffers pending release. The sync is
|
||||
necessary because the X server does not perform operations
|
||||
immediately after the Xlib function is called. */
|
||||
/* Unlink the role if it is still linked. */
|
||||
|
||||
if (role->link.next)
|
||||
{
|
||||
role->link.next->last = role->link.last;
|
||||
role->link.last->next = role->link.next;
|
||||
}
|
||||
|
||||
/* Release all buffers pending release. The sync is necessary
|
||||
because the X server does not perform operations immediately
|
||||
after the Xlib function is called. */
|
||||
|
||||
XSync (compositor.display, False);
|
||||
FreeRecords (role->release_records);
|
||||
|
||||
|
@ -756,6 +785,9 @@ ReleaseBacking (XdgRole *role)
|
|||
RenderDestroyRenderTarget (role->target);
|
||||
XDestroyWindow (compositor.display, role->window);
|
||||
|
||||
/* Free associated ping events. */
|
||||
XLListFree (role->ping_events, XLFree);
|
||||
|
||||
/* And the association. */
|
||||
XLDeleteAssoc (surfaces, role->window);
|
||||
|
||||
|
@ -1146,6 +1178,24 @@ NoteBounds (void *data, int min_x, int min_y,
|
|||
RunReconstrainCallbacks (role);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteRedirectProperty (XdgRole *role)
|
||||
{
|
||||
unsigned long bypass_compositor;
|
||||
|
||||
if (role->state & StateAllowUnredirection)
|
||||
/* The subcompositor determined that the window should be
|
||||
uncomposited to allow for direct buffer flipping. */
|
||||
bypass_compositor = 0;
|
||||
else
|
||||
bypass_compositor = 2;
|
||||
|
||||
XChangeProperty (compositor.display, role->window,
|
||||
_NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL,
|
||||
32, PropModeReplace,
|
||||
(unsigned char *) &bypass_compositor, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
NoteFrame (FrameMode mode, uint64_t id, void *data)
|
||||
{
|
||||
|
@ -1159,16 +1209,25 @@ NoteFrame (FrameMode mode, uint64_t id, void *data)
|
|||
/* Record this frame counter as the pending frame. */
|
||||
role->pending_frame = id;
|
||||
|
||||
if (!(role->state & StateFrameStarted))
|
||||
{
|
||||
role->state |= StateFrameStarted;
|
||||
XLFrameClockStartFrame (role->clock, False);
|
||||
}
|
||||
if (!(role->state & StateFrameStarted)
|
||||
&& XLFrameClockStartFrame (role->clock, False))
|
||||
role->state |= StateFrameStarted;
|
||||
|
||||
/* Also run role "commit inside frame" hook. */
|
||||
if (role->impl && role->impl->funcs.commit_inside_frame)
|
||||
role->impl->funcs.commit_inside_frame (&role->role,
|
||||
role->impl);
|
||||
break;
|
||||
|
||||
case ModeNotifyDisablePresent:
|
||||
/* The subcompositor will draw to the frame directly, so make
|
||||
the compositing manager redirect the frame again. */
|
||||
|
||||
if (role->state & StateAllowUnredirection)
|
||||
{
|
||||
role->state &= ~StateAllowUnredirection;
|
||||
WriteRedirectProperty (role);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
|
@ -1199,6 +1258,21 @@ NoteFrame (FrameMode mode, uint64_t id, void *data)
|
|||
being performed. */
|
||||
|| !IsRoleMapped (role))
|
||||
RunFrameCallbacksConditionally (role);
|
||||
|
||||
if (mode == ModePresented
|
||||
&& renderer_flags & SupportsDirectPresent)
|
||||
{
|
||||
/* Since a presentation was successful, assume future
|
||||
frames will be presented as well. In that case, let
|
||||
the compositing manager unredirect the window, so
|
||||
buffers can be directly flipped to the screen. */
|
||||
|
||||
if (!(role->state & StateAllowUnredirection))
|
||||
{
|
||||
role->state |= StateAllowUnredirection;
|
||||
WriteRedirectProperty (role);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1315,18 +1389,6 @@ NoteDesyncChild (Surface *surface, Role *role)
|
|||
XLFrameClockSetPredictRefresh (xdg_role->clock);
|
||||
}
|
||||
|
||||
static void
|
||||
WriteRedirectProperty (XdgRole *role)
|
||||
{
|
||||
unsigned long bypass_compositor;
|
||||
|
||||
bypass_compositor = 2;
|
||||
XChangeProperty (compositor.display, role->window,
|
||||
_NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL,
|
||||
32, PropModeReplace,
|
||||
(unsigned char *) &bypass_compositor, 1);
|
||||
}
|
||||
|
||||
static void
|
||||
HandleFreeze (void *data)
|
||||
{
|
||||
|
@ -1370,8 +1432,10 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
|||
XSetWindowAttributes attrs;
|
||||
unsigned int flags;
|
||||
Surface *surface;
|
||||
XdgWmBase *wm_base;
|
||||
|
||||
surface = wl_resource_get_user_data (surface_resource);
|
||||
wm_base = wl_resource_get_user_data (resource);
|
||||
|
||||
if (surface->role || (surface->role_type != AnythingType
|
||||
&& surface->role_type != XdgType))
|
||||
|
@ -1419,6 +1483,14 @@ XLGetXdgSurface (struct wl_client *client, struct wl_resource *resource,
|
|||
wl_resource_set_implementation (role->role.resource, &xdg_surface_impl,
|
||||
role, HandleResourceDestroy);
|
||||
|
||||
/* Link the role onto the wm base. */
|
||||
role->link.next = wm_base->list.next;
|
||||
role->link.last = &wm_base->list;
|
||||
role->link.role = &role->role;
|
||||
wm_base->list.next->last = &role->link;
|
||||
wm_base->list.next = &role->link;
|
||||
role->wm_base = wm_base;
|
||||
|
||||
/* Add a reference to this role struct since a wl_resource now
|
||||
refers to it. */
|
||||
role->refcount++;
|
||||
|
@ -1965,3 +2037,52 @@ XLXdgRoleNoteRejectedConfigure (Role *role)
|
|||
XLFrameClockUnfreeze (xdg_role->clock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
XLXdgRoleHandlePing (Role *role, XEvent *event,
|
||||
void (*reply_func) (XEvent *))
|
||||
{
|
||||
XdgRole *xdg_role;
|
||||
PingEvent *record;
|
||||
|
||||
xdg_role = XdgRoleFromRole (role);
|
||||
|
||||
/* If the role's xdg_wm_base is detached, just reply to the ping
|
||||
message. */
|
||||
if (!xdg_role->link.next)
|
||||
reply_func (event);
|
||||
else
|
||||
{
|
||||
/* Otherwise, save the event and ping the client. Then, send
|
||||
replies once the client replies. */
|
||||
record = XLMalloc (sizeof *record);
|
||||
record->event = *event;
|
||||
record->reply_func = reply_func;
|
||||
xdg_role->ping_events = XLListPrepend (xdg_role->ping_events,
|
||||
record);
|
||||
XLXdgWmBaseSendPing (xdg_role->wm_base);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ReplyPingEvent (void *data)
|
||||
{
|
||||
PingEvent *event;
|
||||
|
||||
event = data;
|
||||
event->reply_func (&event->event);
|
||||
XLFree (event);
|
||||
}
|
||||
|
||||
void
|
||||
XLXdgRoleReplyPing (Role *role)
|
||||
{
|
||||
XdgRole *xdg_role;
|
||||
|
||||
xdg_role = XdgRoleFromRole (role);
|
||||
|
||||
/* Free the ping event list, calling the reply functions along the
|
||||
way. */
|
||||
XLListFree (xdg_role->ping_events, ReplyPingEvent);
|
||||
xdg_role->ping_events = NULL;
|
||||
}
|
||||
|
|
125
xdg_toplevel.c
125
xdg_toplevel.c
|
@ -17,6 +17,10 @@ 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 <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -934,6 +938,99 @@ HandleWindowGeometryChange (XdgToplevel *toplevel)
|
|||
hints);
|
||||
}
|
||||
|
||||
static Bool
|
||||
GetClientMachine (XTextProperty *client_machine)
|
||||
{
|
||||
struct addrinfo template, *result;
|
||||
int rc;
|
||||
long host_name_max;
|
||||
char *hostname;
|
||||
|
||||
host_name_max = sysconf (_SC_HOST_NAME_MAX);
|
||||
|
||||
if (host_name_max == -1)
|
||||
/* The maximum host name is indeterminate. Use a sane limit like
|
||||
_POSIX_HOST_NAME_MAX. */
|
||||
host_name_max = _POSIX_HOST_NAME_MAX + 1;
|
||||
else
|
||||
host_name_max += 1;
|
||||
|
||||
/* Allocate the buffer holding the hostname. */
|
||||
hostname = alloca (host_name_max + 1);
|
||||
|
||||
/* Get the hostname. */
|
||||
if (gethostname (hostname, host_name_max + 1))
|
||||
/* Obtaining the hostname failed. */
|
||||
return False;
|
||||
|
||||
/* NULL-terminate the hostname. */
|
||||
hostname[host_name_max] = '\0';
|
||||
|
||||
/* Now find the fully-qualified domain name. */
|
||||
memset (&template, 0, sizeof template);
|
||||
template.ai_family = AF_UNSPEC;
|
||||
template.ai_socktype = SOCK_STREAM;
|
||||
template.ai_flags = AI_CANONNAME;
|
||||
|
||||
rc = getaddrinfo (hostname, NULL, &template, &result);
|
||||
|
||||
if (rc || !result)
|
||||
return False;
|
||||
|
||||
/* Copy it to the client machine text property. */
|
||||
client_machine->value
|
||||
= (unsigned char *) XLStrdup (result->ai_canonname);
|
||||
client_machine->encoding = XA_STRING;
|
||||
client_machine->nitems = strlen (result->ai_canonname);
|
||||
client_machine->format = 8;
|
||||
|
||||
/* Free the result. */
|
||||
freeaddrinfo (result);
|
||||
return True;
|
||||
}
|
||||
|
||||
static void
|
||||
WriteCredentialProperties (XdgToplevel *toplevel)
|
||||
{
|
||||
struct wl_client *client;
|
||||
pid_t pid;
|
||||
Window window;
|
||||
unsigned long process_id;
|
||||
XTextProperty client_machine;
|
||||
|
||||
/* Write credential properties such as _NET_WM_PID and
|
||||
WM_CLIENT_MACHINE. The PID is obtained from the Wayland
|
||||
connection. */
|
||||
|
||||
client = wl_resource_get_client (toplevel->resource);
|
||||
|
||||
/* Get the credentials of the client. If the Wayland library cannot
|
||||
obtain those credentials, the client is simply disallowed from
|
||||
connecting to this server. */
|
||||
wl_client_get_credentials (client, &pid, NULL, NULL);
|
||||
|
||||
/* Write the _NET_WM_PID property. */
|
||||
window = XLWindowFromXdgRole (toplevel->role);
|
||||
process_id = pid;
|
||||
XChangeProperty (compositor.display, window, _NET_WM_PID,
|
||||
XA_CARDINAL, 32, PropModeReplace,
|
||||
(unsigned char *) &process_id, 1);
|
||||
|
||||
/* First, let Xlib write WM_CLIENT_MACHINE and WM_LOCALE_NAME. */
|
||||
XSetWMProperties (compositor.display, window, NULL, NULL,
|
||||
NULL, 0, NULL, NULL, NULL);
|
||||
|
||||
/* Next, write the fully-qualified client machine if it can be
|
||||
obtained. */
|
||||
if (GetClientMachine (&client_machine))
|
||||
{
|
||||
XSetWMClientMachine (compositor.display, window,
|
||||
&client_machine);
|
||||
XLFree (client_machine.value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
Attach (Role *role, XdgRoleImplementation *impl)
|
||||
{
|
||||
|
@ -952,6 +1049,10 @@ Attach (Role *role, XdgRoleImplementation *impl)
|
|||
|
||||
protocols[nproto++] = WM_DELETE_WINDOW;
|
||||
|
||||
/* _NET_WM_PING should be disabled when the window manager kills
|
||||
clients using XKillClient. */
|
||||
protocols[nproto++] = _NET_WM_PING;
|
||||
|
||||
if (XLFrameClockSyncSupported ())
|
||||
protocols[nproto++] = _NET_WM_SYNC_REQUEST;
|
||||
|
||||
|
@ -960,6 +1061,10 @@ Attach (Role *role, XdgRoleImplementation *impl)
|
|||
|
||||
WriteHints (toplevel);
|
||||
|
||||
/* Write credential properties: _NET_WM_PID, WM_CLIENT_MACHINE,
|
||||
etc. */
|
||||
WriteCredentialProperties (toplevel);
|
||||
|
||||
/* This tells the window manager not to override size choices made
|
||||
by the client. */
|
||||
toplevel->size_hints.flags |= PSize;
|
||||
|
@ -2028,6 +2133,21 @@ SetMinimized (struct wl_client *client, struct wl_resource *resource)
|
|||
DefaultScreen (compositor.display));
|
||||
}
|
||||
|
||||
static void
|
||||
ReplyToPing (XEvent *event)
|
||||
{
|
||||
XEvent copy;
|
||||
|
||||
copy = *event;
|
||||
|
||||
/* Reply to the ping message by sending it back to the window
|
||||
manager. */
|
||||
copy.xclient.window = DefaultRootWindow (compositor.display);
|
||||
XSendEvent (compositor.display, copy.xclient.window,
|
||||
False, (SubstructureRedirectMask
|
||||
| SubstructureNotifyMask), ©);
|
||||
}
|
||||
|
||||
static const struct xdg_toplevel_interface xdg_toplevel_impl =
|
||||
{
|
||||
.destroy = Destroy,
|
||||
|
@ -2133,6 +2253,11 @@ XLHandleXEventForXdgToplevels (XEvent *event)
|
|||
|
||||
return True;
|
||||
}
|
||||
else if (event->xclient.data.l[0] == _NET_WM_PING)
|
||||
/* _NET_WM_PING arrived. Record the event and send ping
|
||||
to the client. toplevel->role should be non-NULL
|
||||
here. */
|
||||
XLXdgRoleHandlePing (toplevel->role, event, ReplyToPing);
|
||||
|
||||
return False;
|
||||
}
|
||||
|
|
84
xdg_wm.c
84
xdg_wm.c
|
@ -17,15 +17,14 @@ 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"
|
||||
|
||||
/* The xdg_wm_base global. */
|
||||
static struct wl_global *global_xdg_wm_base;
|
||||
|
||||
/* All xdg_wm_base resources. */
|
||||
static XLList *all_xdg_wm_bases;
|
||||
|
||||
static void
|
||||
CreatePositioner (struct wl_client *client, struct wl_resource *resource,
|
||||
uint32_t id)
|
||||
|
@ -44,7 +43,28 @@ static void
|
|||
Pong (struct wl_client *client, struct wl_resource *resource,
|
||||
uint32_t serial)
|
||||
{
|
||||
/* TODO... */
|
||||
XdgWmBase *wm_base;
|
||||
XdgRoleList *role;
|
||||
|
||||
/* Ping-pong implementation. Every time a ping request is received
|
||||
from the window manager, it is linked onto the list of all such
|
||||
requests on the toplevel. Then, ping is sent with a serial.
|
||||
Once the pong with the latest serial arrives from the client,
|
||||
pending requests are sent back to the window manager on all
|
||||
windows. */
|
||||
wm_base = wl_resource_get_user_data (resource);
|
||||
|
||||
if (serial == wm_base->last_ping)
|
||||
{
|
||||
/* Reply to the ping events sent to each surface created with
|
||||
this wm_base. */
|
||||
role = wm_base->list.next;
|
||||
while (role != &wm_base->list)
|
||||
{
|
||||
XLXdgRoleReplyPing (role->role);
|
||||
role = role->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -64,27 +84,60 @@ static const struct xdg_wm_base_interface xdg_wm_base_impl =
|
|||
static void
|
||||
HandleResourceDestroy (struct wl_resource *resource)
|
||||
{
|
||||
all_xdg_wm_bases = XLListRemove (all_xdg_wm_bases, resource);
|
||||
XdgWmBase *wm_base;
|
||||
XdgRoleList *role, *last;
|
||||
|
||||
wm_base = wl_resource_get_user_data (resource);
|
||||
|
||||
/* Detach each surface. */
|
||||
role = wm_base->list.next;
|
||||
while (role != &wm_base->list)
|
||||
{
|
||||
last = role;
|
||||
role = role->next;
|
||||
|
||||
/* Complete all ping events. */
|
||||
XLXdgRoleReplyPing (last->role);
|
||||
|
||||
/* Tell the surface to not bother unlinking itself. */
|
||||
last->next = NULL;
|
||||
last->last = NULL;
|
||||
last->role = NULL;
|
||||
}
|
||||
|
||||
XLFree (wm_base);
|
||||
}
|
||||
|
||||
static void
|
||||
HandleBind (struct wl_client *client, void *data,
|
||||
uint32_t version, uint32_t id)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
XdgWmBase *wm_base;
|
||||
|
||||
resource = wl_resource_create (client, &xdg_wm_base_interface,
|
||||
version, id);
|
||||
wm_base = XLSafeMalloc (sizeof *wm_base);
|
||||
|
||||
if (!resource)
|
||||
if (!wm_base)
|
||||
{
|
||||
wl_client_post_no_memory (client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation (resource, &xdg_wm_base_impl,
|
||||
NULL, HandleResourceDestroy);
|
||||
all_xdg_wm_bases = XLListPrepend (all_xdg_wm_bases, resource);
|
||||
memset (wm_base, 0, sizeof *wm_base);
|
||||
wm_base->resource
|
||||
= wl_resource_create (client, &xdg_wm_base_interface,
|
||||
version, id);
|
||||
|
||||
if (!wm_base->resource)
|
||||
{
|
||||
XLFree (wm_base);
|
||||
wl_client_post_no_memory (client);
|
||||
return;
|
||||
}
|
||||
|
||||
wl_resource_set_implementation (wm_base->resource, &xdg_wm_base_impl,
|
||||
wm_base, HandleResourceDestroy);
|
||||
wm_base->list.next = &wm_base->list;
|
||||
wm_base->list.last = &wm_base->list;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -95,3 +148,10 @@ XLInitXdgWM (void)
|
|||
&xdg_wm_base_interface,
|
||||
5, NULL, HandleBind);
|
||||
}
|
||||
|
||||
void
|
||||
XLXdgWmBaseSendPing (XdgWmBase *wm_base)
|
||||
{
|
||||
xdg_wm_base_send_ping (wm_base->resource,
|
||||
++wm_base->last_ping);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue