This commit is contained in:
Uli Schlachter 2016-05-09 18:40:50 +02:00
commit c62da0dcc7
9 changed files with 192 additions and 17 deletions

View File

@ -18,6 +18,7 @@ local timer = require("gears.timer")
-- --
-- @param obj An object that should have a .screen property. -- @param obj An object that should have a .screen property.
local function check_focus(obj) local function check_focus(obj)
if not obj.screen.valid then return end
-- When no visible client has the focus... -- When no visible client has the focus...
if not client.focus or not client.focus:isvisible() then if not client.focus or not client.focus:isvisible() then
local c = aclient.focus.history.get(screen[obj.screen], 0, aclient.focus.filter) local c = aclient.focus.history.get(screen[obj.screen], 0, aclient.focus.filter)
@ -39,7 +40,7 @@ end
-- @param tag A tag object -- @param tag A tag object
local function check_focus_tag(t) local function check_focus_tag(t)
local s = t.screen local s = t.screen
if not s then return end if (not s) or (not s.valid) then return end
s = screen[s] s = screen[s]
check_focus({ screen = s }) check_focus({ screen = s })
if client.focus and screen[client.focus.screen] ~= s then if client.focus and screen[client.focus.screen] ~= s then

View File

@ -250,12 +250,14 @@ end
-- @see awful.tag.find_fallback -- @see awful.tag.find_fallback
-- @tparam[opt=awful.tag.find_fallback()] tag fallback_tag Tag to assign -- @tparam[opt=awful.tag.find_fallback()] tag fallback_tag Tag to assign
-- stickied tags to. -- stickied tags to.
-- @tparam[opt=false] boolean force Move even non-sticky clients to the fallback
-- tag.
-- @return Returns true if the tag is successfully deleted, nil otherwise. -- @return Returns true if the tag is successfully deleted, nil otherwise.
-- If there are no clients exclusively on this tag then delete it. Any -- If there are no clients exclusively on this tag then delete it. Any
-- stickied clients are assigned to the optional 'fallback_tag'. -- stickied clients are assigned to the optional 'fallback_tag'.
-- If after deleting the tag there is no selected tag, try and restore from -- If after deleting the tag there is no selected tag, try and restore from
-- history or select the first tag on the screen. -- history or select the first tag on the screen.
function tag.object.delete(self, fallback_tag) function tag.object.delete(self, fallback_tag, force)
-- abort if the taf isn't currently activated -- abort if the taf isn't currently activated
if not self.activated then return end if not self.activated then return end
@ -283,8 +285,7 @@ function tag.object.delete(self, fallback_tag)
-- If a client has only this tag, or stickied clients with -- If a client has only this tag, or stickied clients with
-- nowhere to go, abort. -- nowhere to go, abort.
if (not c.sticky and nb_tags == 1) or if (not c.sticky and nb_tags == 1 and not force) then
(c.sticky and fallback_tag == nil) then
return return
-- If a client has multiple tags, then do not move it to fallback -- If a client has multiple tags, then do not move it to fallback
elseif nb_tags < 2 then elseif nb_tags < 2 then
@ -294,6 +295,7 @@ function tag.object.delete(self, fallback_tag)
-- delete the tag -- delete the tag
data.tags[self].screen = nil data.tags[self].screen = nil
data.tags[self] = nil
self.activated = false self.activated = false
-- Update all indexes -- Update all indexes
@ -1350,11 +1352,45 @@ capi.tag.add_signal("property::urgent")
capi.tag.add_signal("property::urgent_count") capi.tag.add_signal("property::urgent_count")
capi.tag.add_signal("property::volatile") capi.tag.add_signal("property::volatile")
capi.tag.add_signal("request::screen")
capi.tag.add_signal("removal-pending")
capi.screen.add_signal("tag::history::update") capi.screen.add_signal("tag::history::update")
capi.screen.connect_signal("tag::history::update", tag.history.update) capi.screen.connect_signal("tag::history::update", tag.history.update)
capi.screen.connect_signal("removed", function(s)
-- First give other code a chance to move the tag to another screen
for _, t in pairs(s.tags) do
t:emit_signal("request::screen")
end
-- Everything that's left: Tell everyone that these tags go away (other code
-- could e.g. save clients)
for _, t in pairs(s.tags) do
t:emit_signal("removal-pending")
end
-- Give other code yet another change to save clients
for _, c in pairs(capi.client.get(s)) do
c:emit_signal("request::tag", nil, { reason = "screen-removed" })
end
-- Then force all clients left to go somewhere random
local fallback = nil
for other_screen in capi.screen do
if #other_screen.tags > 0 then
fallback = other_screen.tags[1]
break
end
end
for _, t in pairs(s.tags) do
t:delete(fallback, true)
end
-- If any tag survived until now, forcefully get rid of it
for _, t in pairs(s.tags) do
t.activated = false
data.tags[t] = nil
end
end)
function tag.mt:__call(...) function tag.mt:__call(...)
return tag.new(...) return tag.new(...)
end end

View File

@ -168,7 +168,9 @@ function taglist.new(screen, filter, buttons, style, update_function, base_widge
-- Add a delayed callback for the first update. -- Add a delayed callback for the first update.
if not queued_update[screen] then if not queued_update[screen] then
timer.delayed_call(function() timer.delayed_call(function()
if screen.valid then
taglist_update(screen, w, buttons, filter, data, style, uf) taglist_update(screen, w, buttons, filter, data, style, uf)
end
queued_update[screen] = false queued_update[screen] = false
end) end)
queued_update[screen] = true queued_update[screen] = true
@ -203,6 +205,9 @@ function taglist.new(screen, filter, buttons, style, update_function, base_widge
capi.client.connect_signal("tagged", uc) capi.client.connect_signal("tagged", uc)
capi.client.connect_signal("untagged", uc) capi.client.connect_signal("untagged", uc)
capi.client.connect_signal("unmanage", uc) capi.client.connect_signal("unmanage", uc)
capi.screen.connect_signal("removed", function(s)
instances[get_screen(s)] = nil
end)
end end
w._do_taglist_update() w._do_taglist_update()
local list = instances[screen] local list = instances[screen]

View File

@ -181,7 +181,9 @@ function tasklist.new(screen, filter, buttons, style, update_function, base_widg
if not queued_update then if not queued_update then
timer.delayed_call(function() timer.delayed_call(function()
queued_update = false queued_update = false
if screen.valid then
tasklist_update(screen, w, buttons, filter, data, style, uf) tasklist_update(screen, w, buttons, filter, data, style, uf)
end
end) end)
queued_update = true queued_update = true
end end
@ -201,9 +203,11 @@ function tasklist.new(screen, filter, buttons, style, update_function, base_widg
end end
local function u() local function u()
for s in pairs(instances) do for s in pairs(instances) do
if s.valid then
us(s) us(s)
end end
end end
end
tag.attached_connect_signal(nil, "property::selected", u) tag.attached_connect_signal(nil, "property::selected", u)
tag.attached_connect_signal(nil, "property::activated", u) tag.attached_connect_signal(nil, "property::activated", u)
@ -238,6 +242,9 @@ function tasklist.new(screen, filter, buttons, style, update_function, base_widg
capi.client.connect_signal("list", u) capi.client.connect_signal("list", u)
capi.client.connect_signal("focus", u) capi.client.connect_signal("focus", u)
capi.client.connect_signal("unfocus", u) capi.client.connect_signal("unfocus", u)
capi.screen.connect_signal("removed", function(s)
instances[get_screen(s)] = nil
end)
end end
w._do_tasklist_update() w._do_tasklist_update()
local list = instances[screen] local list = instances[screen]

View File

@ -154,6 +154,16 @@ screen.connect_for_each_screen(function(s)
} }
end) end)
capi.screen.connect_signal("removed", function(scr)
-- Destroy all notifications on this screen
for _, list in pairs(naughty.notifications[scr]) do
while #list > 0 do
naughty.destroy(list[1])
end
end
naughty.notifications[scr] = nil
end)
--- Notification state --- Notification state
function naughty.is_suspended() function naughty.is_suspended()
return suspended return suspended

View File

@ -1020,6 +1020,57 @@ luaA_screen_count(lua_State *L)
return 1; return 1;
} }
/** Add a fake screen.
* @tparam integer x X-coordinate for screen.
* @tparam integer y Y-coordinate for screen.
* @tparam integer width width for screen.
* @tparam integer height height for screen.
* @return The new screen.
* @function fake_add
*/
static int
luaA_screen_fake_add(lua_State *L)
{
int x = luaL_checkinteger(L, 1);
int y = luaL_checkinteger(L, 2);
int width = luaL_checkinteger(L, 3);
int height = luaL_checkinteger(L, 4);
screen_t *s;
s = screen_add(L, &globalconf.screens);
s->geometry.x = x;
s->geometry.y = y;
s->geometry.width = width;
s->geometry.height = height;
s->valid = true;
luaA_object_push(L, s);
luaA_object_emit_signal(L, -1, "added", 0);
return 1;
}
/** Remove a screen.
* @function fake_remove.
*/
static int
luaA_screen_fake_remove(lua_State *L)
{
screen_t *s = luaA_checkudata(L, 1, &screen_class);
int idx = screen_get_index(s) - 1;
if (idx < 0)
/* WTF? */
return 0;
screen_array_take(&globalconf.screens, idx);
luaA_object_push(L, s);
screen_removed(L, -1);
lua_pop(L, 1);
luaA_object_unref(L, s);
s->valid = false;
return 0;
}
void void
screen_class_setup(lua_State *L) screen_class_setup(lua_State *L)
{ {
@ -1030,6 +1081,7 @@ screen_class_setup(lua_State *L)
{ "__index", luaA_screen_module_index }, { "__index", luaA_screen_module_index },
{ "__newindex", luaA_default_newindex }, { "__newindex", luaA_default_newindex },
{ "__call", luaA_screen_module_call }, { "__call", luaA_screen_module_call },
{ "fake_add", luaA_screen_fake_add },
{ NULL, NULL } { NULL, NULL }
}; };
@ -1037,6 +1089,7 @@ screen_class_setup(lua_State *L)
{ {
LUA_OBJECT_META(screen) LUA_OBJECT_META(screen)
LUA_CLASS_META LUA_CLASS_META
{ "fake_remove", luaA_screen_fake_remove },
{ NULL, NULL }, { NULL, NULL },
}; };

View File

@ -4,29 +4,26 @@ local spawn = require("awful.spawn")
-- It is used to test the `awful.rules` -- It is used to test the `awful.rules`
return function(class, title, use_sn) return function(class, title, use_sn)
class = class or 'test_app'
title = title or 'Awesome test client' title = title or 'Awesome test client'
local cmd = {"lua" , "-e", table.concat { local cmd = {"lua" , "-e", table.concat {
"local lgi = require 'lgi';", "local lgi = require 'lgi';",
"local Gtk = lgi.require('Gtk');", "local Gtk = lgi.require('Gtk');",
"Gtk.init();", "Gtk.init();",
"local class = '", "local class = '",class,"';",
class or 'test_app',"';",
"local window = Gtk.Window {", "local window = Gtk.Window {",
" default_width = 100,", " default_width = 100,",
" default_height = 100,", " default_height = 100,",
" on_destroy = Gtk.main_quit,",
" title = '",title, " title = '",title,
"'};", "'};",
"window:set_wmclass(class, class);", "window:set_wmclass(class, class);",
"local app = Gtk.Application {",
" application_id = 'org.awesomewm.tests.",class,
"'};",
"function app:on_activate()",
" window.application = self;",
"window:show_all();", "window:show_all();",
"end;", "Gtk:main{...}"
"app:run {''}"
}} }}
return spawn(cmd, use_sn) return spawn(cmd, use_sn)
end end
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -100,6 +100,8 @@ end
screen._add_screen {width=320, height=240} screen._add_screen {width=320, height=240}
screen.add_signal("property::workarea") screen.add_signal("property::workarea")
screen.add_signal("added")
screen.add_signal("removed")
return screen return screen

View File

@ -0,0 +1,64 @@
-- Tests for screen additions & removals
local runner = require("_runner")
local test_client = require("_client")
local naughty = require("naughty")
local real_screen = screen[1]
local fake_screen = screen.fake_add(50, 50, 500, 500)
local test_client1, test_client2
local steps = {
-- Step 1: Set up some clients to experiment with and assign them as needed
function(count)
if count == 1 then -- Setup.
test_client()
test_client()
end
local cls = client.get()
if #cls == 2 then
test_client1, test_client2 = cls[1], cls[2]
test_client1.screen = real_screen
test_client2.screen = fake_screen
-- Display a notification on the screen-to-be-removed
naughty.notify{ text = "test", screen = fake_screen }
return true
end
end,
-- Step 2: Say goodbye to the screen
function()
fake_screen:fake_remove()
-- TODO: This is a hack to make the test work, how to do this so that it
-- also works "in the wild"?
mypromptbox[fake_screen] = nil
mylayoutbox[fake_screen] = nil
mytaglist[fake_screen] = nil
mytasklist[fake_screen] = nil
mywibox[fake_screen] = nil
-- Wrap in a weak table to allow garbage collection
fake_screen = setmetatable({ fake_screen }, { __mode = "v" })
return true
end,
-- Step 3: Everything should now be on the main screen, the old screen
-- should be garbage collectable
function()
assert(test_client1.screen == real_screen, test_client1.screen)
assert(test_client2.screen == real_screen, test_client2.screen)
collectgarbage("collect")
if #fake_screen == 0 then
return true
end
end,
}
runner.run_steps(steps)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80