Commit 40a5b4c9 authored by Dave Airlie's avatar Dave Airlie Committed by Bastien Nocera

gnome-rr: add tiled monitor support

This adds the interfaces to allow for tiled
monitor support via gnome-desktop.

1) add output config get/set tiled geometry/rotation support

These hide under the standard APIs, and just set the
state for the tiled outputs by setting the primary
tile up.

2) add output config API to get primary tile

3) add tile flags to modes - add gather function
to create tiled modes for primary outputs.

https://bugzilla.gnome.org/show_bug.cgi?id=750311
parent 456d4817
......@@ -164,6 +164,15 @@ gnome_rr_config_load_current (GnomeRRConfig *config, GError **error)
output->priv->name = g_strdup (gnome_rr_output_get_name (rr_output));
output->priv->connected = TRUE;
output->priv->display_name = g_strdup (gnome_rr_output_get_display_name (rr_output));
output->priv->config = config;
output->priv->is_tiled = _gnome_rr_output_get_tile_info (rr_output,
&output->priv->tile);
if (output->priv->is_tiled)
{
_gnome_rr_output_get_tiled_display_size (rr_output, NULL, NULL,
&output->priv->total_tiled_width,
&output->priv->total_tiled_height);
}
if (!output->priv->connected)
{
......
......@@ -91,6 +91,8 @@ int gnome_rr_output_info_get_preferred_height (GnomeRROutputInfo *self);
gboolean gnome_rr_output_info_get_underscanning (GnomeRROutputInfo *self);
void gnome_rr_output_info_set_underscanning (GnomeRROutputInfo *self, gboolean underscanning);
gboolean gnome_rr_output_info_is_primary_tile (GnomeRROutputInfo *self);
typedef struct _GnomeRRConfig GnomeRRConfig;
typedef struct _GnomeRRConfigClass GnomeRRConfigClass;
typedef struct _GnomeRRConfigPrivate GnomeRRConfigPrivate;
......
......@@ -31,7 +31,8 @@ print_output (GnomeRROutput *output, const char *message)
gsize len = 0;
const guint8 *result = NULL;
int width_mm, height_mm;
GnomeRRMode **modes;
int i;
g_print ("[%s]", gnome_rr_output_get_name (output));
if (message)
g_print (" (%s)", message);
......@@ -63,6 +64,13 @@ print_output (GnomeRROutput *output, const char *message)
g_free (serial);
}
modes = gnome_rr_output_list_modes (output);
for (i = 0; modes[i] != NULL; ++i) {
g_print ("\t mode: %dx%d%s\n",
gnome_rr_mode_get_width (modes[i]),
gnome_rr_mode_get_height (modes[i]),
gnome_rr_mode_get_is_tiled (modes[i]) ? " (tiled)" : "");
}
g_print ("\n");
}
......
......@@ -98,6 +98,72 @@ void gnome_rr_output_info_set_active (GnomeRROutputInfo *self, gboolean active)
self->priv->on = active;
}
static void gnome_rr_output_info_get_tiled_geometry (GnomeRROutputInfo *self,
int *x,
int *y,
int *width,
int *height)
{
GnomeRROutputInfo **outputs;
gboolean active;
int i;
int ht, vt;
int total_w = 0, total_h = 0;
outputs = gnome_rr_config_get_outputs (self->priv->config);
/*
* iterate over all the outputs from 0,0 -> h,v
* find the output for each tile,
* if it is the 0 tile, store the x/y offsets.
* if the tile is active, add the tile to the total w/h
* for the output if the tile is in the 0 row or 0 column.
*/
for (ht = 0; ht < self->priv->tile.max_horiz_tiles; ht++)
{
for (vt = 0; vt < self->priv->tile.max_vert_tiles; vt++)
{
for (i = 0; outputs[i]; i++)
{
GnomeRRTile *this_tile = &outputs[i]->priv->tile;
if (!outputs[i]->priv->is_tiled)
continue;
if (this_tile->group_id != self->priv->tile.group_id)
continue;
if (this_tile->loc_horiz != ht ||
this_tile->loc_vert != vt)
continue;
if (vt == 0 && ht == 0)
{
if (x)
*x = outputs[i]->priv->x;
if (y)
*y = outputs[i]->priv->y;
}
active = gnome_rr_output_info_is_active (outputs[i]);
if (!active)
continue;
if (this_tile->loc_horiz == 0)
total_h += outputs[i]->priv->height;
if (this_tile->loc_vert == 0)
total_w += outputs[i]->priv->width;
}
}
}
if (width)
*width = total_w;
if (height)
*height = total_h;
}
/**
* gnome_rr_output_info_get_geometry:
* @self: a #GnomeRROutputInfo
......@@ -105,11 +171,20 @@ void gnome_rr_output_info_set_active (GnomeRROutputInfo *self, gboolean active)
* @y: (out) (allow-none):
* @width: (out) (allow-none):
* @height: (out) (allow-none):
*
* Get the geometry for the monitor connected to the specified output.
* If the monitor is a tiled monitor, it returns the geometry for the complete monitor.
*/
void gnome_rr_output_info_get_geometry (GnomeRROutputInfo *self, int *x, int *y, int *width, int *height)
{
g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self));
if (self->priv->is_tiled)
{
gnome_rr_output_info_get_tiled_geometry (self, x, y, width, height);
return;
}
if (x)
*x = self->priv->x;
if (y)
......@@ -120,10 +195,106 @@ void gnome_rr_output_info_get_geometry (GnomeRROutputInfo *self, int *x, int *y,
*height = self->priv->height;
}
void gnome_rr_output_info_set_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height)
static void gnome_rr_output_info_set_tiled_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height)
{
GnomeRROutputInfo **outputs;
gboolean primary_tile_only = FALSE;
int ht, vt, i;
int x_off;
primary_tile_only = TRUE;
if (width == self->priv->total_tiled_width &&
height == self->priv->total_tiled_height)
primary_tile_only = FALSE;
outputs = gnome_rr_config_get_outputs (self->priv->config);
/*
* iterate over all the outputs from 0,0 -> h,v
* find the output for each tile,
* if only the primary tile is being set, disable
* the non-primary tiles, and set the output up
* for tile 0 only.
* if all tiles are being set, then store the
* dimensions for this tile, and increase the offsets.
* y_off is reset per column of tiles,
* addx is incremented for the first row of tiles
* to set the correct x offset.
*/
x_off = 0;
for (ht = 0; ht < self->priv->tile.max_horiz_tiles; ht++)
{
int y_off = 0;
int addx = 0;
for (vt = 0; vt < self->priv->tile.max_vert_tiles; vt++)
{
for (i = 0; outputs[i]; i++)
{
GnomeRRTile *this_tile = &outputs[i]->priv->tile;
if (!outputs[i]->priv->is_tiled)
continue;
if (this_tile->group_id != self->priv->tile.group_id)
continue;
if (this_tile->loc_horiz != ht ||
this_tile->loc_vert != vt)
continue;
/* for primary tile only configs turn off non-primary
tiles - turn them on for tiled ones */
if (ht != 0 || vt != 0)
outputs[i]->priv->on = !primary_tile_only;
if (primary_tile_only)
{
if (ht == 0 && vt == 0)
{
outputs[i]->priv->x = x;
outputs[i]->priv->y = y;
outputs[i]->priv->width = width;
outputs[i]->priv->height = height;
}
}
else
{
outputs[i]->priv->x = x + x_off;
outputs[i]->priv->y = y + y_off;
outputs[i]->priv->width = this_tile->width;
outputs[i]->priv->height = this_tile->height;
y_off += this_tile->height;
if (vt == 0)
addx = this_tile->width;
}
}
}
x_off += addx;
}
}
/**
* gnome_rr_output_info_set_geometry:
* @self: a #GnomeRROutputInfo
* @x: x offset for monitor
* @y: y offset for monitor
* @width: monitor width
* @height: monitor height
*
* Set the geometry for the monitor connected to the specified output.
* If the monitor is a tiled monitor, it sets the geometry for the complete monitor.
*/
void gnome_rr_output_info_set_geometry (GnomeRROutputInfo *self, int x, int y, int width, int height)
{
g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self));
if (self->priv->is_tiled)
{
gnome_rr_output_info_set_tiled_geometry (self, x, y, width, height);
return;
}
self->priv->x = x;
self->priv->y = y;
self->priv->width = width;
......@@ -151,10 +322,90 @@ GnomeRRRotation gnome_rr_output_info_get_rotation (GnomeRROutputInfo *self)
return self->priv->rotation;
}
static void gnome_rr_output_info_set_tiled_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation)
{
GnomeRROutputInfo **outputs;
int x_off;
int base_x = 0, base_y = 0;
int ht, vt;
int i;
outputs = gnome_rr_config_get_outputs (self->priv->config);
x_off = 0;
/*
* iterate over all the outputs from 0,0 -> h,v
* find the output for each tile,
* for all tiles set the rotation,
* for the 0 tile use the base X/Y offsets
* for non-0 tile, rotate the offsets of each
* tile so things display correctly.
*/
for (ht = 0; ht < self->priv->tile.max_horiz_tiles; ht++)
{
int y_off = 0;
int addx = 0;
for (vt = 0; vt < self->priv->tile.max_vert_tiles; vt++)
{
for (i = 0; outputs[i] != NULL; i++)
{
GnomeRRTile *this_tile = &outputs[i]->priv->tile;
int new_x, new_y;
if (!outputs[i]->priv->is_tiled)
continue;
if (this_tile->group_id != self->priv->tile.group_id)
continue;
if (this_tile->loc_horiz != ht ||
this_tile->loc_vert != vt)
continue;
/* set tile rotation */
outputs[i]->priv->rotation = rotation;
/* for non-zero tiles - change the offsets */
if (ht == 0 && vt == 0)
{
base_x = outputs[i]->priv->x;
base_y = outputs[i]->priv->y;
}
else
{
if ((rotation & GNOME_RR_ROTATION_90) || (rotation & GNOME_RR_ROTATION_270))
{
new_x = base_x + y_off;
new_y = base_y + x_off;
}
else
{
new_x = base_x + x_off;
new_y = base_y + y_off;
}
outputs[i]->priv->x = new_x;
outputs[i]->priv->y = new_y;
outputs[i]->priv->width = this_tile->width;
outputs[i]->priv->height = this_tile->height;
}
y_off += this_tile->height;
if (vt == 0)
addx = this_tile->width;
}
}
x_off += addx;
}
}
void gnome_rr_output_info_set_rotation (GnomeRROutputInfo *self, GnomeRRRotation rotation)
{
g_return_if_fail (GNOME_IS_RR_OUTPUT_INFO (self));
if (self->priv->is_tiled)
{
gnome_rr_output_info_set_tiled_rotation (self, rotation);
return;
}
self->priv->rotation = rotation;
}
......@@ -266,3 +517,25 @@ void gnome_rr_output_info_set_underscanning (GnomeRROutputInfo *self,
self->priv->underscanning = underscanning;
}
/**
* gnome_rr_output_info_is_primary_tile
* @self: a #GnomeRROutputInfo
*
* Returns: %TRUE if the specified output is connected to
* the primary tile of a monitor or to an untiled monitor,
* %FALSE if the output is connected to a secondary tile.
*/
gboolean gnome_rr_output_info_is_primary_tile(GnomeRROutputInfo *self)
{
g_return_val_if_fail (GNOME_IS_RR_OUTPUT_INFO (self), FALSE);
if (!self->priv->is_tiled)
return TRUE;
if (self->priv->tile.loc_horiz == 0 &&
self->priv->tile.loc_vert == 0)
return TRUE;
return FALSE;
}
......@@ -51,6 +51,20 @@ struct GnomeRRScreenPrivate
MetaDBusDisplayConfig *proxy;
};
#define UNDEFINED_GROUP_ID 0
struct GnomeRRTile {
guint group_id;
guint flags;
guint max_horiz_tiles;
guint max_vert_tiles;
guint loc_horiz;
guint loc_vert;
guint width;
guint height;
};
typedef struct GnomeRRTile GnomeRRTile;
struct _GnomeRROutputInfoPrivate
{
char * name;
......@@ -74,6 +88,14 @@ struct _GnomeRROutputInfoPrivate
char * display_name;
gboolean primary;
gboolean underscanning;
gboolean is_tiled;
GnomeRRTile tile;
int total_tiled_width;
int total_tiled_height;
/* ptr back to info */
GnomeRRConfig *config;
};
struct _GnomeRRConfigPrivate
......@@ -91,4 +113,11 @@ gboolean _gnome_rr_screen_apply_configuration (GnomeRRScreen *screen,
GVariant *outputs,
GError **error);
gboolean _gnome_rr_output_get_tile_info (GnomeRROutput *output,
GnomeRRTile *tile);
gboolean _gnome_rr_output_get_tiled_display_size (GnomeRROutput *output,
int *tile_w, int *tile_h,
int *width, int *height);
#endif
......@@ -81,6 +81,7 @@ struct GnomeRROutput
gboolean is_primary;
gboolean is_presentation;
gboolean is_underscanning;
GnomeRRTile tile_info;
};
struct GnomeRRCrtc
......@@ -100,6 +101,7 @@ struct GnomeRRCrtc
int gamma_size;
};
#define UNDEFINED_MODE_ID 0
struct GnomeRRMode
{
ScreenInfo * info;
......@@ -108,6 +110,7 @@ struct GnomeRRMode
int width;
int height;
int freq; /* in mHz */
gboolean tiled;
};
/* GnomeRRCrtc */
......@@ -280,6 +283,98 @@ has_similar_mode (GnomeRROutput *output, GnomeRRMode *mode)
return FALSE;
}
gboolean
_gnome_rr_output_get_tiled_display_size (GnomeRROutput *output,
int *tile_w, int *tile_h,
int *total_width, int *total_height)
{
GnomeRRTile tile;
int ht, vt, i;
int total_h = 0, total_w = 0;
if (!_gnome_rr_output_get_tile_info (output, &tile))
return FALSE;
if (tile.loc_horiz != 0 ||
tile.loc_vert != 0)
return FALSE;
if (tile_w)
*tile_w = tile.width;
if (tile_h)
*tile_h = tile.height;
for (ht = 0; ht < tile.max_horiz_tiles; ht++)
{
for (vt = 0; vt < tile.max_vert_tiles; vt++)
{
for (i = 0; output->info->outputs[i]; i++)
{
GnomeRRTile this_tile;
if (!_gnome_rr_output_get_tile_info (output->info->outputs[i], &this_tile))
continue;
if (this_tile.group_id != tile.group_id)
continue;
if (this_tile.loc_horiz != ht ||
this_tile.loc_vert != vt)
continue;
if (this_tile.loc_horiz == 0)
total_h += this_tile.height;
if (this_tile.loc_vert == 0)
total_w += this_tile.width;
}
}
}
*total_width = total_w;
*total_height = total_h;
return TRUE;
}
static void
gather_tile_modes_output (ScreenInfo *info, GnomeRROutput *output)
{
GPtrArray *a;
GnomeRRMode *mode;
int width, height;
int tile_w, tile_h;
int i;
if (!_gnome_rr_output_get_tiled_display_size (output, &tile_w, &tile_h,
&width, &height))
return;
/* now stick the mode into the modelist */
a = g_ptr_array_new ();
mode = mode_new (info, UNDEFINED_MODE_ID);
mode->winsys_id = 0;
mode->width = width;
mode->height = height;
mode->freq = 0;
mode->tiled = TRUE;
g_ptr_array_add (a, mode);
for (i = 0; output->modes[i]; i++)
g_ptr_array_add (a, output->modes[i]);
g_ptr_array_add (a, NULL);
output->modes = (GnomeRRMode **)g_ptr_array_free (a, FALSE);
}
static void
gather_tile_modes (ScreenInfo *info)
{
int i;
for (i = 0; info->outputs[i]; i++)
gather_tile_modes_output (info, info->outputs[i]);
}
static void
gather_clone_modes (ScreenInfo *info)
{
......@@ -410,6 +505,8 @@ fill_screen_info_from_resources (ScreenInfo *info,
}
gather_clone_modes (info);
gather_tile_modes (info);
}
static gboolean
......@@ -1197,7 +1294,7 @@ output_initialize (GnomeRROutput *output, GVariant *info)
{
GPtrArray *a;
GVariantIter *crtcs, *clones, *modes;
GVariant *properties, *edid;
GVariant *properties, *edid, *tile;
int current_crtc_id;
guint id;
......@@ -1276,6 +1373,18 @@ output_initialize (GnomeRROutput *output, GVariant *info)
else
g_variant_lookup (properties, "edid-file", "s", &output->edid_file);
if ((tile = g_variant_lookup_value (properties, "tile", G_VARIANT_TYPE ("(uuuuuuuu)"))))
{
g_variant_get (tile, "(uuuuuuuu)",
&output->tile_info.group_id, &output->tile_info.flags,
&output->tile_info.max_horiz_tiles, &output->tile_info.max_vert_tiles,
&output->tile_info.loc_horiz, &output->tile_info.loc_vert,
&output->tile_info.width, &output->tile_info.height);
g_variant_unref (tile);
}
else
memset(&output->tile_info, 0, sizeof(output->tile_info));
if (output->is_primary)
output->info->primary = output;
}
......@@ -1556,12 +1665,24 @@ GnomeRRMode *
gnome_rr_output_get_current_mode (GnomeRROutput *output)
{
GnomeRRCrtc *crtc;
GnomeRRMode *mode;
g_return_val_if_fail (output != NULL, NULL);
if ((crtc = gnome_rr_output_get_crtc (output)))
{
int total_w, total_h, tile_w, tile_h;
mode = gnome_rr_crtc_get_current_mode (crtc);
if (_gnome_rr_output_get_tiled_display_size (output, &tile_w, &tile_h, &total_w, &total_h))
{
if (mode->width == tile_w &&
mode->height == tile_h) {
if (output->modes[0]->tiled)
return output->modes[0];
}
}
return gnome_rr_crtc_get_current_mode (crtc);
}
return NULL;
}
......@@ -1887,6 +2008,20 @@ gnome_rr_mode_get_height (GnomeRRMode *mode)
return mode->height;
}
/**
* gnome_rr_mode_get_is_tiled:
* @mode: a #GnomeRRMode
*
* Returns TRUE if this mode is a tiled
* mode created for span a tiled monitor.
*/
gboolean
gnome_rr_mode_get_is_tiled (GnomeRRMode *mode)
{
g_return_val_if_fail (mode != NULL, FALSE);
return mode->tiled;
}
static void
mode_initialize (GnomeRRMode *mode, GVariant *info)
{
......@@ -2040,3 +2175,17 @@ gnome_rr_output_get_is_underscanning (GnomeRROutput *output)
g_assert(output != NULL);
return output->is_underscanning;
}
gboolean
_gnome_rr_output_get_tile_info (GnomeRROutput *output,
GnomeRRTile *tile)
{
if (output->tile_info.group_id == UNDEFINED_GROUP_ID)
return FALSE;
if (!tile)
return FALSE;
*tile = output->tile_info;
return TRUE;
}
......@@ -178,6 +178,7 @@ guint32 gnome_rr_mode_get_id (GnomeRRMode *mode)
guint gnome_rr_mode_get_width (GnomeRRMode *mode);
guint gnome_rr_mode_get_height (GnomeRRMode *mode);
int gnome_rr_mode_get_freq (GnomeRRMode *mode);
gboolean gnome_rr_mode_get_is_tiled (GnomeRRMode *mode);
/* GnomeRRCrtc */
guint32 gnome_rr_crtc_get_id (GnomeRRCrtc *crtc);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment