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#include <gcstruct.h>
25
26uint32_t
27present_query_capabilities(RRCrtcPtr crtc)
28{
29    present_screen_priv_ptr screen_priv;
30
31    if (!crtc)
32        return 0;
33
34    screen_priv = present_screen_priv(crtc->pScreen);
35
36    if (!screen_priv)
37        return 0;
38
39    return screen_priv->query_capabilities(screen_priv);
40}
41
42RRCrtcPtr
43present_get_crtc(WindowPtr window)
44{
45    ScreenPtr                   screen = window->drawable.pScreen;
46    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
47    RRCrtcPtr                   crtc = NULL;
48
49    if (!screen_priv)
50        return NULL;
51
52    crtc = screen_priv->get_crtc(screen_priv, window);
53    if (crtc && !present_screen_priv(crtc->pScreen)) {
54        crtc = RRFirstEnabledCrtc(screen);
55    }
56    if (crtc && !present_screen_priv(crtc->pScreen)) {
57        crtc = NULL;
58    }
59    return crtc;
60}
61
62/*
63 * Copies the update region from a pixmap to the target drawable
64 */
65void
66present_copy_region(DrawablePtr drawable,
67                    PixmapPtr pixmap,
68                    RegionPtr update,
69                    int16_t x_off,
70                    int16_t y_off)
71{
72    ScreenPtr   screen = drawable->pScreen;
73    GCPtr       gc;
74
75    gc = GetScratchGC(drawable->depth, screen);
76    if (update) {
77        ChangeGCVal     changes[2];
78
79        changes[0].val = x_off;
80        changes[1].val = y_off;
81        ChangeGC(serverClient, gc,
82                 GCClipXOrigin|GCClipYOrigin,
83                 changes);
84        (*gc->funcs->ChangeClip)(gc, CT_REGION, update, 0);
85    }
86    ValidateGC(drawable, gc);
87    (*gc->ops->CopyArea)(&pixmap->drawable,
88                         drawable,
89                         gc,
90                         0, 0,
91                         pixmap->drawable.width, pixmap->drawable.height,
92                         x_off, y_off);
93    if (update)
94        (*gc->funcs->ChangeClip)(gc, CT_NONE, NULL, 0);
95    FreeScratchGC(gc);
96}
97
98void
99present_pixmap_idle(PixmapPtr pixmap, WindowPtr window, CARD32 serial, struct present_fence *present_fence)
100{
101    if (present_fence)
102        present_fence_set_triggered(present_fence);
103    if (window) {
104        DebugPresent(("\ti %08" PRIx32 "\n", pixmap ? pixmap->drawable.id : 0));
105        present_send_idle_notify(window, serial, pixmap, present_fence);
106    }
107}
108
109struct pixmap_visit {
110    PixmapPtr   old;
111    PixmapPtr   new;
112};
113
114static int
115present_set_tree_pixmap_visit(WindowPtr window, void *data)
116{
117    struct pixmap_visit *visit = data;
118    ScreenPtr           screen = window->drawable.pScreen;
119
120    if ((*screen->GetWindowPixmap)(window) != visit->old)
121        return WT_DONTWALKCHILDREN;
122    (*screen->SetWindowPixmap)(window, visit->new);
123    return WT_WALKCHILDREN;
124}
125
126void
127present_set_tree_pixmap(WindowPtr window,
128                        PixmapPtr expected,
129                        PixmapPtr pixmap)
130{
131    struct pixmap_visit visit;
132    ScreenPtr           screen = window->drawable.pScreen;
133
134    visit.old = (*screen->GetWindowPixmap)(window);
135    if (expected && visit.old != expected)
136        return;
137
138    visit.new = pixmap;
139    if (visit.old == visit.new)
140        return;
141    TraverseTree(window, present_set_tree_pixmap_visit, &visit);
142}
143
144Bool
145present_can_window_flip(WindowPtr window)
146{
147    ScreenPtr                   screen = window->drawable.pScreen;
148    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
149
150    return screen_priv->can_window_flip(window);
151}
152
153uint64_t
154present_get_target_msc(uint64_t target_msc_arg,
155                       uint64_t crtc_msc,
156                       uint64_t divisor,
157                       uint64_t remainder,
158                       uint32_t options)
159{
160    const Bool  synced_flip = !(options & PresentOptionAsync);
161    uint64_t    target_msc;
162
163    /* If the specified target-msc lies in the future, then this
164     * defines the target-msc according to Present protocol.
165     */
166    if (msc_is_after(target_msc_arg, crtc_msc))
167        return target_msc_arg;
168
169    /* If no divisor is specified, the modulo is undefined
170     * and we do present instead asap.
171     */
172    if (divisor == 0) {
173        target_msc = crtc_msc;
174
175        /* When no async presentation is forced, by default we sync the
176         * presentation with vblank. But in this case we can't target
177         * the current crtc-msc, which already has begun, but must aim
178         * for the upcoming one.
179         */
180        if (synced_flip)
181            target_msc++;
182
183        return target_msc;
184    }
185
186    /* Calculate target-msc by the specified modulo parameters. According
187     * to Present protocol this is after the next field with:
188     *
189     *      field-msc % divisor == remainder.
190     *
191     * The following formula calculates a target_msc solving above equation
192     * and with |target_msc - crtc_msc| < divisor.
193     *
194     * Example with crtc_msc = 10, divisor = 4 and remainder = 3, 2, 1, 0:
195     *      11 = 10 - 2 + 3 = 10 - (10 % 4) + 3,
196     *      10 = 10 - 2 + 2 = 10 - (10 % 4) + 2,
197     *       9 = 10 - 2 + 1 = 10 - (10 % 4) + 1,
198     *       8 = 10 - 2 + 0 = 10 - (10 % 4) + 0.
199     */
200    target_msc = crtc_msc - (crtc_msc % divisor) + remainder;
201
202    /* Here we already found the correct field-msc. */
203    if (msc_is_after(target_msc, crtc_msc))
204        return target_msc;
205    /*
206     * Here either:
207     * a) target_msc == crtc_msc, i.e. crtc_msc actually solved
208     * above equation with crtc_msc % divisor == remainder.
209     *
210     * => This means we want to present at target_msc + divisor for a synced
211     *    flip or directly now for an async flip.
212     *
213     * b) target_msc < crtc_msc with target_msc + divisor > crtc_msc.
214     *
215     * => This means in any case we want to present at target_msc + divisor.
216     */
217    if (synced_flip || msc_is_after(crtc_msc, target_msc))
218        target_msc += divisor;
219    return target_msc;
220}
221
222int
223present_pixmap(WindowPtr window,
224               PixmapPtr pixmap,
225               CARD32 serial,
226               RegionPtr valid,
227               RegionPtr update,
228               int16_t x_off,
229               int16_t y_off,
230               RRCrtcPtr target_crtc,
231               SyncFence *wait_fence,
232               SyncFence *idle_fence,
233               uint32_t options,
234               uint64_t window_msc,
235               uint64_t divisor,
236               uint64_t remainder,
237               present_notify_ptr notifies,
238               int num_notifies)
239{
240    ScreenPtr                   screen = window->drawable.pScreen;
241    present_screen_priv_ptr     screen_priv = present_screen_priv(screen);
242
243    return screen_priv->present_pixmap(window,
244                                       pixmap,
245                                       serial,
246                                       valid,
247                                       update,
248                                       x_off,
249                                       y_off,
250                                       target_crtc,
251                                       wait_fence,
252                                       idle_fence,
253                                       options,
254                                       window_msc,
255                                       divisor,
256                                       remainder,
257                                       notifies,
258                                       num_notifies);
259}
260
261int
262present_notify_msc(WindowPtr window,
263                   CARD32 serial,
264                   uint64_t target_msc,
265                   uint64_t divisor,
266                   uint64_t remainder)
267{
268    return present_pixmap(window,
269                          NULL,
270                          serial,
271                          NULL, NULL,
272                          0, 0,
273                          NULL,
274                          NULL, NULL,
275                          divisor == 0 ? PresentOptionAsync : 0,
276                          target_msc, divisor, remainder, NULL, 0);
277}
278