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
|
|
|
*/
|
|
|
|
|
2015-02-27 00:24:23 +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}.
|
|
|
|
*
|
|
|
|
* @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)
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
|
2016-04-04 08:35:57 +02:00
|
|
|
/**
|
|
|
|
* The primary screen.
|
|
|
|
*
|
|
|
|
* @tfield screen primary
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If RANDR information is available, a list of outputs
|
2015-02-27 00:24:23 +01:00
|
|
|
* for this screen and their size in mm.
|
2016-04-04 08:35:57 +02:00
|
|
|
*
|
|
|
|
* Please note that the table content may vary.
|
|
|
|
*
|
|
|
|
* **Signal:**
|
|
|
|
*
|
|
|
|
* * *property::outputs*
|
|
|
|
*
|
|
|
|
* **Immutable:** true
|
|
|
|
* @property outputs
|
|
|
|
* @param table
|
|
|
|
* @tfield table table.name A table with the screen name as key (like `eDP1` on a laptop)
|
|
|
|
* @tfield integer table.name.mm_width The screen physical width
|
|
|
|
* @tfield integer table.name.mm_height The screen physical height
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The screen workarea.
|
|
|
|
*
|
|
|
|
* 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);
|
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)
|
|
|
|
|
|
|
|
/** Collect a screen. */
|
|
|
|
static void
|
|
|
|
screen_wipe(screen_t *s)
|
|
|
|
{
|
|
|
|
screen_output_array_wipe(&s->outputs);
|
|
|
|
}
|
2009-09-21 16:29:19 +02:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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;
|
2016-04-15 19:59:36 +02:00
|
|
|
return new_screen;
|
2013-09-27 23:07:33 +02:00
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
/* Monitors were introduced in RandR 1.5 */
|
|
|
|
#ifdef XCB_RANDR_GET_MONITORS
|
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;
|
|
|
|
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;
|
|
|
|
|
2016-04-15 19:59:36 +02:00
|
|
|
new_screen = screen_add(L, screens);
|
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
|
|
|
|
|
|
|
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 {
|
2016-04-15 19:59:36 +02:00
|
|
|
output.name = a_strdup("unknown");
|
2016-04-12 13:26:17 +02:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
#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
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Prepare the new screen */
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_t *new_screen = screen_add(L, screens);
|
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
|
|
|
|
|
|
|
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;
|
|
|
|
|
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 (!output_info_r) {
|
|
|
|
warn("RANDR GetOutputInfo failed; this should not be possible");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-04-12 13:26:17 +02:00
|
|
|
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);
|
2016-04-15 20:05:03 +02:00
|
|
|
|
|
|
|
if (A_STREQ(name, "default"))
|
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
screen_array_wipe(screens);
|
|
|
|
screen_array_init(screens);
|
|
|
|
|
|
|
|
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
|
|
|
|
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
|
|
|
|
|
|
|
if (screens->len == 0)
|
|
|
|
{
|
|
|
|
/* 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++)
|
|
|
|
{
|
|
|
|
screen_t *s = screen_add(L, screens);
|
|
|
|
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;
|
2016-04-15 19:59:36 +02:00
|
|
|
screen_t *s = screen_add(L, screens);
|
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);
|
|
|
|
luaA_object_emit_signal(L, -1, "added", 0);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2010-07-21 14:42:45 +02:00
|
|
|
/** Get screens informations and fill global configuration.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
screen_scan(void)
|
|
|
|
{
|
2016-04-15 19:59:36 +02:00
|
|
|
lua_State *L;
|
|
|
|
|
|
|
|
L = globalconf_get_lua_State();
|
|
|
|
|
|
|
|
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);
|
2017-05-13 23:22:15 +02:00
|
|
|
check(globalconf.screens.len > 0);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
screen_update_primary();
|
2008-09-18 16:03:05 +02:00
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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];
|
|
|
|
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(outputs_changed) {
|
|
|
|
luaA_object_push(L, existing_screen);
|
|
|
|
luaA_object_emit_signal(L, -1, "property::outputs", 0);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
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);
|
|
|
|
|
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;
|
2016-04-15 20:29:08 +02:00
|
|
|
foreach(new_screen, new_screens)
|
|
|
|
found |= (*new_screen)->xid == old_screen->xid;
|
|
|
|
if(!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)
|
|
|
|
{
|
|
|
|
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-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)
|
|
|
|
{
|
|
|
|
foreach(output, (*screen)->outputs)
|
2016-04-12 13:26:17 +02:00
|
|
|
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());
|
|
|
|
|
2009-09-21 20:35:14 +02:00
|
|
|
foreach(screen, globalconf.screens)
|
2014-03-30 16:37:19 +02:00
|
|
|
foreach(output, (*screen)->outputs)
|
2011-11-17 16:38:39 +01:00
|
|
|
if(A_STREQ(output->name, name))
|
2016-02-27 16:45:43 +01:00
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
2014-03-30 16:37:19 +02:00
|
|
|
lua_createtable(L, 0, s->outputs.len);
|
|
|
|
foreach(output, s->outputs)
|
|
|
|
{
|
|
|
|
lua_createtable(L, 2, 0);
|
2009-07-13 12:07:10 +02:00
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
lua_pushinteger(L, output->mm_width);
|
|
|
|
lua_setfield(L, -2, "mm_width");
|
|
|
|
lua_pushinteger(L, output->mm_height);
|
|
|
|
lua_setfield(L, -2, "mm_height");
|
2009-07-13 12:07:10 +02:00
|
|
|
|
2014-03-30 16:37:19 +02:00
|
|
|
lua_setfield(L, -2, output->name);
|
|
|
|
}
|
|
|
|
/* The table of tables we created. */
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
screen_t *s;
|
|
|
|
|
|
|
|
s = screen_add(L, &globalconf.screens);
|
|
|
|
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);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Remove a screen.
|
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;
|
|
|
|
}
|
|
|
|
|
2016-05-08 19:22:55 +02:00
|
|
|
/** Fake-resize a screen
|
|
|
|
* @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
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-10-23 15:01:12 +02:00
|
|
|
/** Swap a screen with another one in global screen list.
|
|
|
|
* @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 },
|
|
|
|
{ "__index", luaA_screen_module_index },
|
|
|
|
{ "__newindex", luaA_default_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,
|
|
|
|
(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);
|
|
|
|
luaA_class_add_property(&screen_class, "outputs",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_outputs,
|
|
|
|
NULL);
|
|
|
|
luaA_class_add_property(&screen_class, "workarea",
|
|
|
|
NULL,
|
|
|
|
(lua_class_propfunc_t) luaA_screen_get_workarea,
|
|
|
|
NULL);
|
|
|
|
}
|
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
|