1/*
2 * Copyright © 2013 Keith Packard
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission.  The copyright holders make no representations
11 * about the suitability of this software for any purpose.  It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22
23#include "present_priv.h"
24
25static RESTYPE present_event_type;
26
27static int
28present_free_event(void *data, XID id)
29{
30    present_event_ptr present_event = (present_event_ptr) data;
31    present_window_priv_ptr window_priv = present_window_priv(present_event->window);
32    present_event_ptr *previous, current;
33
34    for (previous = &window_priv->events; (current = *previous); previous = &current->next) {
35        if (current == present_event) {
36            *previous = present_event->next;
37            break;
38        }
39    }
40    free((void *) present_event);
41    return 1;
42
43}
44
45void
46present_free_events(WindowPtr window)
47{
48    present_window_priv_ptr window_priv = present_window_priv(window);
49    present_event_ptr event;
50
51    if (!window_priv)
52        return;
53
54    while ((event = window_priv->events))
55        FreeResource(event->id, RT_NONE);
56}
57
58static void
59present_event_swap(xGenericEvent *from, xGenericEvent *to)
60{
61    *to = *from;
62    swaps(&to->sequenceNumber);
63    swapl(&to->length);
64    swaps(&to->evtype);
65    switch (from->evtype) {
66    case PresentConfigureNotify: {
67        xPresentConfigureNotify *c = (xPresentConfigureNotify *) to;
68
69        swapl(&c->eid);
70        swapl(&c->window);
71        swaps(&c->x);
72        swaps(&c->y);
73        swaps(&c->width);
74        swaps(&c->height);
75        swaps(&c->off_x);
76        swaps(&c->off_y);
77        swaps(&c->pixmap_width);
78        swaps(&c->pixmap_height);
79        swapl(&c->pixmap_flags);
80        break;
81    }
82    case PresentCompleteNotify:
83    {
84        xPresentCompleteNotify *c = (xPresentCompleteNotify *) to;
85        swapl(&c->eid);
86        swapl(&c->window);
87        swapl(&c->serial);
88        swapll(&c->ust);
89        swapll(&c->msc);
90        break;
91    }
92    case PresentIdleNotify:
93    {
94        xPresentIdleNotify *c = (xPresentIdleNotify *) to;
95        swapl(&c->eid);
96        swapl(&c->window);
97        swapl(&c->serial);
98        swapl(&c->idle_fence);
99        break;
100    }
101    }
102}
103
104void
105present_send_config_notify(WindowPtr window, int x, int y, int w, int h,
106                           int bw, WindowPtr sibling, CARD32 flags)
107{
108    present_window_priv_ptr window_priv = present_window_priv(window);
109
110    if (window_priv) {
111        xPresentConfigureNotify cn = {
112            .type = GenericEvent,
113            .extension = present_request,
114            .length = (sizeof(xPresentConfigureNotify) - 32) >> 2,
115            .evtype = PresentConfigureNotify,
116            .eid = 0,
117            .window = window->drawable.id,
118            .x = x,
119            .y = y,
120            .width = w,
121            .height = h,
122            .off_x = 0,
123            .off_y = 0,
124            .pixmap_width = w,
125            .pixmap_height = h,
126            .pixmap_flags = flags
127        };
128        present_event_ptr event;
129
130        for (event = window_priv->events; event; event = event->next) {
131            if (event->mask & (1 << PresentConfigureNotify)) {
132                cn.eid = event->id;
133                WriteEventsToClient(event->client, 1, (xEvent *) &cn);
134            }
135        }
136    }
137}
138
139static present_complete_notify_proc complete_notify;
140
141void
142present_register_complete_notify(present_complete_notify_proc proc)
143{
144    complete_notify = proc;
145}
146
147void
148present_send_complete_notify(WindowPtr window, CARD8 kind, CARD8 mode, CARD32 serial, uint64_t ust, uint64_t msc)
149{
150    present_window_priv_ptr window_priv = present_window_priv(window);
151
152    if (window_priv) {
153        xPresentCompleteNotify cn = {
154            .type = GenericEvent,
155            .extension = present_request,
156            .length = (sizeof(xPresentCompleteNotify) - 32) >> 2,
157            .evtype = PresentCompleteNotify,
158            .kind = kind,
159            .mode = mode,
160            .eid = 0,
161            .window = window->drawable.id,
162            .serial = serial,
163            .ust = ust,
164            .msc = msc,
165        };
166        present_event_ptr event;
167
168        for (event = window_priv->events; event; event = event->next) {
169            if (event->mask & PresentCompleteNotifyMask) {
170                cn.eid = event->id;
171                WriteEventsToClient(event->client, 1, (xEvent *) &cn);
172            }
173        }
174    }
175    if (complete_notify)
176        (*complete_notify)(window, kind, mode, serial, ust, msc);
177}
178
179void
180present_send_idle_notify(WindowPtr window, CARD32 serial, PixmapPtr pixmap, struct present_fence *idle_fence)
181{
182    present_window_priv_ptr window_priv = present_window_priv(window);
183
184    if (window_priv) {
185        xPresentIdleNotify in = {
186            .type = GenericEvent,
187            .extension = present_request,
188            .length = (sizeof(xPresentIdleNotify) - 32) >> 2,
189            .evtype = PresentIdleNotify,
190            .eid = 0,
191            .window = window->drawable.id,
192            .serial = serial,
193            .pixmap = pixmap->drawable.id,
194            .idle_fence = present_fence_id(idle_fence)
195        };
196        present_event_ptr event;
197
198        for (event = window_priv->events; event; event = event->next) {
199            if (event->mask & PresentIdleNotifyMask) {
200                in.eid = event->id;
201                WriteEventsToClient(event->client, 1, (xEvent *) &in);
202            }
203        }
204    }
205}
206
207int
208present_select_input(ClientPtr client, XID eid, WindowPtr window, CARD32 mask)
209{
210    present_window_priv_ptr window_priv;
211    present_event_ptr event;
212    int ret;
213
214    /* Check to see if we're modifying an existing event selection */
215    ret = dixLookupResourceByType((void **) &event, eid, present_event_type,
216                                 client, DixWriteAccess);
217    if (ret == Success) {
218        /* Match error for the wrong window; also don't modify some other
219         * client's event selection
220         */
221        if (event->window != window || event->client != client)
222            return BadMatch;
223
224        if (mask)
225            event->mask = mask;
226        else
227            FreeResource(eid, RT_NONE);
228        return Success;
229    }
230    if (ret != BadValue)
231        return ret;
232
233    if (mask == 0)
234        return Success;
235
236    LEGAL_NEW_RESOURCE(eid, client);
237
238    window_priv = present_get_window_priv(window, TRUE);
239    if (!window_priv)
240        return BadAlloc;
241
242    event = calloc (1, sizeof (present_event_rec));
243    if (!event)
244        return BadAlloc;
245
246    event->client = client;
247    event->window = window;
248    event->id = eid;
249    event->mask = mask;
250
251    event->next = window_priv->events;
252    window_priv->events = event;
253
254    if (!AddResource(event->id, present_event_type, (void *) event))
255        return BadAlloc;
256
257    return Success;
258}
259
260Bool
261present_event_init(void)
262{
263    present_event_type = CreateNewResourceType(present_free_event, "PresentEvent");
264    if (!present_event_type)
265        return FALSE;
266
267    GERegisterExtension(present_request, present_event_swap);
268    return TRUE;
269}
270