saver.c revision 35c4bbdf
1/*
2 *
3Copyright (c) 1992  X Consortium
4
5Permission is hereby granted, free of charge, to any person obtaining a copy
6of this software and associated documentation files (the "Software"), to deal
7in the Software without restriction, including without limitation the rights
8to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9copies of the Software, and to permit persons to whom the Software is
10furnished to do so, subject to the following conditions:
11
12The above copyright notice and this permission notice shall be included in
13all copies or substantial portions of the Software.
14
15THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
18X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
19AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of the X Consortium shall not be
23used in advertising or otherwise to promote the sale, use or other dealings
24in this Software without prior written authorization from the X Consortium.
25 *
26 * Author:  Keith Packard, MIT X Consortium
27 */
28
29#ifdef HAVE_DIX_CONFIG_H
30#include <dix-config.h>
31#endif
32
33#include <X11/X.h>
34#include <X11/Xproto.h>
35#include "misc.h"
36#include "os.h"
37#include "windowstr.h"
38#include "scrnintstr.h"
39#include "pixmapstr.h"
40#include "extnsionst.h"
41#include "dixstruct.h"
42#include "resource.h"
43#include "opaque.h"
44#include <X11/extensions/saverproto.h>
45#include "gcstruct.h"
46#include "cursorstr.h"
47#include "colormapst.h"
48#include "xace.h"
49#include "inputstr.h"
50#ifdef PANORAMIX
51#include "panoramiX.h"
52#include "panoramiXsrv.h"
53#endif
54#ifdef DPMSExtension
55#include <X11/extensions/dpmsconst.h>
56#endif
57#include "protocol-versions.h"
58
59#include <stdio.h>
60
61#include "extinit.h"
62
63static int ScreenSaverEventBase = 0;
64
65static Bool ScreenSaverHandle(ScreenPtr /* pScreen */ ,
66                              int /* xstate */ ,
67                              Bool      /* force */
68    );
69
70static Bool
71 CreateSaverWindow(ScreenPtr    /* pScreen */
72    );
73
74static Bool
75 DestroySaverWindow(ScreenPtr   /* pScreen */
76    );
77
78static void
79 UninstallSaverColormap(ScreenPtr       /* pScreen */
80    );
81
82static void
83 CheckScreenPrivate(ScreenPtr   /* pScreen */
84    );
85
86static void SScreenSaverNotifyEvent(xScreenSaverNotifyEvent * /* from */ ,
87                                    xScreenSaverNotifyEvent *   /* to */
88    );
89
90static RESTYPE SuspendType;     /* resource type for suspension records */
91
92typedef struct _ScreenSaverSuspension *ScreenSaverSuspensionPtr;
93
94/* List of clients that are suspending the screensaver. */
95static ScreenSaverSuspensionPtr suspendingClients = NULL;
96
97/*
98 * clientResource is a resource ID that's added when the record is
99 * allocated, so the record is freed and the screensaver resumed when
100 * the client disconnects. count is the number of times the client has
101 * requested the screensaver be suspended.
102 */
103typedef struct _ScreenSaverSuspension {
104    ScreenSaverSuspensionPtr next;
105    ClientPtr pClient;
106    XID clientResource;
107    int count;
108} ScreenSaverSuspensionRec;
109
110static int ScreenSaverFreeSuspend(void *value, XID id);
111
112/*
113 * each screen has a list of clients requesting
114 * ScreenSaverNotify events.  Each client has a resource
115 * for each screen it selects ScreenSaverNotify input for,
116 * this resource is used to delete the ScreenSaverNotifyRec
117 * entry from the per-screen queue.
118 */
119
120static RESTYPE SaverEventType;  /* resource type for event masks */
121
122typedef struct _ScreenSaverEvent *ScreenSaverEventPtr;
123
124typedef struct _ScreenSaverEvent {
125    ScreenSaverEventPtr next;
126    ClientPtr client;
127    ScreenPtr screen;
128    XID resource;
129    CARD32 mask;
130} ScreenSaverEventRec;
131
132static int ScreenSaverFreeEvents(void * value, XID id);
133
134static Bool setEventMask(ScreenPtr      pScreen,
135                         ClientPtr      client,
136                         unsigned long  mask);
137
138static unsigned long getEventMask(ScreenPtr     pScreen,
139                                  ClientPtr     client);
140
141/*
142 * when a client sets the screen saver attributes, a resource is
143 * kept to be freed when the client exits
144 */
145
146static RESTYPE AttrType;        /* resource type for attributes */
147
148typedef struct _ScreenSaverAttr {
149    ScreenPtr screen;
150    ClientPtr client;
151    XID resource;
152    short x, y;
153    unsigned short width, height, borderWidth;
154    unsigned char class;
155    unsigned char depth;
156    VisualID visual;
157    CursorPtr pCursor;
158    PixmapPtr pBackgroundPixmap;
159    PixmapPtr pBorderPixmap;
160    Colormap colormap;
161    unsigned long mask;         /* no pixmaps or cursors */
162    unsigned long *values;
163} ScreenSaverAttrRec, *ScreenSaverAttrPtr;
164
165static int ScreenSaverFreeAttr(void *value, XID id);
166
167static void FreeAttrs(ScreenSaverAttrPtr pAttr);
168
169static void FreeScreenAttr(ScreenSaverAttrPtr pAttr);
170
171static void
172SendScreenSaverNotify(ScreenPtr pScreen,
173                      int       state,
174                      Bool      forced);
175
176typedef struct _ScreenSaverScreenPrivate {
177    ScreenSaverEventPtr events;
178    ScreenSaverAttrPtr attr;
179    Bool hasWindow;
180    Colormap installedMap;
181} ScreenSaverScreenPrivateRec, *ScreenSaverScreenPrivatePtr;
182
183static ScreenSaverScreenPrivatePtr MakeScreenPrivate(ScreenPtr pScreen);
184
185static DevPrivateKeyRec ScreenPrivateKeyRec;
186
187#define ScreenPrivateKey (&ScreenPrivateKeyRec)
188
189#define GetScreenPrivate(s) ((ScreenSaverScreenPrivatePtr) \
190    dixLookupPrivate(&(s)->devPrivates, ScreenPrivateKey))
191#define SetScreenPrivate(s,v) \
192    dixSetPrivate(&(s)->devPrivates, ScreenPrivateKey, v);
193#define SetupScreen(s)	ScreenSaverScreenPrivatePtr pPriv = (s ? GetScreenPrivate(s) : NULL)
194
195#define New(t)	(malloc(sizeof (t)))
196
197static void
198CheckScreenPrivate(ScreenPtr pScreen)
199{
200    SetupScreen(pScreen);
201
202    if (!pPriv)
203        return;
204    if (!pPriv->attr && !pPriv->events &&
205        !pPriv->hasWindow && pPriv->installedMap == None) {
206        free(pPriv);
207        SetScreenPrivate(pScreen, NULL);
208        pScreen->screensaver.ExternalScreenSaver = NULL;
209    }
210}
211
212static ScreenSaverScreenPrivatePtr
213MakeScreenPrivate(ScreenPtr pScreen)
214{
215    SetupScreen(pScreen);
216
217    if (pPriv)
218        return pPriv;
219    pPriv = New(ScreenSaverScreenPrivateRec);
220    if (!pPriv)
221        return 0;
222    pPriv->events = 0;
223    pPriv->attr = 0;
224    pPriv->hasWindow = FALSE;
225    pPriv->installedMap = None;
226    SetScreenPrivate(pScreen, pPriv);
227    pScreen->screensaver.ExternalScreenSaver = ScreenSaverHandle;
228    return pPriv;
229}
230
231static unsigned long
232getEventMask(ScreenPtr pScreen, ClientPtr client)
233{
234    SetupScreen(pScreen);
235    ScreenSaverEventPtr pEv;
236
237    if (!pPriv)
238        return 0;
239    for (pEv = pPriv->events; pEv; pEv = pEv->next)
240        if (pEv->client == client)
241            return pEv->mask;
242    return 0;
243}
244
245static Bool
246setEventMask(ScreenPtr pScreen, ClientPtr client, unsigned long mask)
247{
248    SetupScreen(pScreen);
249    ScreenSaverEventPtr pEv, *pPrev;
250
251    if (getEventMask(pScreen, client) == mask)
252        return TRUE;
253    if (!pPriv) {
254        pPriv = MakeScreenPrivate(pScreen);
255        if (!pPriv)
256            return FALSE;
257    }
258    for (pPrev = &pPriv->events; (pEv = *pPrev) != 0; pPrev = &pEv->next)
259        if (pEv->client == client)
260            break;
261    if (mask == 0) {
262        FreeResource(pEv->resource, SaverEventType);
263        *pPrev = pEv->next;
264        free(pEv);
265        CheckScreenPrivate(pScreen);
266    }
267    else {
268        if (!pEv) {
269            pEv = New(ScreenSaverEventRec);
270            if (!pEv) {
271                CheckScreenPrivate(pScreen);
272                return FALSE;
273            }
274            *pPrev = pEv;
275            pEv->next = NULL;
276            pEv->client = client;
277            pEv->screen = pScreen;
278            pEv->resource = FakeClientID(client->index);
279            if (!AddResource(pEv->resource, SaverEventType, (void *) pEv))
280                return FALSE;
281        }
282        pEv->mask = mask;
283    }
284    return TRUE;
285}
286
287static void
288FreeAttrs(ScreenSaverAttrPtr pAttr)
289{
290    PixmapPtr pPixmap;
291    CursorPtr pCursor;
292
293    if ((pPixmap = pAttr->pBackgroundPixmap) != 0)
294        (*pPixmap->drawable.pScreen->DestroyPixmap) (pPixmap);
295    if ((pPixmap = pAttr->pBorderPixmap) != 0)
296        (*pPixmap->drawable.pScreen->DestroyPixmap) (pPixmap);
297    if ((pCursor = pAttr->pCursor) != 0)
298        FreeCursor(pCursor, (Cursor) 0);
299}
300
301static void
302FreeScreenAttr(ScreenSaverAttrPtr pAttr)
303{
304    FreeAttrs(pAttr);
305    free(pAttr->values);
306    free(pAttr);
307}
308
309static int
310ScreenSaverFreeEvents(void *value, XID id)
311{
312    ScreenSaverEventPtr pOld = (ScreenSaverEventPtr) value;
313    ScreenPtr pScreen = pOld->screen;
314
315    SetupScreen(pScreen);
316    ScreenSaverEventPtr pEv, *pPrev;
317
318    if (!pPriv)
319        return TRUE;
320    for (pPrev = &pPriv->events; (pEv = *pPrev) != 0; pPrev = &pEv->next)
321        if (pEv == pOld)
322            break;
323    if (!pEv)
324        return TRUE;
325    *pPrev = pEv->next;
326    free(pEv);
327    CheckScreenPrivate(pScreen);
328    return TRUE;
329}
330
331static int
332ScreenSaverFreeAttr(void *value, XID id)
333{
334    ScreenSaverAttrPtr pOldAttr = (ScreenSaverAttrPtr) value;
335    ScreenPtr pScreen = pOldAttr->screen;
336
337    SetupScreen(pScreen);
338
339    if (!pPriv)
340        return TRUE;
341    if (pPriv->attr != pOldAttr)
342        return TRUE;
343    FreeScreenAttr(pOldAttr);
344    pPriv->attr = NULL;
345    if (pPriv->hasWindow) {
346        dixSaveScreens(serverClient, SCREEN_SAVER_FORCER, ScreenSaverReset);
347        dixSaveScreens(serverClient, SCREEN_SAVER_FORCER, ScreenSaverActive);
348    }
349    CheckScreenPrivate(pScreen);
350    return TRUE;
351}
352
353static int
354ScreenSaverFreeSuspend(void *value, XID id)
355{
356    ScreenSaverSuspensionPtr data = (ScreenSaverSuspensionPtr) value;
357    ScreenSaverSuspensionPtr *prev, this;
358
359    /* Unlink and free the suspension record for the client */
360    for (prev = &suspendingClients; (this = *prev); prev = &this->next) {
361        if (this == data) {
362            *prev = this->next;
363            free(this);
364            break;
365        }
366    }
367
368    /* Reenable the screensaver if this was the last client suspending it. */
369    if (screenSaverSuspended && suspendingClients == NULL) {
370        screenSaverSuspended = FALSE;
371
372        /* The screensaver could be active, since suspending it (by design)
373           doesn't prevent it from being forceably activated */
374#ifdef DPMSExtension
375        if (screenIsSaved != SCREEN_SAVER_ON && DPMSPowerLevel == DPMSModeOn)
376#else
377        if (screenIsSaved != SCREEN_SAVER_ON)
378#endif
379        {
380            DeviceIntPtr dev;
381            UpdateCurrentTimeIf();
382            nt_list_for_each_entry(dev, inputInfo.devices, next)
383                NoticeTime(dev, currentTime);
384            SetScreenSaverTimer();
385        }
386    }
387
388    return Success;
389}
390
391static void
392SendScreenSaverNotify(ScreenPtr pScreen, int state, Bool forced)
393{
394    ScreenSaverScreenPrivatePtr pPriv;
395    ScreenSaverEventPtr pEv;
396    unsigned long mask;
397    int kind;
398
399    UpdateCurrentTimeIf();
400    mask = ScreenSaverNotifyMask;
401    if (state == ScreenSaverCycle)
402        mask = ScreenSaverCycleMask;
403    pScreen = screenInfo.screens[pScreen->myNum];
404    pPriv = GetScreenPrivate(pScreen);
405    if (!pPriv)
406        return;
407    if (pPriv->attr)
408        kind = ScreenSaverExternal;
409    else if (ScreenSaverBlanking != DontPreferBlanking)
410        kind = ScreenSaverBlanked;
411    else
412        kind = ScreenSaverInternal;
413    for (pEv = pPriv->events; pEv; pEv = pEv->next) {
414        if (pEv->mask & mask) {
415            xScreenSaverNotifyEvent ev = {
416                .type = ScreenSaverNotify + ScreenSaverEventBase,
417                .state = state,
418                .timestamp = currentTime.milliseconds,
419                .root = pScreen->root->drawable.id,
420                .window = pScreen->screensaver.wid,
421                .kind = kind,
422                .forced = forced
423            };
424            WriteEventsToClient(pEv->client, 1, (xEvent *) &ev);
425        }
426    }
427}
428
429static void
430SScreenSaverNotifyEvent(xScreenSaverNotifyEvent * from,
431                        xScreenSaverNotifyEvent * to)
432{
433    to->type = from->type;
434    to->state = from->state;
435    cpswaps(from->sequenceNumber, to->sequenceNumber);
436    cpswapl(from->timestamp, to->timestamp);
437    cpswapl(from->root, to->root);
438    cpswapl(from->window, to->window);
439    to->kind = from->kind;
440    to->forced = from->forced;
441}
442
443static void
444UninstallSaverColormap(ScreenPtr pScreen)
445{
446    SetupScreen(pScreen);
447    ColormapPtr pCmap;
448    int rc;
449
450    if (pPriv && pPriv->installedMap != None) {
451        rc = dixLookupResourceByType((void **) &pCmap, pPriv->installedMap,
452                                     RT_COLORMAP, serverClient,
453                                     DixUninstallAccess);
454        if (rc == Success)
455            (*pCmap->pScreen->UninstallColormap) (pCmap);
456        pPriv->installedMap = None;
457        CheckScreenPrivate(pScreen);
458    }
459}
460
461static Bool
462CreateSaverWindow(ScreenPtr pScreen)
463{
464    SetupScreen(pScreen);
465    ScreenSaverStuffPtr pSaver;
466    ScreenSaverAttrPtr pAttr;
467    WindowPtr pWin;
468    int result;
469    unsigned long mask;
470    Colormap wantMap;
471    ColormapPtr pCmap;
472
473    pSaver = &pScreen->screensaver;
474    if (pSaver->pWindow) {
475        pSaver->pWindow = NullWindow;
476        FreeResource(pSaver->wid, RT_NONE);
477        if (pPriv) {
478            UninstallSaverColormap(pScreen);
479            pPriv->hasWindow = FALSE;
480            CheckScreenPrivate(pScreen);
481        }
482    }
483
484    if (!pPriv || !(pAttr = pPriv->attr))
485        return FALSE;
486
487    pPriv->installedMap = None;
488
489    if (GrabInProgress && GrabInProgress != pAttr->client->index)
490        return FALSE;
491
492    pWin = CreateWindow(pSaver->wid, pScreen->root,
493                        pAttr->x, pAttr->y, pAttr->width, pAttr->height,
494                        pAttr->borderWidth, pAttr->class,
495                        pAttr->mask, (XID *) pAttr->values,
496                        pAttr->depth, serverClient, pAttr->visual, &result);
497    if (!pWin)
498        return FALSE;
499
500    if (!AddResource(pWin->drawable.id, RT_WINDOW, pWin))
501        return FALSE;
502
503    mask = 0;
504    if (pAttr->pBackgroundPixmap) {
505        pWin->backgroundState = BackgroundPixmap;
506        pWin->background.pixmap = pAttr->pBackgroundPixmap;
507        pAttr->pBackgroundPixmap->refcnt++;
508        mask |= CWBackPixmap;
509    }
510    if (pAttr->pBorderPixmap) {
511        pWin->borderIsPixel = FALSE;
512        pWin->border.pixmap = pAttr->pBorderPixmap;
513        pAttr->pBorderPixmap->refcnt++;
514        mask |= CWBorderPixmap;
515    }
516    if (pAttr->pCursor) {
517        CursorPtr cursor;
518        if (!pWin->optional)
519            if (!MakeWindowOptional(pWin)) {
520                FreeResource(pWin->drawable.id, RT_NONE);
521                return FALSE;
522            }
523        cursor = RefCursor(pAttr->pCursor);
524        if (pWin->optional->cursor)
525            FreeCursor(pWin->optional->cursor, (Cursor) 0);
526        pWin->optional->cursor = cursor;
527        pWin->cursorIsNone = FALSE;
528        CheckWindowOptionalNeed(pWin);
529        mask |= CWCursor;
530    }
531    if (mask)
532        (*pScreen->ChangeWindowAttributes) (pWin, mask);
533
534    if (pAttr->colormap != None)
535        (void) ChangeWindowAttributes(pWin, CWColormap, &pAttr->colormap,
536                                      serverClient);
537
538    MapWindow(pWin, serverClient);
539
540    pPriv->hasWindow = TRUE;
541    pSaver->pWindow = pWin;
542
543    /* check and install our own colormap if it isn't installed now */
544    wantMap = wColormap(pWin);
545    if (wantMap == None || IsMapInstalled(wantMap, pWin))
546        return TRUE;
547
548    result = dixLookupResourceByType((void **) &pCmap, wantMap, RT_COLORMAP,
549                                     serverClient, DixInstallAccess);
550    if (result != Success)
551        return TRUE;
552
553    pPriv->installedMap = wantMap;
554
555    (*pCmap->pScreen->InstallColormap) (pCmap);
556
557    return TRUE;
558}
559
560static Bool
561DestroySaverWindow(ScreenPtr pScreen)
562{
563    SetupScreen(pScreen);
564    ScreenSaverStuffPtr pSaver;
565
566    if (!pPriv || !pPriv->hasWindow)
567        return FALSE;
568
569    pSaver = &pScreen->screensaver;
570    if (pSaver->pWindow) {
571        pSaver->pWindow = NullWindow;
572        FreeResource(pSaver->wid, RT_NONE);
573    }
574    pPriv->hasWindow = FALSE;
575    CheckScreenPrivate(pScreen);
576    UninstallSaverColormap(pScreen);
577    return TRUE;
578}
579
580static Bool
581ScreenSaverHandle(ScreenPtr pScreen, int xstate, Bool force)
582{
583    int state = 0;
584    Bool ret = FALSE;
585    ScreenSaverScreenPrivatePtr pPriv;
586
587    switch (xstate) {
588    case SCREEN_SAVER_ON:
589        state = ScreenSaverOn;
590        ret = CreateSaverWindow(pScreen);
591        break;
592    case SCREEN_SAVER_OFF:
593        state = ScreenSaverOff;
594        ret = DestroySaverWindow(pScreen);
595        break;
596    case SCREEN_SAVER_CYCLE:
597        state = ScreenSaverCycle;
598        pPriv = GetScreenPrivate(pScreen);
599        if (pPriv && pPriv->hasWindow)
600            ret = TRUE;
601
602    }
603#ifdef PANORAMIX
604    if (noPanoramiXExtension || !pScreen->myNum)
605#endif
606        SendScreenSaverNotify(pScreen, state, force);
607    return ret;
608}
609
610static int
611ProcScreenSaverQueryVersion(ClientPtr client)
612{
613    xScreenSaverQueryVersionReply rep = {
614        .type = X_Reply,
615        .sequenceNumber = client->sequence,
616        .length = 0,
617        .majorVersion = SERVER_SAVER_MAJOR_VERSION,
618        .minorVersion = SERVER_SAVER_MINOR_VERSION
619    };
620
621    REQUEST_SIZE_MATCH(xScreenSaverQueryVersionReq);
622
623    if (client->swapped) {
624        swaps(&rep.sequenceNumber);
625        swapl(&rep.length);
626    }
627    WriteToClient(client, sizeof(xScreenSaverQueryVersionReply), &rep);
628    return Success;
629}
630
631static int
632ProcScreenSaverQueryInfo(ClientPtr client)
633{
634    REQUEST(xScreenSaverQueryInfoReq);
635    xScreenSaverQueryInfoReply rep;
636    int rc;
637    ScreenSaverStuffPtr pSaver;
638    DrawablePtr pDraw;
639    CARD32 lastInput;
640    ScreenSaverScreenPrivatePtr pPriv;
641
642    REQUEST_SIZE_MATCH(xScreenSaverQueryInfoReq);
643    rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0,
644                           DixGetAttrAccess);
645    if (rc != Success)
646        return rc;
647    rc = XaceHook(XACE_SCREENSAVER_ACCESS, client, pDraw->pScreen,
648                  DixGetAttrAccess);
649    if (rc != Success)
650        return rc;
651
652    pSaver = &pDraw->pScreen->screensaver;
653    pPriv = GetScreenPrivate(pDraw->pScreen);
654
655    UpdateCurrentTime();
656    lastInput = GetTimeInMillis() - LastEventTime(XIAllDevices).milliseconds;
657
658    rep = (xScreenSaverQueryInfoReply) {
659        .type = X_Reply,
660        .sequenceNumber = client->sequence,
661        .length = 0,
662        .window = pSaver->wid
663    };
664    if (screenIsSaved != SCREEN_SAVER_OFF) {
665        rep.state = ScreenSaverOn;
666        if (ScreenSaverTime)
667            rep.tilOrSince = lastInput - ScreenSaverTime;
668        else
669            rep.tilOrSince = 0;
670    }
671    else {
672        if (ScreenSaverTime) {
673            rep.state = ScreenSaverOff;
674            if (ScreenSaverTime < lastInput)
675                rep.tilOrSince = 0;
676            else
677                rep.tilOrSince = ScreenSaverTime - lastInput;
678        }
679        else {
680            rep.state = ScreenSaverDisabled;
681            rep.tilOrSince = 0;
682        }
683    }
684    rep.idle = lastInput;
685    rep.eventMask = getEventMask(pDraw->pScreen, client);
686    if (pPriv && pPriv->attr)
687        rep.kind = ScreenSaverExternal;
688    else if (ScreenSaverBlanking != DontPreferBlanking)
689        rep.kind = ScreenSaverBlanked;
690    else
691        rep.kind = ScreenSaverInternal;
692    if (client->swapped) {
693        swaps(&rep.sequenceNumber);
694        swapl(&rep.length);
695        swapl(&rep.window);
696        swapl(&rep.tilOrSince);
697        swapl(&rep.idle);
698        swapl(&rep.eventMask);
699    }
700    WriteToClient(client, sizeof(xScreenSaverQueryInfoReply), &rep);
701    return Success;
702}
703
704static int
705ProcScreenSaverSelectInput(ClientPtr client)
706{
707    REQUEST(xScreenSaverSelectInputReq);
708    DrawablePtr pDraw;
709    int rc;
710
711    REQUEST_SIZE_MATCH(xScreenSaverSelectInputReq);
712    rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0,
713                           DixGetAttrAccess);
714    if (rc != Success)
715        return rc;
716
717    rc = XaceHook(XACE_SCREENSAVER_ACCESS, client, pDraw->pScreen,
718                  DixSetAttrAccess);
719    if (rc != Success)
720        return rc;
721
722    if (!setEventMask(pDraw->pScreen, client, stuff->eventMask))
723        return BadAlloc;
724    return Success;
725}
726
727static int
728ScreenSaverSetAttributes(ClientPtr client)
729{
730    REQUEST(xScreenSaverSetAttributesReq);
731    DrawablePtr pDraw;
732    WindowPtr pParent;
733    ScreenPtr pScreen;
734    ScreenSaverScreenPrivatePtr pPriv = 0;
735    ScreenSaverAttrPtr pAttr = 0;
736    int ret, len, class, bw, depth;
737    unsigned long visual;
738    int idepth, ivisual;
739    Bool fOK;
740    DepthPtr pDepth;
741    WindowOptPtr ancwopt;
742    unsigned int *pVlist;
743    unsigned long *values = 0;
744    unsigned long tmask, imask;
745    unsigned long val;
746    Pixmap pixID;
747    PixmapPtr pPixmap;
748    Cursor cursorID;
749    CursorPtr pCursor;
750    Colormap cmap;
751    ColormapPtr pCmap;
752
753    REQUEST_AT_LEAST_SIZE(xScreenSaverSetAttributesReq);
754    ret = dixLookupDrawable(&pDraw, stuff->drawable, client, 0,
755                            DixGetAttrAccess);
756    if (ret != Success)
757        return ret;
758    pScreen = pDraw->pScreen;
759    pParent = pScreen->root;
760
761    ret = XaceHook(XACE_SCREENSAVER_ACCESS, client, pScreen, DixSetAttrAccess);
762    if (ret != Success)
763        return ret;
764
765    len = stuff->length - bytes_to_int32(sizeof(xScreenSaverSetAttributesReq));
766    if (Ones(stuff->mask) != len)
767        return BadLength;
768    if (!stuff->width || !stuff->height) {
769        client->errorValue = 0;
770        return BadValue;
771    }
772    switch (class = stuff->c_class) {
773    case CopyFromParent:
774    case InputOnly:
775    case InputOutput:
776        break;
777    default:
778        client->errorValue = class;
779        return BadValue;
780    }
781    bw = stuff->borderWidth;
782    depth = stuff->depth;
783    visual = stuff->visualID;
784
785    /* copied directly from CreateWindow */
786
787    if (class == CopyFromParent)
788        class = pParent->drawable.class;
789
790    if ((class != InputOutput) && (class != InputOnly)) {
791        client->errorValue = class;
792        return BadValue;
793    }
794
795    if ((class != InputOnly) && (pParent->drawable.class == InputOnly))
796        return BadMatch;
797
798    if ((class == InputOnly) && ((bw != 0) || (depth != 0)))
799        return BadMatch;
800
801    if ((class == InputOutput) && (depth == 0))
802        depth = pParent->drawable.depth;
803    ancwopt = pParent->optional;
804    if (!ancwopt)
805        ancwopt = FindWindowWithOptional(pParent)->optional;
806    if (visual == CopyFromParent)
807        visual = ancwopt->visual;
808
809    /* Find out if the depth and visual are acceptable for this Screen */
810    if ((visual != ancwopt->visual) || (depth != pParent->drawable.depth)) {
811        fOK = FALSE;
812        for (idepth = 0; idepth < pScreen->numDepths; idepth++) {
813            pDepth = (DepthPtr) &pScreen->allowedDepths[idepth];
814            if ((depth == pDepth->depth) || (depth == 0)) {
815                for (ivisual = 0; ivisual < pDepth->numVids; ivisual++) {
816                    if (visual == pDepth->vids[ivisual]) {
817                        fOK = TRUE;
818                        break;
819                    }
820                }
821            }
822        }
823        if (fOK == FALSE)
824            return BadMatch;
825    }
826
827    if (((stuff->mask & (CWBorderPixmap | CWBorderPixel)) == 0) &&
828        (class != InputOnly) && (depth != pParent->drawable.depth)) {
829        return BadMatch;
830    }
831
832    if (((stuff->mask & CWColormap) == 0) &&
833        (class != InputOnly) &&
834        ((visual != ancwopt->visual) || (ancwopt->colormap == None))) {
835        return BadMatch;
836    }
837
838    /* end of errors from CreateWindow */
839
840    pPriv = GetScreenPrivate(pScreen);
841    if (pPriv && pPriv->attr) {
842        if (pPriv->attr->client != client)
843            return BadAccess;
844    }
845    if (!pPriv) {
846        pPriv = MakeScreenPrivate(pScreen);
847        if (!pPriv)
848            return FALSE;
849    }
850    pAttr = New(ScreenSaverAttrRec);
851    if (!pAttr) {
852        ret = BadAlloc;
853        goto bail;
854    }
855    /* over allocate for override redirect */
856    pAttr->values = values = xallocarray(len + 1, sizeof(unsigned long));
857    if (!values) {
858        ret = BadAlloc;
859        goto bail;
860    }
861    pAttr->screen = pScreen;
862    pAttr->client = client;
863    pAttr->x = stuff->x;
864    pAttr->y = stuff->y;
865    pAttr->width = stuff->width;
866    pAttr->height = stuff->height;
867    pAttr->borderWidth = stuff->borderWidth;
868    pAttr->class = stuff->c_class;
869    pAttr->depth = depth;
870    pAttr->visual = visual;
871    pAttr->colormap = None;
872    pAttr->pCursor = NullCursor;
873    pAttr->pBackgroundPixmap = NullPixmap;
874    pAttr->pBorderPixmap = NullPixmap;
875    /*
876     * go through the mask, checking the values,
877     * looking up pixmaps and cursors and hold a reference
878     * to them.
879     */
880    pAttr->mask = tmask = stuff->mask | CWOverrideRedirect;
881    pVlist = (unsigned int *) (stuff + 1);
882    while (tmask) {
883        imask = lowbit(tmask);
884        tmask &= ~imask;
885        switch (imask) {
886        case CWBackPixmap:
887            pixID = (Pixmap) * pVlist;
888            if (pixID == None) {
889                *values++ = None;
890            }
891            else if (pixID == ParentRelative) {
892                if (depth != pParent->drawable.depth) {
893                    ret = BadMatch;
894                    goto PatchUp;
895                }
896                *values++ = ParentRelative;
897            }
898            else {
899                ret =
900                    dixLookupResourceByType((void **) &pPixmap, pixID,
901                                            RT_PIXMAP, client, DixReadAccess);
902                if (ret == Success) {
903                    if ((pPixmap->drawable.depth != depth) ||
904                        (pPixmap->drawable.pScreen != pScreen)) {
905                        ret = BadMatch;
906                        goto PatchUp;
907                    }
908                    pAttr->pBackgroundPixmap = pPixmap;
909                    pPixmap->refcnt++;
910                    pAttr->mask &= ~CWBackPixmap;
911                }
912                else {
913                    client->errorValue = pixID;
914                    goto PatchUp;
915                }
916            }
917            break;
918        case CWBackPixel:
919            *values++ = (CARD32) *pVlist;
920            break;
921        case CWBorderPixmap:
922            pixID = (Pixmap) * pVlist;
923            if (pixID == CopyFromParent) {
924                if (depth != pParent->drawable.depth) {
925                    ret = BadMatch;
926                    goto PatchUp;
927                }
928                *values++ = CopyFromParent;
929            }
930            else {
931                ret =
932                    dixLookupResourceByType((void **) &pPixmap, pixID,
933                                            RT_PIXMAP, client, DixReadAccess);
934                if (ret == Success) {
935                    if ((pPixmap->drawable.depth != depth) ||
936                        (pPixmap->drawable.pScreen != pScreen)) {
937                        ret = BadMatch;
938                        goto PatchUp;
939                    }
940                    pAttr->pBorderPixmap = pPixmap;
941                    pPixmap->refcnt++;
942                    pAttr->mask &= ~CWBorderPixmap;
943                }
944                else {
945                    client->errorValue = pixID;
946                    goto PatchUp;
947                }
948            }
949            break;
950        case CWBorderPixel:
951            *values++ = (CARD32) *pVlist;
952            break;
953        case CWBitGravity:
954            val = (CARD8) *pVlist;
955            if (val > StaticGravity) {
956                ret = BadValue;
957                client->errorValue = val;
958                goto PatchUp;
959            }
960            *values++ = val;
961            break;
962        case CWWinGravity:
963            val = (CARD8) *pVlist;
964            if (val > StaticGravity) {
965                ret = BadValue;
966                client->errorValue = val;
967                goto PatchUp;
968            }
969            *values++ = val;
970            break;
971        case CWBackingStore:
972            val = (CARD8) *pVlist;
973            if ((val != NotUseful) && (val != WhenMapped) && (val != Always)) {
974                ret = BadValue;
975                client->errorValue = val;
976                goto PatchUp;
977            }
978            *values++ = val;
979            break;
980        case CWBackingPlanes:
981            *values++ = (CARD32) *pVlist;
982            break;
983        case CWBackingPixel:
984            *values++ = (CARD32) *pVlist;
985            break;
986        case CWSaveUnder:
987            val = (BOOL) * pVlist;
988            if ((val != xTrue) && (val != xFalse)) {
989                ret = BadValue;
990                client->errorValue = val;
991                goto PatchUp;
992            }
993            *values++ = val;
994            break;
995        case CWEventMask:
996            *values++ = (CARD32) *pVlist;
997            break;
998        case CWDontPropagate:
999            *values++ = (CARD32) *pVlist;
1000            break;
1001        case CWOverrideRedirect:
1002            if (!(stuff->mask & CWOverrideRedirect))
1003                pVlist--;
1004            else {
1005                val = (BOOL) * pVlist;
1006                if ((val != xTrue) && (val != xFalse)) {
1007                    ret = BadValue;
1008                    client->errorValue = val;
1009                    goto PatchUp;
1010                }
1011            }
1012            *values++ = xTrue;
1013            break;
1014        case CWColormap:
1015            cmap = (Colormap) * pVlist;
1016            ret = dixLookupResourceByType((void **) &pCmap, cmap, RT_COLORMAP,
1017                                          client, DixUseAccess);
1018            if (ret != Success) {
1019                client->errorValue = cmap;
1020                goto PatchUp;
1021            }
1022            if (pCmap->pVisual->vid != visual || pCmap->pScreen != pScreen) {
1023                ret = BadMatch;
1024                goto PatchUp;
1025            }
1026            pAttr->colormap = cmap;
1027            pAttr->mask &= ~CWColormap;
1028            break;
1029        case CWCursor:
1030            cursorID = (Cursor) * pVlist;
1031            if (cursorID == None) {
1032                *values++ = None;
1033            }
1034            else {
1035                ret = dixLookupResourceByType((void **) &pCursor, cursorID,
1036                                              RT_CURSOR, client, DixUseAccess);
1037                if (ret != Success) {
1038                    client->errorValue = cursorID;
1039                    goto PatchUp;
1040                }
1041                pAttr->pCursor = RefCursor(pCursor);
1042                pAttr->mask &= ~CWCursor;
1043            }
1044            break;
1045        default:
1046            ret = BadValue;
1047            client->errorValue = stuff->mask;
1048            goto PatchUp;
1049        }
1050        pVlist++;
1051    }
1052    if (pPriv->attr)
1053        FreeScreenAttr(pPriv->attr);
1054    pPriv->attr = pAttr;
1055    pAttr->resource = FakeClientID(client->index);
1056    if (!AddResource(pAttr->resource, AttrType, (void *) pAttr))
1057        return BadAlloc;
1058    return Success;
1059 PatchUp:
1060    FreeAttrs(pAttr);
1061 bail:
1062    CheckScreenPrivate(pScreen);
1063    if (pAttr)
1064        free(pAttr->values);
1065    free(pAttr);
1066    return ret;
1067}
1068
1069static int
1070ScreenSaverUnsetAttributes(ClientPtr client)
1071{
1072    REQUEST(xScreenSaverSetAttributesReq);
1073    DrawablePtr pDraw;
1074    ScreenSaverScreenPrivatePtr pPriv;
1075    int rc;
1076
1077    REQUEST_SIZE_MATCH(xScreenSaverUnsetAttributesReq);
1078    rc = dixLookupDrawable(&pDraw, stuff->drawable, client, 0,
1079                           DixGetAttrAccess);
1080    if (rc != Success)
1081        return rc;
1082    pPriv = GetScreenPrivate(pDraw->pScreen);
1083    if (pPriv && pPriv->attr && pPriv->attr->client == client) {
1084        FreeResource(pPriv->attr->resource, AttrType);
1085        FreeScreenAttr(pPriv->attr);
1086        pPriv->attr = NULL;
1087        CheckScreenPrivate(pDraw->pScreen);
1088    }
1089    return Success;
1090}
1091
1092static int
1093ProcScreenSaverSetAttributes(ClientPtr client)
1094{
1095#ifdef PANORAMIX
1096    if (!noPanoramiXExtension) {
1097        REQUEST(xScreenSaverSetAttributesReq);
1098        PanoramiXRes *draw;
1099        PanoramiXRes *backPix = NULL;
1100        PanoramiXRes *bordPix = NULL;
1101        PanoramiXRes *cmap = NULL;
1102        int i, status, len;
1103        int pback_offset = 0, pbord_offset = 0, cmap_offset = 0;
1104        XID orig_visual, tmp;
1105
1106        REQUEST_AT_LEAST_SIZE(xScreenSaverSetAttributesReq);
1107
1108        status = dixLookupResourceByClass((void **) &draw, stuff->drawable,
1109                                          XRC_DRAWABLE, client, DixWriteAccess);
1110        if (status != Success)
1111            return (status == BadValue) ? BadDrawable : status;
1112
1113        len =
1114            stuff->length -
1115            bytes_to_int32(sizeof(xScreenSaverSetAttributesReq));
1116        if (Ones(stuff->mask) != len)
1117            return BadLength;
1118
1119        if ((Mask) stuff->mask & CWBackPixmap) {
1120            pback_offset = Ones((Mask) stuff->mask & (CWBackPixmap - 1));
1121            tmp = *((CARD32 *) &stuff[1] + pback_offset);
1122            if ((tmp != None) && (tmp != ParentRelative)) {
1123                status = dixLookupResourceByType((void **) &backPix, tmp,
1124                                                 XRT_PIXMAP, client,
1125                                                 DixReadAccess);
1126                if (status != Success)
1127                    return status;
1128            }
1129        }
1130
1131        if ((Mask) stuff->mask & CWBorderPixmap) {
1132            pbord_offset = Ones((Mask) stuff->mask & (CWBorderPixmap - 1));
1133            tmp = *((CARD32 *) &stuff[1] + pbord_offset);
1134            if (tmp != CopyFromParent) {
1135                status = dixLookupResourceByType((void **) &bordPix, tmp,
1136                                                 XRT_PIXMAP, client,
1137                                                 DixReadAccess);
1138                if (status != Success)
1139                    return status;
1140            }
1141        }
1142
1143        if ((Mask) stuff->mask & CWColormap) {
1144            cmap_offset = Ones((Mask) stuff->mask & (CWColormap - 1));
1145            tmp = *((CARD32 *) &stuff[1] + cmap_offset);
1146            if (tmp != CopyFromParent) {
1147                status = dixLookupResourceByType((void **) &cmap, tmp,
1148                                                 XRT_COLORMAP, client,
1149                                                 DixReadAccess);
1150                if (status != Success)
1151                    return status;
1152            }
1153        }
1154
1155        orig_visual = stuff->visualID;
1156
1157        FOR_NSCREENS_BACKWARD(i) {
1158            stuff->drawable = draw->info[i].id;
1159            if (backPix)
1160                *((CARD32 *) &stuff[1] + pback_offset) = backPix->info[i].id;
1161            if (bordPix)
1162                *((CARD32 *) &stuff[1] + pbord_offset) = bordPix->info[i].id;
1163            if (cmap)
1164                *((CARD32 *) &stuff[1] + cmap_offset) = cmap->info[i].id;
1165
1166            if (orig_visual != CopyFromParent)
1167                stuff->visualID = PanoramiXTranslateVisualID(i, orig_visual);
1168
1169            status = ScreenSaverSetAttributes(client);
1170        }
1171
1172        return status;
1173    }
1174#endif
1175
1176    return ScreenSaverSetAttributes(client);
1177}
1178
1179static int
1180ProcScreenSaverUnsetAttributes(ClientPtr client)
1181{
1182#ifdef PANORAMIX
1183    if (!noPanoramiXExtension) {
1184        REQUEST(xScreenSaverUnsetAttributesReq);
1185        PanoramiXRes *draw;
1186        int rc, i;
1187
1188        rc = dixLookupResourceByClass((void **) &draw, stuff->drawable,
1189                                      XRC_DRAWABLE, client, DixWriteAccess);
1190        if (rc != Success)
1191            return (rc == BadValue) ? BadDrawable : rc;
1192
1193        for (i = PanoramiXNumScreens - 1; i > 0; i--) {
1194            stuff->drawable = draw->info[i].id;
1195            ScreenSaverUnsetAttributes(client);
1196        }
1197
1198        stuff->drawable = draw->info[0].id;
1199    }
1200#endif
1201
1202    return ScreenSaverUnsetAttributes(client);
1203}
1204
1205static int
1206ProcScreenSaverSuspend(ClientPtr client)
1207{
1208    ScreenSaverSuspensionPtr *prev, this;
1209
1210    REQUEST(xScreenSaverSuspendReq);
1211    REQUEST_SIZE_MATCH(xScreenSaverSuspendReq);
1212
1213    /* Check if this client is suspending the screensaver */
1214    for (prev = &suspendingClients; (this = *prev); prev = &this->next)
1215        if (this->pClient == client)
1216            break;
1217
1218    if (this) {
1219        if (stuff->suspend == TRUE)
1220            this->count++;
1221        else if (--this->count == 0)
1222            FreeResource(this->clientResource, RT_NONE);
1223
1224        return Success;
1225    }
1226
1227    /* If we get to this point, this client isn't suspending the screensaver */
1228    if (stuff->suspend == FALSE)
1229        return Success;
1230
1231    /*
1232     * Allocate a suspension record for the client, and stop the screensaver
1233     * if it isn't already suspended by another client. We attach a resource ID
1234     * to the record, so the screensaver will be reenabled and the record freed
1235     * if the client disconnects without reenabling it first.
1236     */
1237    this = malloc(sizeof(ScreenSaverSuspensionRec));
1238
1239    if (!this)
1240        return BadAlloc;
1241
1242    this->next = NULL;
1243    this->pClient = client;
1244    this->count = 1;
1245    this->clientResource = FakeClientID(client->index);
1246
1247    if (!AddResource(this->clientResource, SuspendType, (void *) this)) {
1248        free(this);
1249        return BadAlloc;
1250    }
1251
1252    *prev = this;
1253    if (!screenSaverSuspended) {
1254        screenSaverSuspended = TRUE;
1255        FreeScreenSaverTimer();
1256    }
1257
1258    return Success;
1259}
1260
1261static int (*NormalVector[]) (ClientPtr /* client */ ) = {
1262ProcScreenSaverQueryVersion,
1263        ProcScreenSaverQueryInfo,
1264        ProcScreenSaverSelectInput,
1265        ProcScreenSaverSetAttributes,
1266        ProcScreenSaverUnsetAttributes, ProcScreenSaverSuspend,};
1267
1268#define NUM_REQUESTS	((sizeof NormalVector) / (sizeof NormalVector[0]))
1269
1270static int
1271ProcScreenSaverDispatch(ClientPtr client)
1272{
1273    REQUEST(xReq);
1274
1275    if (stuff->data < NUM_REQUESTS)
1276        return (*NormalVector[stuff->data]) (client);
1277    return BadRequest;
1278}
1279
1280static int
1281SProcScreenSaverQueryVersion(ClientPtr client)
1282{
1283    REQUEST(xScreenSaverQueryVersionReq);
1284    swaps(&stuff->length);
1285    REQUEST_SIZE_MATCH(xScreenSaverQueryVersionReq);
1286    return ProcScreenSaverQueryVersion(client);
1287}
1288
1289static int
1290SProcScreenSaverQueryInfo(ClientPtr client)
1291{
1292    REQUEST(xScreenSaverQueryInfoReq);
1293    swaps(&stuff->length);
1294    REQUEST_SIZE_MATCH(xScreenSaverQueryInfoReq);
1295    swapl(&stuff->drawable);
1296    return ProcScreenSaverQueryInfo(client);
1297}
1298
1299static int
1300SProcScreenSaverSelectInput(ClientPtr client)
1301{
1302    REQUEST(xScreenSaverSelectInputReq);
1303    swaps(&stuff->length);
1304    REQUEST_SIZE_MATCH(xScreenSaverSelectInputReq);
1305    swapl(&stuff->drawable);
1306    swapl(&stuff->eventMask);
1307    return ProcScreenSaverSelectInput(client);
1308}
1309
1310static int
1311SProcScreenSaverSetAttributes(ClientPtr client)
1312{
1313    REQUEST(xScreenSaverSetAttributesReq);
1314    swaps(&stuff->length);
1315    REQUEST_AT_LEAST_SIZE(xScreenSaverSetAttributesReq);
1316    swapl(&stuff->drawable);
1317    swaps(&stuff->x);
1318    swaps(&stuff->y);
1319    swaps(&stuff->width);
1320    swaps(&stuff->height);
1321    swaps(&stuff->borderWidth);
1322    swapl(&stuff->visualID);
1323    swapl(&stuff->mask);
1324    SwapRestL(stuff);
1325    return ProcScreenSaverSetAttributes(client);
1326}
1327
1328static int
1329SProcScreenSaverUnsetAttributes(ClientPtr client)
1330{
1331    REQUEST(xScreenSaverUnsetAttributesReq);
1332    swaps(&stuff->length);
1333    REQUEST_SIZE_MATCH(xScreenSaverUnsetAttributesReq);
1334    swapl(&stuff->drawable);
1335    return ProcScreenSaverUnsetAttributes(client);
1336}
1337
1338static int
1339SProcScreenSaverSuspend(ClientPtr client)
1340{
1341    REQUEST(xScreenSaverSuspendReq);
1342
1343    swaps(&stuff->length);
1344    REQUEST_SIZE_MATCH(xScreenSaverSuspendReq);
1345    return ProcScreenSaverSuspend(client);
1346}
1347
1348static int (*SwappedVector[]) (ClientPtr /* client */ ) = {
1349SProcScreenSaverQueryVersion,
1350        SProcScreenSaverQueryInfo,
1351        SProcScreenSaverSelectInput,
1352        SProcScreenSaverSetAttributes,
1353        SProcScreenSaverUnsetAttributes, SProcScreenSaverSuspend,};
1354
1355static int
1356SProcScreenSaverDispatch(ClientPtr client)
1357{
1358    REQUEST(xReq);
1359
1360    if (stuff->data < NUM_REQUESTS)
1361        return (*SwappedVector[stuff->data]) (client);
1362    return BadRequest;
1363}
1364
1365void
1366ScreenSaverExtensionInit(void)
1367{
1368    ExtensionEntry *extEntry;
1369    int i;
1370    ScreenPtr pScreen;
1371
1372    if (!dixRegisterPrivateKey(&ScreenPrivateKeyRec, PRIVATE_SCREEN, 0))
1373        return;
1374
1375    AttrType = CreateNewResourceType(ScreenSaverFreeAttr, "SaverAttr");
1376    SaverEventType = CreateNewResourceType(ScreenSaverFreeEvents, "SaverEvent");
1377    SuspendType = CreateNewResourceType(ScreenSaverFreeSuspend, "SaverSuspend");
1378
1379    for (i = 0; i < screenInfo.numScreens; i++) {
1380        pScreen = screenInfo.screens[i];
1381        SetScreenPrivate(pScreen, NULL);
1382    }
1383    if (AttrType && SaverEventType && SuspendType &&
1384        (extEntry = AddExtension(ScreenSaverName, ScreenSaverNumberEvents, 0,
1385                                 ProcScreenSaverDispatch,
1386                                 SProcScreenSaverDispatch, NULL,
1387                                 StandardMinorOpcode))) {
1388        ScreenSaverEventBase = extEntry->eventBase;
1389        EventSwapVector[ScreenSaverEventBase] =
1390            (EventSwapPtr) SScreenSaverNotifyEvent;
1391    }
1392}
1393