From b0f18bce527bb5f487f9afbefb4add64b59dc4e1 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Wed, 19 Jun 2019 18:02:49 -0400 Subject: [PATCH] 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. --- lib/awful/screen.lua | 333 ++++++++++++++++++++++++++++----------- lib/awful/screen/dpi.lua | 333 +++++++++++++++++++++++++++++++++++++++ lib/naughty/init.lua | 9 +- objects/screen.c | 38 ++--- 4 files changed, 599 insertions(+), 114 deletions(-) diff --git a/lib/awful/screen.lua b/lib/awful/screen.lua index 589ecde89..9ab558a14 100644 --- a/lib/awful/screen.lua +++ b/lib/awful/screen.lua @@ -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 diff --git a/lib/awful/screen/dpi.lua b/lib/awful/screen/dpi.lua index e69de29bb..cb5828443 100644 --- a/lib/awful/screen/dpi.lua +++ b/lib/awful/screen/dpi.lua @@ -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 diff --git a/lib/naughty/init.lua b/lib/naughty/init.lua index 9083064f3..7ca990164 100644 --- a/lib/naughty/init.lua +++ b/lib/naughty/init.lua @@ -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 diff --git a/objects/screen.c b/objects/screen.c index deb7d42c6..6a8de6500 100644 --- a/objects/screen.c +++ b/objects/screen.c @@ -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);