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:
oldosfan 2022-10-20 03:55:19 +00:00
parent 17571b6f97
commit d143b45496
12 changed files with 817 additions and 97 deletions

View file

@ -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 ();

View file

@ -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

View file

@ -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);

View file

@ -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;
@ -281,6 +284,9 @@ enum
ImmediateRelease = 1 << 2,
/* The render target supports explicit synchronization. */
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;

View file

@ -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);

View file

@ -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,
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
View file

@ -167,6 +167,9 @@ HandleOneXEvent (XEvent *event)
if (XLHandleOneXEventForXSettings (event))
return;
if (HandleOneXEventForTime (event))
return;
}
static void

View file

@ -2577,8 +2577,17 @@ SubcompositorUpdate (Subcompositor *subcompositor)
}
}
else
{
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,
if it contains the entire update region. */

237
time.c
View file

@ -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);
}
}

View file

@ -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))
{
if (!(role->state & StateFrameStarted)
&& XLFrameClockStartFrame (role->clock, False))
role->state |= StateFrameStarted;
XLFrameClockStartFrame (role->clock, False);
}
/* 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;
}

View file

@ -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), &copy);
}
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;
}

View file

@ -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);
}