/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-xlib-renderer.h" #include "cogl-util.h" #include "cogl-object.h" #include "cogl-output-private.h" #include "cogl-renderer-private.h" #include "cogl-xlib-renderer-private.h" #include "cogl-x11-renderer-private.h" #include "cogl-poll-private.h" #include "winsys/cogl-winsys-private.h" #include #include #include #include #include static char *_cogl_x11_display_name = NULL; static GList *_cogl_xlib_renderers = NULL; static void _xlib_renderer_data_free (CoglXlibRenderer *data) { if (data->xvisinfo) XFree (data->xvisinfo); g_slice_free (CoglXlibRenderer, data); } CoglXlibRenderer * _cogl_xlib_renderer_get_data (CoglRenderer *renderer) { /* Constructs a CoglXlibRenderer struct on demand and attaches it to the object using user data. It's done this way instead of using a subclassing hierarchy in the winsys data because all EGL winsys's need the EGL winsys data but only one of them wants the Xlib data. */ if (!renderer->custom_winsys_user_data) renderer->custom_winsys_user_data = g_slice_new0 (CoglXlibRenderer); return renderer->custom_winsys_user_data; } static void register_xlib_renderer (CoglRenderer *renderer) { GList *l; for (l = _cogl_xlib_renderers; l; l = l->next) if (l->data == renderer) return; _cogl_xlib_renderers = g_list_prepend (_cogl_xlib_renderers, renderer); } static void unregister_xlib_renderer (CoglRenderer *renderer) { _cogl_xlib_renderers = g_list_remove (_cogl_xlib_renderers, renderer); } static CoglRenderer * get_renderer_for_xdisplay (Display *xdpy) { GList *l; for (l = _cogl_xlib_renderers; l; l = l->next) { CoglRenderer *renderer = l->data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); if (xlib_renderer->xdpy == xdpy) return renderer; } return NULL; } static int error_handler (Display *xdpy, XErrorEvent *error) { CoglRenderer *renderer; CoglXlibRenderer *xlib_renderer; renderer = get_renderer_for_xdisplay (xdpy); xlib_renderer = _cogl_xlib_renderer_get_data (renderer); g_assert (xlib_renderer->trap_state); xlib_renderer->trap_state->trapped_error_code = error->error_code; return 0; } void _cogl_xlib_renderer_trap_errors (CoglRenderer *renderer, CoglXlibTrapState *state) { CoglXlibRenderer *xlib_renderer; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); state->trapped_error_code = 0; state->old_error_handler = XSetErrorHandler (error_handler); state->old_state = xlib_renderer->trap_state; xlib_renderer->trap_state = state; } int _cogl_xlib_renderer_untrap_errors (CoglRenderer *renderer, CoglXlibTrapState *state) { CoglXlibRenderer *xlib_renderer; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); g_assert (state == xlib_renderer->trap_state); XSetErrorHandler (state->old_error_handler); xlib_renderer->trap_state = state->old_state; return state->trapped_error_code; } static Display * assert_xlib_display (CoglRenderer *renderer, GError **error) { Display *xdpy = cogl_xlib_renderer_get_foreign_display (renderer); CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); /* A foreign display may have already been set... */ if (xdpy) { xlib_renderer->xdpy = xdpy; return xdpy; } xdpy = XOpenDisplay (_cogl_x11_display_name); if (xdpy == NULL) { g_set_error (error, COGL_RENDERER_ERROR, COGL_RENDERER_ERROR_XLIB_DISPLAY_OPEN, "Failed to open X Display %s", _cogl_x11_display_name); return NULL; } xlib_renderer->xdpy = xdpy; return xdpy; } static int compare_outputs (CoglOutput *a, CoglOutput *b) { return strcmp (a->name, b->name); } #define CSO(X) COGL_SUBPIXEL_ORDER_ ## X static CoglSubpixelOrder subpixel_map[6][6] = { { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR) }, /* 0 */ { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB) }, /* 90 */ { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB) }, /* 180 */ { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR) }, /* 270 */ { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR) }, /* Reflect_X */ { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB) }, /* Reflect_Y */ }; #undef CSO static void update_outputs (CoglRenderer *renderer, gboolean notify) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); XRRScreenResources *resources; CoglXlibTrapState state; gboolean error = FALSE; GList *new_outputs = NULL; GList *l, *m; gboolean changed = FALSE; int i; xlib_renderer->outputs_update_serial = XNextRequest (xlib_renderer->xdpy); resources = XRRGetScreenResources (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy)); _cogl_xlib_renderer_trap_errors (renderer, &state); for (i = 0; resources && i < resources->ncrtc && !error; i++) { XRRCrtcInfo *crtc_info = NULL; XRROutputInfo *output_info = NULL; CoglOutput *output; float refresh_rate = 0; int j; crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy, resources, resources->crtcs[i]); if (crtc_info == NULL) { error = TRUE; goto next; } if (crtc_info->mode == None) goto next; for (j = 0; j < resources->nmode; j++) { if (resources->modes[j].id == crtc_info->mode) refresh_rate = (resources->modes[j].dotClock / ((float)resources->modes[j].hTotal * resources->modes[j].vTotal)); } output_info = XRRGetOutputInfo (xlib_renderer->xdpy, resources, crtc_info->outputs[0]); if (output_info == NULL) { error = TRUE; goto next; } output = _cogl_output_new (output_info->name); output->x = crtc_info->x; output->y = crtc_info->y; output->width = crtc_info->width; output->height = crtc_info->height; if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0) { output->mm_width = output_info->mm_height; output->mm_height = output_info->mm_width; } else { output->mm_width = output_info->mm_width; output->mm_height = output_info->mm_height; } output->refresh_rate = refresh_rate; switch (output_info->subpixel_order) { case SubPixelUnknown: default: output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; break; case SubPixelNone: output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; break; case SubPixelHorizontalRGB: output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; break; case SubPixelHorizontalBGR: output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; break; case SubPixelVerticalRGB: output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; break; case SubPixelVerticalBGR: output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; break; } output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; /* Handle the effect of rotation and reflection on subpixel order (ugh) */ for (j = 0; j < 6; j++) { if ((crtc_info->rotation & (1 << j)) != 0) output->subpixel_order = subpixel_map[j][output->subpixel_order]; } new_outputs = g_list_prepend (new_outputs, output); next: if (crtc_info != NULL) XFree (crtc_info); if (output_info != NULL) XFree (output_info); } XFree (resources); if (!error) { new_outputs = g_list_sort (new_outputs, (GCompareFunc)compare_outputs); l = new_outputs; m = renderer->outputs; while (l || m) { int cmp; CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL; CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL; if (l && m) cmp = compare_outputs (output_l, output_m); else if (l) cmp = -1; else cmp = 1; if (cmp == 0) { GList *m_next = m->next; if (!_cogl_output_values_equal (output_l, output_m)) { renderer->outputs = g_list_remove_link (renderer->outputs, m); renderer->outputs = g_list_insert_before (renderer->outputs, m_next, output_l); cogl_object_ref (output_l); changed = TRUE; } l = l->next; m = m_next; } else if (cmp < 0) { renderer->outputs = g_list_insert_before (renderer->outputs, m, output_l); cogl_object_ref (output_l); changed = TRUE; l = l->next; } else { GList *m_next = m->next; renderer->outputs = g_list_remove_link (renderer->outputs, m); changed = TRUE; m = m_next; } } } g_list_free_full (new_outputs, (GDestroyNotify)cogl_object_unref); _cogl_xlib_renderer_untrap_errors (renderer, &state); if (changed) { const CoglWinsysVtable *winsys = renderer->winsys_vtable; if (notify) COGL_NOTE (WINSYS, "Outputs changed:"); else COGL_NOTE (WINSYS, "Outputs:"); for (l = renderer->outputs; l; l = l->next) { CoglOutput *output = l->data; const char *subpixel_string; switch (output->subpixel_order) { case COGL_SUBPIXEL_ORDER_UNKNOWN: default: subpixel_string = "unknown"; break; case COGL_SUBPIXEL_ORDER_NONE: subpixel_string = "none"; break; case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: subpixel_string = "horizontal_rgb"; break; case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: subpixel_string = "horizontal_bgr"; break; case COGL_SUBPIXEL_ORDER_VERTICAL_RGB: subpixel_string = "vertical_rgb"; break; case COGL_SUBPIXEL_ORDER_VERTICAL_BGR: subpixel_string = "vertical_bgr"; break; } COGL_NOTE (WINSYS, " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f " "subpixel_order=%s refresh_rate=%.3f", output->name, output->x, output->y, output->width, output->height, output->mm_width, output->mm_height, output->width / (output->mm_width / 25.4), output->height / (output->mm_height / 25.4), subpixel_string, output->refresh_rate); } if (notify && winsys->renderer_outputs_changed != NULL) winsys->renderer_outputs_changed (renderer); } } static CoglFilterReturn randr_filter (XEvent *event, void *data) { CoglRenderer *renderer = data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglX11Renderer *x11_renderer = (CoglX11Renderer *) xlib_renderer; if (x11_renderer->randr_base != -1 && (event->xany.type == x11_renderer->randr_base + RRScreenChangeNotify || event->xany.type == x11_renderer->randr_base + RRNotify) && event->xany.serial >= xlib_renderer->outputs_update_serial) update_outputs (renderer, TRUE); return COGL_FILTER_CONTINUE; } static int64_t prepare_xlib_events_timeout (void *user_data) { CoglRenderer *renderer = user_data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); return XPending (xlib_renderer->xdpy) ? 0 : -1; } static void dispatch_xlib_events (void *user_data, int revents) { CoglRenderer *renderer = user_data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); if (renderer->xlib_enable_event_retrieval) while (XPending (xlib_renderer->xdpy)) { XEvent xevent; XNextEvent (xlib_renderer->xdpy, &xevent); cogl_xlib_renderer_handle_event (renderer, &xevent); } } gboolean _cogl_xlib_renderer_connect (CoglRenderer *renderer, GError **error) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglX11Renderer *x11_renderer = (CoglX11Renderer *) xlib_renderer; int damage_error; int randr_error; if (!assert_xlib_display (renderer, error)) return FALSE; if (getenv ("COGL_X11_SYNC")) XSynchronize (xlib_renderer->xdpy, TRUE); /* Check whether damage events are supported on this display */ if (!XDamageQueryExtension (xlib_renderer->xdpy, &x11_renderer->damage_base, &damage_error)) x11_renderer->damage_base = -1; /* Check whether randr is supported on this display */ if (!XRRQueryExtension (xlib_renderer->xdpy, &x11_renderer->randr_base, &randr_error)) x11_renderer->randr_base = -1; xlib_renderer->trap_state = NULL; if (renderer->xlib_enable_event_retrieval) { _cogl_poll_renderer_add_fd (renderer, ConnectionNumber (xlib_renderer->xdpy), COGL_POLL_FD_EVENT_IN, prepare_xlib_events_timeout, dispatch_xlib_events, renderer); } XRRSelectInput(xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask); update_outputs (renderer, FALSE); register_xlib_renderer (renderer); cogl_xlib_renderer_add_filter (renderer, randr_filter, renderer); return TRUE; } void _cogl_xlib_renderer_disconnect (CoglRenderer *renderer) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); g_list_free_full (renderer->outputs, (GDestroyNotify)cogl_object_unref); renderer->outputs = NULL; if (!renderer->foreign_xdpy && xlib_renderer->xdpy) XCloseDisplay (xlib_renderer->xdpy); g_clear_pointer (&renderer->custom_winsys_user_data, _xlib_renderer_data_free); unregister_xlib_renderer (renderer); } Display * cogl_xlib_renderer_get_display (CoglRenderer *renderer) { CoglXlibRenderer *xlib_renderer; g_return_val_if_fail (cogl_is_renderer (renderer), NULL); xlib_renderer = _cogl_xlib_renderer_get_data (renderer); return xlib_renderer->xdpy; } CoglFilterReturn cogl_xlib_renderer_handle_event (CoglRenderer *renderer, XEvent *event) { return _cogl_renderer_handle_native_event (renderer, event); } void cogl_xlib_renderer_add_filter (CoglRenderer *renderer, CoglXlibFilterFunc func, void *data) { _cogl_renderer_add_native_filter (renderer, (CoglNativeFilterFunc)func, data); } void cogl_xlib_renderer_remove_filter (CoglRenderer *renderer, CoglXlibFilterFunc func, void *data) { _cogl_renderer_remove_native_filter (renderer, (CoglNativeFilterFunc)func, data); } CoglOutput * _cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer, int x, int y, int width, int height) { int max_overlap = 0; CoglOutput *max_overlapped = NULL; GList *l; int xa1 = x, xa2 = x + width; int ya1 = y, ya2 = y + height; for (l = renderer->outputs; l; l = l->next) { CoglOutput *output = l->data; int xb1 = output->x, xb2 = output->x + output->width; int yb1 = output->y, yb2 = output->y + output->height; int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1); int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1); if (overlap_x > 0 && overlap_y > 0) { int overlap = overlap_x * overlap_y; if (overlap > max_overlap) { max_overlap = overlap; max_overlapped = output; } } } return max_overlapped; }