Merge pull request #828 from psychon/screen-scan2

Stop restarting on RandR changes
This commit is contained in:
Daniel Hahler 2016-05-01 17:00:05 +02:00
commit 6b6cbf4a1f
7 changed files with 311 additions and 150 deletions

View File

@ -400,8 +400,7 @@ event_handle_configurenotify(xcb_configure_notify_event_t *ev)
xcb_screen_t *screen = globalconf.screen;
if(ev->window == screen->root)
/* it's not that we panic, but restart */
awesome_restart();
globalconf.screen_need_refresh = true;
/* Copy what XRRUpdateConfiguration() would do: Update the configuration */
if(ev->window == screen->root) {
@ -782,7 +781,7 @@ event_handle_randr_screen_change_notify(xcb_randr_screen_change_notify_event_t *
globalconf.screen->height_in_pixels = ev->height;
}
awesome_restart();
globalconf.screen_need_refresh = true;
}
/** XRandR event handler for RRNotify subtype XRROutputChangeNotifyEvent
@ -992,10 +991,8 @@ void event_init(void)
const xcb_query_extension_reply_t *reply;
reply = xcb_get_extension_data(globalconf.connection, &xcb_randr_id);
if (reply && reply->present) {
xcb_randr_select_input(globalconf.connection, globalconf.screen->root, XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE);
if (reply && reply->present)
globalconf.event_base_randr = reply->first_event;
}
reply = xcb_get_extension_data(globalconf.connection, &xcb_shape_id);
if (reply && reply->present)

View File

@ -38,9 +38,13 @@ void drawin_refresh(void);
void client_focus_refresh(void);
void client_border_refresh(void);
/* objects/screen.c */
void screen_refresh(void);
static inline int
awesome_refresh(void)
{
screen_refresh();
luaA_emit_refresh();
banning_refresh();
stack_refresh();

View File

@ -92,6 +92,10 @@ typedef struct
xcb_window_t selection_owner_window;
/** Do we have RandR 1.3 or newer? */
bool have_randr_13;
/** Do we have RandR 1.5 or newer? */
bool have_randr_15;
/** Do we have a RandR screen update pending? */
bool screen_need_refresh;
/** Check for XTest extension */
bool have_xtest;
/** Check for SHAPE extension */

View File

@ -446,12 +446,14 @@ function client.object.to_selected_tags(self)
end
end
if #tags == 0 then
tags = self.screen.selected_tags
end
if self.screen then
if #tags == 0 then
tags = self.screen.selected_tags
end
if #tags == 0 then
tags = self.screen.tags
if #tags == 0 then
tags = self.screen.tags
end
end
if #tags ~= 0 then

View File

@ -285,6 +285,14 @@ end
capi.client.connect_signal("property::struts", update_wiboxes_on_struts)
capi.client.connect_signal("unmanage", update_wiboxes_on_struts)
capi.screen.connect_signal("removed", function(s)
for _, wprop in ipairs(wiboxes) do
if wprop.screen == s then
wprop.wibox.visible = false
end
end
end)
function awfulwibox.mt:__call(...)
return awfulwibox.new(...)
end

View File

@ -50,6 +50,7 @@
#include "banning.h"
#include "objects/client.h"
#include "objects/drawin.h"
#include "event.h"
#include <stdio.h>
@ -182,6 +183,13 @@ screen_wipe(screen_t *s)
screen_output_array_wipe(&s->outputs);
}
/** Check if a screen is valid */
static bool
screen_checker(screen_t *s)
{
return s->valid;
}
/** Get a screen argument from the lua stack */
screen_t *
luaA_checkscreen(lua_State *L, int sidx)
@ -197,56 +205,52 @@ luaA_checkscreen(lua_State *L, int sidx)
return luaA_checkudata(L, sidx, &screen_class);
}
static inline area_t
screen_xsitoarea(xcb_xinerama_screen_info_t si)
{
area_t a =
{
.x = si.x_org,
.y = si.y_org,
.width = si.width,
.height = si.height
};
return a;
}
static void
screen_add(lua_State *L, int sidx)
screen_deduplicate(lua_State *L, screen_array_t *screens)
{
screen_t *new_screen = luaA_checkudata(L, sidx, &screen_class);
/* Remove duplicate screens */
for(int first = 0; first < screens->len; first++) {
screen_t *first_screen = screens->tab[first];
for(int second = 0; second < screens->len; second++) {
screen_t *second_screen = screens->tab[second];
if (first == second)
continue;
foreach(screen_to_test, globalconf.screens)
if(new_screen->geometry.x == (*screen_to_test)->geometry.x
&& new_screen->geometry.y == (*screen_to_test)->geometry.y)
{
/* we already have a screen for this area, just check if
* it's not bigger and drop it */
(*screen_to_test)->geometry.width =
MAX(new_screen->geometry.width, (*screen_to_test)->geometry.width);
(*screen_to_test)->geometry.height =
MAX(new_screen->geometry.height, (*screen_to_test)->geometry.height);
lua_remove(L, sidx);
if (first_screen->geometry.width < second_screen->geometry.width
&& first_screen->geometry.height < second_screen->geometry.height)
/* Don't drop a smaller screen */
continue;
if (first_screen->geometry.x == second_screen->geometry.x
&& first_screen->geometry.y == second_screen->geometry.y) {
/* Found a duplicate */
first_screen->geometry.width = MAX(first_screen->geometry.width, second_screen->geometry.width);
first_screen->geometry.height = MAX(first_screen->geometry.height, second_screen->geometry.height);
screen_array_take(screens, second);
luaA_object_unref(L, second_screen);
/* Restart the search */
screen_deduplicate(L, screens);
return;
}
sidx = luaA_absindex(L, sidx);
lua_pushvalue(L, sidx);
luaA_object_ref(L, sidx);
screen_array_append(&globalconf.screens, new_screen);
luaA_object_emit_signal(L, -1, "added", 0);
lua_pop(L, 1);
}
}
}
static bool
screens_exist(void)
static screen_t *
screen_add(lua_State *L, screen_array_t *screens)
{
return globalconf.screens.len > 0;
screen_t *new_screen = screen_new(L);
luaA_object_ref(L, -1);
screen_array_append(screens, new_screen);
return new_screen;
}
/* Monitors were introduced in RandR 1.5 */
#ifdef XCB_RANDR_GET_MONITORS
static bool
screen_scan_randr_monitors(void)
static void
screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
{
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);
@ -256,7 +260,6 @@ screen_scan_randr_monitors(void)
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;
@ -266,11 +269,12 @@ screen_scan_randr_monitors(void)
if(!xcb_randr_monitor_info_outputs_length(monitor_iter.data))
continue;
new_screen = screen_new(L);
new_screen = screen_add(L, screens);
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;
new_screen->geometry.width = monitor_iter.data->width;
new_screen->geometry.height = monitor_iter.data->height;
new_screen->xid = monitor_iter.data->name;
output.mm_width = monitor_iter.data->width_in_millimeters;
output.mm_height = monitor_iter.data->height_in_millimeters;
@ -285,7 +289,7 @@ screen_scan_randr_monitors(void)
output.name[len] = '\0';
p_delete(&name_r);
} else {
output.name = strdup("unknown");
output.name = a_strdup("unknown");
}
randr_output_array_init(&output.outputs);
@ -295,24 +299,19 @@ screen_scan_randr_monitors(void)
}
screen_output_array_append(&new_screen->outputs, output);
found = true;
screen_add(L, -1);
}
p_delete(&monitors_r);
return found;
}
#else
static bool
screen_scan_randr_monitors(void)
static void
screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
{
return false;
}
#endif
static bool
screen_scan_randr_crtcs(void)
static void
screen_scan_randr_crtcs(lua_State *L, screen_array_t *screens)
{
/* A quick XRandR recall:
* You have CRTC that manages a part of a SCREEN.
@ -320,21 +319,11 @@ screen_scan_randr_crtcs(void)
xcb_randr_get_screen_resources_cookie_t screen_res_c = xcb_randr_get_screen_resources(globalconf.connection, globalconf.screen->root);
xcb_randr_get_screen_resources_reply_t *screen_res_r = xcb_randr_get_screen_resources_reply(globalconf.connection, screen_res_c, NULL);
/* Only use the data from XRandR if there is more than one screen
* defined. This should work around the broken nvidia driver. */
if (screen_res_r->num_crtcs <= 1)
{
p_delete(&screen_res_r);
return false;
}
/* 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++)
{
lua_State *L = globalconf_get_lua_State();
/* 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);
@ -344,11 +333,12 @@ screen_scan_randr_crtcs(void)
continue;
/* Prepare the new screen */
screen_t *new_screen = screen_new(L);
screen_t *new_screen = screen_add(L, screens);
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;
new_screen->xid = randr_crtcs[i];
xcb_randr_output_t *randr_outputs = xcb_randr_get_crtc_info_outputs(crtc_info_r);
@ -373,111 +363,119 @@ screen_scan_randr_crtcs(void)
p_delete(&output_info_r);
}
screen_add(L, -1);
if (A_STREQ(name, "default"))
{
/* non RandR 1.2+ X driver don't return any usable multihead
* data. I'm looking at you, nvidia binary blob!
*/
warn("Ignoring RandR, only a compatibility layer is present.");
/* Get rid of the screens that we already created */
foreach(screen, *screens)
luaA_object_unref(L, *screen);
screen_array_wipe(screens);
screen_array_init(screens);
return;
}
}
p_delete(&crtc_info_r);
}
p_delete(&screen_res_r);
return true;
}
static bool
screen_scan_randr(void)
static void
screen_scan_randr(lua_State *L, screen_array_t *screens)
{
xcb_randr_query_version_reply_t *version_reply;
uint32_t major_version;
uint32_t minor_version;
/* 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;
if(!xcb_get_extension_data(globalconf.connection, &xcb_randr_id)->present)
return;
p_delete(&version_reply);
version_reply =
xcb_randr_query_version_reply(globalconf.connection,
xcb_randr_query_version(globalconf.connection, 1, 5), 0);
if(!version_reply)
return;
/* Do we agree on a supported version? */
if (major_version != 1 || minor_version < 2)
return false;
major_version = version_reply->major_version;
minor_version = version_reply->minor_version;
p_delete(&version_reply);
globalconf.have_randr_13 = minor_version >= 3;
/* Do we agree on a supported version? */
if (major_version != 1 || minor_version < 2)
return;
if (minor_version >= 5)
found = screen_scan_randr_monitors();
else
found = screen_scan_randr_crtcs();
globalconf.have_randr_13 = minor_version >= 3;
#if XCB_RANDR_MAJOR_VERSION > 1 || XCB_RANDR_MINOR_VERSION >= 5
globalconf.have_randr_15 = minor_version >= 5;
#else
globalconf.have_randr_15 = false;
#endif
if (found)
screen_update_primary();
else
return false;
return screens_exist();
}
}
/* We want to know when something changes */
xcb_randr_select_input(globalconf.connection,
globalconf.screen->root,
XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE);
return false;
if (globalconf.have_randr_15)
screen_scan_randr_monitors(L, screens);
else
screen_scan_randr_crtcs(L, screens);
}
static bool
screen_scan_xinerama(void)
static void
screen_scan_xinerama(lua_State *L, screen_array_t *screens)
{
bool xinerama_is_active = false;
bool xinerama_is_active;
xcb_xinerama_is_active_reply_t *xia;
xcb_xinerama_query_screens_reply_t *xsq;
xcb_xinerama_screen_info_t *xsi;
int xinerama_screen_number;
/* Check for extension before checking for Xinerama */
if(xcb_get_extension_data(globalconf.connection, &xcb_xinerama_id)->present)
if(!xcb_get_extension_data(globalconf.connection, &xcb_xinerama_id)->present)
return;
xia = xcb_xinerama_is_active_reply(globalconf.connection, xcb_xinerama_is_active(globalconf.connection), NULL);
xinerama_is_active = xia->state;
p_delete(&xia);
if(!xinerama_is_active)
return;
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);
for(int screen = 0; screen < xinerama_screen_number; screen++)
{
xcb_xinerama_is_active_reply_t *xia;
xia = xcb_xinerama_is_active_reply(globalconf.connection, xcb_xinerama_is_active(globalconf.connection), NULL);
xinerama_is_active = xia->state;
p_delete(&xia);
screen_t *s = screen_add(L, screens);
s->geometry.x = xsi[screen].x_org;
s->geometry.y = xsi[screen].y_org;
s->geometry.width = xsi[screen].width;
s->geometry.height = xsi[screen].height;
}
if(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++)
{
lua_State *L = globalconf_get_lua_State();
screen_t *s = screen_new(L);
s->geometry = screen_xsitoarea(xsi[screen]);
screen_add(L, -1);
}
p_delete(&xsq);
return screens_exist();
}
return false;
p_delete(&xsq);
}
static void screen_scan_x11(void)
static void screen_scan_x11(lua_State *L, screen_array_t *screens)
{
lua_State *L = globalconf_get_lua_State();
xcb_screen_t *xcb_screen = globalconf.screen;
screen_t *s = screen_new(L);
screen_t *s = screen_add(L, screens);
s->geometry.x = 0;
s->geometry.y = 0;
s->geometry.width = xcb_screen->width_in_pixels;
s->geometry.height = xcb_screen->height_in_pixels;
screen_add(L, -1);
}
/** Get screens informations and fill global configuration.
@ -485,8 +483,145 @@ static void screen_scan_x11(void)
void
screen_scan(void)
{
if(!screen_scan_randr() && !screen_scan_xinerama())
screen_scan_x11();
lua_State *L;
L = globalconf_get_lua_State();
screen_scan_randr(L, &globalconf.screens);
if (globalconf.screens.len == 0)
screen_scan_xinerama(L, &globalconf.screens);
if (globalconf.screens.len == 0)
screen_scan_x11(L, &globalconf.screens);
assert(globalconf.screens.len > 0);
screen_deduplicate(L, &globalconf.screens);
foreach(screen, globalconf.screens) {
(*screen)->valid = true;
luaA_object_push(L, *screen);
luaA_object_emit_signal(L, -1, "added", 0);
lua_pop(L, 1);
}
screen_update_primary();
}
/* Called when a screen is removed, removes references to the old screen */
static void
screen_removed(lua_State *L, int sidx)
{
screen_t *screen = luaA_checkudata(L, sidx, &screen_class);
luaA_object_emit_signal(L, sidx, "removed", 0);
if (globalconf.primary_screen == screen)
globalconf.primary_screen = NULL;
foreach(c, globalconf.clients) {
if((*c)->screen == screen)
screen_client_moveto(*c, screen_getbycoord(
(*c)->geometry.x, (*c)->geometry.y), false);
}
}
static void
screen_modified(screen_t *existing_screen, screen_t *other_screen)
{
lua_State *L = globalconf_get_lua_State();
if(!AREA_EQUAL(existing_screen->geometry, other_screen->geometry)) {
existing_screen->geometry = other_screen->geometry;
luaA_object_push(L, existing_screen);
luaA_object_emit_signal(L, -1, "property::geometry", 0);
luaA_object_emit_signal(L, -1, "property::workarea", 0);
lua_pop(L, 1);
}
bool outputs_changed = existing_screen->outputs.len != other_screen->outputs.len;
if(!outputs_changed)
for(int i = 0; i < existing_screen->outputs.len; i++) {
screen_output_t *existing_output = &existing_screen->outputs.tab[i];
screen_output_t *other_output = &other_screen->outputs.tab[i];
outputs_changed |= existing_output->mm_width != other_output->mm_width;
outputs_changed |= existing_output->mm_height != other_output->mm_height;
outputs_changed |= A_STRNEQ(existing_output->name, other_output->name);
}
/* Brute-force update the outputs by swapping */
screen_output_array_t tmp = other_screen->outputs;
other_screen->outputs = existing_screen->outputs;
existing_screen->outputs = tmp;
if(outputs_changed) {
luaA_object_push(L, existing_screen);
luaA_object_emit_signal(L, -1, "property::outputs", 0);
lua_pop(L, 1);
}
}
void
screen_refresh(void)
{
if(!globalconf.screen_need_refresh)
return;
globalconf.screen_need_refresh = false;
screen_array_t new_screens;
lua_State *L = globalconf_get_lua_State();
screen_array_init(&new_screens);
if (globalconf.have_randr_15)
screen_scan_randr_monitors(L, &new_screens);
else
screen_scan_randr_crtcs(L, &new_screens);
screen_deduplicate(L, &new_screens);
/* Add new screens */
foreach(new_screen, new_screens) {
bool found = false;
foreach(old_screen, globalconf.screens)
found |= (*new_screen)->xid == (*old_screen)->xid;
if(!found) {
screen_array_append(&globalconf.screens, *new_screen);
(*new_screen)->valid = true;
luaA_object_push(L, *new_screen);
luaA_object_emit_signal(L, -1, "added", 0);
/* Get an extra reference since both new_screens and
* globalconf.screens reference this screen now */
luaA_object_ref(L, -1);
}
}
/* Remove screens which are gone */
for(int i = 0; i < globalconf.screens.len; i++) {
screen_t *old_screen = globalconf.screens.tab[i];
bool found = false;
foreach(new_screen, new_screens)
found |= (*new_screen)->xid == old_screen->xid;
if(!found) {
screen_array_take(&globalconf.screens, i);
i--;
luaA_object_push(L, old_screen);
screen_removed(L, -1);
lua_pop(L, 1);
luaA_object_unref(L, old_screen);
old_screen->valid = false;
}
}
/* Update changed screens */
foreach(existing_screen, globalconf.screens)
foreach(new_screen, new_screens)
if((*existing_screen)->xid == (*new_screen)->xid)
screen_modified(*existing_screen, *new_screen);
foreach(screen, new_screens)
luaA_object_unref(L, *screen);
screen_array_wipe(&new_screens);
screen_update_primary();
}
/** Return the squared distance of the given screen to the coordinates.
@ -904,7 +1039,7 @@ screen_class_setup(lua_State *L)
luaA_class_setup(L, &screen_class, "screen", NULL,
(lua_class_allocator_t) screen_new,
(lua_class_collector_t) screen_wipe,
NULL,
(lua_class_checker_t) screen_checker,
luaA_class_index_miss_property, luaA_class_newindex_miss_property,
screen_methods, screen_meta);
luaA_class_add_property(&screen_class, "geometry",
@ -925,6 +1060,8 @@ screen_class_setup(lua_State *L)
NULL);
signal_add(&screen_class.signals, "property::workarea");
signal_add(&screen_class.signals, "property::geometry");
signal_add(&screen_class.signals, "property::outputs");
/**
* @signal .primary_changed
*/
@ -934,6 +1071,11 @@ screen_class_setup(lua_State *L)
* @signal .added
*/
signal_add(&screen_class.signals, "added");
/**
* This signal is emitted when a screen is removed from the setup.
* @signal removed
*/
signal_add(&screen_class.signals, "removed");
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -33,10 +33,14 @@ ARRAY_TYPE(screen_output_t, screen_output)
struct a_screen
{
LUA_OBJECT_HEADER
/** Is this screen still valid and may be used? */
bool valid;
/** Screen geometry */
area_t geometry;
/** The screen outputs informations */
screen_output_array_t outputs;
/** Some XID identifying this screen */
uint32_t xid;
};
ARRAY_FUNCS(screen_t *, screen, DO_NOTHING)