105b261ecSmrg/*
205b261ecSmrg *
305b261ecSmrg * Copyright © 2002 Keith Packard, member of The XFree86 Project, Inc.
405b261ecSmrg *
505b261ecSmrg * Permission to use, copy, modify, distribute, and sell this software and its
605b261ecSmrg * documentation for any purpose is hereby granted without fee, provided that
705b261ecSmrg * the above copyright notice appear in all copies and that both that
805b261ecSmrg * copyright notice and this permission notice appear in supporting
905b261ecSmrg * documentation, and that the name of Keith Packard not be used in
1005b261ecSmrg * advertising or publicity pertaining to distribution of the software without
1105b261ecSmrg * specific, written prior permission.  Keith Packard makes no
1205b261ecSmrg * representations about the suitability of this software for any purpose.  It
1305b261ecSmrg * is provided "as is" without express or implied warranty.
1405b261ecSmrg *
1505b261ecSmrg * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1605b261ecSmrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1705b261ecSmrg * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1805b261ecSmrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1905b261ecSmrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
2005b261ecSmrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2105b261ecSmrg * PERFORMANCE OF THIS SOFTWARE.
2205b261ecSmrg */
2305b261ecSmrg
2405b261ecSmrg/*
2505b261ecSmrg * Animated cursors for X.  Not specific to Render in any way, but
2605b261ecSmrg * stuck there because Render has the other cool cursor extension.
2705b261ecSmrg * Besides, everyone has Render.
2805b261ecSmrg *
2905b261ecSmrg * Implemented as a simple layer over the core cursor code; it
3005b261ecSmrg * creates composite cursors out of a set of static cursors and
3105b261ecSmrg * delta times between each image.
3205b261ecSmrg */
3305b261ecSmrg
3405b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
3505b261ecSmrg#include <dix-config.h>
3605b261ecSmrg#endif
3705b261ecSmrg
3805b261ecSmrg#include <X11/X.h>
3905b261ecSmrg#include <X11/Xmd.h>
4005b261ecSmrg#include "servermd.h"
4105b261ecSmrg#include "scrnintstr.h"
4205b261ecSmrg#include "dixstruct.h"
4305b261ecSmrg#include "cursorstr.h"
4405b261ecSmrg#include "dixfontstr.h"
4505b261ecSmrg#include "opaque.h"
4605b261ecSmrg#include "picturestr.h"
474642e01fSmrg#include "inputstr.h"
484642e01fSmrg#include "xace.h"
4905b261ecSmrg
5005b261ecSmrgtypedef struct _AnimCurElt {
5135c4bbdfSmrg    CursorPtr pCursor;          /* cursor to show */
5235c4bbdfSmrg    CARD32 delay;               /* in ms */
5305b261ecSmrg} AnimCurElt;
5405b261ecSmrg
5505b261ecSmrgtypedef struct _AnimCur {
5635c4bbdfSmrg    int nelt;                   /* number of elements in the elts array */
5735c4bbdfSmrg    AnimCurElt *elts;           /* actually allocated right after the structure */
581b5d61b8Smrg    OsTimerPtr timer;
5905b261ecSmrg} AnimCurRec, *AnimCurPtr;
6005b261ecSmrg
6105b261ecSmrgtypedef struct _AnimScrPriv {
6235c4bbdfSmrg    CloseScreenProcPtr CloseScreen;
6335c4bbdfSmrg    CursorLimitsProcPtr CursorLimits;
6435c4bbdfSmrg    DisplayCursorProcPtr DisplayCursor;
6535c4bbdfSmrg    SetCursorPositionProcPtr SetCursorPosition;
6635c4bbdfSmrg    RealizeCursorProcPtr RealizeCursor;
6735c4bbdfSmrg    UnrealizeCursorProcPtr UnrealizeCursor;
6835c4bbdfSmrg    RecolorCursorProcPtr RecolorCursor;
6905b261ecSmrg} AnimCurScreenRec, *AnimCurScreenPtr;
7005b261ecSmrg
7105b261ecSmrgstatic unsigned char empty[4];
7205b261ecSmrg
7335c4bbdfSmrgstatic CursorBits animCursorBits = {
7405b261ecSmrg    empty, empty, 2, 1, 1, 0, 0, 1
7505b261ecSmrg};
7605b261ecSmrg
776747b715Smrgstatic DevPrivateKeyRec AnimCurScreenPrivateKeyRec;
7835c4bbdfSmrg
794642e01fSmrg#define IsAnimCur(c)	    ((c) && ((c)->bits == &animCursorBits))
806747b715Smrg#define GetAnimCur(c)	    ((AnimCurPtr) ((((char *)(c) + CURSOR_REC_SIZE))))
811b5d61b8Smrg#define GetAnimCurScreen(s) ((AnimCurScreenPtr)dixLookupPrivate(&(s)->devPrivates, &AnimCurScreenPrivateKeyRec))
8205b261ecSmrg
8305b261ecSmrg#define Wrap(as,s,elt,func) (((as)->elt = (s)->elt), (s)->elt = func)
8405b261ecSmrg#define Unwrap(as,s,elt)    ((s)->elt = (as)->elt)
8505b261ecSmrg
8605b261ecSmrgstatic Bool
8735c4bbdfSmrgAnimCurCloseScreen(ScreenPtr pScreen)
8805b261ecSmrg{
8935c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
9035c4bbdfSmrg    Bool ret;
9105b261ecSmrg
9205b261ecSmrg    Unwrap(as, pScreen, CloseScreen);
9305b261ecSmrg
9405b261ecSmrg    Unwrap(as, pScreen, CursorLimits);
9505b261ecSmrg    Unwrap(as, pScreen, DisplayCursor);
9605b261ecSmrg    Unwrap(as, pScreen, SetCursorPosition);
9705b261ecSmrg    Unwrap(as, pScreen, RealizeCursor);
9805b261ecSmrg    Unwrap(as, pScreen, UnrealizeCursor);
9905b261ecSmrg    Unwrap(as, pScreen, RecolorCursor);
10035c4bbdfSmrg    ret = (*pScreen->CloseScreen) (pScreen);
10105b261ecSmrg    return ret;
10205b261ecSmrg}
10305b261ecSmrg
10435c4bbdfSmrgstatic void
10535c4bbdfSmrgAnimCurCursorLimits(DeviceIntPtr pDev,
10635c4bbdfSmrg                    ScreenPtr pScreen,
10735c4bbdfSmrg                    CursorPtr pCursor, BoxPtr pHotBox, BoxPtr pTopLeftBox)
10805b261ecSmrg{
10935c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
11005b261ecSmrg
11135c4bbdfSmrg    Unwrap(as, pScreen, CursorLimits);
11235c4bbdfSmrg    if (IsAnimCur(pCursor)) {
11335c4bbdfSmrg        AnimCurPtr ac = GetAnimCur(pCursor);
11405b261ecSmrg
11535c4bbdfSmrg        (*pScreen->CursorLimits) (pDev, pScreen, ac->elts[0].pCursor,
11635c4bbdfSmrg                                  pHotBox, pTopLeftBox);
11705b261ecSmrg    }
11835c4bbdfSmrg    else {
11935c4bbdfSmrg        (*pScreen->CursorLimits) (pDev, pScreen, pCursor, pHotBox, pTopLeftBox);
12005b261ecSmrg    }
12135c4bbdfSmrg    Wrap(as, pScreen, CursorLimits, AnimCurCursorLimits);
12205b261ecSmrg}
12305b261ecSmrg
12405b261ecSmrg/*
1251b5d61b8Smrg * The cursor animation timer has expired, go display any relevant cursor changes
1261b5d61b8Smrg * and compute a new timeout value
12705b261ecSmrg */
12805b261ecSmrg
1291b5d61b8Smrgstatic CARD32
1301b5d61b8SmrgAnimCurTimerNotify(OsTimerPtr timer, CARD32 now, void *arg)
13105b261ecSmrg{
1321b5d61b8Smrg    DeviceIntPtr dev = arg;
1331b5d61b8Smrg    ScreenPtr pScreen = dev->spriteInfo->anim.pScreen;
13435c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
13535c4bbdfSmrg
1361b5d61b8Smrg    AnimCurPtr ac = GetAnimCur(dev->spriteInfo->sprite->current);
1371b5d61b8Smrg    int elt = (dev->spriteInfo->anim.elt + 1) % ac->nelt;
1381b5d61b8Smrg    DisplayCursorProcPtr DisplayCursor = pScreen->DisplayCursor;
13935c4bbdfSmrg
1401b5d61b8Smrg    /*
1411b5d61b8Smrg     * Not a simple Unwrap/Wrap as this isn't called along the DisplayCursor
1421b5d61b8Smrg     * wrapper chain.
1431b5d61b8Smrg     */
1441b5d61b8Smrg    pScreen->DisplayCursor = as->DisplayCursor;
1451b5d61b8Smrg    (void) (*pScreen->DisplayCursor) (dev, pScreen, ac->elts[elt].pCursor);
1461b5d61b8Smrg    as->DisplayCursor = pScreen->DisplayCursor;
1471b5d61b8Smrg    pScreen->DisplayCursor = DisplayCursor;
1484642e01fSmrg
1491b5d61b8Smrg    dev->spriteInfo->anim.elt = elt;
1501b5d61b8Smrg    dev->spriteInfo->anim.pCursor = ac->elts[elt].pCursor;
15135c4bbdfSmrg
1521b5d61b8Smrg    return ac->elts[elt].delay;
1531b5d61b8Smrg}
1544642e01fSmrg
1551b5d61b8Smrgstatic void
1561b5d61b8SmrgAnimCurCancelTimer(DeviceIntPtr pDev)
1571b5d61b8Smrg{
1581b5d61b8Smrg    CursorPtr cur = pDev->spriteInfo->sprite ?
1591b5d61b8Smrg                    pDev->spriteInfo->sprite->current : NULL;
1604642e01fSmrg
1611b5d61b8Smrg    if (IsAnimCur(cur))
1621b5d61b8Smrg        TimerCancel(GetAnimCur(cur)->timer);
16305b261ecSmrg}
16405b261ecSmrg
16505b261ecSmrgstatic Bool
16635c4bbdfSmrgAnimCurDisplayCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
16705b261ecSmrg{
16835c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
1691b5d61b8Smrg    Bool ret = TRUE;
17005b261ecSmrg
17135c4bbdfSmrg    if (IsFloating(pDev))
17235c4bbdfSmrg        return FALSE;
17305b261ecSmrg
17435c4bbdfSmrg    Unwrap(as, pScreen, DisplayCursor);
17535c4bbdfSmrg    if (IsAnimCur(pCursor)) {
1761b5d61b8Smrg        if (pCursor != pDev->spriteInfo->sprite->current) {
17735c4bbdfSmrg            AnimCurPtr ac = GetAnimCur(pCursor);
17835c4bbdfSmrg
1791b5d61b8Smrg            AnimCurCancelTimer(pDev);
1801b5d61b8Smrg            ret = (*pScreen->DisplayCursor) (pDev, pScreen,
1811b5d61b8Smrg                                             ac->elts[0].pCursor);
1821b5d61b8Smrg
18335c4bbdfSmrg            if (ret) {
18435c4bbdfSmrg                pDev->spriteInfo->anim.elt = 0;
18535c4bbdfSmrg                pDev->spriteInfo->anim.pCursor = pCursor;
18635c4bbdfSmrg                pDev->spriteInfo->anim.pScreen = pScreen;
18735c4bbdfSmrg
1881b5d61b8Smrg                ac->timer = TimerSet(ac->timer, 0, ac->elts[0].delay,
1891b5d61b8Smrg                                     AnimCurTimerNotify, pDev);
19035c4bbdfSmrg            }
19135c4bbdfSmrg        }
19205b261ecSmrg    }
19335c4bbdfSmrg    else {
1941b5d61b8Smrg        AnimCurCancelTimer(pDev);
19535c4bbdfSmrg        pDev->spriteInfo->anim.pCursor = 0;
19635c4bbdfSmrg        pDev->spriteInfo->anim.pScreen = 0;
19735c4bbdfSmrg        ret = (*pScreen->DisplayCursor) (pDev, pScreen, pCursor);
19805b261ecSmrg    }
19935c4bbdfSmrg    Wrap(as, pScreen, DisplayCursor, AnimCurDisplayCursor);
20005b261ecSmrg    return ret;
20105b261ecSmrg}
20205b261ecSmrg
20305b261ecSmrgstatic Bool
20435c4bbdfSmrgAnimCurSetCursorPosition(DeviceIntPtr pDev,
20535c4bbdfSmrg                         ScreenPtr pScreen, int x, int y, Bool generateEvent)
20605b261ecSmrg{
20735c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
20835c4bbdfSmrg    Bool ret;
20935c4bbdfSmrg
21035c4bbdfSmrg    Unwrap(as, pScreen, SetCursorPosition);
2119ace9065Smrg    if (pDev->spriteInfo->anim.pCursor) {
21235c4bbdfSmrg        pDev->spriteInfo->anim.pScreen = pScreen;
2139ace9065Smrg    }
2144642e01fSmrg    ret = (*pScreen->SetCursorPosition) (pDev, pScreen, x, y, generateEvent);
21535c4bbdfSmrg    Wrap(as, pScreen, SetCursorPosition, AnimCurSetCursorPosition);
21605b261ecSmrg    return ret;
21705b261ecSmrg}
21805b261ecSmrg
21935c4bbdfSmrgstatic Bool
22035c4bbdfSmrgAnimCurRealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
22105b261ecSmrg{
22235c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
22335c4bbdfSmrg    Bool ret;
22435c4bbdfSmrg
22535c4bbdfSmrg    Unwrap(as, pScreen, RealizeCursor);
22605b261ecSmrg    if (IsAnimCur(pCursor))
22735c4bbdfSmrg        ret = TRUE;
22805b261ecSmrg    else
22935c4bbdfSmrg        ret = (*pScreen->RealizeCursor) (pDev, pScreen, pCursor);
23035c4bbdfSmrg    Wrap(as, pScreen, RealizeCursor, AnimCurRealizeCursor);
23105b261ecSmrg    return ret;
23205b261ecSmrg}
23305b261ecSmrg
23435c4bbdfSmrgstatic Bool
23535c4bbdfSmrgAnimCurUnrealizeCursor(DeviceIntPtr pDev, ScreenPtr pScreen, CursorPtr pCursor)
23605b261ecSmrg{
23735c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
23835c4bbdfSmrg    Bool ret;
23935c4bbdfSmrg
24035c4bbdfSmrg    Unwrap(as, pScreen, UnrealizeCursor);
24135c4bbdfSmrg    if (IsAnimCur(pCursor)) {
24235c4bbdfSmrg        AnimCurPtr ac = GetAnimCur(pCursor);
24335c4bbdfSmrg        int i;
24435c4bbdfSmrg
24535c4bbdfSmrg        if (pScreen->myNum == 0)
24635c4bbdfSmrg            for (i = 0; i < ac->nelt; i++)
24735c4bbdfSmrg                FreeCursor(ac->elts[i].pCursor, 0);
24835c4bbdfSmrg        ret = TRUE;
24905b261ecSmrg    }
25005b261ecSmrg    else
25135c4bbdfSmrg        ret = (*pScreen->UnrealizeCursor) (pDev, pScreen, pCursor);
25235c4bbdfSmrg    Wrap(as, pScreen, UnrealizeCursor, AnimCurUnrealizeCursor);
25305b261ecSmrg    return ret;
25405b261ecSmrg}
25505b261ecSmrg
25605b261ecSmrgstatic void
25735c4bbdfSmrgAnimCurRecolorCursor(DeviceIntPtr pDev,
25835c4bbdfSmrg                     ScreenPtr pScreen, CursorPtr pCursor, Bool displayed)
25905b261ecSmrg{
26035c4bbdfSmrg    AnimCurScreenPtr as = GetAnimCurScreen(pScreen);
26135c4bbdfSmrg
26235c4bbdfSmrg    Unwrap(as, pScreen, RecolorCursor);
26335c4bbdfSmrg    if (IsAnimCur(pCursor)) {
26435c4bbdfSmrg        AnimCurPtr ac = GetAnimCur(pCursor);
26535c4bbdfSmrg        int i;
26605b261ecSmrg
26705b261ecSmrg        for (i = 0; i < ac->nelt; i++)
26835c4bbdfSmrg            (*pScreen->RecolorCursor) (pDev, pScreen, ac->elts[i].pCursor,
26935c4bbdfSmrg                                       displayed &&
27035c4bbdfSmrg                                       pDev->spriteInfo->anim.elt == i);
27105b261ecSmrg    }
27205b261ecSmrg    else
27335c4bbdfSmrg        (*pScreen->RecolorCursor) (pDev, pScreen, pCursor, displayed);
27435c4bbdfSmrg    Wrap(as, pScreen, RecolorCursor, AnimCurRecolorCursor);
27505b261ecSmrg}
27605b261ecSmrg
27705b261ecSmrgBool
27835c4bbdfSmrgAnimCurInit(ScreenPtr pScreen)
27905b261ecSmrg{
28035c4bbdfSmrg    AnimCurScreenPtr as;
28105b261ecSmrg
2821b5d61b8Smrg    if (!dixRegisterPrivateKey(&AnimCurScreenPrivateKeyRec, PRIVATE_SCREEN,
2831b5d61b8Smrg                               sizeof(AnimCurScreenRec)))
28435c4bbdfSmrg        return FALSE;
2856747b715Smrg
2861b5d61b8Smrg    as = GetAnimCurScreen(pScreen);
28705b261ecSmrg
2881b5d61b8Smrg    Wrap(as, pScreen, CloseScreen, AnimCurCloseScreen);
28905b261ecSmrg
29005b261ecSmrg    Wrap(as, pScreen, CursorLimits, AnimCurCursorLimits);
29105b261ecSmrg    Wrap(as, pScreen, DisplayCursor, AnimCurDisplayCursor);
29205b261ecSmrg    Wrap(as, pScreen, SetCursorPosition, AnimCurSetCursorPosition);
29305b261ecSmrg    Wrap(as, pScreen, RealizeCursor, AnimCurRealizeCursor);
29405b261ecSmrg    Wrap(as, pScreen, UnrealizeCursor, AnimCurUnrealizeCursor);
29505b261ecSmrg    Wrap(as, pScreen, RecolorCursor, AnimCurRecolorCursor);
29605b261ecSmrg    return TRUE;
29705b261ecSmrg}
29805b261ecSmrg
29905b261ecSmrgint
30035c4bbdfSmrgAnimCursorCreate(CursorPtr *cursors, CARD32 *deltas, int ncursor,
30135c4bbdfSmrg                 CursorPtr *ppCursor, ClientPtr client, XID cid)
30205b261ecSmrg{
30335c4bbdfSmrg    CursorPtr pCursor;
3041b5d61b8Smrg    int rc = BadAlloc, i;
30535c4bbdfSmrg    AnimCurPtr ac;
30605b261ecSmrg
30754b5899cSmrg    if (ncursor <= 0)
30854b5899cSmrg        return BadValue;
30954b5899cSmrg
31005b261ecSmrg    for (i = 0; i < screenInfo.numScreens; i++)
31135c4bbdfSmrg        if (!GetAnimCurScreen(screenInfo.screens[i]))
31235c4bbdfSmrg            return BadImplementation;
31305b261ecSmrg
31405b261ecSmrg    for (i = 0; i < ncursor; i++)
31535c4bbdfSmrg        if (IsAnimCur(cursors[i]))
31635c4bbdfSmrg            return BadMatch;
31735c4bbdfSmrg
3186747b715Smrg    pCursor = (CursorPtr) calloc(CURSOR_REC_SIZE +
31935c4bbdfSmrg                                 sizeof(AnimCurRec) +
32035c4bbdfSmrg                                 ncursor * sizeof(AnimCurElt), 1);
32105b261ecSmrg    if (!pCursor)
3221b5d61b8Smrg        return rc;
3236747b715Smrg    dixInitPrivates(pCursor, pCursor + 1, PRIVATE_CURSOR);
32405b261ecSmrg    pCursor->bits = &animCursorBits;
32505b261ecSmrg    pCursor->refcnt = 1;
32635c4bbdfSmrg
32705b261ecSmrg    pCursor->foreRed = cursors[0]->foreRed;
32805b261ecSmrg    pCursor->foreGreen = cursors[0]->foreGreen;
32905b261ecSmrg    pCursor->foreBlue = cursors[0]->foreBlue;
33035c4bbdfSmrg
33105b261ecSmrg    pCursor->backRed = cursors[0]->backRed;
33205b261ecSmrg    pCursor->backGreen = cursors[0]->backGreen;
33305b261ecSmrg    pCursor->backBlue = cursors[0]->backBlue;
33405b261ecSmrg
3354642e01fSmrg    pCursor->id = cid;
3364642e01fSmrg
3371b5d61b8Smrg    ac = GetAnimCur(pCursor);
3381b5d61b8Smrg    ac->timer = TimerSet(NULL, 0, 0, AnimCurTimerNotify, NULL);
3391b5d61b8Smrg
3404642e01fSmrg    /* security creation/labeling check */
3411b5d61b8Smrg    if (ac->timer)
3421b5d61b8Smrg        rc = XaceHook(XACE_RESOURCE_ACCESS, client, cid, RT_CURSOR, pCursor,
3431b5d61b8Smrg                      RT_NONE, NULL, DixCreateAccess);
3441b5d61b8Smrg
3454642e01fSmrg    if (rc != Success) {
3461b5d61b8Smrg        TimerFree(ac->timer);
34735c4bbdfSmrg        dixFiniPrivates(pCursor, PRIVATE_CURSOR);
34835c4bbdfSmrg        free(pCursor);
34935c4bbdfSmrg        return rc;
3504642e01fSmrg    }
35135c4bbdfSmrg
35205b261ecSmrg    /*
35305b261ecSmrg     * Fill in the AnimCurRec
35405b261ecSmrg     */
3554642e01fSmrg    animCursorBits.refcnt++;
35605b261ecSmrg    ac->nelt = ncursor;
35705b261ecSmrg    ac->elts = (AnimCurElt *) (ac + 1);
35835c4bbdfSmrg
35935c4bbdfSmrg    for (i = 0; i < ncursor; i++) {
36035c4bbdfSmrg        ac->elts[i].pCursor = RefCursor(cursors[i]);
36135c4bbdfSmrg        ac->elts[i].delay = deltas[i];
36205b261ecSmrg    }
36335c4bbdfSmrg
36405b261ecSmrg    *ppCursor = pCursor;
36505b261ecSmrg    return Success;
36605b261ecSmrg}
367