local gears_obj = require("gears.object") local grect = require("gears.geometry").rectangle local clients = {} local client, meta = awesome._shim_fake_class() rawset(client, "_autotags", true) -- Make sure `awful.client` properties like `floating` get handled by the -- miss handler rather than set directly on the object. The reason it's done -- than way is ancient and convoluted. local native_property_defaults = { -- Layers. ontop = false, below = false, above = false, sticky = false, urgent = false, modal = false, focusable = true, hidden = false, -- Other properties. name = "", transient_for = nil, skip_taskbar = false, type = "normal", class = "", instance = "", role = "", pid = 1, leader_window = nil, machine = "", icon_name = "", screen = nil, minimized = false, motif_wm_hints = {}, group_window = nil, icon = nil, icon_sizes = {}, size_hints_honor = true, size_hints = {}, startup_id = nil, valid = true, -- The shims can't really handle those properly. shape_bounding = nil, content = nil, shape_clip = nil, shape_input = nil, client_shape_bounding = nil, client_shape_clip = nil, -- Those have special code. maximized = nil, fullscreen = nil, maximized_horizontal = nil, maximized_vertical = nil, -- Those are not really native, but it simplify the shims a lot. x = 0, y = 0, width = 1, height = 1, } -- Keep an history of the geometry for validation and images local function push_geometry(c) table.insert(c._old_geo, c:geometry()) end -- Undo the last move, but keep it in history -- local function pop_geometry(c) -- CURRENTLY UNUSED -- end local function titlebar_meta(c) for _, position in ipairs {"top", "bottom", "left", "right" } do c["titlebar_"..position] = function(_, size) local prop = "titlebar_"..position.."_size" c._private[prop] = c._private[prop] or size return drawin{}, c._private[prop] or 0 end 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._private[prop] or false end properties["set_"..prop] = function(self, value) self._private[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._private.screen or screen[1] end function properties.set_screen(self, s) s = screen[s] self._private.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 function properties:get_first_tag() return self:tags()[1] end -- Create fake clients to move around function client.gen_fake(args) local ret = gears_obj() awesome._forward_class(ret, client) ret._private = {} ret.type = native_property_defaults.type ret.valid = native_property_defaults.valid ret.size_hints = native_property_defaults.size_hints ret._border_width = 1 ret._tags = args and args.tags or nil ret.icon_sizes = {{16,16}} ret.name = "Example Client" ret._private._struts = { top = 0, right = 0, left = 0, bottom = 0 } --TODO v5: remove this. This was a private API and thus doesn't need to be -- officially deprecated. ret.data = ret._private -- This is a hack because there's a `:is_transient_for(c2)` method -- and a `transient_for` property. It will cause a stack overflow -- since the auto-alias will kick in if the property is allowed to -- be `nil`. ret.transient_for = false -- Apply the native (capi) properties. for k,v in pairs(args or {}) do if native_property_defaults[k] then ret[k] = v end end -- 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 function ret:geometry(new) local new_full = new and { x = new.x or ret.x, y = new.y or ret.y, width = new.width or ret.width, height = new.height or ret.height, } or nil if new and not grect.are_equal(ret, new_full) then for k,v in pairs(new) do ret[k] = v ret:emit_signal("property::"..k, v) end ret:emit_signal("property::geometry", ret:geometry()) push_geometry(ret) end return { x = ret.x, y = ret.y, width = ret.width, height = ret.height, label = ret._label, } end function ret:isvisible() if ret.minimized or ret.hidden then return false end local vis = false for _, tag in ipairs(ret:tags()) do vis = vis or tag.selected end return vis end -- Used for screenshots function ret:set_label(text) ret._old_geo[#ret._old_geo]._label = text end -- Used for screenshots, hide the current client position function ret:_hide() ret._old_geo[#ret._old_geo]._hide = true end function ret:_hide_all() for _, geo in ipairs(ret._old_geo) do geo._hide = true end end function ret:get_xproperty() return nil end function ret:set_xproperty(prop, value) ret[prop] = value end function ret:get_icon(_) return require("beautiful").awesome_icon end function ret:raise() --TODO end function ret:lower() --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("untagged", ret) t:emit_signal("property::tags") end assert(not ret.valid) end function ret:swap(other) local idx1, idx2 = nil, nil for k, c in ipairs(clients) do if c == ret then idx1 = k elseif c == other then idx2 = k end end if not (idx1 and idx2) then return end clients[idx1], clients[idx2] = other, ret ret:emit_signal("swapped", other, true) other:emit_signal("swapped", ret, false) client.emit_signal("list") end titlebar_meta(ret) function ret:tags(new) --FIXME if new then ret._tags = new ret:emit_signal("property::tags") end if ret._tags then return ret._tags end if client._autotags then for _, t in ipairs(root._tags) do if t.screen == ret.screen then return {t} end end end return {} end function ret:struts(new) for k, v in pairs(new or {}) do ret._private._struts[k] = v end return ret._private._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 -- Good enough for the geometry and border ret.drawin = ret ret.drawable = ret -- Make sure the layer properties are not `nil` local defaults = setmetatable({}, {__index = native_property_defaults}) -- Declare the deprecated buttons and keys methods. function ret:_keys(new) if new then ret._private.keys = new end return ret._private.keys or {} end function ret:_buttons(new) if new then ret._private.buttons = new end return ret._private.buttons or {} end -- Add to the client list table.insert(clients, ret) client.focus = ret setmetatable(ret, { __index = function(self, key) if properties["get_"..key] then return properties["get_"..key](self) elseif defaults[key] ~= nil then return defaults[key] end return meta.__index(self, key) end, __newindex = function(self, key, value) if properties["set_"..key] then return properties["set_"..key](self, value) end if defaults[key] ~= nil then defaults[key] = value else meta.__newindex(self, key, value) end ret:emit_signal("property::"..key, value) --client.emit_signal("property::"..key, ret, value) end }) -- Apply non-native (`awful.client`) properties. for k,v in pairs(args or {}) do if (not native_property_defaults[k]) and (not rawget(ret, k)) then ret[k] = v end end client.emit_signal("request::manage", ret) --TODO v6 remove this. client.emit_signal("manage", ret) assert(not args.screen or (args.screen == ret.screen)) return ret end function client.get(s) if not s then return clients end local s2 = screen[s] local ret = {} for _,c in ipairs(clients) do if c.screen == s2 then table.insert(ret, c) end end return ret end return client -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80