diff --git a/event.c b/event.c index 3062c5ce..920b09c7 100644 --- a/event.c +++ b/event.c @@ -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) diff --git a/event.h b/event.h index 1d471aa3..5849f732 100644 --- a/event.h +++ b/event.h @@ -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(); diff --git a/globalconf.h b/globalconf.h index dc407596..877be221 100644 --- a/globalconf.h +++ b/globalconf.h @@ -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 */ diff --git a/lib/awful/client.lua b/lib/awful/client.lua index 7cbd82f3..60fd9b01 100644 --- a/lib/awful/client.lua +++ b/lib/awful/client.lua @@ -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 diff --git a/lib/awful/wibox.lua b/lib/awful/wibox.lua index e98a65fb..9cc1e18a 100644 --- a/lib/awful/wibox.lua +++ b/lib/awful/wibox.lua @@ -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 diff --git a/objects/screen.c b/objects/screen.c index dbee218a..423d1be4 100644 --- a/objects/screen.c +++ b/objects/screen.c @@ -50,6 +50,7 @@ #include "banning.h" #include "objects/client.h" #include "objects/drawin.h" +#include "event.h" #include @@ -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 diff --git a/objects/screen.h b/objects/screen.h index d6e6149f..bba8810f 100644 --- a/objects/screen.h +++ b/objects/screen.h @@ -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)