Commit | Line | Data |
---|---|---|
a09e091a JB |
1 | /* |
2 | * Xephyr - A kdrive X server thats runs in a host X window. | |
3 | * Authored by Matthew Allum <mallum@openedhand.com> | |
4 | * | |
5 | * Copyright © 2007 OpenedHand Ltd | |
6 | * | |
7 | * Permission to use, copy, modify, distribute, and sell this software and its | |
8 | * documentation for any purpose is hereby granted without fee, provided that | |
9 | * the above copyright notice appear in all copies and that both that | |
10 | * copyright notice and this permission notice appear in supporting | |
11 | * documentation, and that the name of OpenedHand Ltd not be used in | |
12 | * advertising or publicity pertaining to distribution of the software without | |
13 | * specific, written prior permission. OpenedHand Ltd makes no | |
14 | * representations about the suitability of this software for any purpose. It | |
15 | * is provided "as is" without express or implied warranty. | |
16 | * | |
17 | * OpenedHand Ltd DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, | |
18 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO | |
19 | * EVENT SHALL OpenedHand Ltd BE LIABLE FOR ANY SPECIAL, INDIRECT OR | |
20 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, | |
21 | * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER | |
22 | * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | |
23 | * PERFORMANCE OF THIS SOFTWARE. | |
24 | * | |
25 | * a lots of the content of this file has been adapted from the mesa source | |
26 | * code. | |
27 | * Authors: | |
28 | * Dodji Seketeli <dodji@openedhand.com> | |
29 | */ | |
30 | #ifdef HAVE_CONFIG_H | |
31 | #include <kdrive-config.h> | |
32 | #endif | |
33 | ||
34 | #include <X11/Xdefs.h> | |
35 | #include <X11/Xmd.h> | |
36 | #include <GL/glxproto.h> | |
37 | #include <xcb/glx.h> | |
38 | #include "ephyrhostglx.h" | |
39 | #define _HAVE_XALLOC_DECLS | |
40 | #include "ephyrlog.h" | |
41 | #include "hostx.h" | |
42 | ||
43 | static int glx_major, glx_minor; | |
44 | ||
45 | enum VisualConfRequestType { | |
46 | EPHYR_GET_FB_CONFIG, | |
47 | EPHYR_VENDOR_PRIV_GET_FB_CONFIG_SGIX, | |
48 | EPHYR_GET_VISUAL_CONFIGS | |
49 | }; | |
50 | ||
51 | static Bool ephyrHostGLXGetVisualConfigsInternal | |
52 | (enum VisualConfRequestType a_type, | |
53 | xcb_glx_get_visual_configs_reply_t *reply, | |
54 | int32_t a_screen, | |
55 | int32_t *a_num_visuals, | |
56 | int32_t *a_num_props, | |
57 | int32_t *a_props_buf_size, | |
58 | int32_t **a_props_buf); | |
59 | ||
60 | Bool | |
61 | ephyrHostGLXQueryVersion(int *a_major, int *a_minor) | |
62 | { | |
63 | Bool is_ok = FALSE; | |
64 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
65 | xcb_glx_query_version_cookie_t cookie; | |
66 | xcb_glx_query_version_reply_t *reply; | |
67 | ||
68 | EPHYR_RETURN_VAL_IF_FAIL(a_major && a_minor, FALSE); | |
69 | EPHYR_LOG("enter\n"); | |
70 | ||
71 | if (glx_major) { | |
72 | *a_major = glx_major; | |
73 | *a_minor = glx_minor; | |
74 | return TRUE; | |
75 | } | |
76 | ||
77 | /* Send the glXQueryVersion request */ | |
78 | cookie = xcb_glx_query_version(conn, 2, 1); | |
79 | reply = xcb_glx_query_version_reply(conn, cookie, NULL); | |
80 | if (!reply) | |
81 | goto out; | |
82 | *a_major = reply->major_version; | |
83 | *a_minor = reply->minor_version; | |
84 | free(reply); | |
85 | ||
86 | EPHYR_LOG("major:%d, minor:%d\n", *a_major, *a_minor); | |
87 | ||
88 | is_ok = TRUE; | |
89 | out: | |
90 | EPHYR_LOG("leave\n"); | |
91 | return is_ok; | |
92 | } | |
93 | ||
94 | Bool | |
95 | ephyrHostGLXGetString(int a_context_tag, | |
96 | int a_string_name, | |
97 | char **a_string) | |
98 | { | |
99 | Bool is_ok = FALSE; | |
100 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
101 | xcb_glx_get_string_cookie_t cookie; | |
102 | xcb_glx_get_string_reply_t *reply; | |
103 | ||
104 | EPHYR_RETURN_VAL_IF_FAIL(conn && a_string, FALSE); | |
105 | ||
106 | EPHYR_LOG("enter\n"); | |
107 | cookie = xcb_glx_get_string(conn, a_context_tag, a_string_name); | |
108 | reply = xcb_glx_get_string_reply(conn, cookie, NULL); | |
109 | if (!reply) | |
110 | goto out; | |
111 | *a_string = malloc(reply->n + 1); | |
112 | memcpy(*a_string, xcb_glx_get_string_string(reply), reply->n); | |
113 | (*a_string)[reply->n] = '\0'; | |
114 | free(reply); | |
115 | is_ok = TRUE; | |
116 | out: | |
117 | EPHYR_LOG("leave\n"); | |
118 | return is_ok; | |
119 | } | |
120 | ||
121 | Bool ephyrHostGLXQueryServerString(int a_screen_number, | |
122 | int a_string_name, | |
123 | char **a_string) | |
124 | { | |
125 | Bool is_ok = FALSE; | |
126 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
127 | int default_screen = hostx_get_screen(); | |
128 | xcb_glx_query_server_string_cookie_t cookie; | |
129 | xcb_glx_query_server_string_reply_t *reply; | |
130 | ||
131 | EPHYR_RETURN_VAL_IF_FAIL(conn && a_string, FALSE); | |
132 | ||
133 | EPHYR_LOG("enter\n"); | |
134 | cookie = xcb_glx_query_server_string(conn, default_screen, a_string_name); | |
135 | reply = xcb_glx_query_server_string_reply(conn, cookie, NULL); | |
136 | if (!reply) | |
137 | goto out; | |
138 | *a_string = malloc(reply->str_len + 1); | |
139 | memcpy(*a_string, xcb_glx_query_server_string_string(reply), reply->str_len); | |
140 | (*a_string)[reply->str_len] = '\0'; | |
141 | free(reply); | |
142 | is_ok = TRUE; | |
143 | out: | |
144 | EPHYR_LOG("leave\n"); | |
145 | return is_ok; | |
146 | } | |
147 | ||
148 | static Bool | |
149 | ephyrHostGLXGetVisualConfigsInternal(enum VisualConfRequestType a_type, | |
150 | xcb_glx_get_visual_configs_reply_t *reply, | |
151 | int32_t a_screen, | |
152 | int32_t * a_num_visuals, | |
153 | int32_t * a_num_props, | |
154 | int32_t * a_props_buf_size, | |
155 | int32_t ** a_props_buf) | |
156 | { | |
157 | Bool is_ok = FALSE; | |
158 | int num_props = 0, num_visuals = 0, props_buf_size = 0; | |
159 | int props_per_visual_size = 0; | |
160 | int32_t *props_buf = NULL; | |
161 | ||
162 | if (!reply->num_visuals) { | |
163 | EPHYR_LOG_ERROR("screen does not support GL rendering\n"); | |
164 | goto out; | |
165 | } | |
166 | num_visuals = reply->num_visuals; | |
167 | ||
168 | num_props = reply->num_properties; | |
169 | ||
170 | if (a_type != EPHYR_GET_VISUAL_CONFIGS) { | |
171 | num_props *= 2; | |
172 | } | |
173 | props_per_visual_size = num_props * sizeof(uint32_t); | |
174 | props_buf_size = props_per_visual_size * reply->num_visuals; | |
175 | props_buf = malloc(props_buf_size); | |
176 | if (!props_buf) | |
177 | goto out; | |
178 | memcpy(props_buf, xcb_glx_get_visual_configs_property_list(reply), | |
179 | props_buf_size); | |
180 | ||
181 | *a_num_visuals = num_visuals; | |
182 | *a_num_props = reply->num_properties; | |
183 | *a_props_buf_size = props_buf_size; | |
184 | *a_props_buf = props_buf; | |
185 | is_ok = TRUE; | |
186 | ||
187 | out: | |
188 | return is_ok; | |
189 | } | |
190 | ||
191 | Bool | |
192 | ephyrHostGLXGetVisualConfigs(int32_t a_screen, | |
193 | int32_t * a_num_visuals, | |
194 | int32_t * a_num_props, | |
195 | int32_t * a_props_buf_size, int32_t ** a_props_buf) | |
196 | { | |
197 | Bool is_ok = FALSE; | |
198 | xcb_glx_get_visual_configs_cookie_t cookie; | |
199 | xcb_glx_get_visual_configs_reply_t *reply; | |
200 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
201 | int screen = hostx_get_screen(); | |
202 | ||
203 | EPHYR_LOG("enter\n"); | |
204 | cookie = xcb_glx_get_visual_configs(conn, screen); | |
205 | reply = xcb_glx_get_visual_configs_reply(conn, cookie, NULL); | |
206 | if (!reply) | |
207 | goto out; | |
208 | is_ok = ephyrHostGLXGetVisualConfigsInternal | |
209 | (EPHYR_GET_VISUAL_CONFIGS, | |
210 | reply, | |
211 | a_screen, | |
212 | a_num_visuals, | |
213 | a_num_props, | |
214 | a_props_buf_size, | |
215 | a_props_buf); | |
216 | ||
217 | out: | |
218 | free(reply); | |
219 | EPHYR_LOG("leave:%d\n", is_ok); | |
220 | return is_ok; | |
221 | } | |
222 | ||
223 | Bool | |
224 | ephyrHostGLXVendorPrivGetFBConfigsSGIX(int a_screen, | |
225 | int32_t * a_num_visuals, | |
226 | int32_t * a_num_props, | |
227 | int32_t * a_props_buf_size, | |
228 | int32_t ** a_props_buf) | |
229 | { | |
230 | Bool is_ok=FALSE; | |
231 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
232 | int screen = hostx_get_screen(); | |
233 | xcb_glx_vendor_private_with_reply_cookie_t cookie; | |
234 | union { | |
235 | xcb_glx_vendor_private_with_reply_reply_t *vprep; | |
236 | xcb_glx_get_visual_configs_reply_t *rep; | |
237 | } reply; | |
238 | ||
239 | EPHYR_LOG("enter\n"); | |
240 | cookie = xcb_glx_vendor_private_with_reply(conn, | |
241 | X_GLXvop_GetFBConfigsSGIX, | |
242 | 0, 4, (uint8_t *)&screen); | |
243 | reply.vprep = xcb_glx_vendor_private_with_reply_reply(conn, cookie, NULL); | |
244 | if (!reply.vprep) | |
245 | goto out; | |
246 | is_ok = ephyrHostGLXGetVisualConfigsInternal | |
247 | (EPHYR_VENDOR_PRIV_GET_FB_CONFIG_SGIX, | |
248 | reply.rep, | |
249 | a_screen, | |
250 | a_num_visuals, | |
251 | a_num_props, | |
252 | a_props_buf_size, | |
253 | a_props_buf); | |
254 | out: | |
255 | free(reply.vprep); | |
256 | EPHYR_LOG("leave\n"); | |
257 | return is_ok; | |
258 | } | |
259 | ||
260 | Bool | |
261 | ephyrHostGLXSendClientInfo(int32_t a_major, int32_t a_minor, | |
262 | const char *a_extension_list) | |
263 | { | |
264 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
265 | int size; | |
266 | ||
267 | EPHYR_RETURN_VAL_IF_FAIL(conn && a_extension_list, FALSE); | |
268 | ||
269 | size = strlen (a_extension_list) + 1; | |
270 | xcb_glx_client_info(conn, a_major, a_minor, size, a_extension_list); | |
271 | ||
272 | return TRUE; | |
273 | } | |
274 | ||
275 | Bool | |
276 | ephyrHostGLXCreateContext(int a_screen, | |
277 | int a_generic_id, | |
278 | int a_context_id, | |
279 | int a_share_list_ctxt_id, | |
280 | int a_render_type, | |
281 | Bool a_direct, | |
282 | int code) | |
283 | { | |
284 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
285 | Bool is_ok = FALSE; | |
286 | int remote_context_id = 0; | |
287 | ||
288 | EPHYR_LOG("enter. screen:%d, generic_id:%d, contextid:%d, rendertype:%d, " | |
289 | "direct:%d\n", a_screen, a_generic_id, a_context_id, | |
290 | a_render_type, a_direct); | |
291 | ||
292 | if (!hostx_allocate_resource_id_peer(a_context_id, &remote_context_id)) { | |
293 | EPHYR_LOG_ERROR("failed to peer the context id %d host X", | |
294 | remote_context_id); | |
295 | goto out; | |
296 | } | |
297 | ||
298 | switch (code) { | |
299 | case X_GLXCreateContext: { | |
300 | xcb_glx_create_context(conn, | |
301 | remote_context_id, | |
302 | a_generic_id, | |
303 | hostx_get_screen(), | |
304 | a_share_list_ctxt_id, | |
305 | a_direct); | |
306 | } | |
307 | ||
308 | case X_GLXCreateNewContext: { | |
309 | xcb_glx_create_new_context(conn, | |
310 | remote_context_id, | |
311 | a_generic_id, | |
312 | hostx_get_screen(), | |
313 | a_render_type, | |
314 | a_share_list_ctxt_id, | |
315 | a_direct); | |
316 | } | |
317 | ||
318 | default: | |
319 | /* This should never be reached !*/ | |
320 | EPHYR_LOG("Internal error! Invalid CreateContext code!\n"); | |
321 | } | |
322 | ||
323 | is_ok = TRUE; | |
324 | ||
325 | out: | |
326 | EPHYR_LOG("leave\n"); | |
327 | return is_ok; | |
328 | } | |
329 | ||
330 | Bool | |
331 | ephyrHostDestroyContext(int a_ctxt_id) | |
332 | { | |
333 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
334 | Bool is_ok = FALSE; | |
335 | int remote_ctxt_id = 0; | |
336 | ||
337 | EPHYR_LOG("enter:%d\n", a_ctxt_id); | |
338 | ||
339 | if (!hostx_get_resource_id_peer(a_ctxt_id, &remote_ctxt_id)) { | |
340 | EPHYR_LOG_ERROR("failed to get remote glx ctxt id\n"); | |
341 | goto out; | |
342 | } | |
343 | EPHYR_LOG("host context id:%d\n", remote_ctxt_id); | |
344 | ||
345 | xcb_glx_destroy_context(conn, remote_ctxt_id); | |
346 | ||
347 | is_ok = TRUE; | |
348 | ||
349 | out: | |
350 | EPHYR_LOG("leave\n"); | |
351 | return is_ok; | |
352 | } | |
353 | ||
354 | Bool | |
355 | ephyrHostGLXMakeCurrent(int a_drawable, int a_readable, | |
356 | int a_glx_ctxt_id, int a_old_ctxt_tag, int *a_ctxt_tag) | |
357 | { | |
358 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
359 | Bool is_ok = FALSE; | |
360 | int remote_glx_ctxt_id = 0; | |
361 | ||
362 | EPHYR_RETURN_VAL_IF_FAIL(a_ctxt_tag, FALSE); | |
363 | ||
364 | EPHYR_LOG("enter. drawable:%d, read:%d, context:%d, oldtag:%d\n", | |
365 | a_drawable, a_readable, a_glx_ctxt_id, a_old_ctxt_tag); | |
366 | ||
367 | if (!hostx_get_resource_id_peer(a_glx_ctxt_id, &remote_glx_ctxt_id)) { | |
368 | EPHYR_LOG_ERROR("failed to get remote glx ctxt id\n"); | |
369 | goto out; | |
370 | } | |
371 | ||
372 | /* If both drawables are the same, use the old MakeCurrent request. | |
373 | * Otherwise, if we have GLX 1.3 or higher, use the MakeContextCurrent | |
374 | * request which supports separate read and draw targets. Failing that, | |
375 | * try the SGI MakeCurrentRead extension. Logic cribbed from Mesa. */ | |
376 | if (a_drawable == a_readable) { | |
377 | xcb_glx_make_current_cookie_t cookie; | |
378 | xcb_glx_make_current_reply_t *reply; | |
379 | cookie = xcb_glx_make_current(conn, | |
380 | a_drawable, | |
381 | remote_glx_ctxt_id, | |
382 | a_old_ctxt_tag); | |
383 | reply = xcb_glx_make_current_reply(conn, cookie, NULL); | |
384 | if (!reply) | |
385 | goto out; | |
386 | *a_ctxt_tag = reply->context_tag; | |
387 | free(reply); | |
388 | } | |
389 | else if (glx_major > 1 || glx_minor >= 3) { | |
390 | xcb_glx_make_context_current_cookie_t cookie; | |
391 | xcb_glx_make_context_current_reply_t *reply; | |
392 | cookie = xcb_glx_make_context_current(conn, | |
393 | a_old_ctxt_tag, | |
394 | a_drawable, | |
395 | a_readable, | |
396 | remote_glx_ctxt_id); | |
397 | reply = xcb_glx_make_context_current_reply(conn, cookie, NULL); | |
398 | if (!reply) | |
399 | goto out; | |
400 | *a_ctxt_tag = reply->context_tag; | |
401 | free(reply); | |
402 | } | |
403 | else { | |
404 | xcb_glx_vendor_private_with_reply_cookie_t cookie; | |
405 | xcb_glx_vendor_private_with_reply_reply_t *reply; | |
406 | uint32_t data[3] = { | |
407 | a_drawable, a_readable, remote_glx_ctxt_id, | |
408 | }; | |
409 | ||
410 | EPHYR_LOG("enter\n"); | |
411 | cookie = xcb_glx_vendor_private_with_reply(conn, | |
412 | X_GLXvop_MakeCurrentReadSGI, | |
413 | a_old_ctxt_tag, | |
414 | sizeof(data), | |
415 | (uint8_t *)data); | |
416 | reply = xcb_glx_vendor_private_with_reply_reply(conn, cookie, NULL); | |
417 | ||
418 | *a_ctxt_tag = reply->retval; | |
419 | ||
420 | free(reply); | |
421 | } | |
422 | ||
423 | EPHYR_LOG("context tag:%d\n", *a_ctxt_tag); | |
424 | is_ok = TRUE; | |
425 | ||
426 | out: | |
427 | EPHYR_LOG("leave\n"); | |
428 | return is_ok; | |
429 | } | |
430 | ||
431 | Bool | |
432 | ephyrHostGetIntegerValue(int a_current_context_tag, int a_int, int *a_val) | |
433 | { | |
434 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
435 | Bool is_ok = FALSE; | |
436 | int size = 0; | |
437 | xcb_glx_get_integerv_cookie_t cookie; | |
438 | xcb_glx_get_integerv_reply_t *reply; | |
439 | ||
440 | EPHYR_RETURN_VAL_IF_FAIL(a_val, FALSE); | |
441 | ||
442 | EPHYR_LOG("enter\n"); | |
443 | cookie = xcb_glx_get_integerv(conn, a_current_context_tag, a_int); | |
444 | reply = xcb_glx_get_integerv_reply(conn, cookie, NULL); | |
445 | if (!reply) | |
446 | goto out; | |
447 | size = reply->n; | |
448 | if (!size) { | |
449 | EPHYR_LOG_ERROR("X_GLsop_GetIngerv failed\n"); | |
450 | goto out; | |
451 | } | |
452 | *a_val = reply->datum; | |
453 | is_ok = TRUE; | |
454 | ||
455 | out: | |
456 | free(reply); | |
457 | EPHYR_LOG("leave\n"); | |
458 | return is_ok; | |
459 | } | |
460 | ||
461 | Bool | |
462 | ephyrHostIsContextDirect(int a_ctxt_id, int *a_is_direct) | |
463 | { | |
464 | Bool is_ok = FALSE; | |
465 | xcb_connection_t *conn = hostx_get_xcbconn(); | |
466 | xcb_glx_is_direct_cookie_t cookie; | |
467 | xcb_glx_is_direct_reply_t *reply = NULL; | |
468 | int remote_glx_ctxt_id = 0; | |
469 | ||
470 | EPHYR_LOG("enter\n"); | |
471 | if (!hostx_get_resource_id_peer (a_ctxt_id, &remote_glx_ctxt_id)) { | |
472 | EPHYR_LOG_ERROR ("failed to get remote glx ctxt id\n"); | |
473 | goto out; | |
474 | } | |
475 | ||
476 | /* Send the glXIsDirect request */ | |
477 | cookie = xcb_glx_is_direct(conn, remote_glx_ctxt_id); | |
478 | reply = xcb_glx_is_direct_reply(conn, cookie, NULL); | |
479 | if (!reply) { | |
480 | EPHYR_LOG_ERROR("fail in reading reply from host\n"); | |
481 | goto out; | |
482 | } | |
483 | *a_is_direct = reply->is_direct; | |
484 | is_ok = TRUE; | |
485 | ||
486 | out: | |
487 | free(reply); | |
488 | EPHYR_LOG("leave\n"); | |
489 | return is_ok; | |
490 | } |