mirror of
https://github.com/brl/mutter.git
synced 2024-11-24 17:10:40 -05:00
f5dac3a5d9
Group all the three config files from clutter/cogl/meta into one and also remove unnused configurations and replace duplicated ones This also fixes Cogl usage of HAS_X11/HAS_XLIB to match the expected build options Part-of: <https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/3368>
566 lines
17 KiB
C
566 lines
17 KiB
C
/*
|
|
* 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 <robert@linux.intel.com>
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "cogl/cogl-xlib-renderer.h"
|
|
#include "cogl/cogl-util.h"
|
|
|
|
#include "cogl/cogl-output-private.h"
|
|
#include "cogl/cogl-renderer-private.h"
|
|
#include "cogl/cogl-xlib-renderer-private.h"
|
|
#include "cogl/cogl-x11-renderer-private.h"
|
|
#include "cogl/cogl-poll-private.h"
|
|
#include "cogl/winsys/cogl-winsys-private.h"
|
|
#include "mtk/mtk-x11.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/extensions/Xdamage.h>
|
|
#include <X11/extensions/Xrandr.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
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_free (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_new0 (CoglXlibRenderer, 1);
|
|
|
|
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 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;
|
|
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));
|
|
|
|
mtk_x11_error_trap_push (xlib_renderer->xdpy);
|
|
|
|
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);
|
|
g_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);
|
|
g_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)g_object_unref);
|
|
mtk_x11_error_trap_pop (xlib_renderer->xdpy);
|
|
|
|
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;
|
|
|
|
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)g_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;
|
|
}
|