[xserver] dpms: Add support for DPMSInfoNotify event

Submitted by Alexander Volkov on Oct. 2, 2018, 1:05 p.m.

Details

Message ID 20181002130550.17328-2-avolkov@astralinux.ru
State New
Series "Add DPMSInfoNotify event"
Headers show

Commit Message

Alexander Volkov Oct. 2, 2018, 1:05 p.m.
From: Alexander Volkov <a.volkov@rusbitech.ru>

It was introduced in DPMS 1.2 (xorgproto).
This allows applications to respond to changes of power level
of a monitor, e.g. an application may stop rendering and related
calculations when the monitor is off.

Related bug: https://bugs.freedesktop.org/57120

Signed-off-by: Alexander Volkov <a.volkov@rusbitech.ru>
---
 Xext/dpms.c | 212 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 207 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/Xext/dpms.c b/Xext/dpms.c
index e43a37974..3ac8b5795 100644
--- a/Xext/dpms.c
+++ b/Xext/dpms.c
@@ -50,6 +50,173 @@  CARD32 DPMSSuspendTime = -1;
 CARD32 DPMSOffTime = -1;
 Bool DPMSEnabled;
 
+static int DPMSEventBase = 0;
+static RESTYPE ClientType, DPMSEventType;  /* resource types for event masks */
+static XID eventResource;
+
+typedef struct _DPMSEvent *DPMSEventPtr;
+typedef struct _DPMSEvent {
+    DPMSEventPtr next;
+    ClientPtr client;
+    XID clientResource;
+    unsigned int mask;
+} DPMSEventRec;
+
+ /*ARGSUSED*/ static int
+DPMSFreeClient(void *data, XID id)
+{
+    DPMSEventPtr pEvent;
+    DPMSEventPtr *pHead, pCur, pPrev;
+
+    pEvent = (DPMSEventPtr) data;
+    dixLookupResourceByType((void *) &pHead, eventResource, DPMSEventType,
+                            NullClient, DixUnknownAccess);
+    if (pHead) {
+        pPrev = 0;
+        for (pCur = *pHead; pCur && pCur != pEvent; pCur = pCur->next)
+            pPrev = pCur;
+        if (pCur) {
+            if (pPrev)
+                pPrev->next = pEvent->next;
+            else
+                *pHead = pEvent->next;
+        }
+    }
+    free((void *) pEvent);
+    return 1;
+}
+
+ /*ARGSUSED*/ static int
+DPMSFreeEvents(void *data, XID id)
+{
+    DPMSEventPtr *pHead, pCur, pNext;
+
+    pHead = (DPMSEventPtr *) data;
+    for (pCur = *pHead; pCur; pCur = pNext) {
+        pNext = pCur->next;
+        FreeResource(pCur->clientResource, ClientType);
+        free((void *) pCur);
+    }
+    free((void *) pHead);
+    return 1;
+}
+
+static void
+SDPMSInfoNotifyEvent(xDPMSInfoNotifyEvent * from,
+                     xDPMSInfoNotifyEvent * to)
+{
+    to->type = from->type;
+    cpswaps(from->sequenceNumber, to->sequenceNumber);
+    cpswapl(from->timestamp, to->timestamp);
+    cpswaps(from->power_level, to->power_level);
+    to->state = from->state;
+}
+
+static int
+ProcDPMSSelectInput(register ClientPtr client)
+{
+    REQUEST(xDPMSSelectInputReq);
+    DPMSEventPtr pEvent, pNewEvent, *pHead;
+    XID clientResource;
+    int i;
+
+    REQUEST_SIZE_MATCH(xDPMSSelectInputReq);
+    i = dixLookupResourceByType((void **)&pHead, eventResource, DPMSEventType,
+                                client,
+                                DixWriteAccess);
+    if (stuff->eventMask != 0) {
+        if (i == Success && pHead) {
+            /* check for existing entry. */
+            for (pEvent = *pHead; pEvent; pEvent = pEvent->next) {
+                if (pEvent->client == client) {
+                    pEvent->mask = stuff->eventMask;
+                    return Success;
+                }
+            }
+        }
+
+        /* build the entry */
+        pNewEvent = (DPMSEventPtr)malloc(sizeof(DPMSEventRec));
+        if (!pNewEvent)
+            return BadAlloc;
+        pNewEvent->next = 0;
+        pNewEvent->client = client;
+        pNewEvent->mask = stuff->eventMask;
+        /*
+         * add a resource that will be deleted when
+         * the client goes away
+         */
+        clientResource = FakeClientID(client->index);
+        pNewEvent->clientResource = clientResource;
+        if (!AddResource(clientResource, ClientType, (void *)pNewEvent))
+            return BadAlloc;
+        /*
+         * create a resource to contain a pointer to the list
+         * of clients selecting input.  This must be indirect as
+         * the list may be arbitrarily rearranged which cannot be
+         * done through the resource database.
+         */
+        if (i != Success || !pHead) {
+            pHead = (DPMSEventPtr *)malloc(sizeof(DPMSEventPtr));
+            if (!pHead ||
+                    !AddResource(eventResource, DPMSEventType, (void *)pHead)) {
+                FreeResource(clientResource, RT_NONE);
+                return BadAlloc;
+            }
+            *pHead = 0;
+        }
+        pNewEvent->next = *pHead;
+        *pHead = pNewEvent;
+    }
+    else if (stuff->eventMask == 0) {
+        /* delete the interest */
+        if (i == Success && pHead) {
+            pNewEvent = 0;
+            for (pEvent = *pHead; pEvent; pEvent = pEvent->next) {
+                if (pEvent->client == client)
+                    break;
+                pNewEvent = pEvent;
+            }
+            if (pEvent) {
+                FreeResource(pEvent->clientResource, ClientType);
+                if (pNewEvent)
+                    pNewEvent->next = pEvent->next;
+                else
+                    *pHead = pEvent->next;
+                free(pEvent);
+            }
+        }
+    }
+    else {
+        client->errorValue = stuff->eventMask;
+        return BadValue;
+    }
+    return Success;
+}
+
+static void
+SendDPMSInfoNotify(void)
+{
+    DPMSEventPtr *pHead, pEvent;
+    xDPMSInfoNotifyEvent se;
+    int i;
+
+    i = dixLookupResourceByType((void **)&pHead, eventResource, DPMSEventType,
+                                serverClient,
+                                DixReadAccess);
+    if (i != Success || !pHead)
+        return;
+    for (pEvent = *pHead; pEvent; pEvent = pEvent->next) {
+        if ((pEvent->mask & DPMSInfoNotifyMask) == 0)
+            continue;
+        se.type = DPMSEventBase + DPMSInfoNotify;
+        se.timestamp = currentTime.milliseconds;
+        se.power_level = DPMSPowerLevel;
+        se.state = DPMSEnabled;
+        WriteEventsToClient(pEvent->client, 1, (xEvent *)&se);
+    }
+}
+
 Bool
 DPMSSupported(void)
 {
@@ -86,6 +253,7 @@  int
 DPMSSet(ClientPtr client, int level)
 {
     int rc, i;
+    int old_level = DPMSPowerLevel;
 
     DPMSPowerLevel = level;
 
@@ -109,6 +277,9 @@  DPMSSet(ClientPtr client, int level)
         if (screenInfo.gpuscreens[i]->DPMS != NULL)
             screenInfo.gpuscreens[i]->DPMS(screenInfo.gpuscreens[i], level);
 
+    if (DPMSPowerLevel != old_level)
+        SendDPMSInfoNotify();
+
     return Success;
 }
 
@@ -212,8 +383,10 @@  ProcDPMSEnable(ClientPtr client)
     REQUEST_SIZE_MATCH(xDPMSEnableReq);
 
     DPMSEnabled = TRUE;
-    if (!was_enabled)
+    if (!was_enabled) {
         SetScreenSaverTimer();
+        SendDPMSInfoNotify();
+    }
 
     return Success;
 }
@@ -221,6 +394,8 @@  ProcDPMSEnable(ClientPtr client)
 static int
 ProcDPMSDisable(ClientPtr client)
 {
+    Bool was_enabled = DPMSEnabled;
+
     /* REQUEST(xDPMSDisableReq); */
 
     REQUEST_SIZE_MATCH(xDPMSDisableReq);
@@ -228,6 +403,8 @@  ProcDPMSDisable(ClientPtr client)
     DPMSSet(client, DPMSModeOn);
 
     DPMSEnabled = FALSE;
+    if (was_enabled)
+        SendDPMSInfoNotify();
 
     return Success;
 }
@@ -298,6 +475,8 @@  ProcDPMSDispatch(ClientPtr client)
         return ProcDPMSForceLevel(client);
     case X_DPMSInfo:
         return ProcDPMSInfo(client);
+    case X_DPMSSelectInput:
+        return ProcDPMSSelectInput(client);
     default:
         return BadRequest;
     }
@@ -397,6 +576,18 @@  SProcDPMSInfo(ClientPtr client)
     return ProcDPMSInfo(client);
 }
 
+static int _X_COLD
+SProcDPMSSelectInput(ClientPtr client)
+{
+    REQUEST(xDPMSSelectInputReq);
+    swaps(&stuff->length);
+    REQUEST_SIZE_MATCH(xDPMSSelectInputReq);
+    swapl(&stuff->eventMask);
+    return ProcDPMSSelectInput(client);
+}
+
+
+
 static int _X_COLD
 SProcDPMSDispatch(ClientPtr client)
 {
@@ -418,6 +609,8 @@  SProcDPMSDispatch(ClientPtr client)
         return SProcDPMSForceLevel(client);
     case X_DPMSInfo:
         return SProcDPMSInfo(client);
+    case X_DPMSSelectInput:
+        return SProcDPMSSelectInput(client);
     default:
         return BadRequest;
     }
@@ -432,6 +625,8 @@  DPMSCloseDownExtension(ExtensionEntry *e)
 void
 DPMSExtensionInit(void)
 {
+    ExtensionEntry *extEntry;
+
 #define CONDITIONALLY_SET_DPMS_TIMEOUT(_timeout_value_)         \
     if (_timeout_value_ == -1) { /* not yet set from config */  \
         _timeout_value_ = ScreenSaverTime;                      \
@@ -444,8 +639,15 @@  DPMSExtensionInit(void)
     DPMSPowerLevel = DPMSModeOn;
     DPMSEnabled = DPMSSupported();
 
-    if (DPMSEnabled)
-        AddExtension(DPMSExtensionName, 0, 0,
-                     ProcDPMSDispatch, SProcDPMSDispatch,
-                     DPMSCloseDownExtension, StandardMinorOpcode);
+    ClientType = CreateNewResourceType(DPMSFreeClient, "DPMSClient");
+    DPMSEventType = CreateNewResourceType(DPMSFreeEvents, "DPMSEvent");
+    eventResource = FakeClientID(0);
+
+    if (DPMSEnabled && ClientType && DPMSEventType &&
+        (extEntry = AddExtension(DPMSExtensionName, DPMSNumberEvents, 0,
+                                 ProcDPMSDispatch, SProcDPMSDispatch,
+                                 DPMSCloseDownExtension, StandardMinorOpcode))) {
+        DPMSEventBase = extEntry->eventBase;
+        EventSwapVector[DPMSEventBase] = (EventSwapPtr) SDPMSInfoNotifyEvent;
+    }
 }