2007-10-03 17:26:14 +02:00
|
|
|
/*
|
2007-09-14 11:35:17 +02:00
|
|
|
* screen.c - screen management
|
2007-10-03 17:26:14 +02:00
|
|
|
*
|
2009-07-10 16:26:49 +02:00
|
|
|
* Copyright © 2007-2009 Julien Danjou <julien@danjou.info>
|
2007-10-03 17:26:14 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*
|
2007-09-14 11:35:17 +02:00
|
|
|
*/
|
|
|
|
|
2019-11-21 15:43:15 +01:00
|
|
|
/** awesome screen API.
|
2016-04-04 10:02:46 +02:00
|
|
|
*
|
|
|
|
* Screen objects can be added and removed over time. To get a callback for all
|
|
|
|
* current and future screens, use `awful.screen.connect_for_each_screen`:
|
|
|
|
*
|
|
|
|
* awful.screen.connect_for_each_screen(function(s)
|
|
|
|
* -- do something
|
|
|
|
* end)
|
|
|
|
*
|
|
|
|
* It is also possible loop over all current screens using:
|
|
|
|
*
|
2016-05-16 05:44:09 +02:00
|
|
|
* for s in screen do
|
2016-04-04 10:02:46 +02:00
|
|
|
* -- do something
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* Most basic Awesome objects also have a screen property, see `mouse.screen`
|
|
|
|
* `client.screen`, `wibox.screen` and `tag.screen`.
|
2015-02-27 00:24:23 +01:00
|
|
|
*
|
|
|
|
* Furthermore to the classes described here, one can also use signals as
|
|
|
|
* described in @{signals}.
|
|
|
|
*
|
2019-06-27 08:14:17 +02:00
|
|
|
* @DOC_uml_nav_tables_screen_EXAMPLE@
|
|
|
|
*
|
2015-02-27 00:24:23 +01:00
|
|
|
* @author Julien Danjou <julien@danjou.info>
|
|
|
|
* @copyright 2008-2009 Julien Danjou
|
2019-06-06 08:40:00 +02:00
|
|
|
* @coreclassmod screen
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2014-03-30 20:07:48 +02:00
|
|
|
#include "objects/screen.h"
|
|
|
|
#include "banning.h"
|
|
|
|
#include "objects/client.h"
|
|
|
|
#include "objects/drawin.h"
|
2016-04-15 20:29:08 +02:00
|
|
|
#include "event.h"
|
2014-03-30 20:07:48 +02:00
|
|
|
|
2008-03-21 16:50:17 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include <xcb/xcb.h>
|
2008-09-18 16:03:05 +02:00
|
|
|
#include <xcb/xinerama.h>
|
2009-09-21 16:29:19 +02:00
|
|
|
#include <xcb/randr.h>
|
2008-03-21 16:50:17 +01:00
|
|
|
|
2018-04-22 08:07:31 +02:00
|
|
|
/* The XID that is used on fake screens. X11 guarantees that the top three bits
|
|
|
|
* of a valid XID are zero, so this will not clash with anything.
|
|
|
|
*/
|
|
|
|
#define FAKE_SCREEN_XID ((uint32_t) 0xffffffff)
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
/** 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
|
|
|
|
*/
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** 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.
|
2016-02-27 15:44:25 +01:00
|
|
|
* The primary screen can be accessed as `screen.primary`.
|
2015-02-27 00:24:23 +01:00
|
|
|
* Each screen has a set of properties.
|
|
|
|
*
|
2016-04-04 08:35:57 +02:00
|
|
|
*/
|
|
|
|
|
2016-06-04 17:39:14 +02:00
|
|
|
/**
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal primary_changed
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This signal is emitted when a new screen is added to the current setup.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal added
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This signal is emitted when a screen is removed from the setup.
|
|
|
|
* @signal removed
|
2019-12-01 07:53:12 +01:00
|
|
|
* @request tag screen removed granted When a screen is removed, `request::screen`
|
|
|
|
* is called on all screen tags to try to relocate them.
|
2016-06-04 17:39:14 +02:00
|
|
|
*/
|
|
|
|
|
2016-10-23 15:00:27 +02:00
|
|
|
/** This signal is emitted when the list of available screens changes.
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal list
|
2016-10-23 15:00:27 +02:00
|
|
|
*/
|
|
|
|
|
2016-10-23 15:01:12 +02:00
|
|
|
/** When 2 screens are swapped
|
|
|
|
* @tparam screen screen The other screen
|
|
|
|
* @tparam boolean is_source If self is the source or the destination of the swap
|
2018-05-28 20:02:49 +02:00
|
|
|
* @signal swapped
|
2016-10-23 15:01:12 +02:00
|
|
|
*/
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
/** 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.
|
|
|
|
*
|
2019-06-20 00:02:49 +02:00
|
|
|
* 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.
|
|
|
|
*
|
2018-08-06 22:43:01 +02:00
|
|
|
* @signal property::viewports
|
2019-06-20 00:02:49 +02:00
|
|
|
* @tparam table viewports A table containing all physical viewports.
|
2018-08-06 22:43:01 +02:00
|
|
|
* @see automatic_factory
|
|
|
|
*/
|
|
|
|
|
2016-04-04 08:35:57 +02:00
|
|
|
/**
|
|
|
|
* The primary screen.
|
|
|
|
*
|
|
|
|
* @tfield screen primary
|
|
|
|
*/
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
|
2016-04-04 08:35:57 +02:00
|
|
|
/**
|
|
|
|
* The screen coordinates.
|
|
|
|
*
|
2016-07-24 16:33:06 +02:00
|
|
|
* **Signal:**
|
|
|
|
*
|
|
|
|
* * *property::geometry*
|
|
|
|
*
|
2016-04-04 08:35:57 +02:00
|
|
|
* **Immutable:** true
|
2019-08-19 07:31:53 +02:00
|
|
|
*
|
|
|
|
* @DOC_screen_geometry_EXAMPLE@
|
|
|
|
*
|
2016-04-04 08:35:57 +02:00
|
|
|
* @property geometry
|
|
|
|
* @param table
|
|
|
|
* @tfield integer table.x The horizontal position
|
|
|
|
* @tfield integer table.y The vertical position
|
|
|
|
* @tfield integer table.width The width
|
|
|
|
* @tfield integer table.height The height
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2017-10-21 01:54:36 +02:00
|
|
|
* The internal screen number.
|
2016-04-04 08:35:57 +02:00
|
|
|
*
|
2017-10-21 01:54:36 +02:00
|
|
|
* * The indeces are a continuous sequence from 1 to `screen.count()`.
|
|
|
|
* * It is **NOT** related to the actual screen position relative to each
|
|
|
|
* other.
|
|
|
|
* * 1 is **NOT** necessarily the primary screen.
|
|
|
|
* * When screens are added and removed indices **CAN** change.
|
|
|
|
*
|
|
|
|
* If you really want to keep an array of screens you should use something
|
|
|
|
* along:
|
|
|
|
*
|
|
|
|
* local myscreens = setmetatable({}. {__mode="k"})
|
|
|
|
* myscreens[ screen[1] ] = "mydata"
|
|
|
|
*
|
|
|
|
* But it might be a better option to simply store the data directly in the
|
|
|
|
* screen object as:
|
|
|
|
*
|
|
|
|
* screen[1].mydata = "mydata"
|
|
|
|
*
|
|
|
|
* Remember that screens are also objects, so if you only want to store a simple
|
|
|
|
* property, you can do it directly:
|
|
|
|
*
|
|
|
|
* screen[1].answer = 42
|
2016-04-04 08:35:57 +02:00
|
|
|
*
|
|
|
|
* **Immutable:** true
|
|
|
|
* @property index
|
|
|
|
* @param integer
|
2017-10-21 01:54:36 +02:00
|
|
|
* @see screen
|
2016-04-04 08:35:57 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The screen workarea.
|
|
|
|
*
|
|
|
|
* The workarea is a subsection of the screen where clients can be placed. It
|
2016-05-08 08:10:35 +02:00
|
|
|
* usually excludes the toolbars (see `awful.wibar`) and dockable clients
|
2016-04-04 08:35:57 +02:00
|
|
|
* (see `client.dockable`) like WindowMaker DockAPP.
|
|
|
|
*
|
|
|
|
* It can be modified be altering the `wibox` or `client` struts.
|
|
|
|
*
|
|
|
|
* **Signal:**
|
|
|
|
*
|
|
|
|
* * *property::workarea*
|
|
|
|
*
|
2019-08-19 07:31:53 +02:00
|
|
|
* @DOC_screen_workarea_EXAMPLE@
|
|
|
|
*
|
2016-04-04 08:35:57 +02:00
|
|
|
* @property workarea
|
|
|
|
* @see client.struts
|
|
|
|
* @param table
|
|
|
|
* @tfield integer table.x The horizontal position
|
|
|
|
* @tfield integer table.y The vertical position
|
|
|
|
* @tfield integer table.width The width
|
|
|
|
* @tfield integer table.height The height
|
2015-02-27 00:24:23 +01:00
|
|
|
*/
|
|
|
|
|
2016-04-04 08:35:57 +02:00
|
|
|
|
2015-03-14 10:06:53 +01:00
|
|
|
/** Get the number of instances.
|
|
|
|
*
|
|
|
|
* @return The number of screen objects alive.
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct instances
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2016-04-07 06:50:54 +02:00
|
|
|
/* Set a __index metamethod for all screen instances.
|
2015-03-14 10:06:53 +01:00
|
|
|
* @tparam function cb The meta-method
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct set_index_miss_handler
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2016-04-07 06:50:54 +02:00
|
|
|
/* Set a __newindex metamethod for all screen instances.
|
2015-03-14 10:06:53 +01:00
|
|
|
* @tparam function cb The meta-method
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct set_newindex_miss_handler
|
2015-03-14 10:06:53 +01:00
|
|
|
*/
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
DO_ARRAY(xcb_randr_output_t, randr_output, DO_NOTHING);
|
|
|
|
|
2009-09-21 16:29:19 +02:00
|
|
|
struct screen_output_t
|
|
|
|
{
|
|
|
|
/** The XRandR names of the output */
|
|
|
|
char *name;
|
|
|
|
/** The size in millimeters */
|
|
|
|
uint32_t mm_width, mm_height;
|
2016-02-27 15:57:57 +01:00
|
|
|
/** The XID */
|
2016-04-12 13:26:17 +02:00
|
|
|
randr_output_array_t outputs;
|
2009-09-21 16:29:19 +02:00
|
|
|
};
|
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
static void
|
|
|
|
screen_output_wipe(screen_output_t *output)
|
|
|
|
{
|
2014-03-31 13:17:34 +02:00
|
|
|
p_delete(&output->name);
|
2019-06-17 05:54:02 +02:00
|
|
|
|
|
|
|
randr_output_array_wipe(&output->outputs);
|
2014-03-30 16:37:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
ARRAY_FUNCS(screen_output_t, screen_output, screen_output_wipe)
|
|
|
|
|
|
|
|
static lua_class_t screen_class;
|
|
|
|
LUA_OBJECT_FUNCS(screen_class, screen_t, screen)
|
|
|
|
|
2016-04-16 14:49:09 +02:00
|
|
|
/** Check if a screen is valid */
|
|
|
|
static bool
|
|
|
|
screen_checker(screen_t *s)
|
|
|
|
{
|
|
|
|
return s->valid;
|
|
|
|
}
|
|
|
|
|
2014-03-30 17:55:42 +02:00
|
|
|
/** Get a screen argument from the lua stack */
|
|
|
|
screen_t *
|
|
|
|
luaA_checkscreen(lua_State *L, int sidx)
|
|
|
|
{
|
|
|
|
if (lua_isnumber(L, sidx))
|
|
|
|
{
|
|
|
|
int screen = lua_tointeger(L, sidx);
|
|
|
|
if(screen < 1 || screen > globalconf.screens.len)
|
2018-06-24 21:34:20 +02:00
|
|
|
{
|
|
|
|
luaA_warn(L, "invalid screen number: %d (of %d existing)", screen, globalconf.screens.len);
|
|
|
|
lua_pushnil(L);
|
|
|
|
return NULL;
|
|
|
|
}
|
2014-03-30 17:55:42 +02:00
|
|
|
return globalconf.screens.tab[screen - 1];
|
|
|
|
} else
|
|
|
|
return luaA_checkudata(L, sidx, &screen_class);
|
|
|
|
}
|
|
|
|
|
2016-04-16 14:44:21 +02:00
|
|
|
static void
|
|
|
|
screen_deduplicate(lua_State *L, screen_array_t *screens)
|
|
|
|
{
|
|
|
|
/* Remove duplicate screens */
|
|
|
|
for(int first = 0; first < screens->len; first++) {
|
|
|
|
screen_t *first_screen = screens->tab[first];
|
|
|
|
for(int second = 0; second < screens->len; second++) {
|
|
|
|
screen_t *second_screen = screens->tab[second];
|
|
|
|
if (first == second)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (first_screen->geometry.width < second_screen->geometry.width
|
|
|
|
&& first_screen->geometry.height < second_screen->geometry.height)
|
|
|
|
/* Don't drop a smaller screen */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (first_screen->geometry.x == second_screen->geometry.x
|
|
|
|
&& first_screen->geometry.y == second_screen->geometry.y) {
|
|
|
|
/* Found a duplicate */
|
|
|
|
first_screen->geometry.width = MAX(first_screen->geometry.width, second_screen->geometry.width);
|
|
|
|
first_screen->geometry.height = MAX(first_screen->geometry.height, second_screen->geometry.height);
|
|
|
|
|
|
|
|
screen_array_take(screens, second);
|
|
|
|
luaA_object_unref(L, second_screen);
|
|
|
|
|
|
|
|
/* Restart the search */
|
|
|
|
screen_deduplicate(L, screens);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
/** 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;
|
2019-06-19 23:34:22 +02:00
|
|
|
int id;
|
2018-08-06 22:43:01 +02:00
|
|
|
struct viewport_t *next;
|
|
|
|
screen_t *screen;
|
2019-06-17 05:54:02 +02:00
|
|
|
screen_output_array_t outputs;
|
2018-08-06 22:43:01 +02:00
|
|
|
} viewport_t;
|
|
|
|
|
|
|
|
static viewport_t *first_screen_viewport = NULL;
|
|
|
|
static viewport_t *last_screen_viewport = NULL;
|
2019-06-19 23:34:22 +02:00
|
|
|
static int screen_area_gid = 1;
|
2018-08-06 22:43:01 +02:00
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
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);
|
|
|
|
|
2019-06-20 00:02:49 +02:00
|
|
|
lua_pushstring(L, "viewport_id");
|
|
|
|
lua_pushinteger(L, a->id);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
/* Add to the outputs */
|
|
|
|
lua_rawseti(L, -2, count++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
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);
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
/* Add the outputs table to the arguments */
|
|
|
|
lua_pushstring(L, "outputs");
|
|
|
|
luaA_viewport_get_outputs(L, a);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
|
2019-06-19 23:34:22 +02:00
|
|
|
/* Add an identifier to better detect when screens are removed */
|
|
|
|
lua_pushstring(L, "id");
|
|
|
|
lua_pushinteger(L, a->id);
|
|
|
|
lua_settable(L, -3);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
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);
|
|
|
|
|
2019-06-20 00:02:49 +02:00
|
|
|
luaA_class_emit_signal(L, &screen_class, "property::_viewports", 1);
|
2018-08-06 22:43:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-06-19 23:34:22 +02:00
|
|
|
node->id = screen_area_gid++;
|
2018-08-06 22:43:01 +02:00
|
|
|
node->next = NULL;
|
|
|
|
node->screen = NULL;
|
|
|
|
node->marked = true;
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
screen_output_array_init(&node->outputs);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
if (!first_screen_viewport) {
|
|
|
|
first_screen_viewport = node;
|
|
|
|
last_screen_viewport = node;
|
|
|
|
} else {
|
|
|
|
last_screen_viewport->next = node;
|
2019-06-17 05:54:02 +02:00
|
|
|
last_screen_viewport = node;
|
2018-08-06 22:43:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2019-06-17 05:54:02 +02:00
|
|
|
|
|
|
|
foreach(existing_screen, globalconf.screens)
|
|
|
|
if ((*existing_screen)->viewport == cur)
|
|
|
|
(*existing_screen)->viewport = NULL;
|
|
|
|
|
|
|
|
screen_output_array_wipe(&cur->outputs);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
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;
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
foreach(existing_screen, globalconf.screens)
|
|
|
|
if ((*existing_screen)->viewport == tmp)
|
|
|
|
(*existing_screen)->viewport = NULL;
|
|
|
|
|
|
|
|
screen_output_array_wipe(&tmp->outputs);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
free(tmp);
|
|
|
|
} else
|
|
|
|
cur = cur->next;
|
|
|
|
|
|
|
|
} while(cur);
|
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
static screen_t *
|
|
|
|
screen_add(lua_State *L, screen_array_t *screens)
|
2008-09-18 16:03:05 +02:00
|
|
|
{
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_t *new_screen = screen_new(L);
|
|
|
|
luaA_object_ref(L, -1);
|
|
|
|
screen_array_append(screens, new_screen);
|
2018-04-22 08:07:31 +02:00
|
|
|
new_screen->xid = XCB_NONE;
|
2019-07-11 02:59:53 +02:00
|
|
|
new_screen->lifecycle = SCREEN_LIFECYCLE_USER;
|
2016-04-15 19:59:36 +02:00
|
|
|
return new_screen;
|
2013-09-27 23:07:33 +02:00
|
|
|
}
|
|
|
|
|
2019-07-26 03:53:21 +02:00
|
|
|
static void
|
|
|
|
screen_wipe(screen_t *c)
|
|
|
|
{
|
|
|
|
if (c->name) {
|
|
|
|
free(c->name);
|
|
|
|
c->name = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
/* Monitors were introduced in RandR 1.5 */
|
|
|
|
#ifdef XCB_RANDR_GET_MONITORS
|
2019-06-17 05:54:02 +02:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
static void
|
|
|
|
screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
|
2016-04-12 13:26:17 +02:00
|
|
|
{
|
|
|
|
xcb_randr_get_monitors_cookie_t monitors_c = xcb_randr_get_monitors(globalconf.connection, globalconf.screen->root, 1);
|
|
|
|
xcb_randr_get_monitors_reply_t *monitors_r = xcb_randr_get_monitors_reply(globalconf.connection, monitors_c, NULL);
|
|
|
|
xcb_randr_monitor_info_iterator_t monitor_iter;
|
|
|
|
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
if (monitors_r == NULL) {
|
|
|
|
warn("RANDR GetMonitors failed; this should not be possible");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
for(monitor_iter = xcb_randr_get_monitors_monitors_iterator(monitors_r);
|
|
|
|
monitor_iter.rem; xcb_randr_monitor_info_next(&monitor_iter))
|
|
|
|
{
|
|
|
|
screen_t *new_screen;
|
|
|
|
|
|
|
|
if(!xcb_randr_monitor_info_outputs_length(monitor_iter.data))
|
|
|
|
continue;
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
screen_output_t output = screen_get_randr_output(L, &monitor_iter);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport_t *viewport = viewport_add(L,
|
|
|
|
monitor_iter.data->x,
|
|
|
|
monitor_iter.data->y,
|
|
|
|
monitor_iter.data->width,
|
|
|
|
monitor_iter.data->height
|
|
|
|
);
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
screen_output_array_append(&viewport->outputs, output);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
if (globalconf.ignore_screens)
|
|
|
|
continue;
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
new_screen = screen_add(L, screens);
|
2019-07-11 02:59:53 +02:00
|
|
|
new_screen->lifecycle |= SCREEN_LIFECYCLE_C;
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport->screen = new_screen;
|
2019-06-17 05:54:02 +02:00
|
|
|
new_screen->viewport = viewport;
|
2016-04-12 13:26:17 +02:00
|
|
|
new_screen->geometry.x = monitor_iter.data->x;
|
|
|
|
new_screen->geometry.y = monitor_iter.data->y;
|
2016-04-15 20:29:08 +02:00
|
|
|
new_screen->geometry.width = monitor_iter.data->width;
|
|
|
|
new_screen->geometry.height = monitor_iter.data->height;
|
|
|
|
new_screen->xid = monitor_iter.data->name;
|
2016-04-12 13:26:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
p_delete(&monitors_r);
|
|
|
|
}
|
|
|
|
#else
|
2016-04-15 19:59:36 +02:00
|
|
|
static void
|
|
|
|
screen_scan_randr_monitors(lua_State *L, screen_array_t *screens)
|
2016-04-12 13:26:17 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
static void
|
|
|
|
screen_scan_randr_crtcs(lua_State *L, screen_array_t *screens)
|
2016-04-12 13:26:17 +02:00
|
|
|
{
|
|
|
|
/* A quick XRandR recall:
|
|
|
|
* You have CRTC that manages a part of a SCREEN.
|
|
|
|
* Each CRTC can draw stuff on one or more OUTPUT. */
|
|
|
|
xcb_randr_get_screen_resources_cookie_t screen_res_c = xcb_randr_get_screen_resources(globalconf.connection, globalconf.screen->root);
|
|
|
|
xcb_randr_get_screen_resources_reply_t *screen_res_r = xcb_randr_get_screen_resources_reply(globalconf.connection, screen_res_c, NULL);
|
|
|
|
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
if (screen_res_r == NULL) {
|
|
|
|
warn("RANDR GetScreenResources failed; this should not be possible");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
/* We go through CRTC, and build a screen for each one. */
|
|
|
|
xcb_randr_crtc_t *randr_crtcs = xcb_randr_get_screen_resources_crtcs(screen_res_r);
|
|
|
|
|
|
|
|
for(int i = 0; i < screen_res_r->num_crtcs; i++)
|
|
|
|
{
|
|
|
|
/* Get info on the output crtc */
|
|
|
|
xcb_randr_get_crtc_info_cookie_t crtc_info_c = xcb_randr_get_crtc_info(globalconf.connection, randr_crtcs[i], XCB_CURRENT_TIME);
|
|
|
|
xcb_randr_get_crtc_info_reply_t *crtc_info_r = xcb_randr_get_crtc_info_reply(globalconf.connection, crtc_info_c, NULL);
|
|
|
|
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
if(!crtc_info_r) {
|
|
|
|
warn("RANDR GetCRTCInfo failed; this should not be possible");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
/* If CRTC has no OUTPUT, ignore it */
|
|
|
|
if(!xcb_randr_get_crtc_info_outputs_length(crtc_info_r))
|
|
|
|
continue;
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport_t *viewport = viewport_add(L,
|
|
|
|
crtc_info_r->x,
|
|
|
|
crtc_info_r->y,
|
|
|
|
crtc_info_r->width,
|
|
|
|
crtc_info_r->height
|
|
|
|
);
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
screen_get_randr_crtcs_outputs(L, crtc_info_r, &viewport->outputs);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
if (globalconf.ignore_screens)
|
|
|
|
{
|
|
|
|
p_delete(&crtc_info_r);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
/* Prepare the new screen */
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_t *new_screen = screen_add(L, screens);
|
2019-07-11 02:59:53 +02:00
|
|
|
new_screen->lifecycle |= SCREEN_LIFECYCLE_C;
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport->screen = new_screen;
|
2019-06-17 05:54:02 +02:00
|
|
|
new_screen->viewport = viewport;
|
2016-04-12 13:26:17 +02:00
|
|
|
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;
|
2016-04-15 20:29:08 +02:00
|
|
|
new_screen->xid = randr_crtcs[i];
|
2016-04-12 13:26:17 +02:00
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
/* Detect the older NVIDIA blobs */
|
|
|
|
foreach(output, new_screen->viewport->outputs) {
|
|
|
|
if (A_STREQ(output->name, "default")) {
|
2016-04-15 20:05:03 +02:00
|
|
|
/* non RandR 1.2+ X driver don't return any usable multihead
|
|
|
|
* data. I'm looking at you, nvidia binary blob!
|
|
|
|
*/
|
|
|
|
warn("Ignoring RandR, only a compatibility layer is present.");
|
|
|
|
|
|
|
|
/* Get rid of the screens that we already created */
|
|
|
|
foreach(screen, *screens)
|
|
|
|
luaA_object_unref(L, *screen);
|
2019-06-17 05:54:02 +02:00
|
|
|
|
2016-04-15 20:05:03 +02:00
|
|
|
screen_array_wipe(screens);
|
|
|
|
screen_array_init(screens);
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
p_delete(&screen_res_r);
|
|
|
|
|
2016-04-15 20:05:03 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-04-12 13:26:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
p_delete(&crtc_info_r);
|
|
|
|
}
|
|
|
|
|
|
|
|
p_delete(&screen_res_r);
|
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
static void
|
|
|
|
screen_scan_randr(lua_State *L, screen_array_t *screens)
|
2008-09-18 16:03:05 +02:00
|
|
|
{
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
const xcb_query_extension_reply_t *extension_reply;
|
2016-04-15 19:59:36 +02:00
|
|
|
xcb_randr_query_version_reply_t *version_reply;
|
|
|
|
uint32_t major_version;
|
|
|
|
uint32_t minor_version;
|
2016-02-27 15:57:57 +01:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
/* Check for extension before checking for XRandR */
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
extension_reply = xcb_get_extension_data(globalconf.connection, &xcb_randr_id);
|
|
|
|
if(!extension_reply || !extension_reply->present)
|
2016-04-15 19:59:36 +02:00
|
|
|
return;
|
2010-09-18 16:01:44 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
version_reply =
|
|
|
|
xcb_randr_query_version_reply(globalconf.connection,
|
|
|
|
xcb_randr_query_version(globalconf.connection, 1, 5), 0);
|
|
|
|
if(!version_reply)
|
|
|
|
return;
|
2016-02-27 15:57:57 +01:00
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
major_version = version_reply->major_version;
|
|
|
|
minor_version = version_reply->minor_version;
|
|
|
|
p_delete(&version_reply);
|
2016-02-27 15:57:57 +01:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
/* Do we agree on a supported version? */
|
|
|
|
if (major_version != 1 || minor_version < 2)
|
|
|
|
return;
|
2008-09-18 16:03:05 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
globalconf.have_randr_13 = minor_version >= 3;
|
2016-04-15 20:29:08 +02:00
|
|
|
#if XCB_RANDR_MAJOR_VERSION > 1 || XCB_RANDR_MINOR_VERSION >= 5
|
|
|
|
globalconf.have_randr_15 = minor_version >= 5;
|
|
|
|
#else
|
|
|
|
globalconf.have_randr_15 = false;
|
|
|
|
#endif
|
2009-09-21 16:29:19 +02:00
|
|
|
|
2016-04-10 13:39:27 +02:00
|
|
|
/* We want to know when something changes */
|
|
|
|
xcb_randr_select_input(globalconf.connection,
|
|
|
|
globalconf.screen->root,
|
|
|
|
XCB_RANDR_NOTIFY_MASK_OUTPUT_CHANGE);
|
|
|
|
|
2016-04-15 20:29:08 +02:00
|
|
|
if (globalconf.have_randr_15)
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_scan_randr_monitors(L, screens);
|
|
|
|
else
|
|
|
|
screen_scan_randr_crtcs(L, screens);
|
2016-07-31 16:52:44 +02:00
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
if (screens->len == 0 && !globalconf.ignore_screens)
|
2016-07-31 16:52:44 +02:00
|
|
|
{
|
|
|
|
/* Scanning failed, disable randr again */
|
|
|
|
xcb_randr_select_input(globalconf.connection,
|
|
|
|
globalconf.screen->root,
|
|
|
|
0);
|
|
|
|
globalconf.have_randr_13 = false;
|
|
|
|
globalconf.have_randr_15 = false;
|
|
|
|
}
|
2010-07-21 14:42:45 +02:00
|
|
|
}
|
2009-09-21 16:29:19 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
static void
|
|
|
|
screen_scan_xinerama(lua_State *L, screen_array_t *screens)
|
2010-07-21 14:42:45 +02:00
|
|
|
{
|
2016-04-15 19:59:36 +02:00
|
|
|
bool xinerama_is_active;
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
const xcb_query_extension_reply_t *extension_reply;
|
2016-04-15 19:59:36 +02:00
|
|
|
xcb_xinerama_is_active_reply_t *xia;
|
|
|
|
xcb_xinerama_query_screens_reply_t *xsq;
|
|
|
|
xcb_xinerama_screen_info_t *xsi;
|
|
|
|
int xinerama_screen_number;
|
2010-08-16 14:20:45 +02:00
|
|
|
|
2010-07-21 14:42:45 +02:00
|
|
|
/* Check for extension before checking for Xinerama */
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
extension_reply = xcb_get_extension_data(globalconf.connection, &xcb_xinerama_id);
|
|
|
|
if(!extension_reply || !extension_reply->present)
|
2016-04-15 19:59:36 +02:00
|
|
|
return;
|
2009-09-21 16:29:19 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
xia = xcb_xinerama_is_active_reply(globalconf.connection, xcb_xinerama_is_active(globalconf.connection), NULL);
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
xinerama_is_active = xia && xia->state;
|
2016-04-15 19:59:36 +02:00
|
|
|
p_delete(&xia);
|
|
|
|
if(!xinerama_is_active)
|
|
|
|
return;
|
2009-09-21 16:29:19 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
xsq = xcb_xinerama_query_screens_reply(globalconf.connection,
|
|
|
|
xcb_xinerama_query_screens_unchecked(globalconf.connection),
|
|
|
|
NULL);
|
2009-09-21 16:29:19 +02:00
|
|
|
|
Handle unexpected XCB failures (#1260)
We have many places where we are sending an XCB request and expect an
answer where the protocol guarantees that no error can occur and we are
sure to get an answer. However, for example if the X11 server crashes,
these places can still fail. This commit tries to handle failures at all
these places.
I went through the code and tried to add missing error checking (well,
NULL-pointer-checking) to all affected places.
In most cases these errors are just silently ignored. The exception is
in screen querying during startup. If, for example, querying RandR info
fails, we will fall back to Xinerama or zaphod mode. This is serious
enough that it warrants a warning. In most cases, we should exit shortly
afterwards anyway, because, as explained above, these requests should
only fail when our connection to the X11 server breaks.
References: https://github.com/awesomeWM/awesome/issues/1205#issuecomment-265869874
Signed-off-by: Uli Schlachter <psychon@znc.in>
2016-12-10 00:48:53 +01:00
|
|
|
if(!xsq) {
|
|
|
|
warn("Xinerama QueryScreens failed; this should not be possible");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
xsi = xcb_xinerama_query_screens_screen_info(xsq);
|
|
|
|
xinerama_screen_number = xcb_xinerama_query_screens_screen_info_length(xsq);
|
2010-07-21 14:42:45 +02:00
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
for(int screen = 0; screen < xinerama_screen_number; screen++)
|
|
|
|
{
|
2018-08-06 22:43:01 +02:00
|
|
|
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;
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_t *s = screen_add(L, screens);
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport->screen = s;
|
2019-06-17 05:54:02 +02:00
|
|
|
s->viewport = viewport;
|
2019-07-11 02:59:53 +02:00
|
|
|
s->lifecycle |= SCREEN_LIFECYCLE_C;
|
2016-04-15 19:59:36 +02:00
|
|
|
s->geometry.x = xsi[screen].x_org;
|
|
|
|
s->geometry.y = xsi[screen].y_org;
|
|
|
|
s->geometry.width = xsi[screen].width;
|
|
|
|
s->geometry.height = xsi[screen].height;
|
2010-07-21 14:42:45 +02:00
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
p_delete(&xsq);
|
2010-07-21 14:42:45 +02:00
|
|
|
}
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
static void screen_scan_x11(lua_State *L, screen_array_t *screens)
|
2010-07-21 14:42:45 +02:00
|
|
|
{
|
2010-08-16 14:10:58 +02:00
|
|
|
xcb_screen_t *xcb_screen = globalconf.screen;
|
2018-08-06 22:43:01 +02:00
|
|
|
|
|
|
|
viewport_t *viewport = viewport_add(L,
|
|
|
|
0,
|
|
|
|
0,
|
|
|
|
xcb_screen->width_in_pixels,
|
|
|
|
xcb_screen->height_in_pixels
|
|
|
|
);
|
|
|
|
|
|
|
|
if (globalconf.ignore_screens)
|
|
|
|
return;
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_t *s = screen_add(L, screens);
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport->screen = s;
|
2019-07-11 02:59:53 +02:00
|
|
|
s->lifecycle |= SCREEN_LIFECYCLE_C;
|
2019-06-17 05:54:02 +02:00
|
|
|
s->viewport = viewport;
|
2014-03-30 16:37:19 +02:00
|
|
|
s->geometry.x = 0;
|
|
|
|
s->geometry.y = 0;
|
|
|
|
s->geometry.width = xcb_screen->width_in_pixels;
|
|
|
|
s->geometry.height = xcb_screen->height_in_pixels;
|
2010-07-21 14:42:45 +02:00
|
|
|
}
|
|
|
|
|
2016-05-08 16:30:37 +02:00
|
|
|
static void
|
|
|
|
screen_added(lua_State *L, screen_t *screen)
|
|
|
|
{
|
|
|
|
screen->workarea = screen->geometry;
|
|
|
|
screen->valid = true;
|
|
|
|
luaA_object_push(L, screen);
|
2019-07-11 05:14:33 +02:00
|
|
|
luaA_object_emit_signal(L, -1, "_added", 0);
|
2016-05-08 16:30:37 +02:00
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2010-07-21 14:42:45 +02:00
|
|
|
void
|
2018-08-06 22:43:01 +02:00
|
|
|
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)
|
2010-07-21 14:42:45 +02:00
|
|
|
{
|
2016-04-15 19:59:36 +02:00
|
|
|
lua_State *L;
|
|
|
|
|
|
|
|
L = globalconf_get_lua_State();
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
monitor_unmark();
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
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);
|
2018-08-06 22:43:01 +02:00
|
|
|
|
|
|
|
check(globalconf.screens.len > 0 || globalconf.ignore_screens);
|
2016-04-15 19:59:36 +02:00
|
|
|
|
2016-04-16 14:44:21 +02:00
|
|
|
screen_deduplicate(L, &globalconf.screens);
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
foreach(screen, globalconf.screens) {
|
2016-05-08 16:30:37 +02:00
|
|
|
screen_added(L, *screen);
|
2016-04-15 19:59:36 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport_purge();
|
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
viewports_notify(L);
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_update_primary();
|
2008-09-18 16:03:05 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
/** 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;
|
|
|
|
}
|
|
|
|
|
2016-04-16 14:58:00 +02:00
|
|
|
/* Called when a screen is removed, removes references to the old screen */
|
|
|
|
static void
|
|
|
|
screen_removed(lua_State *L, int sidx)
|
|
|
|
{
|
|
|
|
screen_t *screen = luaA_checkudata(L, sidx, &screen_class);
|
|
|
|
|
|
|
|
luaA_object_emit_signal(L, sidx, "removed", 0);
|
|
|
|
|
|
|
|
if (globalconf.primary_screen == screen)
|
|
|
|
globalconf.primary_screen = NULL;
|
|
|
|
|
|
|
|
foreach(c, globalconf.clients) {
|
|
|
|
if((*c)->screen == screen)
|
|
|
|
screen_client_moveto(*c, screen_getbycoord(
|
|
|
|
(*c)->geometry.x, (*c)->geometry.y), false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
void screen_cleanup(void)
|
|
|
|
{
|
|
|
|
while(globalconf.screens.len)
|
|
|
|
screen_array_take(&globalconf.screens, 0);
|
|
|
|
|
|
|
|
monitor_unmark();
|
|
|
|
viewport_purge();
|
|
|
|
}
|
|
|
|
|
2016-04-16 17:37:04 +02:00
|
|
|
static void
|
|
|
|
screen_modified(screen_t *existing_screen, screen_t *other_screen)
|
|
|
|
{
|
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
|
|
|
|
if(!AREA_EQUAL(existing_screen->geometry, other_screen->geometry)) {
|
2016-07-24 16:32:12 +02:00
|
|
|
area_t old_geometry = existing_screen->geometry;
|
2016-04-16 17:37:04 +02:00
|
|
|
existing_screen->geometry = other_screen->geometry;
|
|
|
|
luaA_object_push(L, existing_screen);
|
2016-07-24 16:32:12 +02:00
|
|
|
luaA_pusharea(L, old_geometry);
|
|
|
|
luaA_object_emit_signal(L, -2, "property::geometry", 1);
|
2016-04-16 17:37:04 +02:00
|
|
|
lua_pop(L, 1);
|
2016-05-08 16:30:37 +02:00
|
|
|
screen_update_workarea(existing_screen);
|
2016-04-16 17:37:04 +02:00
|
|
|
}
|
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
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];
|
2016-04-16 17:37:04 +02:00
|
|
|
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 */
|
2019-06-17 05:54:02 +02:00
|
|
|
if(existing_screen->viewport || other_screen->viewport) {
|
|
|
|
viewport_t *tmp = other_screen->viewport;
|
|
|
|
other_screen->viewport = existing_screen->viewport;
|
2016-04-16 17:37:04 +02:00
|
|
|
|
2019-06-17 05:54:02 +02:00
|
|
|
existing_screen->viewport = tmp;
|
|
|
|
|
|
|
|
if(outputs_changed) {
|
|
|
|
luaA_object_push(L, existing_screen);
|
2019-06-20 00:02:49 +02:00
|
|
|
luaA_object_emit_signal(L, -1, "property::_outputs", 0);
|
2019-06-17 05:54:02 +02:00
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
2016-04-16 17:37:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-18 10:42:29 +01:00
|
|
|
static gboolean
|
|
|
|
screen_refresh(gpointer unused)
|
2016-04-15 20:29:08 +02:00
|
|
|
{
|
2019-02-18 10:42:29 +01:00
|
|
|
globalconf.screen_refresh_pending = false;
|
2016-04-15 20:29:08 +02:00
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
monitor_unmark();
|
|
|
|
|
2016-04-15 20:29:08 +02:00
|
|
|
screen_array_t new_screens;
|
2017-11-19 15:53:07 +01:00
|
|
|
screen_array_t removed_screens;
|
2016-04-15 20:29:08 +02:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2016-10-23 15:00:27 +02:00
|
|
|
bool list_changed = false;
|
2016-04-15 20:29:08 +02:00
|
|
|
|
|
|
|
screen_array_init(&new_screens);
|
|
|
|
if (globalconf.have_randr_15)
|
|
|
|
screen_scan_randr_monitors(L, &new_screens);
|
|
|
|
else
|
|
|
|
screen_scan_randr_crtcs(L, &new_screens);
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
viewport_purge();
|
|
|
|
|
|
|
|
viewports_notify(L);
|
|
|
|
|
2016-04-16 14:44:21 +02:00
|
|
|
screen_deduplicate(L, &new_screens);
|
|
|
|
|
2018-06-28 15:36:53 +02:00
|
|
|
/* Running without any screens at all is no fun. */
|
|
|
|
if (new_screens.len == 0)
|
|
|
|
screen_scan_x11(L, &new_screens);
|
|
|
|
|
2016-04-15 20:29:08 +02:00
|
|
|
/* Add new screens */
|
|
|
|
foreach(new_screen, new_screens) {
|
|
|
|
bool found = false;
|
|
|
|
foreach(old_screen, globalconf.screens)
|
|
|
|
found |= (*new_screen)->xid == (*old_screen)->xid;
|
|
|
|
if(!found) {
|
|
|
|
screen_array_append(&globalconf.screens, *new_screen);
|
2016-05-08 16:30:37 +02:00
|
|
|
screen_added(L, *new_screen);
|
2016-04-15 20:29:08 +02:00
|
|
|
/* Get an extra reference since both new_screens and
|
|
|
|
* globalconf.screens reference this screen now */
|
2016-05-08 16:30:37 +02:00
|
|
|
luaA_object_push(L, *new_screen);
|
2016-04-15 20:29:08 +02:00
|
|
|
luaA_object_ref(L, -1);
|
2016-10-23 15:00:27 +02:00
|
|
|
|
|
|
|
list_changed = true;
|
2016-04-15 20:29:08 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove screens which are gone */
|
2017-11-19 15:53:07 +01:00
|
|
|
screen_array_init(&removed_screens);
|
2016-04-15 20:29:08 +02:00
|
|
|
for(int i = 0; i < globalconf.screens.len; i++) {
|
|
|
|
screen_t *old_screen = globalconf.screens.tab[i];
|
2018-04-22 08:07:31 +02:00
|
|
|
bool found = old_screen->xid == FAKE_SCREEN_XID;
|
2019-07-11 02:59:53 +02:00
|
|
|
|
2016-04-15 20:29:08 +02:00
|
|
|
foreach(new_screen, new_screens)
|
|
|
|
found |= (*new_screen)->xid == old_screen->xid;
|
2019-07-11 02:59:53 +02:00
|
|
|
|
|
|
|
if(old_screen->lifecycle & SCREEN_LIFECYCLE_C && !found) {
|
2016-04-16 14:58:00 +02:00
|
|
|
screen_array_take(&globalconf.screens, i);
|
|
|
|
i--;
|
|
|
|
|
2017-11-19 15:53:07 +01:00
|
|
|
screen_array_append(&removed_screens, old_screen);
|
2016-10-23 15:00:27 +02:00
|
|
|
list_changed = true;
|
2016-04-15 20:29:08 +02:00
|
|
|
}
|
|
|
|
}
|
2017-11-19 15:53:07 +01:00
|
|
|
foreach(old_screen, removed_screens) {
|
|
|
|
luaA_object_push(L, *old_screen);
|
|
|
|
screen_removed(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
(*old_screen)->valid = false;
|
|
|
|
luaA_object_unref(L, *old_screen);
|
|
|
|
}
|
|
|
|
screen_array_wipe(&removed_screens);
|
2016-04-15 20:29:08 +02:00
|
|
|
|
2016-04-16 17:37:04 +02:00
|
|
|
/* Update changed screens */
|
|
|
|
foreach(existing_screen, globalconf.screens)
|
|
|
|
foreach(new_screen, new_screens)
|
|
|
|
if((*existing_screen)->xid == (*new_screen)->xid)
|
|
|
|
screen_modified(*existing_screen, *new_screen);
|
|
|
|
|
2016-04-15 20:29:08 +02:00
|
|
|
foreach(screen, new_screens)
|
|
|
|
luaA_object_unref(L, *screen);
|
|
|
|
screen_array_wipe(&new_screens);
|
|
|
|
|
|
|
|
screen_update_primary();
|
2016-10-23 15:00:27 +02:00
|
|
|
|
|
|
|
if (list_changed)
|
|
|
|
luaA_class_emit_signal(L, &screen_class, "list", 0);
|
2019-02-18 10:42:29 +01:00
|
|
|
|
|
|
|
return G_SOURCE_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
screen_schedule_refresh(void)
|
|
|
|
{
|
|
|
|
if(globalconf.screen_refresh_pending || !globalconf.have_randr_13)
|
|
|
|
return;
|
|
|
|
|
|
|
|
globalconf.screen_refresh_pending = true;
|
|
|
|
g_idle_add_full(G_PRIORITY_LOW, screen_refresh, NULL, NULL);
|
2016-04-15 20:29:08 +02:00
|
|
|
}
|
|
|
|
|
2015-10-10 20:10:23 +02:00
|
|
|
/** Return the squared distance of the given screen to the coordinates.
|
|
|
|
* \param screen The screen
|
|
|
|
* \param x X coordinate
|
|
|
|
* \param y Y coordinate
|
|
|
|
* \return Squared distance of the point to the screen.
|
|
|
|
*/
|
|
|
|
static unsigned int
|
|
|
|
screen_get_distance_squared(screen_t *s, int x, int y)
|
|
|
|
{
|
|
|
|
int sx = s->geometry.x, sy = s->geometry.y;
|
|
|
|
int sheight = s->geometry.height, swidth = s->geometry.width;
|
|
|
|
unsigned int dist_x, dist_y;
|
|
|
|
|
|
|
|
/* Calculate distance in X coordinate */
|
|
|
|
if (x < sx)
|
|
|
|
dist_x = sx - x;
|
|
|
|
else if (x < sx + swidth)
|
|
|
|
dist_x = 0;
|
|
|
|
else
|
|
|
|
dist_x = x - sx - swidth;
|
|
|
|
|
|
|
|
/* Calculate distance in Y coordinate */
|
|
|
|
if (y < sy)
|
|
|
|
dist_y = sy - y;
|
|
|
|
else if (y < sy + sheight)
|
|
|
|
dist_y = 0;
|
|
|
|
else
|
|
|
|
dist_y = y - sy - sheight;
|
|
|
|
|
|
|
|
return dist_x * dist_x + dist_y * dist_y;
|
|
|
|
}
|
|
|
|
|
2015-07-21 15:16:37 +02:00
|
|
|
/** Return the first screen number where the coordinates belong to.
|
2008-09-03 22:37:43 +02:00
|
|
|
* \param x X coordinate
|
|
|
|
* \param y Y coordinate
|
2009-04-17 16:14:09 +02:00
|
|
|
* \return Screen pointer or screen param if no match or no multi-head.
|
2008-09-03 22:37:43 +02:00
|
|
|
*/
|
2009-04-17 16:14:09 +02:00
|
|
|
screen_t *
|
2010-08-16 14:25:12 +02:00
|
|
|
screen_getbycoord(int x, int y)
|
2008-09-03 22:37:43 +02:00
|
|
|
{
|
2009-04-17 16:14:09 +02:00
|
|
|
foreach(s, globalconf.screens)
|
2015-07-21 15:16:37 +02:00
|
|
|
if(screen_coord_in_screen(*s, x, y))
|
2014-03-30 16:37:19 +02:00
|
|
|
return *s;
|
2008-09-03 22:37:43 +02:00
|
|
|
|
2015-07-25 20:12:45 +02:00
|
|
|
/* No screen found, find nearest screen. */
|
2016-04-04 20:13:27 +02:00
|
|
|
screen_t *nearest_screen = NULL;
|
2015-10-10 20:10:23 +02:00
|
|
|
unsigned int nearest_dist = UINT_MAX;
|
2015-07-25 20:12:45 +02:00
|
|
|
foreach(s, globalconf.screens)
|
|
|
|
{
|
2015-10-10 20:10:23 +02:00
|
|
|
unsigned int dist_sq = screen_get_distance_squared(*s, x, y);
|
|
|
|
if(dist_sq < nearest_dist)
|
2015-07-25 20:12:45 +02:00
|
|
|
{
|
2015-10-10 20:10:23 +02:00
|
|
|
nearest_dist = dist_sq;
|
|
|
|
nearest_screen = *s;
|
2015-07-25 20:12:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nearest_screen;
|
2008-09-03 22:37:43 +02:00
|
|
|
}
|
|
|
|
|
2015-07-21 15:16:37 +02:00
|
|
|
/** Are the given coordinates in a given screen?
|
|
|
|
* \param screen The logical screen number.
|
|
|
|
* \param x X coordinate
|
|
|
|
* \param y Y coordinate
|
|
|
|
* \return True if the X/Y coordinates are in the given screen.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
screen_coord_in_screen(screen_t *s, int x, int y)
|
|
|
|
{
|
2015-07-25 20:12:45 +02:00
|
|
|
return (x >= s->geometry.x && x < s->geometry.x + s->geometry.width)
|
|
|
|
&& (y >= s->geometry.y && y < s->geometry.y + s->geometry.height);
|
2008-09-03 22:37:43 +02:00
|
|
|
}
|
|
|
|
|
2016-09-02 21:15:00 +02:00
|
|
|
/** Is there any overlap between the given geometry and a given screen?
|
|
|
|
* \param screen The logical screen number.
|
|
|
|
* \param geom The geometry
|
|
|
|
* \return True if there is any overlap between the geometry and a given screen.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
screen_area_in_screen(screen_t *s, area_t geom)
|
|
|
|
{
|
2018-08-06 22:43:01 +02:00
|
|
|
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);
|
2016-09-02 21:15:00 +02:00
|
|
|
}
|
|
|
|
|
2016-05-08 16:30:37 +02:00
|
|
|
void screen_update_workarea(screen_t *screen)
|
2007-09-14 11:35:17 +02:00
|
|
|
{
|
2009-04-17 16:14:09 +02:00
|
|
|
area_t area = screen->geometry;
|
2008-09-03 22:15:14 +02:00
|
|
|
uint16_t top = 0, bottom = 0, left = 0, right = 0;
|
2007-09-14 11:35:17 +02:00
|
|
|
|
2009-08-18 15:23:05 +02:00
|
|
|
#define COMPUTE_STRUT(o) \
|
|
|
|
{ \
|
|
|
|
if((o)->strut.top_start_x || (o)->strut.top_end_x || (o)->strut.top) \
|
|
|
|
{ \
|
|
|
|
if((o)->strut.top) \
|
|
|
|
top = MAX(top, (o)->strut.top); \
|
|
|
|
else \
|
|
|
|
top = MAX(top, ((o)->geometry.y - area.y) + (o)->geometry.height); \
|
|
|
|
} \
|
|
|
|
if((o)->strut.bottom_start_x || (o)->strut.bottom_end_x || (o)->strut.bottom) \
|
|
|
|
{ \
|
|
|
|
if((o)->strut.bottom) \
|
|
|
|
bottom = MAX(bottom, (o)->strut.bottom); \
|
|
|
|
else \
|
|
|
|
bottom = MAX(bottom, (area.y + area.height) - (o)->geometry.y); \
|
|
|
|
} \
|
|
|
|
if((o)->strut.left_start_y || (o)->strut.left_end_y || (o)->strut.left) \
|
|
|
|
{ \
|
|
|
|
if((o)->strut.left) \
|
|
|
|
left = MAX(left, (o)->strut.left); \
|
|
|
|
else \
|
|
|
|
left = MAX(left, ((o)->geometry.x - area.x) + (o)->geometry.width); \
|
|
|
|
} \
|
|
|
|
if((o)->strut.right_start_y || (o)->strut.right_end_y || (o)->strut.right) \
|
|
|
|
{ \
|
|
|
|
if((o)->strut.right) \
|
|
|
|
right = MAX(right, (o)->strut.right); \
|
|
|
|
else \
|
|
|
|
right = MAX(right, (area.x + area.width) - (o)->geometry.x); \
|
|
|
|
} \
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach(c, globalconf.clients)
|
2011-03-27 20:07:14 +02:00
|
|
|
if((*c)->screen == screen && client_isvisible(*c))
|
2009-08-18 15:23:05 +02:00
|
|
|
COMPUTE_STRUT(*c)
|
|
|
|
|
2010-10-06 13:35:14 +02:00
|
|
|
foreach(drawin, globalconf.drawins)
|
2011-03-27 16:21:49 +02:00
|
|
|
if((*drawin)->visible)
|
|
|
|
{
|
|
|
|
screen_t *d_screen =
|
|
|
|
screen_getbycoord((*drawin)->geometry.x, (*drawin)->geometry.y);
|
|
|
|
if (d_screen == screen)
|
|
|
|
COMPUTE_STRUT(*drawin)
|
|
|
|
}
|
2009-08-18 15:23:05 +02:00
|
|
|
|
|
|
|
#undef COMPUTE_STRUT
|
2008-09-03 22:15:14 +02:00
|
|
|
|
|
|
|
area.x += left;
|
|
|
|
area.y += top;
|
2016-05-14 23:34:06 +02:00
|
|
|
area.width -= MIN(area.width, left + right);
|
|
|
|
area.height -= MIN(area.height, top + bottom);
|
2008-09-03 22:15:14 +02:00
|
|
|
|
2016-05-08 16:30:37 +02:00
|
|
|
if (AREA_EQUAL(area, screen->workarea))
|
|
|
|
return;
|
|
|
|
|
2016-07-24 16:29:56 +02:00
|
|
|
area_t old_workarea = screen->workarea;
|
2016-05-08 16:30:37 +02:00
|
|
|
screen->workarea = area;
|
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
luaA_object_push(L, screen);
|
2016-07-24 16:29:56 +02:00
|
|
|
luaA_pusharea(L, old_workarea);
|
|
|
|
luaA_object_emit_signal(L, -2, "property::workarea", 1);
|
2016-05-08 16:30:37 +02:00
|
|
|
lua_pop(L, 1);
|
2007-09-14 11:35:17 +02:00
|
|
|
}
|
2007-09-14 11:55:36 +02:00
|
|
|
|
2008-06-08 11:02:34 +02:00
|
|
|
/** Move a client to a virtual screen.
|
|
|
|
* \param c The client to move.
|
2009-08-30 07:26:19 +02:00
|
|
|
* \param new_screen The destination screen.
|
2008-06-09 21:43:09 +02:00
|
|
|
* \param doresize Set to true if we also move the client to the new x and
|
|
|
|
* y of the new screen.
|
2007-10-03 00:20:34 +02:00
|
|
|
*/
|
2007-09-28 11:30:51 +02:00
|
|
|
void
|
2009-08-24 16:32:19 +02:00
|
|
|
screen_client_moveto(client_t *c, screen_t *new_screen, bool doresize)
|
2007-09-28 11:30:51 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_State *L = globalconf_get_lua_State();
|
2009-04-17 16:14:09 +02:00
|
|
|
screen_t *old_screen = c->screen;
|
2008-06-09 21:43:09 +02:00
|
|
|
area_t from, to;
|
2011-07-07 18:49:58 +02:00
|
|
|
bool had_focus = false;
|
2007-11-15 15:44:16 +01:00
|
|
|
|
2008-12-07 18:03:36 +01:00
|
|
|
if(new_screen == c->screen)
|
|
|
|
return;
|
|
|
|
|
2011-07-07 18:49:58 +02:00
|
|
|
if (globalconf.focus.client == c)
|
|
|
|
had_focus = true;
|
|
|
|
|
2008-06-09 21:43:09 +02:00
|
|
|
c->screen = new_screen;
|
2007-12-27 16:29:45 +01:00
|
|
|
|
2009-03-29 20:26:39 +02:00
|
|
|
if(!doresize)
|
2009-04-17 23:54:08 +02:00
|
|
|
{
|
2014-12-06 11:56:58 +01:00
|
|
|
luaA_object_push(L, c);
|
2016-02-27 10:57:07 +01:00
|
|
|
if(old_screen != NULL)
|
2016-03-26 18:13:55 +01:00
|
|
|
luaA_object_push(L, old_screen);
|
2015-01-31 22:34:43 +01:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
luaA_object_emit_signal(L, -2, "property::screen", 1);
|
2014-12-06 11:56:58 +01:00
|
|
|
lua_pop(L, 1);
|
2011-07-07 18:49:58 +02:00
|
|
|
if(had_focus)
|
|
|
|
client_focus(c);
|
2008-12-07 18:03:36 +01:00
|
|
|
return;
|
2009-04-17 23:54:08 +02:00
|
|
|
}
|
2008-12-07 18:03:36 +01:00
|
|
|
|
2016-05-08 16:30:37 +02:00
|
|
|
from = old_screen->geometry;
|
|
|
|
to = c->screen->geometry;
|
2008-12-07 18:03:36 +01:00
|
|
|
|
|
|
|
area_t new_geometry = c->geometry;
|
|
|
|
|
2009-10-05 17:13:29 +02:00
|
|
|
new_geometry.x = to.x + new_geometry.x - from.x;
|
|
|
|
new_geometry.y = to.y + new_geometry.y - from.y;
|
2008-12-07 18:03:36 +01:00
|
|
|
|
2009-10-05 17:13:29 +02:00
|
|
|
/* resize the client if it doesn't fit the new screen */
|
|
|
|
if(new_geometry.width > to.width)
|
|
|
|
new_geometry.width = to.width;
|
|
|
|
if(new_geometry.height > to.height)
|
|
|
|
new_geometry.height = to.height;
|
2008-12-07 18:03:36 +01:00
|
|
|
|
2009-10-05 17:13:29 +02:00
|
|
|
/* make sure the client is still on the screen */
|
|
|
|
if(new_geometry.x + new_geometry.width > to.x + to.width)
|
|
|
|
new_geometry.x = to.x + to.width - new_geometry.width;
|
|
|
|
if(new_geometry.y + new_geometry.height > to.y + to.height)
|
|
|
|
new_geometry.y = to.y + to.height - new_geometry.height;
|
2017-05-13 12:10:40 +02:00
|
|
|
if(!screen_area_in_screen(new_screen, new_geometry))
|
|
|
|
{
|
|
|
|
/* If all else fails, force the client to end up on screen. */
|
|
|
|
new_geometry.x = to.x;
|
|
|
|
new_geometry.y = to.y;
|
|
|
|
}
|
2008-12-07 18:03:36 +01:00
|
|
|
|
|
|
|
/* move / resize the client */
|
2013-03-10 12:13:32 +01:00
|
|
|
client_resize(c, new_geometry, false);
|
2015-03-20 16:58:33 +01:00
|
|
|
|
2017-05-13 12:10:40 +02:00
|
|
|
/* emit signal */
|
|
|
|
luaA_object_push(L, c);
|
|
|
|
if(old_screen != NULL)
|
|
|
|
luaA_object_push(L, old_screen);
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
luaA_object_emit_signal(L, -2, "property::screen", 1);
|
|
|
|
lua_pop(L, 1);
|
2015-03-20 16:58:33 +01:00
|
|
|
|
2011-07-07 18:49:58 +02:00
|
|
|
if(had_focus)
|
|
|
|
client_focus(c);
|
2007-09-28 11:30:51 +02:00
|
|
|
}
|
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
/** Get a screen's index. */
|
|
|
|
int
|
|
|
|
screen_get_index(screen_t *s)
|
2009-07-13 14:26:18 +02:00
|
|
|
{
|
2014-03-30 16:37:19 +02:00
|
|
|
int res = 0;
|
|
|
|
foreach(screen, globalconf.screens)
|
|
|
|
{
|
|
|
|
res++;
|
|
|
|
if (*screen == s)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2009-07-13 14:26:18 +02:00
|
|
|
}
|
|
|
|
|
2016-02-27 15:57:57 +01:00
|
|
|
void
|
|
|
|
screen_update_primary(void)
|
|
|
|
{
|
|
|
|
if (!globalconf.have_randr_13)
|
|
|
|
return;
|
|
|
|
|
|
|
|
screen_t *primary_screen = NULL;
|
|
|
|
xcb_randr_get_output_primary_reply_t *primary =
|
|
|
|
xcb_randr_get_output_primary_reply(globalconf.connection,
|
|
|
|
xcb_randr_get_output_primary(globalconf.connection, globalconf.screen->root),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!primary)
|
|
|
|
return;
|
|
|
|
|
|
|
|
foreach(screen, globalconf.screens)
|
|
|
|
{
|
2019-06-17 05:54:02 +02:00
|
|
|
if ((*screen)->viewport)
|
|
|
|
foreach(output, (*screen)->viewport->outputs)
|
|
|
|
foreach (randr_output, output->outputs)
|
|
|
|
if (*randr_output == primary->output)
|
|
|
|
primary_screen = *screen;
|
2016-02-27 15:57:57 +01:00
|
|
|
}
|
|
|
|
p_delete(&primary);
|
|
|
|
|
|
|
|
if (!primary_screen || primary_screen == globalconf.primary_screen)
|
|
|
|
return;
|
|
|
|
|
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
screen_t *old = globalconf.primary_screen;
|
|
|
|
globalconf.primary_screen = primary_screen;
|
|
|
|
|
|
|
|
if (old)
|
|
|
|
{
|
|
|
|
luaA_object_push(L, old);
|
|
|
|
luaA_object_emit_signal(L, -1, "primary_changed", 0);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
luaA_object_push(L, primary_screen);
|
|
|
|
luaA_object_emit_signal(L, -1, "primary_changed", 0);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2016-02-27 15:44:25 +01:00
|
|
|
screen_t *
|
|
|
|
screen_get_primary(void)
|
|
|
|
{
|
|
|
|
if (!globalconf.primary_screen && globalconf.screens.len > 0)
|
2017-03-03 13:04:48 +01:00
|
|
|
{
|
2016-02-27 15:44:25 +01:00
|
|
|
globalconf.primary_screen = globalconf.screens.tab[0];
|
2017-03-03 13:04:48 +01:00
|
|
|
|
|
|
|
lua_State *L = globalconf_get_lua_State();
|
|
|
|
luaA_object_push(L, globalconf.primary_screen);
|
|
|
|
luaA_object_emit_signal(L, -1, "primary_changed", 0);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
2016-02-27 15:44:25 +01:00
|
|
|
return globalconf.primary_screen;
|
|
|
|
}
|
|
|
|
|
2008-08-11 17:14:02 +02:00
|
|
|
/** Screen module.
|
|
|
|
* \param L The Lua VM state.
|
|
|
|
* \return The number of elements pushed on stack.
|
|
|
|
* \luastack
|
2008-08-12 12:08:20 +02:00
|
|
|
* \lfield number The screen number, to get a screen.
|
2008-08-11 17:14:02 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_module_index(lua_State *L)
|
|
|
|
{
|
2009-09-21 20:35:14 +02:00
|
|
|
const char *name;
|
|
|
|
|
2015-06-13 11:18:47 +02:00
|
|
|
if(lua_type(L, 2) == LUA_TSTRING && (name = lua_tostring(L, 2)))
|
2016-02-27 15:44:25 +01:00
|
|
|
{
|
|
|
|
if(A_STREQ(name, "primary"))
|
|
|
|
return luaA_object_push(L, screen_get_primary());
|
2018-08-06 22:43:01 +02:00
|
|
|
else if (A_STREQ(name, "automatic_factory"))
|
|
|
|
{
|
|
|
|
lua_pushboolean(L, !globalconf.ignore_screens);
|
|
|
|
return 1;
|
|
|
|
}
|
2016-02-27 15:44:25 +01:00
|
|
|
|
2009-09-21 20:35:14 +02:00
|
|
|
foreach(screen, globalconf.screens)
|
2019-07-26 03:53:21 +02:00
|
|
|
if ((*screen)->name && A_STREQ(name, (*screen)->name))
|
|
|
|
return luaA_object_push(L, *screen);
|
|
|
|
else if ((*screen)->viewport)
|
2019-06-17 05:54:02 +02:00
|
|
|
foreach(output, (*screen)->viewport->outputs)
|
|
|
|
if(A_STREQ(output->name, name))
|
|
|
|
return luaA_object_push(L, *screen);
|
|
|
|
|
2018-06-21 00:24:45 +02:00
|
|
|
luaA_warn(L, "Unknown screen output name: %s", name);
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
2016-02-27 15:44:25 +01:00
|
|
|
}
|
2009-09-21 20:35:14 +02:00
|
|
|
|
2014-03-30 17:55:42 +02:00
|
|
|
return luaA_object_push(L, luaA_checkscreen(L, 2));
|
2008-08-11 17:14:02 +02:00
|
|
|
}
|
|
|
|
|
2018-08-06 22:43:01 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-03-06 13:25:10 +01:00
|
|
|
/** Iterate over screens.
|
|
|
|
* @usage
|
|
|
|
* for s in screen do
|
|
|
|
* print("Oh, wow, we have screen " .. tostring(s))
|
|
|
|
* end
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct screen
|
2016-03-06 13:25:10 +01:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_module_call(lua_State *L)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
if (lua_isnoneornil(L, 3))
|
|
|
|
idx = 0;
|
|
|
|
else
|
|
|
|
idx = screen_get_index(luaA_checkscreen(L, 3));
|
|
|
|
if (idx >= 0 && idx < globalconf.screens.len)
|
|
|
|
/* No +1 needed, index starts at 1, C array at 0 */
|
2016-03-26 18:13:55 +01:00
|
|
|
luaA_object_push(L, globalconf.screens.tab[idx]);
|
2016-03-06 13:25:10 +01:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
LUA_OBJECT_EXPORT_PROPERTY(screen, screen_t, geometry, luaA_pusharea)
|
2008-08-11 17:14:02 +02:00
|
|
|
|
2010-08-25 23:00:36 +02:00
|
|
|
static int
|
2014-03-30 16:37:19 +02:00
|
|
|
luaA_screen_get_index(lua_State *L, screen_t *s)
|
2010-08-25 23:00:36 +02:00
|
|
|
{
|
2014-03-30 16:37:19 +02:00
|
|
|
lua_pushinteger(L, screen_get_index(s));
|
|
|
|
return 1;
|
2010-08-25 23:00:36 +02:00
|
|
|
}
|
|
|
|
|
2009-07-13 12:07:10 +02:00
|
|
|
static int
|
2014-03-30 16:37:19 +02:00
|
|
|
luaA_screen_get_outputs(lua_State *L, screen_t *s)
|
2009-07-13 12:07:10 +02:00
|
|
|
{
|
2019-06-17 05:54:02 +02:00
|
|
|
luaA_viewport_get_outputs(L, s->viewport);
|
2009-07-13 12:07:10 +02:00
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
/* The table of tables we created. */
|
|
|
|
return 1;
|
2009-07-13 12:07:10 +02:00
|
|
|
}
|
|
|
|
|
2019-07-11 02:59:53 +02:00
|
|
|
static int
|
|
|
|
luaA_screen_get_managed(lua_State *L, screen_t *s)
|
|
|
|
{
|
|
|
|
if (s->lifecycle & SCREEN_LIFECYCLE_LUA)
|
|
|
|
lua_pushstring(L, "Lua");
|
|
|
|
else if (s->lifecycle & SCREEN_LIFECYCLE_C)
|
|
|
|
lua_pushstring(L, "C");
|
|
|
|
else
|
|
|
|
lua_pushstring(L, "none");
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2009-07-13 12:07:10 +02:00
|
|
|
static int
|
2014-03-30 16:37:19 +02:00
|
|
|
luaA_screen_get_workarea(lua_State *L, screen_t *s)
|
2009-07-13 12:07:10 +02:00
|
|
|
{
|
2016-05-08 16:30:37 +02:00
|
|
|
luaA_pusharea(L, s->workarea);
|
2014-03-30 16:37:19 +02:00
|
|
|
return 1;
|
2009-07-13 12:07:10 +02:00
|
|
|
}
|
|
|
|
|
2019-07-26 03:53:21 +02:00
|
|
|
static int
|
|
|
|
luaA_screen_set_name(lua_State *L, screen_t *s)
|
|
|
|
{
|
|
|
|
const char *buf = luaL_checkstring(L, -1);
|
|
|
|
|
|
|
|
if (s->name)
|
|
|
|
free(s->name);
|
|
|
|
|
|
|
|
s->name = a_strdup(buf);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
luaA_screen_get_name(lua_State *L, screen_t *s)
|
|
|
|
{
|
|
|
|
lua_pushstring(L, s->name ? s->name : "screen");
|
|
|
|
|
|
|
|
/* Fallback to "screen1", "screen2", etc if no name is set */
|
|
|
|
if (!s->name) {
|
|
|
|
lua_pushinteger(L, screen_get_index(s));
|
|
|
|
lua_concat(L, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2015-02-27 00:24:23 +01:00
|
|
|
/** Get the number of screens.
|
2008-08-11 17:14:02 +02:00
|
|
|
*
|
2015-02-27 00:24:23 +01:00
|
|
|
* @return The screen count, at least 1.
|
2019-06-08 01:08:05 +02:00
|
|
|
* @staticfct count
|
2008-08-11 17:14:02 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_count(lua_State *L)
|
|
|
|
{
|
2015-05-26 07:09:12 +02:00
|
|
|
lua_pushinteger(L, globalconf.screens.len);
|
2008-08-11 17:14:02 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2016-04-16 17:47:52 +02:00
|
|
|
/** Add a fake screen.
|
2016-12-20 03:23:54 +01:00
|
|
|
*
|
|
|
|
* To vertically split the first screen in 2 equal parts, use:
|
|
|
|
*
|
|
|
|
* local geo = screen[1].geometry
|
|
|
|
* local new_width = math.ceil(geo.width/2)
|
|
|
|
* local new_width2 = geo.width - new_width
|
|
|
|
* screen[1]:fake_resize(geo.x, geo.y, new_width, geo.height)
|
|
|
|
* screen.fake_add(geo.x + new_width, geo.y, new_width2, geo.height)
|
|
|
|
*
|
|
|
|
* Both virtual screens will have their own taglist and wibars.
|
|
|
|
*
|
2016-04-16 17:47:52 +02:00
|
|
|
* @tparam integer x X-coordinate for screen.
|
|
|
|
* @tparam integer y Y-coordinate for screen.
|
|
|
|
* @tparam integer width width for screen.
|
|
|
|
* @tparam integer height height for screen.
|
|
|
|
* @return The new screen.
|
2019-06-07 20:59:34 +02:00
|
|
|
* @constructorfct fake_add
|
2016-04-16 17:47:52 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_fake_add(lua_State *L)
|
|
|
|
{
|
|
|
|
int x = luaL_checkinteger(L, 1);
|
|
|
|
int y = luaL_checkinteger(L, 2);
|
|
|
|
int width = luaL_checkinteger(L, 3);
|
|
|
|
int height = luaL_checkinteger(L, 4);
|
2019-07-11 02:59:53 +02:00
|
|
|
|
|
|
|
/* If the screen is managed by internal Lua code */
|
|
|
|
bool managed = false;
|
|
|
|
|
|
|
|
/* Allow undocumented arguments for internal use only */
|
|
|
|
if(lua_istable(L, 5)) {
|
|
|
|
lua_getfield(L, 5, "_managed");
|
|
|
|
managed = lua_isboolean(L, 6) && luaA_checkboolean(L, 6);
|
|
|
|
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2016-04-16 17:47:52 +02:00
|
|
|
screen_t *s;
|
|
|
|
|
|
|
|
s = screen_add(L, &globalconf.screens);
|
2019-07-11 02:59:53 +02:00
|
|
|
s->lifecycle |= managed ? SCREEN_LIFECYCLE_LUA : SCREEN_LIFECYCLE_USER;
|
2016-04-16 17:47:52 +02:00
|
|
|
s->geometry.x = x;
|
|
|
|
s->geometry.y = y;
|
|
|
|
s->geometry.width = width;
|
|
|
|
s->geometry.height = height;
|
2018-04-22 08:07:31 +02:00
|
|
|
s->xid = FAKE_SCREEN_XID;
|
2016-05-09 19:22:21 +02:00
|
|
|
|
|
|
|
screen_added(L, s);
|
2016-10-23 15:00:27 +02:00
|
|
|
luaA_class_emit_signal(L, &screen_class, "list", 0);
|
2016-04-16 17:47:52 +02:00
|
|
|
luaA_object_push(L, s);
|
|
|
|
|
2019-06-25 06:31:37 +02:00
|
|
|
foreach(c, globalconf.clients) {
|
|
|
|
screen_client_moveto(*c, screen_getbycoord(
|
|
|
|
(*c)->geometry.x, (*c)->geometry.y), false);
|
|
|
|
}
|
|
|
|
|
2016-04-16 17:47:52 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove a screen.
|
2019-09-30 05:16:34 +02:00
|
|
|
*
|
|
|
|
* @DOC_sequences_screen_fake_remove_EXAMPLE@
|
|
|
|
*
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method fake_remove
|
2016-04-16 17:47:52 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_fake_remove(lua_State *L)
|
|
|
|
{
|
|
|
|
screen_t *s = luaA_checkudata(L, 1, &screen_class);
|
|
|
|
int idx = screen_get_index(s) - 1;
|
|
|
|
if (idx < 0)
|
|
|
|
/* WTF? */
|
|
|
|
return 0;
|
|
|
|
|
2018-06-28 15:36:53 +02:00
|
|
|
if (globalconf.screens.len == 1) {
|
|
|
|
luaA_warn(L, "Removing last screen through fake_remove(). "
|
|
|
|
"This is a very, very, very bad idea!");
|
|
|
|
}
|
|
|
|
|
2016-04-16 17:47:52 +02:00
|
|
|
screen_array_take(&globalconf.screens, idx);
|
|
|
|
luaA_object_push(L, s);
|
|
|
|
screen_removed(L, -1);
|
|
|
|
lua_pop(L, 1);
|
2016-10-23 15:00:27 +02:00
|
|
|
luaA_class_emit_signal(L, &screen_class, "list", 0);
|
2016-04-16 17:47:52 +02:00
|
|
|
luaA_object_unref(L, s);
|
|
|
|
s->valid = false;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-09-30 05:16:34 +02:00
|
|
|
/** Resize a screen.
|
|
|
|
*
|
|
|
|
* Calling this will resize the screen even if it no longer matches the viewport
|
|
|
|
* size.
|
|
|
|
*
|
|
|
|
* @DOC_sequences_screen_fake_resize_EXAMPLE@
|
|
|
|
*
|
2016-05-08 19:22:55 +02:00
|
|
|
* @tparam integer x The new X-coordinate for screen.
|
|
|
|
* @tparam integer y The new Y-coordinate for screen.
|
|
|
|
* @tparam integer width The new width for screen.
|
|
|
|
* @tparam integer height The new height for screen.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method fake_resize
|
2016-05-08 19:22:55 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_fake_resize(lua_State *L)
|
|
|
|
{
|
|
|
|
screen_t *screen = luaA_checkudata(L, 1, &screen_class);
|
|
|
|
int x = luaL_checkinteger(L, 2);
|
|
|
|
int y = luaL_checkinteger(L, 3);
|
|
|
|
int width = luaL_checkinteger(L, 4);
|
|
|
|
int height = luaL_checkinteger(L, 5);
|
2016-07-24 16:32:12 +02:00
|
|
|
area_t old_geometry = screen->geometry;
|
2016-05-08 19:22:55 +02:00
|
|
|
|
|
|
|
screen->geometry.x = x;
|
|
|
|
screen->geometry.y = y;
|
|
|
|
screen->geometry.width = width;
|
|
|
|
screen->geometry.height = height;
|
|
|
|
|
|
|
|
screen_update_workarea(screen);
|
|
|
|
|
2016-07-24 16:32:12 +02:00
|
|
|
luaA_pusharea(L, old_geometry);
|
|
|
|
luaA_object_emit_signal(L, 1, "property::geometry", 1);
|
2016-05-08 19:22:55 +02:00
|
|
|
|
2019-06-25 06:31:37 +02:00
|
|
|
/* Note: calling `screen_client_moveto` from here will create more issues
|
|
|
|
* than it would fix. Keep in mind that it means `c.screen` will be wrong
|
|
|
|
* until Lua it `fake_add` fixes it.
|
|
|
|
*/
|
|
|
|
|
2016-05-08 19:22:55 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-23 15:01:12 +02:00
|
|
|
/** Swap a screen with another one in global screen list.
|
2019-09-30 05:16:34 +02:00
|
|
|
*
|
|
|
|
* @DOC_sequences_screen_swap_EXAMPLE@
|
|
|
|
*
|
2016-10-23 15:01:12 +02:00
|
|
|
* @client s A screen to swap with.
|
2019-06-06 22:32:53 +02:00
|
|
|
* @method swap
|
2016-10-23 15:01:12 +02:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
luaA_screen_swap(lua_State *L)
|
|
|
|
{
|
|
|
|
screen_t *s = luaA_checkudata(L, 1, &screen_class);
|
|
|
|
screen_t *swap = luaA_checkudata(L, 2, &screen_class);
|
|
|
|
|
|
|
|
if(s != swap)
|
|
|
|
{
|
|
|
|
screen_t **ref_s = NULL, **ref_swap = NULL;
|
|
|
|
foreach(item, globalconf.screens)
|
|
|
|
{
|
|
|
|
if(*item == s)
|
|
|
|
ref_s = item;
|
|
|
|
else if(*item == swap)
|
|
|
|
ref_swap = item;
|
|
|
|
if(ref_s && ref_swap)
|
|
|
|
break;
|
|
|
|
}
|
2017-11-19 15:40:20 +01:00
|
|
|
if(!ref_s || !ref_swap)
|
|
|
|
return luaL_error(L, "Invalid call to screen:swap()");
|
|
|
|
|
2016-10-23 15:01:12 +02:00
|
|
|
/* swap ! */
|
|
|
|
*ref_s = swap;
|
|
|
|
*ref_swap = s;
|
|
|
|
|
|
|
|
luaA_class_emit_signal(L, &screen_class, "list", 0);
|
|
|
|
|
|
|
|
luaA_object_push(L, swap);
|
|
|
|
lua_pushboolean(L, true);
|
|
|
|
luaA_object_emit_signal(L, -4, "swapped", 2);
|
|
|
|
|
|
|
|
luaA_object_push(L, swap);
|
|
|
|
luaA_object_push(L, s);
|
|
|
|
lua_pushboolean(L, false);
|
|
|
|
luaA_object_emit_signal(L, -3, "swapped", 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
void
|
|
|
|
screen_class_setup(lua_State *L)
|
2008-08-11 17:14:02 +02:00
|
|
|
{
|
2014-03-30 16:37:19 +02:00
|
|
|
static const struct luaL_Reg screen_methods[] =
|
|
|
|
{
|
|
|
|
LUA_CLASS_METHODS(screen)
|
|
|
|
{ "count", luaA_screen_count },
|
2018-08-06 22:43:01 +02:00
|
|
|
{ "_viewports", luaA_viewports },
|
|
|
|
{ "_scan_quiet", luaA_scan_quiet },
|
2014-03-30 16:37:19 +02:00
|
|
|
{ "__index", luaA_screen_module_index },
|
2018-08-06 22:43:01 +02:00
|
|
|
{ "__newindex", luaA_screen_module_newindex },
|
2016-03-06 13:25:10 +01:00
|
|
|
{ "__call", luaA_screen_module_call },
|
2016-04-16 17:47:52 +02:00
|
|
|
{ "fake_add", luaA_screen_fake_add },
|
2014-03-30 16:37:19 +02:00
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
2008-08-11 17:14:02 +02:00
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
static const struct luaL_Reg screen_meta[] =
|
|
|
|
{
|
|
|
|
LUA_OBJECT_META(screen)
|
|
|
|
LUA_CLASS_META
|
2016-04-16 17:47:52 +02:00
|
|
|
{ "fake_remove", luaA_screen_fake_remove },
|
2016-05-08 19:22:55 +02:00
|
|
|
{ "fake_resize", luaA_screen_fake_resize },
|
2016-10-23 15:01:12 +02:00
|
|
|
{ "swap", luaA_screen_swap },
|
2014-03-30 16:37:19 +02:00
|
|
|
{ NULL, NULL },
|
|
|
|
};
|
|
|
|
|
|
|
|
luaA_class_setup(L, &screen_class, "screen", NULL,
|
|
|
|
(lua_class_allocator_t) screen_new,
|
2019-07-26 03:53:21 +02:00
|
|
|
(lua_class_collector_t) screen_wipe,
|
2016-04-16 14:49:09 +02:00
|
|
|
(lua_class_checker_t) screen_checker,
|
2014-03-30 16:37:19 +02:00
|
|
|
luaA_class_index_miss_property, luaA_class_newindex_miss_property,
|
|
|
|
screen_methods, screen_meta);
|
|
|
|
luaA_class_add_property(&screen_class, "geometry",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_geometry,
|
|
|
|
NULL);
|
|
|
|
luaA_class_add_property(&screen_class, "index",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_index,
|
|
|
|
NULL);
|
2019-06-20 00:02:49 +02:00
|
|
|
luaA_class_add_property(&screen_class, "_outputs",
|
2014-03-30 16:37:19 +02:00
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_outputs,
|
|
|
|
NULL);
|
2019-07-11 02:59:53 +02:00
|
|
|
luaA_class_add_property(&screen_class, "_managed",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_managed,
|
|
|
|
NULL);
|
2014-03-30 16:37:19 +02:00
|
|
|
luaA_class_add_property(&screen_class, "workarea",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_workarea,
|
|
|
|
NULL);
|
2019-07-26 03:53:21 +02:00
|
|
|
luaA_class_add_property(&screen_class, "name",
|
|
|
|
(lua_class_propfunc_t) luaA_screen_set_name,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_name,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_set_name);
|
2014-03-30 16:37:19 +02:00
|
|
|
}
|
2008-08-11 17:14:02 +02:00
|
|
|
|
2017-10-21 01:54:36 +02:00
|
|
|
/* @DOC_cobject_COMMON@ */
|
|
|
|
|
2011-09-11 16:50:01 +02:00
|
|
|
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|