screen: Allow `outputs` to be changed.
This moves the handling of the `outputs` property away from C and into Lua. It will allow the use of `screen.fake_add` to have outputs.
This commit is contained in:
parent
ae99e8d7bb
commit
b0f18bce52
|
@ -13,6 +13,7 @@ local capi =
|
|||
screen = screen,
|
||||
client = client,
|
||||
awesome = awesome,
|
||||
root = root,
|
||||
}
|
||||
local gdebug = require("gears.debug")
|
||||
local gmath = require("gears.math")
|
||||
|
@ -260,6 +261,58 @@ function screen.object.set_padding(self, padding)
|
|||
end
|
||||
end
|
||||
|
||||
--- A list of outputs for this screen with their size in mm.
|
||||
--
|
||||
-- Please note that the table content may vary. In some case, it might also be
|
||||
-- empty.
|
||||
--
|
||||
-- An easy way to check if a screen is the laptop screen is usually:
|
||||
--
|
||||
-- if s.outputs["LVDS-1"] then
|
||||
-- -- do something
|
||||
-- end
|
||||
--
|
||||
-- **Signal:**
|
||||
--
|
||||
-- * *property::outputs*
|
||||
--
|
||||
-- **Immutable:** true
|
||||
-- @property outputs
|
||||
-- @param table
|
||||
-- @tfield table table.name A table with the screen name as key (like `eDP1` on a laptop)
|
||||
-- @tfield integer table.mm_width The screen physical width.
|
||||
-- @tfield integer table.mm_height The screen physical height.
|
||||
-- @tfield integer table.name The output name.
|
||||
-- @tfield integer table.viewport_id The identifier of the viewport this output
|
||||
-- corresponds to.
|
||||
|
||||
function screen.object.get_outputs(s)
|
||||
local ret = {}
|
||||
|
||||
local outputs = s._custom_outputs
|
||||
or (s.data.viewport and s.data.viewport.outputs or s._outputs)
|
||||
|
||||
-- The reason this exists is because output with name as keys is very
|
||||
-- convenient for quick name lookup by the users, but inconvenient in
|
||||
-- the lower layers since knowing the output count (using #) is better.
|
||||
for k, v in ipairs(outputs) do
|
||||
ret[v.name or k] = v
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function screen.object.set_outputs(self, outputs)
|
||||
self._custom_outputs = outputs
|
||||
self:emit_signal("property::outputs", screen.object.get_outputs(self))
|
||||
end
|
||||
|
||||
capi.screen.connect_signal("property::_outputs", function(s)
|
||||
if not s._custom_outputs then
|
||||
s:emit_signal("property::outputs", screen.object.get_outputs(s))
|
||||
end
|
||||
end)
|
||||
|
||||
--- Get the preferred screen in the context of a client.
|
||||
--
|
||||
-- This is exactly the same as `awful.screen.focused` except that it avoids
|
||||
|
@ -486,7 +539,7 @@ end
|
|||
function screen.object.get_tags(s, unordered)
|
||||
local tags = {}
|
||||
|
||||
for _, t in ipairs(root.tags()) do
|
||||
for _, t in ipairs(capi.root.tags()) do
|
||||
if get_screen(t.screen) == s then
|
||||
table.insert(tags, t)
|
||||
end
|
||||
|
@ -560,75 +613,32 @@ function screen.set_auto_dpi_enabled(enabled)
|
|||
data.autodpi = enabled
|
||||
end
|
||||
|
||||
|
||||
--- The number of pixels per inch of the screen.
|
||||
--
|
||||
-- The default DPI comes from the X11 server. In most case, it will be 96. If
|
||||
-- `autodpi` is set to `true` on the screen, it will use the least dense dpi
|
||||
-- from the screen outputs. Most of the time, screens only have a single output,
|
||||
-- however it will have two (or more) when "clone mode" is used (eg, when a
|
||||
-- screen is duplicated on a projector).
|
||||
--
|
||||
-- @property dpi
|
||||
-- @param number the DPI value.
|
||||
|
||||
local xft_dpi, fallback_dpi
|
||||
--- The lowest density DPI from all of the (physical) outputs.
|
||||
-- @property minimum_dpi
|
||||
-- @param number the DPI value.
|
||||
|
||||
local function get_fallback()
|
||||
local mm_per_inch = 25.4
|
||||
--- The highest density DPI from all of the (physical) outputs.
|
||||
-- @property maximum_dpi
|
||||
-- @param number the DPI value.
|
||||
|
||||
-- Following Keith Packard's whitepaper on Xft,
|
||||
-- https://keithp.com/~keithp/talks/xtc2001/paper/xft.html#sec-editing
|
||||
-- the proper fallback for Xft.dpi is the vertical DPI reported by
|
||||
-- the X server. This will generally be 96 on Xorg, unless the user
|
||||
-- has configured it differently
|
||||
if root and not fallback_dpi then
|
||||
local _, h = root.size()
|
||||
local _, hmm = root.size_mm()
|
||||
fallback_dpi = hmm ~= 0 and h * mm_per_inch / hmm
|
||||
end
|
||||
|
||||
return fallback_dpi or 96
|
||||
end
|
||||
|
||||
function screen.object.get_dpi(s)
|
||||
local mm_per_inch = 25.4
|
||||
|
||||
if s.data.dpi or s.data.dpi_cache then
|
||||
return s.data.dpi or s.data.dpi_cache
|
||||
end
|
||||
|
||||
-- Xft.dpi is explicit user configuration, so honor it
|
||||
if not xft_dpi and awesome and awesome.xrdb_get_value then
|
||||
xft_dpi = tonumber(awesome.xrdb_get_value("", "Xft.dpi")) or false
|
||||
end
|
||||
|
||||
if xft_dpi then
|
||||
s.data.dpi_cache = xft_dpi
|
||||
return s.data.dpi_cache
|
||||
end
|
||||
|
||||
if not data.autodpi then
|
||||
s.data.dpi_cache = get_fallback()
|
||||
return s.data.dpi_cache
|
||||
end
|
||||
|
||||
-- Try to compute DPI based on outputs (use the minimum)
|
||||
local dpi = nil
|
||||
local geo = s.geometry
|
||||
for _, o in pairs(s.outputs) do
|
||||
-- Ignore outputs with width/height 0
|
||||
if o.mm_width ~= 0 and o.mm_height ~= 0 then
|
||||
local dpix = geo.width * mm_per_inch / o.mm_width
|
||||
local dpiy = geo.height * mm_per_inch / o.mm_height
|
||||
dpi = math.min(dpix, dpiy, dpi or dpix)
|
||||
end
|
||||
end
|
||||
if dpi then
|
||||
s.data.dpi_cache = dpi
|
||||
return dpi
|
||||
end
|
||||
|
||||
s.data.dpi_cache = get_fallback()
|
||||
return s.data.dpi_cache
|
||||
end
|
||||
|
||||
function screen.object.set_dpi(s, dpi)
|
||||
s.data.dpi = dpi
|
||||
end
|
||||
--- The preferred DPI from all of the (physical) outputs.
|
||||
--
|
||||
-- This is computed by normalizing all output to fill the area, then picking
|
||||
-- the lowest of the resulting virtual DPIs.
|
||||
--
|
||||
-- @property preferred_dpi
|
||||
-- @param number the DPI value.
|
||||
|
||||
--- Emitted when a new screen is added.
|
||||
--
|
||||
|
@ -652,6 +662,177 @@ end
|
|||
-- @signal request::wallpaper
|
||||
-- @tparam screen s The screen object.
|
||||
|
||||
--- When a new (physical) screen area has been added.
|
||||
--
|
||||
-- Important: This only exists when Awesome is started with `--screen off`.
|
||||
-- Please also note that this doesn't mean it will appear when a screen is
|
||||
-- physically plugged. Depending on the configuration a tool like `arandr` or
|
||||
-- the `xrandr` command is needed.
|
||||
--
|
||||
-- The default handler will create a screen that fills the area.
|
||||
--
|
||||
-- To disconnect the default handler, use:
|
||||
--
|
||||
-- screen.disconnect_signal(
|
||||
-- "request::create", awful.screen.create_screen_handler
|
||||
-- )
|
||||
--
|
||||
-- @signal request::create
|
||||
-- @tparam table viewport
|
||||
-- @tparam table viewport.geometry A table with `x`, `y`, `width` and `height`
|
||||
-- keys.
|
||||
-- @tparam table viewport.outputs A table with the monitor name and possibly the
|
||||
-- `mm_width` and `mm_height` values if they are available.
|
||||
-- @tparam number viewport.id An identifier for this viewport (by pixel
|
||||
-- resolution). It
|
||||
-- will not change when outputs are modified, but will change when the
|
||||
-- resolution changes. Note that if it fully disappear, the next time an
|
||||
-- viewport with the same resolution appears, it will have a different `id`.
|
||||
-- @tparam number viewport.minimum_dpi The least dense DPI.
|
||||
-- @tparam number viewport.maximum_dpi The most dense DPI.
|
||||
-- @tparam number viewport.preferred_dpi The relative least dense DPI.
|
||||
-- @tparam table args
|
||||
-- @tparam string args.context Why was this signal sent.
|
||||
-- @see outputs
|
||||
-- @see awful.screen.create_screen_handler
|
||||
|
||||
--- When a physical monitor viewport has been removed.
|
||||
--
|
||||
-- Important: This only exists when Awesome is started with `--screen off`.
|
||||
--
|
||||
-- If you replace the default handler, it is up to you to find the screen(s)
|
||||
-- associated with this viewport.
|
||||
--
|
||||
-- To disconnect the default handler, use:
|
||||
--
|
||||
-- screen.disconnect_signal(
|
||||
-- "request::remove", awful.screen.remove_screen_handler
|
||||
-- )
|
||||
--
|
||||
-- @signal request::remove
|
||||
-- @tparam table viewport
|
||||
-- @tparam table viewport.geometry A table with `x`, `y`, `width` and `height`
|
||||
-- keys.
|
||||
-- @tparam table viewport.outputs A table with the monitor name and possibly the
|
||||
-- `mm_width` and `mm_height` values if they are available.
|
||||
-- @tparam number viewport.id An identifier for this viewport (by pixel
|
||||
-- resolution). It will not change when outputs are modified, but will change
|
||||
-- when the resolution changes. Note that if it fully disappear, the next time
|
||||
-- an viewport with the same resolution appears, it will have a different `id`.
|
||||
-- @tparam number viewport.minimum_dpi The least dense DPI.
|
||||
-- @tparam number viewport.maximum_dpi The most dense DPI.
|
||||
-- @tparam number viewport.preferred_dpi The relative least dense DPI.
|
||||
-- @tparam table args
|
||||
-- @tparam string args.context Why was this signal sent.
|
||||
-- @see awful.screen.remove_screen_handler
|
||||
|
||||
--- When a physical viewport resolution has changed or it has been replaced.
|
||||
--
|
||||
-- Important: This only exists when Awesome is started with `--screen off`.
|
||||
--
|
||||
-- Note that given the viewports are not the same, the `id` wont be the same.
|
||||
-- Also note that if multiple new viewports fit within a single "old" viewport,
|
||||
-- the resized screen will be the one with the largest total overlapping
|
||||
-- viewport (`intersection.width*intersection.height`), regardless of the
|
||||
-- outputs names.
|
||||
--
|
||||
-- To disconnect the default handler, use:
|
||||
--
|
||||
-- screen.disconnect_signal(
|
||||
-- "request::resize", awful.screen.resize_screen_handler
|
||||
-- )
|
||||
--
|
||||
-- @signal request::resize
|
||||
-- @tparam table old_viewport
|
||||
-- @tparam table old_viewport.geometry A table with `x`, `y`, `width` and
|
||||
-- `height` keys.
|
||||
-- @tparam table old_viewport.outputs A table with the monitor name and
|
||||
-- possibly the `mm_width` and `mm_height` values if they are available.
|
||||
-- @tparam number old_viewport.id An identifier for this viewport (by pixel
|
||||
-- resolution). It will not change when outputs are modified, but will change
|
||||
-- when the resolution changes. Note that if it fully disappear, the next
|
||||
-- time an viewport with the same resolution appears, it will have a different
|
||||
-- `id`.
|
||||
-- @tparam number old_viewport.minimum_dpi The least dense DPI.
|
||||
-- @tparam number old_viewport.maximum_dpi The most dense DPI.
|
||||
-- @tparam number old_viewport.preferred_dpi The relative least dense DPI.
|
||||
-- @tparam table new_viewport
|
||||
-- @tparam table new_viewport.geometry A table with `x`, `y`, `width` and
|
||||
-- `height` keys.
|
||||
-- @tparam table new_viewport.outputs A table with the monitor name and
|
||||
-- possibly the
|
||||
-- `mm_width` and `mm_height` values if they are available.
|
||||
-- @tparam number new_viewport.id An identifier for this viewport (by pixel
|
||||
-- resolution). It will not change when outputs are modified, but will change
|
||||
-- when the resolution changes. Note that if it fully disappear, the next time
|
||||
-- an viewport with the same resolution appears, it will have a different `id`.
|
||||
-- @tparam number new_viewport.minimum_dpi The least dense DPI.
|
||||
-- @tparam number new_viewport.maximum_dpi The most dense DPI.
|
||||
-- @tparam number new_viewport.preferred_dpi The relative least dense DPI.
|
||||
-- @tparam table args
|
||||
-- @tparam string args.context Why was this signal sent.
|
||||
-- @see awful.screen.resize_screen_handler
|
||||
|
||||
--- Default handler for `request::create`.
|
||||
--
|
||||
-- Important: This only exists when Awesome is started with `--screen off`.
|
||||
--
|
||||
-- A simplified implementation looks like:
|
||||
--
|
||||
-- function(viewport --[[, args]])
|
||||
-- local geo = viewport.geometry
|
||||
-- local s = screen.fake_add(geo.x, geo.y, geo.width, geo.height)
|
||||
-- end
|
||||
--
|
||||
-- If you implement this by hand, you must also implement handler for the
|
||||
-- `request::remove` and `request::resize`.
|
||||
--
|
||||
-- @signalhandler awful.screen.create_screen_handler
|
||||
-- @see request::create
|
||||
|
||||
--- Default handler for `request::remove`.
|
||||
--
|
||||
-- Important: This only exists when Awesome is started with `--screen off`.
|
||||
--
|
||||
-- A simplified version of the logic is:
|
||||
--
|
||||
-- function (viewport --[[, args]])
|
||||
-- local geo = viewport.geometry
|
||||
-- for s in screen do
|
||||
-- if gears.geometry.rectangle.are_equal(geo, s.geometry) then
|
||||
-- s:fake_remove()
|
||||
-- return
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- @signalhandler awful.screen.remove_screen_handler
|
||||
-- @see request::remove
|
||||
|
||||
--- Default handler for `request::resize`.
|
||||
--
|
||||
-- Important: This only exists when Awesome is started with `--screen off`.
|
||||
--
|
||||
-- A simplified version of the logic is:
|
||||
--
|
||||
-- function (old_viewport, new_viewport --[[, args]])
|
||||
-- local old_geo, new_geo = old_viewport.geometry, new_viewport.geometry
|
||||
-- for s in screen do
|
||||
-- local sgeo = new_viewport.geometry
|
||||
-- if gears.geometry.rectangle.are_equal(old_geo, s.geometry) then
|
||||
-- s:fake_resize(
|
||||
-- sgeo.x, sgeo.y, sgeo.width, sgeo.height
|
||||
-- )
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- @signalhandler awful.screen.resize_screen_handler
|
||||
-- @see request::resize
|
||||
|
||||
-- Add the DPI properties.
|
||||
require("awful.screen.dpi")(screen, data)
|
||||
|
||||
-- Set the wallpaper(s) and create the bar(s) for new screens
|
||||
capi.screen.connect_signal("added", function(s)
|
||||
s:emit_signal("request::desktop_decoration")
|
||||
|
@ -679,30 +860,6 @@ capi.screen.connect_signal("request::wallpaper::connected", function(new_handler
|
|||
end
|
||||
end)
|
||||
|
||||
-- Create some screens when none exist. This can happen when AwesomeWM is
|
||||
-- started with `--screen off` and no handler is used.
|
||||
capi.screen.connect_signal("scanned", function()
|
||||
if capi.screen.count() == 0 then
|
||||
-- Private API to scan for screens now.
|
||||
if #capi.screen._viewports() == 0 then
|
||||
capi.screen._scan_quiet()
|
||||
end
|
||||
|
||||
local viewports = capi.screen._viewports()
|
||||
|
||||
if #viewports > 0 then
|
||||
for _, area in ipairs(viewports) do
|
||||
local geo = area.geometry
|
||||
capi.screen.fake_add(geo.x, geo.y, geo.width, geo.height)
|
||||
end
|
||||
else
|
||||
capi.screen.fake_add(0, 0, 640, 480)
|
||||
end
|
||||
|
||||
assert(capi.screen.count() > 0, "Creating screens failed")
|
||||
end
|
||||
end)
|
||||
|
||||
--- When the tag history changed.
|
||||
-- @signal tag::history::update
|
||||
|
||||
|
|
|
@ -0,0 +1,333 @@
|
|||
---------------------------------------------------------------------------
|
||||
-- DPI detection code. --
|
||||
---------------------------------------------------------------------------
|
||||
|
||||
local capi = {screen = screen}
|
||||
local gtable = require("gears.table")
|
||||
local grect = require("gears.geometry").rectangle
|
||||
local gdebug = require("gears.debug")
|
||||
|
||||
local module = {}
|
||||
|
||||
local ascreen, data = nil, nil
|
||||
|
||||
-- Metric to Imperial conversion constant.
|
||||
local mm_per_inch = 25.4
|
||||
|
||||
local xft_dpi, fallback_dpi
|
||||
|
||||
local function get_fallback_dpi()
|
||||
-- Following Keith Packard's whitepaper on Xft,
|
||||
-- https://keithp.com/~keithp/talks/xtc2001/paper/xft.html#sec-editing
|
||||
-- the proper fallback for Xft.dpi is the vertical DPI reported by
|
||||
-- the X server. This will generally be 96 on Xorg, unless the user
|
||||
-- has configured it differently
|
||||
if not fallback_dpi then
|
||||
local _, h = root.size()
|
||||
local _, hmm = root.size_mm()
|
||||
fallback_dpi = hmm ~= 0 and h * mm_per_inch / hmm
|
||||
end
|
||||
|
||||
return fallback_dpi or 96
|
||||
end
|
||||
|
||||
local function dpi_for_output(viewport, output)
|
||||
local dpi = nil
|
||||
local geo = viewport.geometry
|
||||
|
||||
-- Ignore outputs with width/height 0
|
||||
if output.mm_width ~= 0 and output.mm_height ~= 0 then
|
||||
local dpix = geo.width * mm_per_inch / output.mm_width
|
||||
local dpiy = geo.height * mm_per_inch / output.mm_height
|
||||
dpi = math.min(dpix, dpiy, dpi or dpix)
|
||||
elseif ascreen._get_xft_dpi() then
|
||||
dpi = ascreen._get_xft_dpi()
|
||||
end
|
||||
|
||||
return dpi or get_fallback_dpi()
|
||||
end
|
||||
|
||||
local function dpis_for_outputs(viewport)
|
||||
local max, min = 0, math.huge
|
||||
|
||||
for _, o in pairs(viewport.outputs) do
|
||||
local dpi = dpi_for_output(viewport, o)
|
||||
o.dpi = dpi
|
||||
max = math.max(max, dpi)
|
||||
min = math.min(min, dpi)
|
||||
end
|
||||
|
||||
-- When there is no output.
|
||||
if min == math.huge then
|
||||
min = get_fallback_dpi()
|
||||
max = min
|
||||
end
|
||||
|
||||
--TODO Some output may have a lower resolution than the viewport, so their
|
||||
-- DPI is currently wrong. Once fixed, the preferred DPI can become
|
||||
-- different from the minimum one.
|
||||
local pref = min
|
||||
|
||||
viewport.minimum_dpi, viewport.maximum_dpi, viewport.preferred_dpi = min, max, pref
|
||||
|
||||
return max, min, pref
|
||||
end
|
||||
|
||||
local function update_outputs(old_viewport, new_viewport)
|
||||
gtable.diff_merge(
|
||||
old_viewport.outputs,
|
||||
new_viewport.outputs,
|
||||
function(o)
|
||||
return o.name or (
|
||||
(o.mm_height or -7)*9999 * (o.mm_width or 5)*123
|
||||
)
|
||||
end,
|
||||
gtable.crush
|
||||
)
|
||||
end
|
||||
|
||||
-- Fetch the current viewports and compare them to the caches ones.
|
||||
--
|
||||
-- The idea is to keep whatever metadata kept within the existing ones and know
|
||||
-- what is added and removed.
|
||||
local function update_viewports(force)
|
||||
if #ascreen._viewports > 0 and not force then return ascreen._viewports end
|
||||
|
||||
local new = ascreen._get_viewports()
|
||||
|
||||
local _, add, rem = gtable.diff_merge(
|
||||
ascreen._viewports,
|
||||
new,
|
||||
function(a) return a.id end,
|
||||
update_outputs
|
||||
)
|
||||
|
||||
for _, viewport in ipairs(ascreen._viewports) do
|
||||
dpis_for_outputs(viewport)
|
||||
end
|
||||
|
||||
assert(#ascreen._viewports > 0 or #new == 0)
|
||||
|
||||
return ascreen._viewports, add, rem
|
||||
end
|
||||
|
||||
-- Compute more useful viewport metadata frrom_sparse(add)om the list of output.
|
||||
-- @treturn table An viewport with more information.
|
||||
local function update_screen_viewport(s)
|
||||
local viewport = s.data.viewport
|
||||
|
||||
if #ascreen._viewports == 0 then
|
||||
ascreen._viewports = update_viewports(false)
|
||||
end
|
||||
|
||||
-- The maximum is equal to the screen viewport, so no need for many loops.
|
||||
if not viewport then
|
||||
local big_a, i_size = nil, 0
|
||||
|
||||
for _, a in ipairs(ascreen._viewports) do
|
||||
local int = grect.get_intersection(a.geometry, s.geometry)
|
||||
|
||||
if int.width*int.height > i_size then
|
||||
big_a, i_size = a, int.width*int.height
|
||||
end
|
||||
|
||||
if i_size == s.geometry.width*s.geometry.height then break end
|
||||
end
|
||||
|
||||
if big_a then
|
||||
viewport, s.data.viewport = big_a, big_a
|
||||
end
|
||||
end
|
||||
|
||||
if not viewport then
|
||||
gdebug.print_warning("Screen "..tostring(s)..
|
||||
" doesn't overlap a known physical monitor")
|
||||
end
|
||||
end
|
||||
|
||||
function module.create_screen_handler(viewport)
|
||||
local geo = viewport.geometry
|
||||
|
||||
local s = capi.screen.fake_add(
|
||||
geo.x,
|
||||
geo.y,
|
||||
geo.width,
|
||||
geo.height
|
||||
)
|
||||
|
||||
update_screen_viewport(s)
|
||||
end
|
||||
|
||||
function module.remove_screen_handler(viewport)
|
||||
for s in capi.screen do
|
||||
if s.data.viewport and s.data.viewport.id == viewport.id then
|
||||
s:fake_remove()
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function module.resize_screen_handler(old_viewport, new_viewport)
|
||||
for s in capi.screen do
|
||||
if s.data.viewport and s.data.viewport.id == old_viewport.id then
|
||||
local ngeo = new_viewport.geometry
|
||||
s:fake_resize(
|
||||
ngeo.x, ngeo.y, ngeo.width, ngeo.height
|
||||
)
|
||||
s.data.viewport = new_viewport
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Xft.dpi is explicit user configuration, so honor it.
|
||||
-- (It isn't a local function to allow the tests to replace it)
|
||||
function module._get_xft_dpi()
|
||||
if not xft_dpi then
|
||||
xft_dpi = tonumber(awesome.xrdb_get_value("", "Xft.dpi")) or false
|
||||
end
|
||||
|
||||
return xft_dpi
|
||||
end
|
||||
|
||||
-- Provide a way for the tests to replace `capi.screen._viewports`.
|
||||
function module._get_viewports()
|
||||
assert(type(capi.screen._viewports()) == "table")
|
||||
return capi.screen._viewports()
|
||||
end
|
||||
|
||||
local function get_dpi(s)
|
||||
if s.data.dpi or s.data.dpi_cache then
|
||||
return s.data.dpi or s.data.dpi_cache
|
||||
end
|
||||
|
||||
if not s.data.viewport then
|
||||
update_screen_viewport(s)
|
||||
end
|
||||
|
||||
-- Pick a DPI (from best to worst).
|
||||
local dpi = ascreen._get_xft_dpi()
|
||||
or (s.data.viewport and s.data.viewport.preferred_dpi or nil)
|
||||
or get_fallback_dpi()
|
||||
|
||||
-- Pick the screen DPI depending on the `autodpi` settings.
|
||||
-- Historically, AwesomeWM size unit was the pixel. This assumption is
|
||||
-- present in a lot, if not most, user config and is why this cannot be
|
||||
-- enable by default for existing users.
|
||||
s.data.dpi_cache = data.autodpi and dpi
|
||||
or ascreen._get_xft_dpi()
|
||||
or get_fallback_dpi()
|
||||
|
||||
return s.data.dpi_cache
|
||||
end
|
||||
|
||||
local function set_dpi(s, dpi)
|
||||
s.data.dpi = dpi
|
||||
end
|
||||
|
||||
screen.connect_signal("request::create", module.create_screen_handler)
|
||||
screen.connect_signal("request::remove", module.remove_screen_handler)
|
||||
screen.connect_signal("request::resize", module.resize_screen_handler)
|
||||
|
||||
-- Create some screens when none exist. This can happen when AwesomeWM is
|
||||
-- started with `--screen manual` and no handler is used.
|
||||
capi.screen.connect_signal("scanned", function()
|
||||
if capi.screen.count() == 0 then
|
||||
-- Private API to scan for screens now.
|
||||
if #ascreen._get_viewports() == 0 then
|
||||
capi.screen._scan_quiet()
|
||||
end
|
||||
|
||||
local cur_viewports = ascreen._get_viewports()
|
||||
|
||||
if #cur_viewports > 0 then
|
||||
for _, viewport in ipairs(cur_viewports) do
|
||||
module.create_screen_handler(viewport)
|
||||
end
|
||||
else
|
||||
capi.screen.fake_add(0, 0, 640, 480)
|
||||
end
|
||||
|
||||
assert(capi.screen.count() > 0, "Creating screens failed")
|
||||
end
|
||||
end)
|
||||
|
||||
-- This is the (undocumented) signal sent by capi.
|
||||
capi.screen.connect_signal("property::_viewports", function(a)
|
||||
if capi.screen.automatic_factory then return end
|
||||
|
||||
assert(#a > 0)
|
||||
|
||||
local _, added, removed = update_viewports(true)
|
||||
|
||||
local resized = {}
|
||||
|
||||
-- Try to detect viewports being replaced or resized.
|
||||
for k2, viewport in ipairs(removed) do
|
||||
local candidate, best_size, best_idx = {}, 0, nil
|
||||
|
||||
for k, viewport2 in ipairs(added) do
|
||||
local int = grect.get_intersection(viewport.geometry, viewport2.geometry)
|
||||
|
||||
if (int.width*int.height) > best_size then
|
||||
best_size, best_idx, candidate = (int.width*int.height), k, viewport2
|
||||
end
|
||||
end
|
||||
|
||||
if candidate and best_size > 0 then
|
||||
table.insert(resized, {removed[k2], added[best_idx]})
|
||||
removed[k2] = nil
|
||||
table.remove(added , best_idx)
|
||||
end
|
||||
end
|
||||
|
||||
gtable.from_sparse(removed)
|
||||
|
||||
-- Drop the cache.
|
||||
for s in capi.screen do
|
||||
s.data.dpi_cache = nil
|
||||
end
|
||||
|
||||
capi.screen.emit_signal("property::viewports", ascreen._get_viewports())
|
||||
|
||||
-- First, ask for screens for these new viewport.
|
||||
for _, viewport in ipairs(added) do
|
||||
capi.screen.emit_signal("request::create", viewport, {context="viewports_changed"})
|
||||
end
|
||||
|
||||
-- Before removing anything, make sure to resize existing screen as it may
|
||||
-- affect where clients will go when the screens are removed.
|
||||
for _, p in ipairs(resized) do
|
||||
capi.screen.emit_signal("request::resize", p[1], p[2], {context="viewports_changed"})
|
||||
end
|
||||
|
||||
-- Remove the now out-of-view screens.
|
||||
for _, viewport in ipairs(removed) do
|
||||
capi.screen.emit_signal("request::remove", viewport, {context="viewports_changed"})
|
||||
end
|
||||
|
||||
end)
|
||||
|
||||
-- Add the DPI related properties
|
||||
return function(screen, d)
|
||||
ascreen, data = screen, d
|
||||
|
||||
-- "Lua" copy of the CAPI viewports. It has more metadata.
|
||||
ascreen._viewports = {}
|
||||
gtable.crush(ascreen, module, true)
|
||||
|
||||
ascreen.object.set_dpi = set_dpi
|
||||
ascreen.object.get_dpi = get_dpi
|
||||
|
||||
for _, prop in ipairs {"minimum", "maximum", "preferred" } do
|
||||
screen.object["get_"..prop.."_dpi"] = function(s)
|
||||
if not s.data.viewport then
|
||||
update_screen_viewport(s)
|
||||
end
|
||||
|
||||
local a = s.data.viewport or {}
|
||||
|
||||
return a[prop.."_dpi"] or nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
local naughty = require("naughty.core")
|
||||
local gdebug = require("gears.debug")
|
||||
local capi = {awesome = awesome}
|
||||
local capi = {awesome = awesome, screen = screen}
|
||||
if dbus then
|
||||
naughty.dbus = require("naughty.dbus")
|
||||
end
|
||||
|
@ -31,7 +31,7 @@ naughty.notification = require("naughty.notification")
|
|||
-- screen.connect_signal("scanned", function() foobar() end)
|
||||
--
|
||||
local function screen_fallback()
|
||||
if screen.count() == 0 then
|
||||
if capi.screen.count() == 0 then
|
||||
gdebug.print_warning("An error occurred before a scrren was added")
|
||||
|
||||
-- Private API to scan for screens now.
|
||||
|
@ -44,10 +44,11 @@ local function screen_fallback()
|
|||
if #viewports > 0 then
|
||||
for _, viewport in ipairs(viewports) do
|
||||
local geo = viewport.geometry
|
||||
screen.fake_add(geo.x, geo.y, geo.width, geo.height)
|
||||
local s = capi.screen.fake_add(geo.x, geo.y, geo.width, geo.height)
|
||||
s.outputs = viewport.outputs
|
||||
end
|
||||
else
|
||||
screen.fake_add(0, 0, 640, 480)
|
||||
capi.screen.fake_add(0, 0, 640, 480)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -130,8 +130,16 @@
|
|||
* Each viewport in the list corresponds to a **physical** screen rectangle, which
|
||||
* is **not** the `viewports` property of the `screen` objects.
|
||||
*
|
||||
* Each entry in the `viewports` entry has the following keys:
|
||||
*
|
||||
* * `geometry` *(table)*: A table with an `x`, `y`, `width` and `height` keys.
|
||||
* * `outputs` *(table)*: All outputs sharing this viewport.
|
||||
* * `maximum_dpi` *(number)*: The DPI of the most dense output.
|
||||
* * `minimum_dpi` *(number)*: The DPI of the least dense output.
|
||||
* * `preferred_dpi` *(number)*: The optimal DPI.
|
||||
*
|
||||
* @signal property::viewports
|
||||
* @tparam table viewports
|
||||
* @tparam table viewports A table containing all physical viewports.
|
||||
* @see automatic_factory
|
||||
*/
|
||||
|
||||
|
@ -202,24 +210,6 @@
|
|||
* @see screen
|
||||
*/
|
||||
|
||||
/**
|
||||
* If RANDR information is available, a list of outputs
|
||||
* for this screen and their size in mm.
|
||||
*
|
||||
* Please note that the table content may vary.
|
||||
*
|
||||
* **Signal:**
|
||||
*
|
||||
* * *property::outputs*
|
||||
*
|
||||
* **Immutable:** true
|
||||
* @property outputs
|
||||
* @param table
|
||||
* @tfield table table.name A table with the screen name as key (like `eDP1` on a laptop)
|
||||
* @tfield integer table.name.mm_width The screen physical width
|
||||
* @tfield integer table.name.mm_height The screen physical height
|
||||
*/
|
||||
|
||||
/**
|
||||
* The screen workarea.
|
||||
*
|
||||
|
@ -413,6 +403,10 @@ luaA_viewport_get_outputs(lua_State *L, viewport_t *a)
|
|||
lua_pushstring(L, output->name);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "viewport_id");
|
||||
lua_pushinteger(L, a->id);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* Add to the outputs */
|
||||
lua_rawseti(L, -2, count++);
|
||||
}
|
||||
|
@ -482,7 +476,7 @@ viewports_notify(lua_State *L)
|
|||
|
||||
luaA_viewports(L);
|
||||
|
||||
luaA_class_emit_signal(L, &screen_class, "property::viewports", 1);
|
||||
luaA_class_emit_signal(L, &screen_class, "property::_viewports", 1);
|
||||
}
|
||||
|
||||
static viewport_t *
|
||||
|
@ -1084,7 +1078,7 @@ screen_modified(screen_t *existing_screen, screen_t *other_screen)
|
|||
|
||||
if(outputs_changed) {
|
||||
luaA_object_push(L, existing_screen);
|
||||
luaA_object_emit_signal(L, -1, "property::outputs", 0);
|
||||
luaA_object_emit_signal(L, -1, "property::_outputs", 0);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
|
@ -1795,7 +1789,7 @@ screen_class_setup(lua_State *L)
|
|||
NULL,
|
||||
(lua_class_propfunc_t) luaA_screen_get_index,
|
||||
NULL);
|
||||
luaA_class_add_property(&screen_class, "outputs",
|
||||
luaA_class_add_property(&screen_class, "_outputs",
|
||||
NULL,
|
||||
(lua_class_propfunc_t) luaA_screen_get_outputs,
|
||||
NULL);
|
||||
|
|
Loading…
Reference in New Issue