1/*
2 * Copyright © 2014 Red Hat, Inc.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Author:
24 *      Adam Jackson <ajax@redhat.com>
25 */
26
27#ifdef HAVE_DIX_CONFIG_H
28#include <dix-config.h>
29#endif
30#include "ephyr.h"
31#include "ephyrlog.h"
32#include "hostx.h"
33#include "cursorstr.h"
34#include <xcb/render.h>
35#include <xcb/xcb_renderutil.h>
36
37static DevPrivateKeyRec ephyrCursorPrivateKey;
38
39typedef struct _ephyrCursor {
40    xcb_cursor_t cursor;
41} ephyrCursorRec, *ephyrCursorPtr;
42
43static ephyrCursorPtr
44ephyrGetCursor(CursorPtr cursor)
45{
46    return dixGetPrivateAddr(&cursor->devPrivates, &ephyrCursorPrivateKey);
47}
48
49static void
50ephyrRealizeCoreCursor(EphyrScrPriv *scr, CursorPtr cursor)
51{
52    ephyrCursorPtr hw = ephyrGetCursor(cursor);
53    xcb_connection_t *conn = hostx_get_xcbconn();
54    xcb_pixmap_t source, mask;
55    xcb_image_t *image;
56    xcb_gcontext_t gc;
57    int w = cursor->bits->width, h = cursor->bits->height;
58    uint32_t gcmask = XCB_GC_FUNCTION |
59                      XCB_GC_PLANE_MASK |
60                      XCB_GC_FOREGROUND |
61                      XCB_GC_BACKGROUND |
62                      XCB_GC_CLIP_MASK;
63    uint32_t val[] = {
64        XCB_GX_COPY,    /* function */
65        ~0,             /* planemask */
66        1L,             /* foreground */
67        0L,             /* background */
68        None,           /* clipmask */
69    };
70
71    source = xcb_generate_id(conn);
72    mask = xcb_generate_id(conn);
73    xcb_create_pixmap(conn, 1, source, scr->win, w, h);
74    xcb_create_pixmap(conn, 1, mask, scr->win, w, h);
75
76    gc = xcb_generate_id(conn);
77    xcb_create_gc(conn, gc, source, gcmask, val);
78
79    image = xcb_image_create_native(conn, w, h, XCB_IMAGE_FORMAT_XY_BITMAP,
80                                    1, NULL, ~0, NULL);
81    image->data = cursor->bits->source;
82    xcb_image_put(conn, source, gc, image, 0, 0, 0);
83    xcb_image_destroy(image);
84
85    image = xcb_image_create_native(conn, w, h, XCB_IMAGE_FORMAT_XY_BITMAP,
86                                    1, NULL, ~0, NULL);
87    image->data = cursor->bits->mask;
88    xcb_image_put(conn, mask, gc, image, 0, 0, 0);
89    xcb_image_destroy(image);
90
91    xcb_free_gc(conn, gc);
92
93    hw->cursor = xcb_generate_id(conn);
94    xcb_create_cursor(conn, hw->cursor, source, mask,
95                      cursor->foreRed, cursor->foreGreen, cursor->foreBlue,
96                      cursor->backRed, cursor->backGreen, cursor->backBlue,
97                      cursor->bits->xhot, cursor->bits->yhot);
98
99    xcb_free_pixmap(conn, source);
100    xcb_free_pixmap(conn, mask);
101}
102
103static xcb_render_pictformat_t
104get_argb_format(void)
105{
106    static xcb_render_pictformat_t format;
107    if (format == None) {
108        xcb_connection_t *conn = hostx_get_xcbconn();
109        xcb_render_query_pict_formats_cookie_t cookie;
110        xcb_render_query_pict_formats_reply_t *formats;
111
112        cookie = xcb_render_query_pict_formats(conn);
113        formats =
114            xcb_render_query_pict_formats_reply(conn, cookie, NULL);
115
116        format =
117            xcb_render_util_find_standard_format(formats,
118                                                 XCB_PICT_STANDARD_ARGB_32)->id;
119
120        free(formats);
121    }
122
123    return format;
124}
125
126static void
127ephyrRealizeARGBCursor(EphyrScrPriv *scr, CursorPtr cursor)
128{
129    ephyrCursorPtr hw = ephyrGetCursor(cursor);
130    xcb_connection_t *conn = hostx_get_xcbconn();
131    xcb_gcontext_t gc;
132    xcb_pixmap_t source;
133    xcb_render_picture_t picture;
134    xcb_image_t *image;
135    int w = cursor->bits->width, h = cursor->bits->height;
136
137    /* dix' storage is PICT_a8r8g8b8 */
138    source = xcb_generate_id(conn);
139    xcb_create_pixmap(conn, 32, source, scr->win, w, h);
140
141    gc = xcb_generate_id(conn);
142    xcb_create_gc(conn, gc, source, 0, NULL);
143    image = xcb_image_create_native(conn, w, h, XCB_IMAGE_FORMAT_Z_PIXMAP,
144                                    32, NULL, ~0, NULL);
145    image->data = (void *)cursor->bits->argb;
146    xcb_image_put(conn, source, gc, image, 0, 0, 0);
147    xcb_free_gc(conn, gc);
148    xcb_image_destroy(image);
149
150    picture = xcb_generate_id(conn);
151    xcb_render_create_picture(conn, picture, source, get_argb_format(),
152                              0, NULL);
153    xcb_free_pixmap(conn, source);
154
155    hw->cursor = xcb_generate_id(conn);
156    xcb_render_create_cursor(conn, hw->cursor, picture,
157                             cursor->bits->xhot, cursor->bits->yhot);
158
159    xcb_render_free_picture(conn, picture);
160}
161
162static Bool
163can_argb_cursor(void)
164{
165    static const xcb_render_query_version_reply_t *v;
166
167    if (!v)
168        v = xcb_render_util_query_version(hostx_get_xcbconn());
169
170    return v->major_version == 0 && v->minor_version >= 5;
171}
172
173static Bool
174ephyrRealizeCursor(DeviceIntPtr dev, ScreenPtr screen, CursorPtr cursor)
175{
176    KdScreenPriv(screen);
177    KdScreenInfo *kscr = pScreenPriv->screen;
178    EphyrScrPriv *scr = kscr->driver;
179
180    if (cursor->bits->argb && can_argb_cursor())
181        ephyrRealizeARGBCursor(scr, cursor);
182    else
183    {
184        ephyrRealizeCoreCursor(scr, cursor);
185    }
186    return TRUE;
187}
188
189static Bool
190ephyrUnrealizeCursor(DeviceIntPtr dev, ScreenPtr screen, CursorPtr cursor)
191{
192    ephyrCursorPtr hw = ephyrGetCursor(cursor);
193
194    if (hw->cursor) {
195        xcb_free_cursor(hostx_get_xcbconn(), hw->cursor);
196        hw->cursor = None;
197    }
198
199    return TRUE;
200}
201
202static void
203ephyrSetCursor(DeviceIntPtr dev, ScreenPtr screen, CursorPtr cursor, int x,
204               int y)
205{
206    KdScreenPriv(screen);
207    KdScreenInfo *kscr = pScreenPriv->screen;
208    EphyrScrPriv *scr = kscr->driver;
209    uint32_t attr = None;
210
211    if (cursor)
212        attr = ephyrGetCursor(cursor)->cursor;
213    else
214        attr = hostx_get_empty_cursor();
215
216    xcb_change_window_attributes(hostx_get_xcbconn(), scr->win,
217                                 XCB_CW_CURSOR, &attr);
218    xcb_flush(hostx_get_xcbconn());
219}
220
221static void
222ephyrMoveCursor(DeviceIntPtr dev, ScreenPtr screen, int x, int y)
223{
224}
225
226static Bool
227ephyrDeviceCursorInitialize(DeviceIntPtr dev, ScreenPtr screen)
228{
229    return TRUE;
230}
231
232static void
233ephyrDeviceCursorCleanup(DeviceIntPtr dev, ScreenPtr screen)
234{
235}
236
237miPointerSpriteFuncRec EphyrPointerSpriteFuncs = {
238    ephyrRealizeCursor,
239    ephyrUnrealizeCursor,
240    ephyrSetCursor,
241    ephyrMoveCursor,
242    ephyrDeviceCursorInitialize,
243    ephyrDeviceCursorCleanup
244};
245
246Bool
247ephyrCursorInit(ScreenPtr screen)
248{
249    if (!dixRegisterPrivateKey(&ephyrCursorPrivateKey, PRIVATE_CURSOR,
250                               sizeof(ephyrCursorRec)))
251        return FALSE;
252
253    miPointerInitialize(screen,
254                        &EphyrPointerSpriteFuncs,
255                        &ephyrPointerScreenFuncs, FALSE);
256
257    return TRUE;
258}
259