screen: XRandR 1.5 support

XRandR 1.5 adds support for the new monitor objects.

'Monitor' is a rectangular subset of the screen which represents a
coherent collection of pixels presented to the user. Each Monitor is be
associated with a list of outputs (which may be empty).

The patch below matches 1:1 screens in AwesomeWM with XRandR's Monitors.
This way I get one screen across my 4K monitor, which is represented by
two CRTCs.

Background info: http://keithp.com/blogs/MST-monitors/

Signed-off-by: Kirill A. Shutemov <kirill@shutemov.name>
This commit is contained in:
Kirill A. Shutemov 2016-04-12 14:26:17 +03:00
parent ae96ed0827
commit c543f59696
1 changed files with 156 additions and 67 deletions

View File

@ -152,6 +152,8 @@
* @function set_newindex_miss_handler
*/
DO_ARRAY(xcb_randr_output_t, randr_output, DO_NOTHING);
struct screen_output_t
{
/** The XRandR names of the output */
@ -159,7 +161,7 @@ struct screen_output_t
/** The size in millimeters */
uint32_t mm_width, mm_height;
/** The XID */
xcb_randr_output_t output;
randr_output_array_t outputs;
};
static void
@ -241,28 +243,76 @@ screens_exist(void)
return globalconf.screens.len > 0;
}
/* Monitors were introduced in RandR 1.5 */
#ifdef XCB_RANDR_GET_MONITORS
static bool
screen_scan_randr(void)
screen_scan_randr_monitors(void)
{
/* Check for extension before checking for XRandR */
if(xcb_get_extension_data(globalconf.connection, &xcb_randr_id)->present)
{
xcb_randr_query_version_reply_t *version_reply =
xcb_randr_query_version_reply(globalconf.connection,
xcb_randr_query_version(globalconf.connection, 1, 3), 0);
if(version_reply)
{
uint32_t major_version = version_reply->major_version;
uint32_t minor_version = version_reply->minor_version;
xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(globalconf.connection, globalconf.screen->root, 1);
xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(globalconf.connection, monitors_c, NULL);
xcb_randr_monitor_info_iterator_t monitor_iter;
bool found = false;
p_delete(&version_reply);
for(monitor_iter = xcb_randr_get_monitors_monitors_iterator(monitors_r);
monitor_iter.rem; xcb_randr_monitor_info_next(&monitor_iter))
{
lua_State *L = globalconf_get_lua_State();
screen_t *new_screen;
screen_output_t output;
xcb_randr_output_t *randr_outputs;
xcb_get_atom_name_cookie_t name_c;
xcb_get_atom_name_reply_t *name_r;
/* Do we agree on a supported version? */
if (major_version != 1 || minor_version < 2)
if(!xcb_randr_monitor_info_outputs_length(monitor_iter.data))
continue;
new_screen = screen_new(L);
new_screen->geometry.x = monitor_iter.data->x;
new_screen->geometry.y = monitor_iter.data->y;
new_screen->geometry.width= monitor_iter.data->width;
new_screen->geometry.height= monitor_iter.data->height;
output.mm_width = monitor_iter.data->width_in_millimeters;
output.mm_height = monitor_iter.data->height_in_millimeters;
name_c = xcb_get_atom_name_unchecked(globalconf.connection, monitor_iter.data->name);
name_r = xcb_get_atom_name_reply(globalconf.connection, name_c, NULL);
if (name_r) {
const char *name = xcb_get_atom_name_name(name_r);
size_t len = xcb_get_atom_name_name_length(name_r);
output.name = memcpy(p_new(char *, len + 1), name, len);
output.name[len] = '\0';
p_delete(&name_r);
} else {
output.name = strdup("unknown");
}
randr_output_array_init(&output.outputs);
randr_outputs = xcb_randr_monitor_info_outputs(monitor_iter.data);
for(int i = 0; i < xcb_randr_monitor_info_outputs_length(monitor_iter.data); i++) {
randr_output_array_append(&output.outputs, randr_outputs[i]);
}
screen_output_array_append(&new_screen->outputs, output);
found = true;
screen_add(L, -1);
}
p_delete(&monitors_r);
return found;
}
#else
screen_scan_randr_monitors(void)
{
return false;
}
#endif
globalconf.have_randr_13 = minor_version >= 3;
static bool
screen_scan_randr_crtcs(void)
{
/* A quick XRandR recall:
* You have CRTC that manages a part of a SCREEN.
* Each CRTC can draw stuff on one or more OUTPUT. */
@ -305,17 +355,21 @@ screen_scan_randr(void)
{
xcb_randr_get_output_info_cookie_t output_info_c = xcb_randr_get_output_info(globalconf.connection, randr_outputs[j], XCB_CURRENT_TIME);
xcb_randr_get_output_info_reply_t *output_info_r = xcb_randr_get_output_info_reply(globalconf.connection, output_info_c, NULL);
screen_output_t output;
int len = xcb_randr_get_output_info_name_length(output_info_r);
/* name is not NULL terminated */
char *name = memcpy(p_new(char *, len + 1), xcb_randr_get_output_info_name(output_info_r), len);
name[len] = '\0';
screen_output_array_append(&new_screen->outputs,
(screen_output_t) { .name = name,
.mm_width = output_info_r->mm_width,
.mm_height = output_info_r->mm_height,
.output = randr_outputs[j] });
output.name = name;
output.mm_width = output_info_r->mm_width;
output.mm_height = output_info_r->mm_height;
randr_output_array_init(&output.outputs);
randr_output_array_append(&output.outputs, randr_outputs[j]);
screen_output_array_append(&new_screen->outputs, output);
p_delete(&output_info_r);
}
@ -326,8 +380,42 @@ screen_scan_randr(void)
}
p_delete(&screen_res_r);
screen_update_primary();
return true;
}
static bool
screen_scan_randr(void)
{
/* Check for extension before checking for XRandR */
if(xcb_get_extension_data(globalconf.connection, &xcb_randr_id)->present)
{
xcb_randr_query_version_reply_t *version_reply =
xcb_randr_query_version_reply(globalconf.connection,
xcb_randr_query_version(globalconf.connection, 1, 5), 0);
if(version_reply)
{
uint32_t major_version = version_reply->major_version;
uint32_t minor_version = version_reply->minor_version;
bool found;
p_delete(&version_reply);
/* Do we agree on a supported version? */
if (major_version != 1 || minor_version < 2)
return false;
globalconf.have_randr_13 = minor_version >= 3;
if (minor_version >= 5)
found = screen_scan_randr_monitors();
else
found = screen_scan_randr_crtcs();
if (found)
screen_update_primary();
else
return false;
return screens_exist();
}
}
@ -664,7 +752,8 @@ screen_update_primary(void)
foreach(screen, globalconf.screens)
{
foreach(output, (*screen)->outputs)
if (output->output == primary->output)
foreach (randr_output, output->outputs)
if (*randr_output == primary->output)
primary_screen = *screen;
}
p_delete(&primary);