1/*
2 * Copyright (c) 2014 Intel Corporation
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 FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 */
24
25#include <X11/Xlib.h>
26#include <X11/Xlib-xcb.h>
27#include <X11/xshmfence.h>
28#include <xcb/xcb.h>
29#include <xcb/dri3.h>
30#include <xcb/sync.h>
31#include <unistd.h>
32#include <stdlib.h>
33
34#include "dri3.h"
35
36Pixmap dri3_create_pixmap(Display *dpy,
37			  Drawable draw,
38			  int width, int height, int depth,
39			  int fd, int bpp, int stride, int size)
40{
41	xcb_connection_t *c = XGetXCBConnection(dpy);
42	if (fd >= 0) {
43		xcb_pixmap_t pixmap = xcb_generate_id(c);
44		xcb_dri3_pixmap_from_buffer(c, pixmap, draw, size, width, height, stride, depth, bpp, fd);
45		return pixmap;
46	}
47	return 0;
48}
49
50int dri3_create_fd(Display *dpy,
51		   Pixmap pixmap,
52		   int *stride)
53{
54	xcb_connection_t *c = XGetXCBConnection(dpy);
55	xcb_dri3_buffer_from_pixmap_cookie_t cookie;
56	xcb_dri3_buffer_from_pixmap_reply_t *reply;
57
58	cookie = xcb_dri3_buffer_from_pixmap(c, pixmap);
59	reply = xcb_dri3_buffer_from_pixmap_reply(c, cookie, NULL);
60	if (!reply)
61		return -1;
62
63	if (reply->nfd != 1)
64		return -1;
65
66	*stride = reply->stride;
67	return xcb_dri3_buffer_from_pixmap_reply_fds(c, reply)[0];
68}
69
70int dri3_create_fence(Display *dpy, Pixmap pixmap, struct dri3_fence *fence)
71{
72	xcb_connection_t *c = XGetXCBConnection(dpy);
73	struct dri3_fence f;
74	int fd;
75
76	fd = xshmfence_alloc_shm();
77	if (fd < 0)
78		return -1;
79
80	f.addr = xshmfence_map_shm(fd);
81	if (f.addr == NULL) {
82		close(fd);
83		return -1;
84	}
85
86	f.xid = xcb_generate_id(c);
87	xcb_dri3_fence_from_fd(c, pixmap, f.xid, 0, fd);
88
89	*fence = f;
90	return 0;
91}
92
93void dri3_fence_sync(Display *dpy, struct dri3_fence *fence)
94{
95	xcb_connection_t *c = XGetXCBConnection(dpy);
96
97	xshmfence_reset(fence->addr);
98
99	xcb_sync_trigger_fence(c, fence->xid);
100	xcb_flush(c);
101
102	xshmfence_await(fence->addr);
103}
104
105void dri3_fence_free(Display *dpy, struct dri3_fence *fence)
106{
107	xcb_connection_t *c = XGetXCBConnection(dpy);
108
109	xshmfence_unmap_shm(fence->addr);
110	xcb_sync_destroy_fence(c, fence->xid);
111}
112
113static void dri3_query_version(xcb_connection_t *c, int *major, int *minor)
114{
115	xcb_dri3_query_version_reply_t *reply;
116
117	reply = xcb_dri3_query_version_reply(c,
118					     xcb_dri3_query_version(c,
119								    XCB_DRI3_MAJOR_VERSION,
120								    XCB_DRI3_MINOR_VERSION),
121					     NULL);
122	if (reply != NULL) {
123		*major = reply->major_version;
124		*minor = reply->minor_version;
125		free(reply);
126	}
127}
128
129static int dri3_exists(xcb_connection_t *c)
130{
131	const xcb_query_extension_reply_t *ext;
132	int major, minor;
133
134	major = minor = -1;
135
136	ext = xcb_get_extension_data(c, &xcb_dri3_id);
137	if (ext != NULL && ext->present)
138		dri3_query_version(c, &major, &minor);
139
140	return major >= 0;
141}
142
143int dri3_open__full(Display *dpy, Window root, unsigned provider)
144{
145	xcb_connection_t *c = XGetXCBConnection(dpy);
146	xcb_dri3_open_cookie_t cookie;
147	xcb_dri3_open_reply_t *reply;
148
149	if (!dri3_exists(c))
150		return -1;
151
152	cookie = xcb_dri3_open(c, root, provider);
153	reply = xcb_dri3_open_reply(c, cookie, NULL);
154
155	if (!reply)
156		return -1;
157
158	if (reply->nfd != 1)
159		return -1;
160
161	return xcb_dri3_open_reply_fds(c, reply)[0];
162}
163
164int dri3_open(Display *dpy)
165{
166	return dri3_open__full(dpy, RootWindow(dpy, DefaultScreen(dpy)), None);
167}
168