From 8c87b7cb57953a4da7abb7bc623880c046f19d6e Mon Sep 17 00:00:00 2001 From: Julien Danjou Date: Mon, 21 Sep 2009 16:29:19 +0200 Subject: [PATCH] screen: store RandR information Signed-off-by: Julien Danjou --- screen.c | 205 +++++++++++++++++++++++++++++++++++++++---------------- screen.h | 5 ++ 2 files changed, 150 insertions(+), 60 deletions(-) diff --git a/screen.c b/screen.c index e8a0b564..8f295c85 100644 --- a/screen.c +++ b/screen.c @@ -23,6 +23,7 @@ #include #include +#include #include "screen.h" #include "ewmh.h" @@ -33,6 +34,16 @@ #include "luaa.h" #include "common/xutil.h" +struct screen_output_t +{ + /** The XRandR names of the output */ + char *name; + /** The size in millimeters */ + uint32_t mm_width, mm_height; +}; + +ARRAY_FUNCS(screen_output_t, screen_output, DO_NOTHING) + static inline area_t screen_xsitoarea(xcb_xinerama_screen_info_t si) { @@ -66,76 +77,150 @@ screen_default_visual(xcb_screen_t *s) void screen_scan(void) { - /* Check for extension before checking for Xinerama */ - if(xcb_get_extension_data(globalconf.connection, &xcb_xinerama_id)->present) + /* Check for extension before checking for XRandR */ + if(xcb_get_extension_data(globalconf.connection, &xcb_randr_id)->present) { - xcb_xinerama_is_active_reply_t *xia; - xia = xcb_xinerama_is_active_reply(globalconf.connection, xcb_xinerama_is_active(globalconf.connection), NULL); - globalconf.xinerama_is_active = xia->state; - p_delete(&xia); - } - - if(globalconf.xinerama_is_active) - { - xcb_xinerama_query_screens_reply_t *xsq; - xcb_xinerama_screen_info_t *xsi; - int xinerama_screen_number; - - xsq = xcb_xinerama_query_screens_reply(globalconf.connection, - xcb_xinerama_query_screens_unchecked(globalconf.connection), - NULL); - - xsi = xcb_xinerama_query_screens_screen_info(xsq); - xinerama_screen_number = xcb_xinerama_query_screens_screen_info_length(xsq); - - /* now check if screens overlaps (same x,y): if so, we take only the biggest one */ - for(int screen = 0; screen < xinerama_screen_number; screen++) + xcb_randr_query_version_reply_t *version_reply = + xcb_randr_query_version_reply(globalconf.connection, + xcb_randr_query_version(globalconf.connection, 1, 1), 0); + if(version_reply) { - bool drop = false; - foreach(screen_to_test, globalconf.screens) - if(xsi[screen].x_org == screen_to_test->geometry.x - && xsi[screen].y_org == screen_to_test->geometry.y) - { - /* we already have a screen for this area, just check if - * it's not bigger and drop it */ - drop = true; - int i = screen_array_indexof(&globalconf.screens, screen_to_test); - screen_to_test->geometry.width = - MAX(xsi[screen].width, xsi[i].width); - screen_to_test->geometry.height = - MAX(xsi[screen].height, xsi[i].height); - } - if(!drop) + /* A quick XRandR recall: + * You have CRTC that manages a part of a SCREEN. + * Each CRTC can draw stuff on one or more OUTPUT. */ + xcb_screen_t *screen = xutil_screen_get(globalconf.connection, globalconf.default_screen); + + xcb_randr_get_screen_resources_cookie_t screen_res_c = xcb_randr_get_screen_resources(globalconf.connection, screen->root); + xcb_randr_get_screen_resources_reply_t *screen_res_r = xcb_randr_get_screen_resources_reply(globalconf.connection, screen_res_c, NULL); + + /* We go through CRTC, and build a screen for each one. */ + xcb_randr_crtc_t *randr_crtcs = xcb_randr_get_screen_resources_crtcs(screen_res_r); + + for(int i = 0; i < screen_res_r->num_crtcs; i++) { - screen_t s; - p_clear(&s, 1); - s.geometry = screen_xsitoarea(xsi[screen]); - screen_array_append(&globalconf.screens, s); + /* Get info on the output crtc */ + xcb_randr_get_crtc_info_cookie_t crtc_info_c = xcb_randr_get_crtc_info(globalconf.connection, randr_crtcs[i], XCB_CURRENT_TIME); + xcb_randr_get_crtc_info_reply_t *crtc_info_r = xcb_randr_get_crtc_info_reply(globalconf.connection, crtc_info_c, NULL); + + /* If CRTC has no OUTPUT, ignore it */ + if(!xcb_randr_get_crtc_info_outputs_length(crtc_info_r)) + continue; + + /* Prepare the new screen */ + screen_t new_screen; + p_clear(&new_screen, 1); + new_screen.geometry.x = crtc_info_r->x; + new_screen.geometry.y = crtc_info_r->y; + new_screen.geometry.width= crtc_info_r->width; + new_screen.geometry.height= crtc_info_r->height; + + xcb_randr_output_t *randr_outputs = xcb_randr_get_crtc_info_outputs(crtc_info_r); + + for(int j = 0; j < xcb_randr_get_crtc_info_outputs_length(crtc_info_r); j++) + { + 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); + + 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 }); + + p_delete(&output_info_r); + } + + screen_array_append(&globalconf.screens, new_screen); + + p_delete(&crtc_info_r); } + + p_delete(&screen_res_r); + + /* If RandR provides more than 2 active CRTC, Xinerama is enabled */ + if(globalconf.screens.len > 1) + globalconf.xinerama_is_active = true; + + globalconf.screens.tab[0].visual = screen_default_visual(xutil_screen_get(globalconf.connection, globalconf.default_screen)); } - - p_delete(&xsq); - - xcb_screen_t *s = xutil_screen_get(globalconf.connection, globalconf.default_screen); - globalconf.screens.tab[0].visual = screen_default_visual(s); } else - /* One screen only / Zaphod mode */ - for(int screen = 0; - screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); - screen++) + { + /* Check for extension before checking for Xinerama */ + if(xcb_get_extension_data(globalconf.connection, &xcb_xinerama_id)->present) { - xcb_screen_t *xcb_screen = xutil_screen_get(globalconf.connection, screen); - screen_t s; - p_clear(&s, 1); - s.geometry.x = 0; - s.geometry.y = 0; - s.geometry.width = xcb_screen->width_in_pixels; - s.geometry.height = xcb_screen->height_in_pixels; - s.visual = screen_default_visual(xcb_screen); - screen_array_append(&globalconf.screens, s); + xcb_xinerama_is_active_reply_t *xia; + xia = xcb_xinerama_is_active_reply(globalconf.connection, xcb_xinerama_is_active(globalconf.connection), NULL); + globalconf.xinerama_is_active = xia->state; + p_delete(&xia); } + if(globalconf.xinerama_is_active) + { + xcb_xinerama_query_screens_reply_t *xsq; + xcb_xinerama_screen_info_t *xsi; + int xinerama_screen_number; + + xsq = xcb_xinerama_query_screens_reply(globalconf.connection, + xcb_xinerama_query_screens_unchecked(globalconf.connection), + NULL); + + xsi = xcb_xinerama_query_screens_screen_info(xsq); + xinerama_screen_number = xcb_xinerama_query_screens_screen_info_length(xsq); + + /* now check if screens overlaps (same x,y): if so, we take only the biggest one */ + for(int screen = 0; screen < xinerama_screen_number; screen++) + { + bool drop = false; + foreach(screen_to_test, globalconf.screens) + if(xsi[screen].x_org == screen_to_test->geometry.x + && xsi[screen].y_org == screen_to_test->geometry.y) + { + /* we already have a screen for this area, just check if + * it's not bigger and drop it */ + drop = true; + int i = screen_array_indexof(&globalconf.screens, screen_to_test); + screen_to_test->geometry.width = + MAX(xsi[screen].width, xsi[i].width); + screen_to_test->geometry.height = + MAX(xsi[screen].height, xsi[i].height); + } + if(!drop) + { + screen_t s; + p_clear(&s, 1); + s.geometry = screen_xsitoarea(xsi[screen]); + screen_array_append(&globalconf.screens, s); + } + } + + p_delete(&xsq); + + xcb_screen_t *s = xutil_screen_get(globalconf.connection, globalconf.default_screen); + globalconf.screens.tab[0].visual = screen_default_visual(s); + } + else + /* One screen only / Zaphod mode */ + for(int screen = 0; + screen < xcb_setup_roots_length(xcb_get_setup(globalconf.connection)); + screen++) + { + xcb_screen_t *xcb_screen = xutil_screen_get(globalconf.connection, screen); + screen_t s; + p_clear(&s, 1); + s.geometry.x = 0; + s.geometry.y = 0; + s.geometry.width = xcb_screen->width_in_pixels; + s.geometry.height = xcb_screen->height_in_pixels; + s.visual = screen_default_visual(xcb_screen); + screen_array_append(&globalconf.screens, s); + } + } + globalconf.screen_focus = globalconf.screens.tab; } diff --git a/screen.h b/screen.h index 6db22d59..d9a5f06f 100644 --- a/screen.h +++ b/screen.h @@ -25,6 +25,9 @@ #include "globalconf.h" #include "draw.h" +typedef struct screen_output_t screen_output_t; +ARRAY_TYPE(screen_output_t, screen_output) + struct a_screen { /** Screen geometry */ @@ -50,6 +53,8 @@ struct a_screen signal_array_t signals; /** True if the banning on this screen needs to be updated */ bool need_lazy_banning; + /** The screen outputs informations */ + screen_output_array_t outputs; }; ARRAY_FUNCS(screen_t, screen, DO_NOTHING)