diff --git a/tests/examples/awful/template.lua b/tests/examples/awful/template.lua index 75c4feec..aa79ff09 100644 --- a/tests/examples/awful/template.lua +++ b/tests/examples/awful/template.lua @@ -35,8 +35,8 @@ local function draw_mouse(x, y) end -- Print an outline for the screens -if not screen.no_outline then - for _, s in ipairs(screen) do +if not rawget(screen, "no_outline") then + for s in screen do cr:save() -- Draw the screen outline cr:set_source(color("#00000044")) diff --git a/tests/examples/shims/awesome.lua b/tests/examples/shims/awesome.lua index 3e97847a..24272af5 100644 --- a/tests/examples/shims/awesome.lua +++ b/tests/examples/shims/awesome.lua @@ -75,6 +75,10 @@ function awesome.pixbuf_to_surface(_, path) return awesome.load_image(path) end +function awesome.xrdb_get_value() + return nil +end + -- Always show deprecated messages awesome.version = "v9999" diff --git a/tests/examples/shims/client.lua b/tests/examples/shims/client.lua index bf76a426..c65e3d03 100644 --- a/tests/examples/shims/client.lua +++ b/tests/examples/shims/client.lua @@ -22,6 +22,45 @@ local function titlebar_meta(c) end end +local properties = {} + +-- Emit the request::geometry signal to make immobilized and awful.ewmh work. +for _, prop in ipairs { + "maximized", "maximized_horizontal", "maximized_vertical", "fullscreen" } do + properties["get_"..prop] = function(self) + return self.data[prop] or false + end + + properties["set_"..prop] = function(self, value) + self.data[prop] = value or false + + if value then + self:emit_signal("request::geometry", prop, nil) + end + + self:emit_signal("property::"..prop, value) + end +end + +function properties.get_screen(self) + return self.data.screen or screen[1] +end + +function properties.set_screen(self, s) + s = screen[s] + self.data.screen = s + + if self.x < s.geometry.x or self.x > s.geometry.x+s.geometry.width then + self:geometry { x = s.geometry.x } + end + + if self.y < s.geometry.y or self.y > s.geometry.y+s.geometry.height then + self:geometry { y = s.geometry.y } + end + + self:emit_signal("property::screen") +end + -- Create fake clients to move around function client.gen_fake(args) local ret = gears_obj() @@ -34,6 +73,7 @@ function client.gen_fake(args) ret.border_width = 1 ret.icon_sizes = {{16,16}} ret.name = "Example Client" + ret.data._struts = { top = 0, right = 0, left = 0, bottom = 0 } -- This is a hack because there's a `:is_transient_for(c2)` method -- and a `transient_for` property. It will cause a stack overflow @@ -49,6 +89,7 @@ function client.gen_fake(args) -- Tests should always set a geometry, but just in case for _, v in ipairs{"x","y","width","height"} do ret[v] = ret[v] or 1 + assert((not args[v]) or ret[v] == args[v]) end -- Emulate capi.client.geometry @@ -89,6 +130,10 @@ function client.gen_fake(args) return nil end + function ret:set_xproperty(prop, value) + ret[prop] = value + end + function ret:get_icon(_) return require("beautiful").awesome_icon end @@ -101,6 +146,32 @@ function client.gen_fake(args) --TODO end + function ret:apply_size_hints(w, h) + return w or ret.width, h or ret.height + end + + function ret:kill() + local old_tags = ret:tags() or {} + + for k, c in ipairs(clients) do + if c == ret then + ret:emit_signal("unmanaged", c) + ret.valid = false + table.remove(clients, k) + break + end + end + + ret._tags = {} + + for _, t in ipairs(old_tags) do + ret:emit_signal("untagged", t) + t:emit_signal("property::tags") + end + + assert(not ret.valid) + end + titlebar_meta(ret) function ret:tags(new) --FIXME @@ -121,14 +192,55 @@ function client.gen_fake(args) return {} end + function ret:struts(new) + for k, v in pairs(new or {}) do + ret.data._struts[k] = v + end + + return ret.data._struts + end + + function ret:struts(new) + for k, v in pairs(new or {}) do + ret.data._struts[k] = v + end + + return ret.data._struts + end + + -- Set a dummy one for now since set_screen will corrupt it. + ret._old_geo = {} + + -- Set the screen attributes. + local s = args.screen + + -- Pick the screen from the geometry. + if not s then + for s2 in screen do + local geo = s2.geometry + if geo.x >= ret.x and geo.y >= ret.y and ret.x < geo.x+geo.width + and ret.y < geo.y+geo.height then + s = s2 + end + end + end + + -- This will happen if the screen coords are not {0,0} and the client had + -- an unspecified position. + s = s or screen[1] + + properties.set_screen(ret, s) + + -- Set the geometry *again* because the screen possibly overwrote it. + for _, v in ipairs{"x","y","width","height"} do + ret[v] = args[v] or ret[v] + end + -- Record the geometry ret._old_geo = {} push_geometry(ret) ret._old_geo[1]._hide = args.hide_first - -- Set the attributes - ret.screen = args.screen or screen[1] - -- Good enough for the geometry and border ret.drawin = ret ret.drawable = ret @@ -138,10 +250,6 @@ function client.gen_fake(args) ret.below = false ret.above = false ret.sticky = false - ret.maximized = false - ret.fullscreen = false - ret.maximized_vertical = false - ret.maximized_horizontal = false -- Add to the client list table.insert(clients, ret) @@ -149,8 +257,21 @@ function client.gen_fake(args) client.focus = ret setmetatable(ret, { - __index = function(...) return meta.__index(...) end, - __newindex = function(...) return meta.__newindex(...) end + __index = function(self, key) + if properties["get_"..key] then + return properties["get_"..key](self) + end + + return meta.__index(self, key) + end, + __newindex = function(self, key, value) + + if properties["set_"..key] then + return properties["set_"..key](self, value) + end + + return meta.__newindex(self, key, value) + end }) client.emit_signal("manage", ret) diff --git a/tests/examples/shims/drawin.lua b/tests/examples/shims/drawin.lua index 1f1e8436..2147b623 100644 --- a/tests/examples/shims/drawin.lua +++ b/tests/examples/shims/drawin.lua @@ -35,11 +35,20 @@ local function new_drawin(_, args) ret.data.drawable.surface = cairo.ImageSurface(cairo.Format.ARGB32, 0, 0) ret.data.drawable.geometry = ret.geometry ret.data.drawable.refresh = function() end + ret.data._struts = { top = 0, right = 0, left = 0, bottom = 0 } - for _, k in pairs{ "buttons", "struts", "get_xproperty", "set_xproperty" } do + for _, k in pairs{ "buttons", "get_xproperty", "set_xproperty" } do ret[k] = function() end end + function ret:struts(new) + for k, v in pairs(new or {}) do + ret.data._struts[k] = v + end + + return ret.data._struts + end + local md = setmetatable(ret, { __index = function(...) return meta.__index(...) end, __newindex = function(...) return meta.__newindex(...) end diff --git a/tests/examples/shims/mouse.lua b/tests/examples/shims/mouse.lua index 20c35f0d..128e49e6 100644 --- a/tests/examples/shims/mouse.lua +++ b/tests/examples/shims/mouse.lua @@ -53,20 +53,42 @@ function mouse.push_history() mouse.history = {} end +local forced_screen = nil + return setmetatable(mouse, { __index = function(self, key) if key == "screen" then - return screen[1] + if forced_screen and screen._deleted[forced_screen] then + forced_screen = nil + end + + for s in screen do + if coords.x > s.geometry.x and coords.x < s.geometry.x +s.geometry.width + and coords.y > s.geometry.y and coords.y < s.geometry.y +s.geometry.height then + return s + end + end + + -- Using capi.mouse.screen is *not* supported when there is zero + -- screen. Nearly all the code uses `mouse.screen` as its ultimate + -- fallback. Having no screens is tolerated during early + -- initialization and that's it. + return screen.count() > 0 and screen[1] or assert( + false, "Calling `mouse.screen` without screens isn't supported" + ) end local h = rawget(mouse,"_i_handler") if h then return h(self, key) end end, - __newindex = function(...) + __newindex = function(_, k, v) local h = rawget(mouse,"_ni_handler") - if h then - h(...) + if k == "screen" then + -- This will assert if the screen is invalid + forced_screen = v and screen[v] or nil + elseif h then + h(_, k, v) end end, }) diff --git a/tests/examples/shims/root.lua b/tests/examples/shims/root.lua index c85abe31..e79e9652 100644 --- a/tests/examples/shims/root.lua +++ b/tests/examples/shims/root.lua @@ -8,12 +8,22 @@ function root:tags() return root._tags end -function root:size() --TODO use the screens - return 0, 0 +function root.size() + local geo = {x1 = math.huge, y1 = math.huge, x2 = 0, y2 = 0} + + for s in screen do + geo.x1 = math.min( geo.x1, s.geometry.x ) + geo.y1 = math.min( geo.y1, s.geometry.y ) + geo.x2 = math.max( geo.x2, s.geometry.x+s.geometry.width ) + geo.y2 = math.max( geo.y2, s.geometry.y+s.geometry.height ) + end + + return math.max(0, geo.x2-geo.x1), math.max(0, geo.y2 - geo.y1) end -function root:size_mm() - return 0, 0 +function root.size_mm() + local w, h = root.size() + return (w/96)*25.4, (h/96)*25.4 end function root.cursor() end diff --git a/tests/examples/shims/screen.lua b/tests/examples/shims/screen.lua index bf7f119c..bc6c25d1 100644 --- a/tests/examples/shims/screen.lua +++ b/tests/examples/shims/screen.lua @@ -1,7 +1,31 @@ local gears_obj = require("gears.object") +local gears_tab = require("gears.table") local screen, meta = awesome._shim_fake_class() -screen._count = 0 +screen._count, screen._deleted = 0, {} + +local function compute_workarea(s) + local struts = {top=0,bottom=0,left=0,right=0} + + for _, c in ipairs(drawin.get()) do + for k,v in pairs(struts) do + struts[k] = v + (c:struts()[k] or 0) + end + end + + for _, c in ipairs(client.get()) do + for k,v in pairs(struts) do + struts[k] = v + (c:struts()[k] or 0) + end + end + + return { + x = s.geometry.x + struts.left, + y = s.geometry.y + struts.top , + width = s.geometry.width - struts.left - struts.right , + height = s.geometry.height - struts.top - struts.bottom, + } +end local function create_screen(args) local s = gears_obj() @@ -19,7 +43,7 @@ local function create_screen(args) } function s._resize(args2) - local old = s.geometry + local old = gears_tab.clone(s.geometry) geo.x = args2.x or geo.x geo.y = args2.y or geo.y geo.width = args2.width or geo.width @@ -27,39 +51,71 @@ local function create_screen(args) s:emit_signal("property::geometry", old) end - s.outputs = { ["LVDS1"] = { - mm_width = 0, - mm_height = 0, + function s.fake_resize(self, x, y, width, height) + self._resize { + x=x,y=y,width=width,height=height + } + end + + function s:fake_remove() + local i = s.index + table.remove(screen, i) + screen._deleted[s] = true + s:emit_signal("removed") + screen[screen[i]] = nil + s.valid = false + end + + function s:swap(other_s) + local s1geo = gears_tab.clone(s.geometry) + local s2geo = gears_tab.clone(other_s.geometry) + + s:fake_resize( + s2geo.x, s2geo.y, s2geo.width, s2geo.height + ) + other_s:fake_resize( + s1geo.x, s1geo.y, s1geo.width, s1geo.height + ) + + s:emit_signal("swapped",other_s) + other_s:emit_signal("swapped",s) + end + + s.outputs = { LVDS1 ={ + name = "LVDS1", + mm_width = (geo.width /96)*25.4, + mm_height = (geo.height/96)*25.4, }} local wa = args.workarea_sides or 10 - -- This will happen if `clear()` is called - if mouse and not mouse.screen then - mouse.screen = s - end - return setmetatable(s,{ __index = function(_, key) - if key == "geometry" then - return { - x = geo.x or 0, - y = geo.y or 0, - width = geo.width , - height = geo.height, - } - elseif key == "workarea" then - return { - x = (geo.x or 0) + wa , - y = (geo.y or 0) + wa , - width = geo.width - 2*wa, - height = geo.height - 2*wa, - } - else - return meta.__index(_, key) - end - end, - __newindex = function(...) return meta.__newindex(...) end -}) + assert(s.valid) + + if key == "geometry" then + return { + x = geo.x or 0, + y = geo.y or 0, + width = geo.width , + height = geo.height, + } + elseif key == "workarea" then + if screen._track_workarea then + return compute_workarea(s) + else + return { + x = (geo.x or 0) + wa , + y = (geo.y or 0) + wa , + width = geo.width - 2*wa, + height = geo.height - 2*wa, + } + end + else + return meta.__index(_, key) + end + end, + __newindex = function(...) assert(s.valid); return meta.__newindex(...) end + }) end local screens = {} @@ -74,11 +130,13 @@ function screen._add_screen(args) screen[#screen+1] = s screen[s] = s screen._count = screen._count + 1 + + return s end function screen._get_extents() local xmax, ymax - for _, v in ipairs(screen) do + for v in screen do if not xmax or v.geometry.x+v.geometry.width > xmax.geometry.x+xmax.geometry.width then xmax = v end @@ -90,8 +148,47 @@ function screen._get_extents() return xmax.geometry.x+xmax.geometry.width, ymax.geometry.y+ymax.geometry.height end +-- The way the `screen` module is used to store both number and object is +-- problematic since it can cause invalid screens to be "resurrected". +local function catch_invalid(_, s) + -- The CAPI implementation allows `nil` + if s == nil then return end + + -- Try to get the screens by output name. + if type(s) == "string" then + for s2 in screen do + for out in pairs(s2.outputs or {}) do + if out == s then + return s2 + end + end + end + end + + assert(screen ~= s, "Some code tried (and failed) to shadow the global `screen`") + + -- Valid numbers wont get this far. + assert(type(s) == "table", "Expected a table, but got a "..type(s)) + + if type(s) == "number" then return end + + assert(s.geometry, "The object is not a screen") + assert(s.outputs, "The object is not a screen") + + assert((not screen._deleted[s]) or (not s.valid), "The shims are broken") + + assert(not screen._deleted[s], "The screen "..tostring(s).."has been deleted") + + -- Other errors. If someone place an object in the `screen` module, it wont + -- get this far. So the remaining cases are probably bugs. + assert(false) +end + function screen._clear() + assert(#screen == screen._count) for i=1, #screen do + screen._deleted[screen[i]] = true + screen[i].valid = false screen[screen[i]] = nil screen[i] = nil end @@ -126,11 +223,21 @@ local function iter_scr(_, _, s) local i = s.index - if i + 1 < #screen then + if i < #screen then return screen[i+1], i+1 end end +function screen._areas() + return {} +end + +function screen.fake_add(x,y,width,height) + return screen._add_screen { + x=x,y=y,width=width,height=height + } +end + screen._add_screen {width=320, height=240} screen._grid_vertical_margin = 10 @@ -138,12 +245,15 @@ screen._grid_horizontal_margin = 10 screen.primary = screen[1] +screen._track_workarea = false + function screen.count() return screen._count end return setmetatable(screen, { - __call = iter_scr + __call = iter_scr, + __index = catch_invalid }) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/tests/examples/shims/tag.lua b/tests/examples/shims/tag.lua index 60ef6b4a..5a4f3cf2 100644 --- a/tests/examples/shims/tag.lua +++ b/tests/examples/shims/tag.lua @@ -16,15 +16,19 @@ local function new_tag(_, args) awesome._forward_class(ret, tag) ret.data = {} - ret.name = args.name or "test" + ret.name = tostring(args.name) or "test" ret.activated = true ret.selected = not has_selected_tag(args.screen) function ret:clients(_) --TODO handle new local list = {} for _, c in ipairs(client.get()) do - if c.screen == (ret.screen or screen[1]) then - table.insert(list, c) + if #c:tags() > 0 then + for _, t in ipairs(c:tags()) do + if t == ret then + table.insert(list, c) + end + end end end