Merge pull request #1453 from Elv13/fix_1321

Refactor maximization. This pull request induce some minor changes to the behavior of Awesome compared to earlier versions. However, it solves 4 bugs and should lay the foundations for a more predictable and stable maximization support.

The reason maximization slightly regressed in 4.0 was due to:

 * The request:: API standardization (2016 e5438790)
 * The fact that a combined `maximize` property was added (2014 ac45dc9f5)

As the request:: API enforce a strict "common protocol" for geometry change, having a split state didn't work back then. This pull request also adds many new config options to compensate for the behavior changes.
This commit is contained in:
Emmanuel Lepage Vallée 2017-02-07 11:27:20 -05:00 committed by GitHub
commit 1f4e20e13a
11 changed files with 238 additions and 98 deletions

5
ewmh.c
View File

@ -49,9 +49,9 @@ ewmh_client_update_hints(lua_State *L)
state[i++] = _NET_WM_STATE_MODAL;
if(c->fullscreen)
state[i++] = _NET_WM_STATE_FULLSCREEN;
if(c->maximized_vertical)
if(c->maximized_vertical || c->maximized)
state[i++] = _NET_WM_STATE_MAXIMIZED_VERT;
if(c->maximized_horizontal)
if(c->maximized_horizontal || c->maximized)
state[i++] = _NET_WM_STATE_MAXIMIZED_HORZ;
if(c->sticky)
state[i++] = _NET_WM_STATE_STICKY;
@ -220,6 +220,7 @@ ewmh_init_lua(void)
luaA_class_connect_signal(L, &client_class, "property::fullscreen" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::maximized_horizontal" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::maximized_vertical" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::maximized" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::sticky" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::skip_taskbar" , ewmh_client_update_hints);
luaA_class_connect_signal(L, &client_class, "property::above" , ewmh_client_update_hints);

View File

@ -138,6 +138,7 @@ function client.tiled(s, stacked)
for _, c in pairs(clients) do
if not client.object.get_floating(c)
and not c.fullscreen
and not c.maximized
and not c.maximized_vertical
and not c.maximized_horizontal then
table.insert(tclients, c)
@ -668,6 +669,7 @@ function client.object.get_floating(c)
or c.fullscreen
or c.maximized_vertical
or c.maximized_horizontal
or c.maximized
or client.object.is_fixed(c) then
return true
end

View File

@ -13,12 +13,21 @@ local util = require("awful.util")
local aclient = require("awful.client")
local aplace = require("awful.placement")
local asuit = require("awful.layout.suit")
local beautiful = require("beautiful")
local ewmh = {
generic_activate_filters = {},
contextual_activate_filters = {},
}
--- Honor the screen padding when maximizing.
-- @beautiful beautiful.maximized_honor_padding
-- @tparam[opt=true] boolean maximized_honor_padding
--- Hide the border on fullscreen clients.
-- @beautiful beautiful.fullscreen_hide_border
-- @tparam[opt=true] boolean fullscreen_hide_border
--- The list of all registered generic request::activate (focus stealing)
-- filters. If a filter is added to only one context, it will be in
-- `ewmh.contextual_activate_filters`["context_name"].
@ -37,26 +46,24 @@ local ewmh = {
--- Update a client's settings when its geometry changes, skipping signals
-- resulting from calls within.
local geometry_change_lock = false
local function geometry_change(window)
if geometry_change_lock then return end
geometry_change_lock = true
local repair_geometry_lock = false
local function repair_geometry(window)
if repair_geometry_lock then return end
repair_geometry_lock = true
-- Fix up the geometry in case this window needs to cover the whole screen.
local bw = window.border_width or 0
local g = window.screen.workarea
if window.maximized_vertical then
window:geometry { height = g.height - 2*bw, y = g.y }
end
if window.maximized_horizontal then
window:geometry { width = g.width - 2*bw, x = g.x }
end
if window.fullscreen then
window.border_width = 0
window:geometry(window.screen.geometry)
-- Re-apply the geometry locking properties to what they should be.
for _, prop in ipairs {
"fullscreen", "maximized", "maximized_vertical", "maximized_horizontal"
} do
if window[prop] then
window:emit_signal("request::geometry", prop, {
store_geometry = false
})
break
end
end
geometry_change_lock = false
repair_geometry_lock = false
end
--- Activate a window.
@ -224,6 +231,7 @@ end
local context_mapper = {
maximized_vertical = "maximize_vertically",
maximized_horizontal = "maximize_horizontally",
maximized = "maximize",
fullscreen = "maximize"
}
@ -264,7 +272,10 @@ function ewmh.geometry(c, context, hints)
-- If the property is boolean and it correspond to the undo operation,
-- restore the stored geometry.
if state == false then
local original = repair_geometry_lock
repair_geometry_lock = true
aplace.restore(c,{context=context})
repair_geometry_lock = original
return
end
@ -274,7 +285,23 @@ function ewmh.geometry(c, context, hints)
props.honor_workarea = honor_default
end
if props.honor_padding == nil and props.honor_workarea and context:match("maximize") then
props.honor_padding = beautiful.maximized_honor_padding ~= false
end
if original_context == "fullscreen" and beautiful.fullscreen_hide_border ~= false then
props.ignore_border_width = true
end
aplace[context](c, props)
-- Remove the border to get a "real" fullscreen.
if original_context == "fullscreen" and beautiful.fullscreen_hide_border ~= false then
local original = repair_geometry_lock
repair_geometry_lock = true
c.border_width = 0
repair_geometry_lock = original
end
end
end
@ -282,11 +309,11 @@ client.connect_signal("request::activate", ewmh.activate)
client.connect_signal("request::tag", ewmh.tag)
client.connect_signal("request::urgent", ewmh.urgent)
client.connect_signal("request::geometry", ewmh.geometry)
client.connect_signal("property::border_width", geometry_change)
client.connect_signal("property::geometry", geometry_change)
client.connect_signal("property::border_width", repair_geometry)
client.connect_signal("property::screen", repair_geometry)
screen.connect_signal("property::workarea", function(s)
for _, c in pairs(client.get(s)) do
geometry_change(c)
repair_geometry(c)
end
end)

View File

@ -82,6 +82,7 @@ function mouse.client.move(c, snap, finished_cb) --luacheck: no unused args
if not c
or c.fullscreen
or c.maximized
or c.type == "desktop"
or c.type == "splash"
or c.type == "dock" then
@ -189,6 +190,7 @@ function mouse.client.resize(c, corner, args)
if not c then return end
if c.fullscreen
or c.maximized
or c.type == "desktop"
or c.type == "splash"
or c.type == "dock" then

View File

@ -291,6 +291,7 @@ local function store_geometry(d, reqtype)
if not data[d][reqtype] then data[d][reqtype] = {} end
data[d][reqtype] = d:geometry()
data[d][reqtype].screen = d.screen
data[d][reqtype].sgeo = d.screen and d.screen.geometry or nil
data[d][reqtype].border_width = d.border_width
end
@ -930,7 +931,7 @@ function placement.under_mouse(d, args)
ngeo.x = math.floor(m_coords.x - ngeo.width / 2)
ngeo.y = math.floor(m_coords.y - ngeo.height / 2)
local bw = d.border_width or 0
local bw = (not args.ignore_border_width) and d.border_width or 0
ngeo.width = ngeo.width - 2*bw
ngeo.height = ngeo.height - 2*bw
@ -1034,7 +1035,7 @@ function placement.resize_to_mouse(d, args)
pts.x_only and ngeo.y + ngeo.height or math.max(p2.y, p1.y)
)
local bw = d.border_width or 0
local bw = (not args.ignore_border_width) and d.border_width or 0
for _, a in ipairs {"width", "height"} do
ngeo[a] = ngeo[a] - 2*bw
@ -1084,7 +1085,7 @@ function placement.align(d, args)
local sgeo = get_parent_geometry(d, args)
local dgeo = geometry_common(d, args)
local bw = d.border_width or 0
local bw = (not args.ignore_border_width) and d.border_width or 0
local pos = align_map[args.position](
sgeo.width ,
@ -1169,7 +1170,7 @@ function placement.stretch(d, args)
local sgeo = get_parent_geometry(d, args)
local dgeo = geometry_common(d, args)
local ngeo = geometry_common(d, args, nil, true)
local bw = d.border_width or 0
local bw = (not args.ignore_border_width) and d.border_width or 0
if args.direction == "left" then
ngeo.x = sgeo.x
@ -1231,7 +1232,7 @@ function placement.maximize(d, args)
local sgeo = get_parent_geometry(d, args)
local ngeo = geometry_common(d, args, nil, true)
local bw = d.border_width or 0
local bw = (not args.ignore_border_width) and d.border_width or 0
if (not args.axis) or args.axis :match "vertical" then
ngeo.y = sgeo.y
@ -1308,7 +1309,7 @@ function placement.scale(d, args)
end
end
local bw = d.border_width or 0
local bw = (not args.ignore_border_width) and d.border_width or 0
ngeo.width = ngeo.width - 2*bw
ngeo.height = ngeo.height - 2*bw
@ -1422,11 +1423,32 @@ function placement.restore(d, args)
if not memento then return false end
memento.screen = nil --TODO use it
local x, y = memento.x, memento.y
-- Some people consider that once moved to another screen, then
-- the memento needs to be upgraded. For now this is only true for
-- maximization until someone complains.
if memento.sgeo and memento.screen and args.context == "maximize"
and d.screen and get_screen(memento.screen) ~= get_screen(d.screen) then
-- Use the absolute geometry as the memento also does
local sgeo = get_screen(d.screen).geometry
x = sgeo.x + (memento.x - memento.sgeo.x)
y = sgeo.y + (memento.y - memento.sgeo.y)
end
d.border_width = memento.border_width
d:geometry(memento)
-- Don't use the memento as it would be "destructive", since `x`, `y`
-- and `screen` have to be modified.
d:geometry {
x = x,
y = y,
width = memento.width,
height = memento.height,
}
return true
end

View File

@ -1,6 +1,22 @@
---------------------------------------------------------------------------
--- Apply rules to clients at startup.
--
-- All existing `client` properties can be used in rules. It is also possible
-- to add random properties that will be later accessible as `c.property_name`
-- (where `c` is a valid client object)
--
-- In addition to the existing properties, the following are supported:
--
-- * placement
-- * honor_padding
-- * honor_workarea
-- * tag
-- * new_tag
-- * switchtotag
-- * focus
-- * titlebars_enabled
-- * callback
--
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2009 Julien Danjou
-- @module awful.rules
@ -353,7 +369,7 @@ function rules.high_priority_properties.new_tag(c, value, props)
return t
end
function rules.extra_properties.placement(c, value)
function rules.extra_properties.placement(c, value, props)
-- Avoid problems
if awesome.startup and
(c.size_hints.user_position or c.size_hints.program_position) then
@ -363,8 +379,8 @@ function rules.extra_properties.placement(c, value)
local ty = type(value)
local args = {
honor_workarea = true,
honor_padding = true
honor_workarea = props.honor_workarea ~= false,
honor_padding = props.honor_padding ~= false
}
if ty == "function" or (ty == "table" and
@ -376,7 +392,7 @@ function rules.extra_properties.placement(c, value)
end
end
function rules.extra_properties.tags(c, value, props)
function rules.high_priority_properties.tags(c, value, props)
local current = c:tags()
local tags, s = {}, nil

View File

@ -460,13 +460,11 @@ end
-- @param c The client for which the button is wanted.
function titlebar.widget.maximizedbutton(c)
local widget = titlebar.widget.button(c, "maximized", function(cl)
return cl.maximized_horizontal or cl.maximized_vertical
return cl.maximized
end, function(cl, state)
cl.maximized_horizontal = not state
cl.maximized_vertical = not state
cl.maximized = not state
end)
c:connect_signal("property::maximized_vertical", widget.update)
c:connect_signal("property::maximized_horizontal", widget.update)
c:connect_signal("property::maximized", widget.update)
return widget
end

View File

@ -453,6 +453,7 @@ function tasklist.new(screen, filter, buttons, style, update_function, base_widg
capi.client.connect_signal("property::floating", u)
capi.client.connect_signal("property::maximized_horizontal", u)
capi.client.connect_signal("property::maximized_vertical", u)
capi.client.connect_signal("property::maximized", u)
capi.client.connect_signal("property::minimized", u)
capi.client.connect_signal("property::name", u)
capi.client.connect_signal("property::icon_name", u)

View File

@ -805,9 +805,17 @@
* @function set_newindex_miss_handler
*/
typedef enum {
CLIENT_MAXIMIZED_NONE = 0 << 0,
CLIENT_MAXIMIZED_V = 1 << 0,
CLIENT_MAXIMIZED_H = 1 << 1,
CLIENT_MAXIMIZED_BOTH = 1 << 2, /* V|H == BOTH, but ~(V|H) != ~(BOTH)... */
} client_maximized_t;
static area_t titlebar_get_area(client_t *c, client_titlebar_t bar);
static drawable_t *titlebar_get_drawable(lua_State *L, client_t *c, int cl_idx, client_titlebar_t bar);
static void client_resize_do(client_t *c, area_t geometry);
static void client_set_maximized_common(lua_State *L, int cidx, bool s, const char* type, const int val);
/** Collect a client.
* \param L The Lua VM state.
@ -1426,8 +1434,6 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, xcb_get_window_at
* (Else, reparent could cause an UnmapNotify) */
xcb_change_window_attributes(globalconf.connection, w, XCB_CW_EVENT_MASK, select_input_val);
luaA_object_emit_signal(L, -1, "property::window", 0);
/* The frame window gets the border, not the real client window */
xcb_configure_window(globalconf.connection, w,
XCB_CONFIG_WINDOW_BORDER_WIDTH,
@ -1450,15 +1456,17 @@ client_manage(xcb_window_t w, xcb_get_geometry_reply_t *wgeom, xcb_get_window_at
/* Store initial geometry and emits signals so we inform that geometry have
* been set. */
#define HANDLE_GEOM(attr) \
c->geometry.attr = wgeom->attr; \
luaA_object_emit_signal(L, -1, "property::" #attr, 0);
HANDLE_GEOM(x)
HANDLE_GEOM(y)
HANDLE_GEOM(width)
HANDLE_GEOM(height)
#undef HANDLE_GEOM
c->geometry.x = wgeom->x;
c->geometry.y = wgeom->y;
c->geometry.width = wgeom->width;
c->geometry.height = wgeom->height;
luaA_object_emit_signal(L, -1, "property::x", 0);
luaA_object_emit_signal(L, -1, "property::y", 0);
luaA_object_emit_signal(L, -1, "property::width", 0);
luaA_object_emit_signal(L, -1, "property::height", 0);
luaA_object_emit_signal(L, -1, "property::window", 0);
luaA_object_emit_signal(L, -1, "property::geometry", 0);
/* Set border width */
@ -1933,53 +1941,79 @@ client_set_fullscreen(lua_State *L, int cidx, bool s)
}
}
/** Get a clients maximized state (horizontally and vertically).
* \param c The client.
* \return The maximized state.
*/
static int
client_get_maximized(client_t *c)
{
return c->maximized_horizontal && c->maximized_vertical;
}
/** Set a client horizontally|vertically maximized.
* \param L The Lua VM state.
* \param cidx The client index.
* \param s The maximized status.
*/
#define DO_FUNCTION_CLIENT_MAXIMIZED(type) \
void \
client_set_maximized_##type(lua_State *L, int cidx, bool s) \
{ \
client_t *c = luaA_checkudata(L, cidx, &client_class); \
if(c->maximized_##type != s) \
{ \
int abs_cidx = luaA_absindex(L, cidx); \
int max_before = client_get_maximized(c); \
c->maximized_##type = s; \
lua_pushstring(L, "maximized_"#type);\
luaA_object_emit_signal(L, abs_cidx, "request::geometry", 1); \
luaA_object_emit_signal(L, abs_cidx, "property::maximized_" #type, 0); \
if(max_before != client_get_maximized(c)) \
luaA_object_emit_signal(L, abs_cidx, "property::maximized", 0); \
stack_windows(); \
} \
}
DO_FUNCTION_CLIENT_MAXIMIZED(vertical)
DO_FUNCTION_CLIENT_MAXIMIZED(horizontal)
#undef DO_FUNCTION_CLIENT_MAXIMIZED
void
client_set_maximized_common(lua_State *L, int cidx, bool s, const char* type, const int val)
{
client_t *c = luaA_checkudata(L, cidx, &client_class);
/* Store the current and next state on 2 bit */
const client_maximized_t current = (
(c->maximized_vertical ? CLIENT_MAXIMIZED_V : CLIENT_MAXIMIZED_NONE)|
(c->maximized_horizontal ? CLIENT_MAXIMIZED_H : CLIENT_MAXIMIZED_NONE)|
(c->maximized ? CLIENT_MAXIMIZED_BOTH : CLIENT_MAXIMIZED_NONE)
);
client_maximized_t next = s ? (val | current) : (current & (~val));
/* When both are already set during startup, assume `maximized` is true*/
if (next == (CLIENT_MAXIMIZED_H|CLIENT_MAXIMIZED_V) && !globalconf.loop)
next = CLIENT_MAXIMIZED_BOTH;
if(current != next)
{
int abs_cidx = luaA_absindex(L, cidx);
int max_before = c->maximized;
int h_before = c->maximized_horizontal;
int v_before = c->maximized_vertical;
/*Update the client properties */
c->maximized_horizontal = !!(next & CLIENT_MAXIMIZED_H );
c->maximized_vertical = !!(next & CLIENT_MAXIMIZED_V );
c->maximized = !!(next & CLIENT_MAXIMIZED_BOTH);
/* Request the changes to be applied */
lua_pushstring(L, type);
luaA_object_emit_signal(L, abs_cidx, "request::geometry", 1);
/* Notify changes in the relevant properties */
if (h_before != c->maximized_horizontal)
luaA_object_emit_signal(L, abs_cidx, "property::maximized_horizontal", 0);
if (v_before != c->maximized_vertical)
luaA_object_emit_signal(L, abs_cidx, "property::maximized_vertical", 0);
if(max_before != c->maximized)
luaA_object_emit_signal(L, abs_cidx, "property::maximized", 0);
stack_windows();
}
}
/** Set a client maximized (horizontally and vertically).
* \param L The Lua VM state.
* \param cidx The client index.
* \param s Set or not the client maximized attribute.
*/
void
client_set_maximized(lua_State *L, int cidx, bool s)
{
client_set_maximized_horizontal(L, cidx, s);
client_set_maximized_vertical(L, cidx, s);
return client_set_maximized_common(
L, cidx, s, "maximized", CLIENT_MAXIMIZED_BOTH
);
}
void
client_set_maximized_horizontal(lua_State *L, int cidx, bool s)
{
return client_set_maximized_common(
L, cidx, s, "maximized_horizontal", CLIENT_MAXIMIZED_H
);
}
void
client_set_maximized_vertical(lua_State *L, int cidx, bool s)
{
return client_set_maximized_common(
L, cidx, s, "maximized_vertical", CLIENT_MAXIMIZED_V
);
}
/** Set a client above, or not.
@ -3020,15 +3054,9 @@ LUA_OBJECT_EXPORT_PROPERTY(client, client_t, sticky, lua_pushboolean)
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, size_hints_honor, lua_pushboolean)
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized_horizontal, lua_pushboolean)
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized_vertical, lua_pushboolean)
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, maximized, lua_pushboolean)
LUA_OBJECT_EXPORT_PROPERTY(client, client_t, startup_id, lua_pushstring)
static int
luaA_client_get_maximized(lua_State *L, client_t *c)
{
lua_pushboolean(L, client_get_maximized(c));
return 1;
}
static int
luaA_client_get_content(lua_State *L, client_t *c)
{

View File

@ -82,6 +82,10 @@ struct client_t
bool maximized_horizontal;
/** True if the client is maximized vertically */
bool maximized_vertical;
/** True if the client is maximized both horizontally and vertically by the
* the user
*/
bool maximized;
/** True if the client is above others */
bool above;
/** True if the client is below others */

View File

@ -23,6 +23,7 @@ local steps = {
local c = client.get()[1]
assert(not c.maximized_horizontal)
assert(not c.maximized_vertical )
assert(not c.maximized )
assert(not c.fullscreen )
c.maximized_horizontal = true
@ -81,10 +82,11 @@ local steps = {
local bw = c.border_width
assert(c.fullscreen)
assert(new_geo.x-bw==sgeo.x)
assert(new_geo.y-bw==sgeo.y)
assert(new_geo.x+new_geo.width+bw==sgeo.x+sgeo.width)
assert(new_geo.y+new_geo.height+bw==sgeo.y+sgeo.height)
assert(new_geo.x==sgeo.x)
assert(new_geo.y==sgeo.y)
assert(new_geo.x+new_geo.width+2*bw==sgeo.x+sgeo.width)
assert(new_geo.y+new_geo.height+2*bw==sgeo.y+sgeo.height)
c.fullscreen = false
@ -99,6 +101,43 @@ local steps = {
assert(new_geo[k] == v)
end
c.floating = true
awful.placement.centered(c)
original_geo = c:geometry()
c.maximized = true
return true
end,
function()
local c = client.get()[1]
local new_geo = c:geometry()
local sgeo = c.screen.workarea
assert(c.maximized)
assert(c.floating)
assert(new_geo.x==sgeo.x)
assert(new_geo.y==sgeo.y)
c.maximized = false
return true
end,
function()
local c = client.get()[1]
assert(not c.maximized)
assert(c.floating)
local new_geo = c:geometry()
for k,v in pairs(original_geo) do
assert(new_geo[k] == v)
end
return true
end
}