Merge pull request #2886 from Elv13/screen_init_merge_part1
(ignore this) Part 1 of the Lua managed screen PR
This commit is contained in:
commit
14c78ef19c
|
@ -50,14 +50,14 @@ addons:
|
|||
|
||||
jobs:
|
||||
include:
|
||||
- env: LUA=5.2 LUANAME=lua5.2 DO_COVERAGE=coveralls
|
||||
- env: LUA=5.2 LUANAME=lua5.2 DO_COVERAGE=coveralls MANUAL_SCREENS=1
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- *BASE_PACKAGES
|
||||
- liblua5.2-dev
|
||||
- lua5.2
|
||||
- env: LUA=5.3 LUANAME=lua5.3 DO_COVERAGE=codecov
|
||||
- env: LUA=5.3 LUANAME=lua5.3 DO_COVERAGE=codecov MANUAL_SCREENS=1
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
|
@ -229,7 +229,8 @@ install:
|
|||
return 0
|
||||
}
|
||||
script:
|
||||
- export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -DLUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION -DSTRICT_TESTS=true -D DO_COVERAGE=$DO_COVERAGE -D CMAKE_C_FLAGS=-Werror"
|
||||
- if [ "$MANUAL_SCREENS" != "1" ]; then export MANUAL_SCREENS=0; fi
|
||||
- export CMAKE_ARGS="-DLUA_LIBRARY=${LUALIBRARY} -D LUA_INCLUDE_DIR=${LUAINCLUDE} -D OVERRIDE_VERSION=$AWESOME_VERSION -D STRICT_TESTS=true -D DO_COVERAGE=$DO_COVERAGE -D TEST_MANUAL_SCREENS=$MANUAL_SCREENS -D CMAKE_C_FLAGS=-Werror"
|
||||
- |
|
||||
if [ "$EMPTY_THEME_WHILE_LOADING" = 1 ]; then
|
||||
# Break beautiful so that trying to access the theme before beautiful.init() causes an error
|
||||
|
|
|
@ -404,6 +404,12 @@ target_link_libraries(test-gravity
|
|||
if(DO_COVERAGE)
|
||||
set(TESTS_RUN_ENV DO_COVERAGE=1)
|
||||
endif()
|
||||
|
||||
# Start AwesomeWM tests with `--screen off`
|
||||
if ("${TEST_MANUAL_SCREENS}" MATCHES "1")
|
||||
set(TEST_RUN_ARGS "--W --m")
|
||||
endif()
|
||||
|
||||
add_custom_target(check-integration
|
||||
${CMAKE_COMMAND} -E env CMAKE_BINARY_DIR='${CMAKE_BINARY_DIR}' ${TESTS_RUN_ENV} ./tests/run.sh \$\${TEST_RUN_ARGS:--W}
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
|
|
54
awesome.c
54
awesome.c
|
@ -131,6 +131,8 @@ awesome_atexit(bool restart)
|
|||
/* Close Lua */
|
||||
lua_close(L);
|
||||
|
||||
screen_cleanup();
|
||||
|
||||
/* X11 is a great protocol. There is a save-set so that reparenting WMs
|
||||
* don't kill clients when they shut down. However, when a focused windows
|
||||
* is saved, the focus will move to its parent with revert-to none.
|
||||
|
@ -559,13 +561,14 @@ exit_help(int exit_code)
|
|||
FILE *outfile = (exit_code == EXIT_SUCCESS) ? stdout : stderr;
|
||||
fprintf(outfile,
|
||||
"Usage: awesome [OPTION]\n\
|
||||
-h, --help show help\n\
|
||||
-v, --version show version\n\
|
||||
-c, --config FILE configuration file to use\n\
|
||||
--search DIR add a directory to the library search path\n\
|
||||
-k, --check check configuration file syntax\n\
|
||||
-a, --no-argb disable client transparency support\n\
|
||||
-r, --replace replace an existing window manager\n");
|
||||
-h, --help show help\n\
|
||||
-v, --version show version\n\
|
||||
-c, --config FILE configuration file to use\n\
|
||||
--search DIR add a directory to the library search path\n\
|
||||
-k, --check check configuration file syntax\n\
|
||||
-a, --no-argb disable client transparency support\n\
|
||||
-m, --screen on|off enable or disable automatic screen creation (default: on)\n\
|
||||
-r, --replace replace an existing window manager\n");
|
||||
exit(exit_code);
|
||||
}
|
||||
|
||||
|
@ -594,6 +597,7 @@ main(int argc, char **argv)
|
|||
{ "search", 1, NULL, 's' },
|
||||
{ "no-argb", 0, NULL, 'a' },
|
||||
{ "replace", 0, NULL, 'r' },
|
||||
{ "screen" , 1, NULL, 'm' },
|
||||
{ "reap", 1, NULL, '\1' },
|
||||
{ NULL, 0, NULL, 0 }
|
||||
};
|
||||
|
@ -634,6 +638,14 @@ main(int argc, char **argv)
|
|||
if (confpath != NULL)
|
||||
fatal("--config may only be specified once");
|
||||
confpath = a_strdup(optarg);
|
||||
break;
|
||||
case 'm':
|
||||
/* Validation */
|
||||
if ((!optarg) || !(A_STREQ(optarg, "off") || A_STREQ(optarg, "on")))
|
||||
fatal("The possible values of -m/--screen are \"on\" or \"off\"");
|
||||
|
||||
globalconf.no_auto_screen = A_STREQ(optarg, "off");
|
||||
|
||||
break;
|
||||
case 's':
|
||||
string_array_append(&searchpath, a_strdup(optarg));
|
||||
|
@ -901,20 +913,44 @@ main(int argc, char **argv)
|
|||
|
||||
ewmh_init_lua();
|
||||
|
||||
/* Parse and run configuration file before adding the screens */
|
||||
if (globalconf.no_auto_screen)
|
||||
{
|
||||
/* Disable automatic screen creation, awful.screen has a fallback */
|
||||
globalconf.ignore_screens = true;
|
||||
|
||||
if(!luaA_parserc(&xdg, confpath))
|
||||
fatal("couldn't find any rc file");
|
||||
}
|
||||
|
||||
/* init screens information */
|
||||
screen_scan();
|
||||
|
||||
/* Parse and run configuration file */
|
||||
if (!luaA_parserc(&xdg, confpath))
|
||||
/* Parse and run configuration file after adding the screens */
|
||||
if (((!globalconf.no_auto_screen) && !luaA_parserc(&xdg, confpath)))
|
||||
fatal("couldn't find any rc file");
|
||||
|
||||
p_delete(&confpath);
|
||||
|
||||
xdgWipeHandle(&xdg);
|
||||
|
||||
/* Both screen scanning mode have this signal, it cannot be in screen_scan
|
||||
since the automatic screen generation don't have executed rc.lua yet. */
|
||||
screen_emit_scanned();
|
||||
|
||||
/* Exit if the user doesn't read the instructions properly */
|
||||
if (globalconf.no_auto_screen && !globalconf.screens.len)
|
||||
fatal("When -m/--screen is set to \"off\", you **must** create a "
|
||||
"screen object before or inside the screen \"scanned\" "
|
||||
" signal. Using AwesomeWM with no screen is **not supported**.");
|
||||
|
||||
client_emit_scanning();
|
||||
|
||||
/* scan existing windows */
|
||||
scan(tree_c);
|
||||
|
||||
client_emit_scanned();
|
||||
|
||||
luaA_emit_startup();
|
||||
|
||||
/* Setup the main context */
|
||||
|
|
|
@ -137,6 +137,7 @@ file = {
|
|||
'../lib/awful/dbus.lua',
|
||||
'../lib/awful/init.lua',
|
||||
'../lib/awful/remote.lua',
|
||||
'../lib/awful/screen/dpi.lua',
|
||||
'../lib/awful/startup_notification.lua',
|
||||
'../lib/awful/mouse/drag_to_tag.lua',
|
||||
'../lib/gears/init.lua',
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
}
|
||||
|
||||
typedef struct drawable_t drawable_t;
|
||||
typedef struct a_screen_area screen_area_t;
|
||||
typedef struct drawin_t drawin_t;
|
||||
typedef struct a_screen screen_t;
|
||||
typedef struct button_t button_t;
|
||||
|
@ -111,6 +112,10 @@ typedef struct
|
|||
bool have_randr_15;
|
||||
/** Do we have a RandR screen update pending? */
|
||||
bool screen_refresh_pending;
|
||||
/** Should screens be created before rc.lua is loaded? */
|
||||
bool no_auto_screen;
|
||||
/** Should the screen be created automatically? */
|
||||
bool ignore_screens;
|
||||
/** Check for XTest extension */
|
||||
bool have_xtest;
|
||||
/** Check for SHAPE extension */
|
||||
|
|
|
@ -21,7 +21,7 @@ end
|
|||
--
|
||||
-- @param obj An object that should have a .screen property.
|
||||
local function check_focus(obj)
|
||||
if not obj.screen.valid then return end
|
||||
if (not obj.screen) or not obj.screen.valid then return end
|
||||
-- When no visible client has the focus...
|
||||
if not client.focus or not client.focus:isvisible() then
|
||||
local c = aclient.focus.history.get(screen[obj.screen], 0, filter_sticky)
|
||||
|
|
|
@ -99,6 +99,9 @@ local delayed_arrange = {}
|
|||
-- @staticfct awful.layout.get
|
||||
function layout.get(screen)
|
||||
screen = screen or capi.mouse.screen
|
||||
|
||||
if not screen then return nil end
|
||||
|
||||
local t = get_screen(screen).selected_tag
|
||||
return tag.getproperty(t, "layout") or layout.suit.floating
|
||||
end
|
||||
|
@ -308,7 +311,7 @@ capi.screen.connect_signal("padding", layout.arrange)
|
|||
|
||||
capi.client.connect_signal("focus", function(c)
|
||||
local screen = c.screen
|
||||
if layout.get(screen).need_focus_update then
|
||||
if screen and layout.get(screen).need_focus_update then
|
||||
layout.arrange(screen)
|
||||
end
|
||||
end)
|
||||
|
|
|
@ -679,6 +679,30 @@ 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
|
||||
|
||||
|
|
|
@ -258,6 +258,88 @@ function gtable.merge(t, set)
|
|||
return t
|
||||
end
|
||||
|
||||
--- Update the `target` table with entries from the `new` table.
|
||||
--
|
||||
-- Compared to `gears.table.merge`, this version is intended to work using both
|
||||
-- an `identifier` function and a `merger` function. This works only for
|
||||
-- indexed tables.
|
||||
--
|
||||
-- The main use case is when changing the table reference is not possible or
|
||||
-- when the `target` contains additional content that must be kept.
|
||||
--
|
||||
-- Note that calling this function involve a lot of looping and should not be
|
||||
-- done often.
|
||||
--
|
||||
-- @tparam table target The table to modify.
|
||||
-- @tparam table new The table which contains the new content.
|
||||
-- @tparam function identifier A function which take the table entry (either
|
||||
-- from the `target` or `new` table) and return an unique identifier. The
|
||||
-- identifier type isn't important as long as `==` works to compare them.
|
||||
-- @tparam[opt] function merger A function takes the entry to modify as first
|
||||
-- parameter and the new entry as second. The function must return the merged
|
||||
-- value. If none is provided, there is no attempt to merge the content.
|
||||
-- @treturn table The target table (for daisy chaining).
|
||||
-- @treturn table The new entries.
|
||||
-- @treturn table The removed entries.
|
||||
-- @treturn table The updated entries.
|
||||
-- @staticfct gears.table.diff_merge
|
||||
-- @usage local output, added, removed, updated = gears.table.diff_merge(
|
||||
-- output, input, function(v) return v.id end, gears.table.crush,
|
||||
-- )
|
||||
function gtable.diff_merge(target, new, identifier, merger)
|
||||
local n_id, o_id, up = {}, {}, {}
|
||||
local add, rem = gtable.clone(new, false), gtable.clone(target, false)
|
||||
|
||||
for _, v in ipairs(target) do
|
||||
o_id[identifier(v)] = v
|
||||
end
|
||||
|
||||
for _, v in ipairs(new) do
|
||||
n_id[identifier(v)] = v
|
||||
end
|
||||
|
||||
for k, v in ipairs(rem) do
|
||||
if n_id[identifier(v)] then
|
||||
rem[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in ipairs(add) do
|
||||
local id = identifier(v)
|
||||
local old = o_id[id]
|
||||
if old then
|
||||
add[k] = nil
|
||||
if merger then
|
||||
o_id[id] = merger(old, v)
|
||||
table.insert(up, old)
|
||||
end
|
||||
else
|
||||
table.insert(target, v)
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in ipairs(target) do
|
||||
local id = identifier(v)
|
||||
if o_id[id] then
|
||||
target[k] = o_id[id]
|
||||
end
|
||||
end
|
||||
|
||||
-- Compact.
|
||||
rem, add = gtable.from_sparse(rem), gtable.from_sparse(add)
|
||||
|
||||
for _, v in ipairs(rem) do
|
||||
for k, v2 in ipairs(target) do
|
||||
if v == v2 then
|
||||
table.remove(target, k)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return target, add, rem, up
|
||||
end
|
||||
|
||||
--- Map a function to a table.
|
||||
--
|
||||
-- The function is applied to each value on the table, returning a modified
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
---------------------------------------------------------------------------
|
||||
|
||||
local naughty = require("naughty.core")
|
||||
local gdebug = require("gears.debug")
|
||||
local capi = {awesome = awesome}
|
||||
if dbus then
|
||||
naughty.dbus = require("naughty.dbus")
|
||||
|
@ -18,11 +19,48 @@ naughty.container = require("naughty.container")
|
|||
naughty.action = require("naughty.action")
|
||||
naughty.notification = require("naughty.notification")
|
||||
|
||||
-- Attempt to handle early errors when using the manual screen mode.
|
||||
--
|
||||
-- Creating a notification popup before the screens are added won't work. To
|
||||
-- work around this, the code below initializes some screens. One potential
|
||||
-- problem is that it could emit enough signal to cause even more errors and
|
||||
-- lose the original error.
|
||||
--
|
||||
-- For example, the following error can be displayed using this fallback:
|
||||
--
|
||||
-- screen.connect_signal("scanned", function() foobar() end)
|
||||
--
|
||||
local function screen_fallback()
|
||||
if screen.count() == 0 then
|
||||
gdebug.print_warning("An error occurred before a scrren was added")
|
||||
|
||||
-- Private API to scan for screens now.
|
||||
if #screen._viewports() == 0 then
|
||||
screen._scan_quiet()
|
||||
end
|
||||
|
||||
local viewports = screen._viewports()
|
||||
|
||||
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)
|
||||
end
|
||||
else
|
||||
screen.fake_add(0, 0, 640, 480)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Handle runtime errors during startup
|
||||
if capi.awesome.startup_errors then
|
||||
|
||||
-- Wait until `rc.lua` is executed before creating the notifications.
|
||||
-- Otherwise nothing is handling them (yet).
|
||||
awesome.connect_signal("startup", function()
|
||||
client.connect_signal("scanning", function()
|
||||
-- A lot of things have to go wrong for this to happen, but it can.
|
||||
screen_fallback()
|
||||
|
||||
naughty.emit_signal(
|
||||
"request::display_error", capi.awesome.startup_errors, true
|
||||
)
|
||||
|
@ -32,15 +70,20 @@ end
|
|||
-- Handle runtime errors after startup
|
||||
do
|
||||
local in_error = false
|
||||
|
||||
capi.awesome.connect_signal("debug::error", function (err)
|
||||
-- Make sure we don't go into an endless error loop
|
||||
if in_error then return end
|
||||
|
||||
in_error = true
|
||||
|
||||
screen_fallback()
|
||||
|
||||
naughty.emit_signal("request::display_error", tostring(err), false)
|
||||
|
||||
in_error = false
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
return naughty
|
||||
|
|
|
@ -44,7 +44,7 @@ local function get_widget_context(self)
|
|||
end
|
||||
|
||||
local context = self._widget_context
|
||||
local dpi = s.dpi
|
||||
local dpi = s and s.dpi or 96
|
||||
if (not context) or context.screen ~= s or context.dpi ~= dpi then
|
||||
context = {
|
||||
screen = s,
|
||||
|
|
19
luaa.c
19
luaa.c
|
@ -19,14 +19,17 @@
|
|||
*
|
||||
*/
|
||||
|
||||
/** awesome core API
|
||||
/** AwesomeWM lifecycle API.
|
||||
*
|
||||
* Additionally to the classes described here, one can also use X properties as
|
||||
* described in @{xproperties}.
|
||||
* This module contains the functions and signal to manage the lifecycle of the
|
||||
* AwesomeWM process. It allows to execute code at specific point from the early
|
||||
* initialization all the way to the last events before exiting or restarting.
|
||||
*
|
||||
* Additionally it handles signals for spawn and keyboard related events.
|
||||
*
|
||||
* @author Julien Danjou <julien@danjou.info>
|
||||
* @copyright 2008-2009 Julien Danjou
|
||||
* @module awesome
|
||||
* @coreclassmod awesome
|
||||
*/
|
||||
|
||||
/** Register a new xproperty.
|
||||
|
@ -150,13 +153,13 @@ extern const struct luaL_Reg awesome_mouse_meta[];
|
|||
* @signal refresh
|
||||
*/
|
||||
|
||||
/** Awesome is about to enter the event loop.
|
||||
/** AwesomeWM is about to enter the event loop.
|
||||
*
|
||||
* This means all initialization has been done.
|
||||
* @signal startup
|
||||
*/
|
||||
|
||||
/** Awesome is exiting / about to restart.
|
||||
/** AwesomeWM is exiting / about to restart.
|
||||
*
|
||||
* This signal is emitted in the `atexit` handler as well when awesome
|
||||
* restarts.
|
||||
|
@ -167,8 +170,8 @@ extern const struct luaL_Reg awesome_mouse_meta[];
|
|||
|
||||
/** The output status of a screen has changed.
|
||||
*
|
||||
* @param output String containing which output has changed.
|
||||
* @param connection_state String containing the connection status of
|
||||
* @tparam string output String containing which output has changed.
|
||||
* @tparam string connection_state String containing the connection status of
|
||||
* the output: It will be either "Connected", "Disconnected" or
|
||||
* "Unknown".
|
||||
* @signal screen::change
|
||||
|
|
|
@ -47,6 +47,8 @@ OPTIONS
|
|||
Ajouter un répertoire au chemin de recherche de bibliothèque.
|
||||
*-a*, *--no-argb*::
|
||||
N'utilise pas le codage ARGB.
|
||||
*-m*, *--screen*:: 'off' ou 'on'::
|
||||
Utiliser "manual" pour exécuter rc.lua avant de créer les écrans.
|
||||
*-r*, *--replace*::
|
||||
Remplace le gestionnaire de fenêtres existant.
|
||||
|
||||
|
|
|
@ -46,6 +46,8 @@ OPTIONS
|
|||
Add a directory to the library search path.
|
||||
*-a*, *--no-argb*::
|
||||
Don't use ARGB visuals.
|
||||
*-m*, *--screen*:: 'off' or 'on'::
|
||||
Use "off" to execute rc.lua before creating the screens.
|
||||
*-r*, *--replace*::
|
||||
Replace an existing window manager.
|
||||
|
||||
|
|
|
@ -132,6 +132,21 @@
|
|||
* @table awful.object
|
||||
*/
|
||||
|
||||
/** AwesomeWM is about to scan for existing clients.
|
||||
*
|
||||
* Connect to this signal when code needs to be executed after screens are
|
||||
* initialized, but before clients are added.
|
||||
*
|
||||
* @signal scanning
|
||||
*/
|
||||
|
||||
/** AwesomeWM is done scanning for clients.
|
||||
*
|
||||
* This is emitted before the `startup` signal and after the `scanning` signal.
|
||||
*
|
||||
* @signal scanned
|
||||
*/
|
||||
|
||||
/** When a client gains focus.
|
||||
* @signal focus
|
||||
*/
|
||||
|
@ -1038,6 +1053,20 @@ DO_CLIENT_SET_STRING_PROPERTY(role)
|
|||
DO_CLIENT_SET_STRING_PROPERTY(machine)
|
||||
#undef DO_CLIENT_SET_STRING_PROPERTY
|
||||
|
||||
void
|
||||
client_emit_scanned(void)
|
||||
{
|
||||
lua_State *L = globalconf_get_lua_State();
|
||||
luaA_class_emit_signal(L, &client_class, "scanned", 0);
|
||||
}
|
||||
|
||||
void
|
||||
client_emit_scanning(void)
|
||||
{
|
||||
lua_State *L = globalconf_get_lua_State();
|
||||
luaA_class_emit_signal(L, &client_class, "scanning", 0);
|
||||
}
|
||||
|
||||
void
|
||||
client_set_motif_wm_hints(lua_State *L, int cidx, motif_wm_hints_t hints)
|
||||
{
|
||||
|
|
|
@ -244,6 +244,8 @@ void client_refresh_partial(client_t *, int16_t, int16_t, uint16_t, uint16_t);
|
|||
void client_class_setup(lua_State *);
|
||||
void client_send_configure(client_t *);
|
||||
void client_find_transient_for(client_t *);
|
||||
void client_emit_scanned(void);
|
||||
void client_emit_scanning(void);
|
||||
drawable_t *client_get_drawable(client_t *, int, int);
|
||||
drawable_t *client_get_drawable_offset(client_t *, int *, int *);
|
||||
|
||||
|
|
658
objects/screen.c
658
objects/screen.c
|
@ -62,6 +62,37 @@
|
|||
*/
|
||||
#define FAKE_SCREEN_XID ((uint32_t) 0xffffffff)
|
||||
|
||||
/** AwesomeWM is about to scan for existing screens.
|
||||
*
|
||||
* Connect to this signal when code needs to be executed after the Lua context
|
||||
* is initialized and modules are loaded, but before screens are added.
|
||||
*
|
||||
* To manage screens manually, set `screen.automatic_factory = false` and
|
||||
* connect to the `property::viewports` signal. It is then possible to use
|
||||
* `screen.fake_add` to create virtual screens. Be careful when using this,
|
||||
* when done incorrectly, no screens will be created. Using Awesome with zero
|
||||
* screens is **not** supported.
|
||||
*
|
||||
* @signal scanning
|
||||
* @see property::viewports
|
||||
* @see screen.fake_add
|
||||
*/
|
||||
|
||||
/** AwesomeWM is done scanning for screens.
|
||||
*
|
||||
* Connect to this signal to execute code after the screens have been created,
|
||||
* but before the clients are added. This signal can also be used to split
|
||||
* physical screens into multiple virtual screens before the clients (and their
|
||||
* rules) are executed.
|
||||
*
|
||||
* Note that if no screens exist at this point, the fallback code will be
|
||||
* triggered and the default (detected) screens will be added.
|
||||
*
|
||||
* @signal scanned
|
||||
* @see screen.fake_resize
|
||||
* @see screen.fake_add
|
||||
*/
|
||||
|
||||
/** Screen is a table where indexes are screen numbers. You can use `screen[1]`
|
||||
* to get access to the first screen, etc. Alternatively, if RANDR information
|
||||
* is available, you can use output names for finding screen objects.
|
||||
|
@ -94,12 +125,33 @@
|
|||
* @signal swapped
|
||||
*/
|
||||
|
||||
/** This signal is emitted when the list of physical screen viewport changes.
|
||||
*
|
||||
* Each viewport in the list corresponds to a **physical** screen rectangle, which
|
||||
* is **not** the `viewports` property of the `screen` objects.
|
||||
*
|
||||
* @signal property::viewports
|
||||
* @tparam table viewports
|
||||
* @see automatic_factory
|
||||
*/
|
||||
|
||||
/**
|
||||
* The primary screen.
|
||||
*
|
||||
* @tfield screen primary
|
||||
*/
|
||||
|
||||
/**
|
||||
* If `screen` objects are created automatically when new viewports are detected.
|
||||
*
|
||||
* Be very, very careful when setting this to false. You might end up with
|
||||
* no screens. This is **not** supported. Always connect to the `scanned`
|
||||
* signal to make sure to create a fallback screen if none were created.
|
||||
*
|
||||
* @tfield[opt=true] boolean screen.automatic_factory
|
||||
* @see property::viewports
|
||||
*/
|
||||
|
||||
/**
|
||||
* The screen coordinates.
|
||||
*
|
||||
|
@ -225,6 +277,8 @@ static void
|
|||
screen_output_wipe(screen_output_t *output)
|
||||
{
|
||||
p_delete(&output->name);
|
||||
|
||||
randr_output_array_wipe(&output->outputs);
|
||||
}
|
||||
|
||||
ARRAY_FUNCS(screen_output_t, screen_output, screen_output_wipe)
|
||||
|
@ -232,13 +286,6 @@ ARRAY_FUNCS(screen_output_t, screen_output, screen_output_wipe)
|
|||
static lua_class_t screen_class;
|
||||
LUA_OBJECT_FUNCS(screen_class, screen_t, screen)
|
||||
|
||||
/** Collect a screen. */
|
||||
static void
|
||||
screen_wipe(screen_t *s)
|
||||
{
|
||||
screen_output_array_wipe(&s->outputs);
|
||||
}
|
||||
|
||||
/** Check if a screen is valid */
|
||||
static bool
|
||||
screen_checker(screen_t *s)
|
||||
|
@ -297,6 +344,243 @@ screen_deduplicate(lua_State *L, screen_array_t *screens)
|
|||
}
|
||||
}
|
||||
|
||||
/** Keep track of the screen viewport(s) independently from the screen objects.
|
||||
*
|
||||
* A viewport is a collection of `outputs` objects and their associated
|
||||
* metadata. This structure is copied into Lua and then further extended from
|
||||
* there. The `id` field allows to differentiate between viewports that share
|
||||
* the same position and dimensions without having to rely on userdata pointer
|
||||
* comparison.
|
||||
*
|
||||
* Screen objects are widely used by the public API and imply a very "visible"
|
||||
* concept. A viewport is a subset of what the concerns the "screen" class
|
||||
* previously handled. It is meant to be used by some low level Lua logic to
|
||||
* create screens from Lua rather than from C. This is required to increase the
|
||||
* flexibility of multi-screen setup or when screens are connected and
|
||||
* disconnected often.
|
||||
*
|
||||
* Design rationals:
|
||||
*
|
||||
* * The structure is not directly shared with Lua to avoid having to use the
|
||||
* slow "miss_handler" and unsafe "valid" systems used by other CAPI objects.
|
||||
* * The `viewport_t` implements a linked-list because its main purpose is to
|
||||
* offers a deduplication algorithm. Random access is never required.
|
||||
* * Everything that can be done in Lua is done in Lua.
|
||||
* * Since the legacy and "new" way to initialize screens share a lot of steps,
|
||||
* the C code is bent to share as much code as possible. This will reduce the
|
||||
* "dead code" and improve code coverage by the tests.
|
||||
*
|
||||
*/
|
||||
typedef struct viewport_t
|
||||
{
|
||||
bool marked;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
struct viewport_t *next;
|
||||
screen_t *screen;
|
||||
screen_output_array_t outputs;
|
||||
} viewport_t;
|
||||
|
||||
static viewport_t *first_screen_viewport = NULL;
|
||||
static viewport_t *last_screen_viewport = NULL;
|
||||
|
||||
static void
|
||||
luaA_viewport_get_outputs(lua_State *L, viewport_t *a)
|
||||
{
|
||||
lua_createtable(L, 0, a ? a->outputs.len : 0);
|
||||
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
int count = 1;
|
||||
|
||||
foreach(output, a->outputs) {
|
||||
lua_createtable(L, 3, 0);
|
||||
|
||||
lua_pushstring(L, "mm_width");
|
||||
lua_pushinteger(L, output->mm_width);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "mm_height");
|
||||
lua_pushinteger(L, output->mm_height);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_pushstring(L, "name");
|
||||
lua_pushstring(L, output->name);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* Add to the outputs */
|
||||
lua_rawseti(L, -2, count++);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
luaA_viewports(lua_State *L)
|
||||
{
|
||||
/* All viewports */
|
||||
lua_newtable(L);
|
||||
|
||||
viewport_t *a = first_screen_viewport;
|
||||
|
||||
if (!a)
|
||||
return 1;
|
||||
|
||||
int count = 1;
|
||||
|
||||
do {
|
||||
lua_newtable(L);
|
||||
|
||||
/* The geometry */
|
||||
lua_pushstring(L, "geometry");
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushstring(L, "x");
|
||||
lua_pushinteger(L, a->x);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "y");
|
||||
lua_pushinteger(L, a->y);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "width");
|
||||
lua_pushinteger(L, a->width);
|
||||
lua_settable(L, -3);
|
||||
lua_pushstring(L, "height");
|
||||
lua_pushinteger(L, a->height);
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* Add the geometry table to the arguments */
|
||||
lua_settable(L, -3);
|
||||
|
||||
/* Add the outputs table to the arguments */
|
||||
lua_pushstring(L, "outputs");
|
||||
luaA_viewport_get_outputs(L, a);
|
||||
lua_settable(L, -3);
|
||||
|
||||
lua_rawseti(L, -2, count++);
|
||||
} while ((a = a->next));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Give Lua a chance to handle or blacklist a viewport before creating the
|
||||
* screen object.
|
||||
*/
|
||||
static void
|
||||
viewports_notify(lua_State *L)
|
||||
{
|
||||
if (!first_screen_viewport)
|
||||
return;
|
||||
|
||||
luaA_viewports(L);
|
||||
|
||||
luaA_class_emit_signal(L, &screen_class, "property::viewports", 1);
|
||||
}
|
||||
|
||||
static viewport_t *
|
||||
viewport_add(lua_State *L, int x, int y, int w, int h)
|
||||
{
|
||||
/* Search existing to avoid having to deduplicate later */
|
||||
viewport_t *a = first_screen_viewport;
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
if (a && a->x == x && a->y == y && a->width == w && a->height == h)
|
||||
{
|
||||
a->marked = true;
|
||||
return a;
|
||||
}
|
||||
} while (a && (a = a->next));
|
||||
|
||||
viewport_t *node = malloc(sizeof(viewport_t));
|
||||
node->x = x;
|
||||
node->y = y;
|
||||
node->width = w;
|
||||
node->height = h;
|
||||
node->next = NULL;
|
||||
node->screen = NULL;
|
||||
node->marked = true;
|
||||
|
||||
screen_output_array_init(&node->outputs);
|
||||
|
||||
if (!first_screen_viewport) {
|
||||
first_screen_viewport = node;
|
||||
last_screen_viewport = node;
|
||||
} else {
|
||||
last_screen_viewport->next = node;
|
||||
last_screen_viewport = node;
|
||||
}
|
||||
|
||||
assert(first_screen_viewport && last_screen_viewport);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void
|
||||
monitor_unmark(void)
|
||||
{
|
||||
viewport_t *a = first_screen_viewport;
|
||||
|
||||
if (!a)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
a->marked = false;
|
||||
} while((a = a->next));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
viewport_purge(void)
|
||||
{
|
||||
viewport_t *cur = first_screen_viewport;
|
||||
|
||||
/* Move the head of the list */
|
||||
while (first_screen_viewport && !first_screen_viewport->marked) {
|
||||
cur = first_screen_viewport;
|
||||
first_screen_viewport = cur->next;
|
||||
|
||||
foreach(existing_screen, globalconf.screens)
|
||||
if ((*existing_screen)->viewport == cur)
|
||||
(*existing_screen)->viewport = NULL;
|
||||
|
||||
screen_output_array_wipe(&cur->outputs);
|
||||
|
||||
free(cur);
|
||||
}
|
||||
|
||||
if (!first_screen_viewport) {
|
||||
last_screen_viewport = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
cur = first_screen_viewport;
|
||||
|
||||
/* Drop unmarked entries */
|
||||
do {
|
||||
if (cur->next && !cur->next->marked) {
|
||||
viewport_t *tmp = cur->next;
|
||||
cur->next = cur->next->next;
|
||||
|
||||
if (tmp == last_screen_viewport)
|
||||
last_screen_viewport = cur;
|
||||
|
||||
foreach(existing_screen, globalconf.screens)
|
||||
if ((*existing_screen)->viewport == tmp)
|
||||
(*existing_screen)->viewport = NULL;
|
||||
|
||||
screen_output_array_wipe(&tmp->outputs);
|
||||
|
||||
free(tmp);
|
||||
} else
|
||||
cur = cur->next;
|
||||
|
||||
} while(cur);
|
||||
}
|
||||
|
||||
static screen_t *
|
||||
screen_add(lua_State *L, screen_array_t *screens)
|
||||
{
|
||||
|
@ -309,6 +593,43 @@ screen_add(lua_State *L, screen_array_t *screens)
|
|||
|
||||
/* Monitors were introduced in RandR 1.5 */
|
||||
#ifdef XCB_RANDR_GET_MONITORS
|
||||
|
||||
static screen_output_t
|
||||
screen_get_randr_output(lua_State *L, xcb_randr_monitor_info_iterator_t *it)
|
||||
{
|
||||
screen_output_t output;
|
||||
xcb_randr_output_t *randr_outputs;
|
||||
xcb_get_atom_name_cookie_t name_c;
|
||||
xcb_get_atom_name_reply_t *name_r;
|
||||
|
||||
output.mm_width = it->data->width_in_millimeters;
|
||||
output.mm_height = it->data->height_in_millimeters;
|
||||
|
||||
name_c = xcb_get_atom_name_unchecked(globalconf.connection, it->data->name);
|
||||
name_r = xcb_get_atom_name_reply(globalconf.connection, name_c, NULL);
|
||||
|
||||
if (name_r) {
|
||||
const char *name = xcb_get_atom_name_name(name_r);
|
||||
size_t len = xcb_get_atom_name_name_length(name_r);
|
||||
|
||||
output.name = memcpy(p_new(char *, len + 1), name, len);
|
||||
output.name[len] = '\0';
|
||||
p_delete(&name_r);
|
||||
} else {
|
||||
output.name = a_strdup("unknown");
|
||||
}
|
||||
|
||||
randr_output_array_init(&output.outputs);
|
||||
|
||||
randr_outputs = xcb_randr_monitor_info_outputs(it->data);
|
||||
|
||||
for(int i = 0; i < xcb_randr_monitor_info_outputs_length(it->data); i++) {
|
||||
randr_output_array_append(&output.outputs, randr_outputs[i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static void
|
||||
screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
|
||||
{
|
||||
|
@ -325,44 +646,32 @@ screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
|
|||
monitor_iter.rem; xcb_randr_monitor_info_next(&monitor_iter))
|
||||
{
|
||||
screen_t *new_screen;
|
||||
screen_output_t output;
|
||||
xcb_randr_output_t *randr_outputs;
|
||||
xcb_get_atom_name_cookie_t name_c;
|
||||
xcb_get_atom_name_reply_t *name_r;
|
||||
|
||||
if(!xcb_randr_monitor_info_outputs_length(monitor_iter.data))
|
||||
continue;
|
||||
|
||||
screen_output_t output = screen_get_randr_output(L, &monitor_iter);
|
||||
|
||||
viewport_t *viewport = viewport_add(L,
|
||||
monitor_iter.data->x,
|
||||
monitor_iter.data->y,
|
||||
monitor_iter.data->width,
|
||||
monitor_iter.data->height
|
||||
);
|
||||
|
||||
screen_output_array_append(&viewport->outputs, output);
|
||||
|
||||
if (globalconf.ignore_screens)
|
||||
continue;
|
||||
|
||||
new_screen = screen_add(L, screens);
|
||||
viewport->screen = new_screen;
|
||||
new_screen->viewport = viewport;
|
||||
new_screen->geometry.x = monitor_iter.data->x;
|
||||
new_screen->geometry.y = monitor_iter.data->y;
|
||||
new_screen->geometry.width = monitor_iter.data->width;
|
||||
new_screen->geometry.height = monitor_iter.data->height;
|
||||
new_screen->xid = monitor_iter.data->name;
|
||||
|
||||
output.mm_width = monitor_iter.data->width_in_millimeters;
|
||||
output.mm_height = monitor_iter.data->height_in_millimeters;
|
||||
|
||||
name_c = xcb_get_atom_name_unchecked(globalconf.connection, monitor_iter.data->name);
|
||||
name_r = xcb_get_atom_name_reply(globalconf.connection, name_c, NULL);
|
||||
if (name_r) {
|
||||
const char *name = xcb_get_atom_name_name(name_r);
|
||||
size_t len = xcb_get_atom_name_name_length(name_r);
|
||||
|
||||
output.name = memcpy(p_new(char *, len + 1), name, len);
|
||||
output.name[len] = '\0';
|
||||
p_delete(&name_r);
|
||||
} else {
|
||||
output.name = a_strdup("unknown");
|
||||
}
|
||||
randr_output_array_init(&output.outputs);
|
||||
|
||||
randr_outputs = xcb_randr_monitor_info_outputs(monitor_iter.data);
|
||||
for(int i = 0; i < xcb_randr_monitor_info_outputs_length(monitor_iter.data); i++) {
|
||||
randr_output_array_append(&output.outputs, randr_outputs[i]);
|
||||
}
|
||||
|
||||
screen_output_array_append(&new_screen->outputs, output);
|
||||
}
|
||||
|
||||
p_delete(&monitors_r);
|
||||
|
@ -374,6 +683,39 @@ screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
screen_get_randr_crtcs_outputs(lua_State *L, xcb_randr_get_crtc_info_reply_t *crtc_info_r, screen_output_array_t *outputs)
|
||||
{
|
||||
xcb_randr_output_t *randr_outputs = xcb_randr_get_crtc_info_outputs(crtc_info_r);
|
||||
|
||||
for(int j = 0; j < xcb_randr_get_crtc_info_outputs_length(crtc_info_r); j++)
|
||||
{
|
||||
xcb_randr_get_output_info_cookie_t output_info_c = xcb_randr_get_output_info(globalconf.connection, randr_outputs[j], XCB_CURRENT_TIME);
|
||||
xcb_randr_get_output_info_reply_t *output_info_r = xcb_randr_get_output_info_reply(globalconf.connection, output_info_c, NULL);
|
||||
screen_output_t output;
|
||||
|
||||
if (!output_info_r) {
|
||||
warn("RANDR GetOutputInfo failed; this should not be possible");
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = xcb_randr_get_output_info_name_length(output_info_r);
|
||||
/* name is not NULL terminated */
|
||||
char *name = memcpy(p_new(char *, len + 1), xcb_randr_get_output_info_name(output_info_r), len);
|
||||
name[len] = '\0';
|
||||
|
||||
output.name = name;
|
||||
output.mm_width = output_info_r->mm_width;
|
||||
output.mm_height = output_info_r->mm_height;
|
||||
randr_output_array_init(&output.outputs);
|
||||
randr_output_array_append(&output.outputs, randr_outputs[j]);
|
||||
|
||||
screen_output_array_append(outputs, output);
|
||||
|
||||
p_delete(&output_info_r);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
screen_scan_randr_crtcs(lua_State *L, screen_array_t *screens)
|
||||
{
|
||||
|
@ -406,45 +748,34 @@ screen_scan_randr_crtcs(lua_State *L, screen_array_t *screens)
|
|||
if(!xcb_randr_get_crtc_info_outputs_length(crtc_info_r))
|
||||
continue;
|
||||
|
||||
viewport_t *viewport = viewport_add(L,
|
||||
crtc_info_r->x,
|
||||
crtc_info_r->y,
|
||||
crtc_info_r->width,
|
||||
crtc_info_r->height
|
||||
);
|
||||
|
||||
screen_get_randr_crtcs_outputs(L, crtc_info_r, &viewport->outputs);
|
||||
|
||||
if (globalconf.ignore_screens)
|
||||
{
|
||||
p_delete(&crtc_info_r);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Prepare the new screen */
|
||||
screen_t *new_screen = screen_add(L, screens);
|
||||
viewport->screen = new_screen;
|
||||
new_screen->viewport = viewport;
|
||||
new_screen->geometry.x = crtc_info_r->x;
|
||||
new_screen->geometry.y = crtc_info_r->y;
|
||||
new_screen->geometry.width= crtc_info_r->width;
|
||||
new_screen->geometry.height= crtc_info_r->height;
|
||||
new_screen->xid = randr_crtcs[i];
|
||||
|
||||
xcb_randr_output_t *randr_outputs = xcb_randr_get_crtc_info_outputs(crtc_info_r);
|
||||
|
||||
for(int j = 0; j < xcb_randr_get_crtc_info_outputs_length(crtc_info_r); j++)
|
||||
{
|
||||
xcb_randr_get_output_info_cookie_t output_info_c = xcb_randr_get_output_info(globalconf.connection, randr_outputs[j], XCB_CURRENT_TIME);
|
||||
xcb_randr_get_output_info_reply_t *output_info_r = xcb_randr_get_output_info_reply(globalconf.connection, output_info_c, NULL);
|
||||
screen_output_t output;
|
||||
|
||||
if (!output_info_r) {
|
||||
warn("RANDR GetOutputInfo failed; this should not be possible");
|
||||
continue;
|
||||
}
|
||||
|
||||
int len = xcb_randr_get_output_info_name_length(output_info_r);
|
||||
/* name is not NULL terminated */
|
||||
char *name = memcpy(p_new(char *, len + 1), xcb_randr_get_output_info_name(output_info_r), len);
|
||||
name[len] = '\0';
|
||||
|
||||
output.name = name;
|
||||
output.mm_width = output_info_r->mm_width;
|
||||
output.mm_height = output_info_r->mm_height;
|
||||
randr_output_array_init(&output.outputs);
|
||||
randr_output_array_append(&output.outputs, randr_outputs[j]);
|
||||
|
||||
screen_output_array_append(&new_screen->outputs, output);
|
||||
|
||||
|
||||
p_delete(&output_info_r);
|
||||
|
||||
if (A_STREQ(name, "default"))
|
||||
{
|
||||
/* Detect the older NVIDIA blobs */
|
||||
foreach(output, new_screen->viewport->outputs) {
|
||||
if (A_STREQ(output->name, "default")) {
|
||||
/* non RandR 1.2+ X driver don't return any usable multihead
|
||||
* data. I'm looking at you, nvidia binary blob!
|
||||
*/
|
||||
|
@ -453,9 +784,12 @@ screen_scan_randr_crtcs(lua_State *L, screen_array_t *screens)
|
|||
/* Get rid of the screens that we already created */
|
||||
foreach(screen, *screens)
|
||||
luaA_object_unref(L, *screen);
|
||||
|
||||
screen_array_wipe(screens);
|
||||
screen_array_init(screens);
|
||||
|
||||
p_delete(&screen_res_r);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -485,6 +819,7 @@ screen_scan_randr(lua_State *L, screen_array_t *screens)
|
|||
if(!version_reply)
|
||||
return;
|
||||
|
||||
|
||||
major_version = version_reply->major_version;
|
||||
minor_version = version_reply->minor_version;
|
||||
p_delete(&version_reply);
|
||||
|
@ -510,7 +845,7 @@ screen_scan_randr(lua_State *L, screen_array_t *screens)
|
|||
else
|
||||
screen_scan_randr_crtcs(L, screens);
|
||||
|
||||
if (screens->len == 0)
|
||||
if (screens->len == 0 && !globalconf.ignore_screens)
|
||||
{
|
||||
/* Scanning failed, disable randr again */
|
||||
xcb_randr_select_input(globalconf.connection,
|
||||
|
@ -556,7 +891,19 @@ screen_scan_xinerama(lua_State *L, screen_array_t *screens)
|
|||
|
||||
for(int screen = 0; screen < xinerama_screen_number; screen++)
|
||||
{
|
||||
viewport_t *viewport = viewport_add(L,
|
||||
xsi[screen].x_org,
|
||||
xsi[screen].y_org,
|
||||
xsi[screen].width,
|
||||
xsi[screen].height
|
||||
);
|
||||
|
||||
if (globalconf.ignore_screens)
|
||||
continue;
|
||||
|
||||
screen_t *s = screen_add(L, screens);
|
||||
viewport->screen = s;
|
||||
s->viewport = viewport;
|
||||
s->geometry.x = xsi[screen].x_org;
|
||||
s->geometry.y = xsi[screen].y_org;
|
||||
s->geometry.width = xsi[screen].width;
|
||||
|
@ -569,7 +916,20 @@ screen_scan_xinerama(lua_State *L, screen_array_t *screens)
|
|||
static void screen_scan_x11(lua_State *L, screen_array_t *screens)
|
||||
{
|
||||
xcb_screen_t *xcb_screen = globalconf.screen;
|
||||
|
||||
viewport_t *viewport = viewport_add(L,
|
||||
0,
|
||||
0,
|
||||
xcb_screen->width_in_pixels,
|
||||
xcb_screen->height_in_pixels
|
||||
);
|
||||
|
||||
if (globalconf.ignore_screens)
|
||||
return;
|
||||
|
||||
screen_t *s = screen_add(L, screens);
|
||||
viewport->screen = s;
|
||||
s->viewport = viewport;
|
||||
s->geometry.x = 0;
|
||||
s->geometry.y = 0;
|
||||
s->geometry.width = xcb_screen->width_in_pixels;
|
||||
|
@ -586,21 +946,36 @@ screen_added(lua_State *L, screen_t *screen)
|
|||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
/** Get screens informations and fill global configuration.
|
||||
*/
|
||||
void
|
||||
screen_scan(void)
|
||||
screen_emit_scanned(void)
|
||||
{
|
||||
lua_State *L = globalconf_get_lua_State();
|
||||
luaA_class_emit_signal(L, &screen_class, "scanned", 0);
|
||||
}
|
||||
|
||||
void
|
||||
screen_emit_scanning(void)
|
||||
{
|
||||
lua_State *L = globalconf_get_lua_State();
|
||||
luaA_class_emit_signal(L, &screen_class, "scanning", 0);
|
||||
}
|
||||
|
||||
static void
|
||||
screen_scan_common(bool quiet)
|
||||
{
|
||||
lua_State *L;
|
||||
|
||||
L = globalconf_get_lua_State();
|
||||
|
||||
monitor_unmark();
|
||||
|
||||
screen_scan_randr(L, &globalconf.screens);
|
||||
if (globalconf.screens.len == 0)
|
||||
screen_scan_xinerama(L, &globalconf.screens);
|
||||
if (globalconf.screens.len == 0)
|
||||
screen_scan_x11(L, &globalconf.screens);
|
||||
check(globalconf.screens.len > 0);
|
||||
|
||||
check(globalconf.screens.len > 0 || globalconf.ignore_screens);
|
||||
|
||||
screen_deduplicate(L, &globalconf.screens);
|
||||
|
||||
|
@ -608,9 +983,30 @@ screen_scan(void)
|
|||
screen_added(L, *screen);
|
||||
}
|
||||
|
||||
viewport_purge();
|
||||
|
||||
if (!quiet)
|
||||
viewports_notify(L);
|
||||
|
||||
screen_update_primary();
|
||||
}
|
||||
|
||||
/** Get screens informations and fill global configuration.
|
||||
*/
|
||||
void
|
||||
screen_scan(void)
|
||||
{
|
||||
screen_emit_scanning();
|
||||
screen_scan_common(false);
|
||||
}
|
||||
|
||||
static int
|
||||
luaA_scan_quiet(lua_State *L)
|
||||
{
|
||||
screen_scan_common(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Called when a screen is removed, removes references to the old screen */
|
||||
static void
|
||||
screen_removed(lua_State *L, int sidx)
|
||||
|
@ -629,6 +1025,15 @@ screen_removed(lua_State *L, int sidx)
|
|||
}
|
||||
}
|
||||
|
||||
void screen_cleanup(void)
|
||||
{
|
||||
while(globalconf.screens.len)
|
||||
screen_array_take(&globalconf.screens, 0);
|
||||
|
||||
monitor_unmark();
|
||||
viewport_purge();
|
||||
}
|
||||
|
||||
static void
|
||||
screen_modified(screen_t *existing_screen, screen_t *other_screen)
|
||||
{
|
||||
|
@ -644,25 +1049,36 @@ screen_modified(screen_t *existing_screen, screen_t *other_screen)
|
|||
screen_update_workarea(existing_screen);
|
||||
}
|
||||
|
||||
bool outputs_changed = existing_screen->outputs.len != other_screen->outputs.len;
|
||||
if(!outputs_changed)
|
||||
for(int i = 0; i < existing_screen->outputs.len; i++) {
|
||||
screen_output_t *existing_output = &existing_screen->outputs.tab[i];
|
||||
screen_output_t *other_output = &other_screen->outputs.tab[i];
|
||||
const int other_len = other_screen->viewport ?
|
||||
other_screen->viewport->outputs.len : 0;
|
||||
|
||||
const int existing_len = existing_screen->viewport ?
|
||||
existing_screen->viewport->outputs.len : 0;
|
||||
|
||||
bool outputs_changed = (!(existing_screen->viewport && other_screen->viewport))
|
||||
|| existing_len != other_len;
|
||||
|
||||
if(existing_screen->viewport && other_screen->viewport && !outputs_changed)
|
||||
for(int i = 0; i < existing_screen->viewport->outputs.len; i++) {
|
||||
screen_output_t *existing_output = &existing_screen->viewport->outputs.tab[i];
|
||||
screen_output_t *other_output = &other_screen->viewport->outputs.tab[i];
|
||||
outputs_changed |= existing_output->mm_width != other_output->mm_width;
|
||||
outputs_changed |= existing_output->mm_height != other_output->mm_height;
|
||||
outputs_changed |= A_STRNEQ(existing_output->name, other_output->name);
|
||||
}
|
||||
|
||||
/* Brute-force update the outputs by swapping */
|
||||
screen_output_array_t tmp = other_screen->outputs;
|
||||
other_screen->outputs = existing_screen->outputs;
|
||||
existing_screen->outputs = tmp;
|
||||
if(existing_screen->viewport || other_screen->viewport) {
|
||||
viewport_t *tmp = other_screen->viewport;
|
||||
other_screen->viewport = existing_screen->viewport;
|
||||
|
||||
if(outputs_changed) {
|
||||
luaA_object_push(L, existing_screen);
|
||||
luaA_object_emit_signal(L, -1, "property::outputs", 0);
|
||||
lua_pop(L, 1);
|
||||
existing_screen->viewport = tmp;
|
||||
|
||||
if(outputs_changed) {
|
||||
luaA_object_push(L, existing_screen);
|
||||
luaA_object_emit_signal(L, -1, "property::outputs", 0);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -671,6 +1087,8 @@ screen_refresh(gpointer unused)
|
|||
{
|
||||
globalconf.screen_refresh_pending = false;
|
||||
|
||||
monitor_unmark();
|
||||
|
||||
screen_array_t new_screens;
|
||||
screen_array_t removed_screens;
|
||||
lua_State *L = globalconf_get_lua_State();
|
||||
|
@ -682,6 +1100,10 @@ screen_refresh(gpointer unused)
|
|||
else
|
||||
screen_scan_randr_crtcs(L, &new_screens);
|
||||
|
||||
viewport_purge();
|
||||
|
||||
viewports_notify(L);
|
||||
|
||||
screen_deduplicate(L, &new_screens);
|
||||
|
||||
/* Running without any screens at all is no fun. */
|
||||
|
@ -837,10 +1259,10 @@ screen_coord_in_screen(screen_t *s, int x, int y)
|
|||
bool
|
||||
screen_area_in_screen(screen_t *s, area_t geom)
|
||||
{
|
||||
return (geom.x < s->geometry.x + s->geometry.width)
|
||||
&& (geom.x + geom.width > s->geometry.x )
|
||||
&& (geom.y < s->geometry.y + s->geometry.height)
|
||||
&& (geom.y + geom.height > s->geometry.y);
|
||||
return (geom.x < s->geometry.x + s->geometry.width)
|
||||
&& (geom.x + geom.width > s->geometry.x )
|
||||
&& (geom.y < s->geometry.y + s->geometry.height)
|
||||
&& (geom.y + geom.height > s->geometry.y);
|
||||
}
|
||||
|
||||
void screen_update_workarea(screen_t *screen)
|
||||
|
@ -1022,10 +1444,11 @@ screen_update_primary(void)
|
|||
|
||||
foreach(screen, globalconf.screens)
|
||||
{
|
||||
foreach(output, (*screen)->outputs)
|
||||
foreach (randr_output, output->outputs)
|
||||
if (*randr_output == primary->output)
|
||||
primary_screen = *screen;
|
||||
if ((*screen)->viewport)
|
||||
foreach(output, (*screen)->viewport->outputs)
|
||||
foreach (randr_output, output->outputs)
|
||||
if (*randr_output == primary->output)
|
||||
primary_screen = *screen;
|
||||
}
|
||||
p_delete(&primary);
|
||||
|
||||
|
@ -1077,11 +1500,18 @@ luaA_screen_module_index(lua_State *L)
|
|||
{
|
||||
if(A_STREQ(name, "primary"))
|
||||
return luaA_object_push(L, screen_get_primary());
|
||||
else if (A_STREQ(name, "automatic_factory"))
|
||||
{
|
||||
lua_pushboolean(L, !globalconf.ignore_screens);
|
||||
return 1;
|
||||
}
|
||||
|
||||
foreach(screen, globalconf.screens)
|
||||
foreach(output, (*screen)->outputs)
|
||||
if(A_STREQ(output->name, name))
|
||||
return luaA_object_push(L, *screen);
|
||||
if ((*screen)->viewport)
|
||||
foreach(output, (*screen)->viewport->outputs)
|
||||
if(A_STREQ(output->name, name))
|
||||
return luaA_object_push(L, *screen);
|
||||
|
||||
luaA_warn(L, "Unknown screen output name: %s", name);
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
|
@ -1090,6 +1520,28 @@ luaA_screen_module_index(lua_State *L)
|
|||
return luaA_object_push(L, luaA_checkscreen(L, 2));
|
||||
}
|
||||
|
||||
static int
|
||||
luaA_screen_module_newindex(lua_State *L)
|
||||
{
|
||||
const char *buf = luaL_checkstring(L, 2);
|
||||
|
||||
if (A_STREQ(buf, "automatic_factory"))
|
||||
{
|
||||
globalconf.ignore_screens = !luaA_checkboolean(L, 3);
|
||||
|
||||
/* It *can* be useful if screens are added/removed later, but generally,
|
||||
* setting this should be done before screens are added
|
||||
*/
|
||||
if (globalconf.ignore_screens && !globalconf.no_auto_screen)
|
||||
luaA_warn(L,
|
||||
"Setting automatic_factory only makes sense when AwesomeWM is"
|
||||
" started with `--screen off`"
|
||||
);
|
||||
}
|
||||
|
||||
return luaA_default_newindex(L);
|
||||
}
|
||||
|
||||
/** Iterate over screens.
|
||||
* @usage
|
||||
* for s in screen do
|
||||
|
@ -1126,18 +1578,8 @@ luaA_screen_get_index(lua_State *L, screen_t *s)
|
|||
static int
|
||||
luaA_screen_get_outputs(lua_State *L, screen_t *s)
|
||||
{
|
||||
lua_createtable(L, 0, s->outputs.len);
|
||||
foreach(output, s->outputs)
|
||||
{
|
||||
lua_createtable(L, 2, 0);
|
||||
luaA_viewport_get_outputs(L, s->viewport);
|
||||
|
||||
lua_pushinteger(L, output->mm_width);
|
||||
lua_setfield(L, -2, "mm_width");
|
||||
lua_pushinteger(L, output->mm_height);
|
||||
lua_setfield(L, -2, "mm_height");
|
||||
|
||||
lua_setfield(L, -2, output->name);
|
||||
}
|
||||
/* The table of tables we created. */
|
||||
return 1;
|
||||
}
|
||||
|
@ -1312,8 +1754,10 @@ screen_class_setup(lua_State *L)
|
|||
{
|
||||
LUA_CLASS_METHODS(screen)
|
||||
{ "count", luaA_screen_count },
|
||||
{ "_viewports", luaA_viewports },
|
||||
{ "_scan_quiet", luaA_scan_quiet },
|
||||
{ "__index", luaA_screen_module_index },
|
||||
{ "__newindex", luaA_default_newindex },
|
||||
{ "__newindex", luaA_screen_module_newindex },
|
||||
{ "__call", luaA_screen_module_call },
|
||||
{ "fake_add", luaA_screen_fake_add },
|
||||
{ NULL, NULL }
|
||||
|
@ -1331,7 +1775,7 @@ screen_class_setup(lua_State *L)
|
|||
|
||||
luaA_class_setup(L, &screen_class, "screen", NULL,
|
||||
(lua_class_allocator_t) screen_new,
|
||||
(lua_class_collector_t) screen_wipe,
|
||||
(lua_class_collector_t) NULL,
|
||||
(lua_class_checker_t) screen_checker,
|
||||
luaA_class_index_miss_property, luaA_class_newindex_miss_property,
|
||||
screen_methods, screen_meta);
|
||||
|
|
|
@ -39,8 +39,8 @@ struct a_screen
|
|||
area_t geometry;
|
||||
/** Screen workarea */
|
||||
area_t workarea;
|
||||
/** The screen outputs informations */
|
||||
screen_output_array_t outputs;
|
||||
/** Opaque pointer to the psysical geometry */
|
||||
struct viewport_t *viewport;
|
||||
/** Some XID identifying this screen */
|
||||
uint32_t xid;
|
||||
};
|
||||
|
@ -57,6 +57,9 @@ void screen_update_primary(void);
|
|||
void screen_update_workarea(screen_t *);
|
||||
screen_t *screen_get_primary(void);
|
||||
void screen_schedule_refresh(void);
|
||||
void screen_emit_scanned(void);
|
||||
void screen_emit_scanning(void);
|
||||
void screen_cleanup(void);
|
||||
|
||||
screen_t *luaA_checkscreen(lua_State *, int);
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ local function iter_scr(_, _, s)
|
|||
end
|
||||
end
|
||||
|
||||
function screen._areas()
|
||||
function screen._viewports()
|
||||
return {}
|
||||
end
|
||||
|
||||
|
|
10
tests/run.sh
10
tests/run.sh
|
@ -23,16 +23,19 @@ Usage: $0 [OPTION]... [FILE]...
|
|||
Options:
|
||||
-v: verbose mode
|
||||
-W: warnings become errors
|
||||
-m: Use --screen off
|
||||
-h: show this help
|
||||
EOF
|
||||
exit "$1"
|
||||
}
|
||||
fail_on_warning=
|
||||
manual_screens=
|
||||
verbose=${VERBOSE:-0}
|
||||
while getopts vWh opt; do
|
||||
while getopts vWmh opt; do
|
||||
case $opt in
|
||||
v) verbose=1 ;;
|
||||
W) fail_on_warning=1 ;;
|
||||
m) manual_screens=" --screen off" ;;
|
||||
h) usage 0 ;;
|
||||
*) usage 64 ;;
|
||||
esac
|
||||
|
@ -105,7 +108,9 @@ fi
|
|||
|
||||
# Add test dir (for _runner.lua).
|
||||
# shellcheck disable=SC2206
|
||||
awesome_options=($AWESOME_OPTIONS --search lib --search "$this_dir")
|
||||
awesome_options=($AWESOME_OPTIONS $manual_screens --search lib --search "$this_dir")
|
||||
|
||||
awesome_options+=(--screen off)
|
||||
|
||||
# Cleanup on errors / aborting.
|
||||
cleanup() {
|
||||
|
@ -172,6 +177,7 @@ fi
|
|||
# Start awesome.
|
||||
start_awesome() {
|
||||
cd "$build_dir"
|
||||
|
||||
# Kill awesome after $TEST_TIMEOUT seconds (e.g. for errors during test setup).
|
||||
# SOURCE_DIRECTORY is used by .luacov.
|
||||
DISPLAY="$D" SOURCE_DIRECTORY="$source_dir" \
|
||||
|
|
|
@ -64,6 +64,34 @@ local steps = {
|
|||
|
||||
return true
|
||||
end
|
||||
end,
|
||||
-- Make sure the error code still works when all screens are gone.
|
||||
function()
|
||||
while screen.count() > 0 do
|
||||
screen[1]:fake_remove()
|
||||
end
|
||||
|
||||
-- Don't make the test fail.
|
||||
local called = false
|
||||
require("gears.debug").print_warning = function() called = true end
|
||||
|
||||
-- Cause an error in a protected call!
|
||||
awesome.emit_signal("debug::error", "err")
|
||||
|
||||
assert(called)
|
||||
|
||||
return true
|
||||
end,
|
||||
-- Test the `automatic_factory` getter and setter.
|
||||
function()
|
||||
assert(type(screen.automatic_factory) == "boolean")
|
||||
local orig = screen.automatic_factory
|
||||
screen.automatic_factory = not orig
|
||||
assert(screen.automatic_factory ~= orig)
|
||||
assert(type(screen.automatic_factory) == "boolean")
|
||||
screen.automatic_factory = not screen.automatic_factory
|
||||
assert(screen.automatic_factory == orig)
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
|
|
|
@ -4,8 +4,22 @@ local runner = require("_runner")
|
|||
local wibox = require("wibox")
|
||||
local awful = require("awful")
|
||||
|
||||
-- Make sure we have at least two screens to test this on
|
||||
screen.fake_add(-100, -100, 50, 50)
|
||||
-- Make sure we have at least two screens to test this on.
|
||||
local origin_width = screen[1].geometry.width
|
||||
screen[1]:fake_resize(
|
||||
screen[1].geometry.x,
|
||||
screen[1].geometry.y,
|
||||
origin_width/2,
|
||||
screen[1].geometry.height
|
||||
)
|
||||
|
||||
screen.fake_add(
|
||||
screen[1].geometry.x+origin_width/2,
|
||||
screen[1].geometry.y,
|
||||
origin_width/2,
|
||||
screen[1].geometry.height
|
||||
)
|
||||
|
||||
assert(screen.count() == 2)
|
||||
|
||||
-- Each screen gets a wibox displaying our only_on_screen widget
|
||||
|
@ -116,7 +130,12 @@ table.insert(steps, function()
|
|||
for s in screen do
|
||||
assert(not widget_visible_on(s))
|
||||
end
|
||||
screen.fake_add(-100, -100, 50, 50)
|
||||
screen.fake_add(
|
||||
screen[1].geometry.x+origin_width/2,
|
||||
screen[1].geometry.y,
|
||||
origin_width/2,
|
||||
screen[1].geometry.height
|
||||
)
|
||||
return true
|
||||
end)
|
||||
table.insert(steps, function()
|
||||
|
|
Loading…
Reference in New Issue