Move size hints handling back into C (FS#1117)

Having this in lua means that size hints are only applied after the client got
resized. The bad side effect of this is some flickering if awesome is being
slow. And apparently, it is slow for way too many people...

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2013-03-10 12:13:32 +01:00
parent 5365dfdb79
commit 23b30be106
6 changed files with 141 additions and 160 deletions

View File

@ -335,7 +335,7 @@ event_handle_configurerequest(xcb_configure_request_event_t *ev)
lua_pop(globalconf.L, 1); lua_pop(globalconf.L, 1);
} }
if(!client_resize(c, geometry)) if(!client_resize(c, geometry, false))
/* ICCCM 4.1.5 / 4.2.3, if nothing was changed, send an event saying so */ /* ICCCM 4.1.5 / 4.2.3, if nothing was changed, send an event saying so */
client_send_configure(c); client_send_configure(c);
} }

View File

@ -1,143 +0,0 @@
---------------------------------------------------------------------------
-- @author Uli Schlachter
-- @copyright 2011 Uli Schlachter
-- @release @AWESOME_VERSION@
---------------------------------------------------------------------------
local client = client
local math = math
--- Implements ICCCM handling.
-- awful.icccm
-- Make sure we don't get into an endless loop
local size_hints_lock = false
local function get_titlebar_size(c)
local res, _ = {}
_, res.top = c:titlebar_top()
_, res.right = c:titlebar_right()
_, res.bottom = c:titlebar_bottom()
_, res.left = c:titlebar_left()
res.x = res.left
res.y = res.top
res.width = res.left + res.right
res.height = res.top + res.bottom
return res
end
local function apply_size_hints(c)
if size_hints_lock then return end
if not c.size_hints_honor then return end
-- Fullscreen clients don't get size hints applied!
if c.fullscreen then return end
size_hints_lock = true
local geom = c:geometry()
local hints = c.size_hints
local titlebar = get_titlebar_size(c)
-- Apply size hints to the client size without titlebars
geom.width = geom.width - titlebar.width
geom.height = geom.height - titlebar.height
local basew, baseh
local real_basew, real_baseh = 0, 0
if hints.base_width then
basew, baseh = hints.base_width, hints.base_height
real_basew, real_baseh = basew, baseh
elseif hints.min_width then
-- Base size is substituted with min size if not specified
basew, baseh = hints.min_width, hints.min_height
else
basew, baseh = 0, 0
end
-- Handle the size aspect ratio
if hints.min_aspect_den then
-- Apply the size aspect
if hints.min_aspect_den > 0 and hints.max_aspect_den > 0 and
geom.height > real_baseh and geom.width > real_basew then
-- ICCCM mandates:
-- If a base size is provided along with the aspect ratio fields, the
-- base size should be subtracted from the window size prior to checking
-- that the aspect ratio falls in range. If a base size is not provided,
-- nothing should be subtracted from the window size. (The minimum size
-- is not to be used in place of the base size for this purpose.)
local dx = geom.width - real_basew
local dy = geom.height - real_baseh
local ratio = dx / dy
local min = hints.min_aspect_num / hints.min_aspect_den
local max = hints.max_aspect_num / hints.max_aspect_den
if max > 0 and min > 0 and ratio > 0 then
if ratio < min then
-- dx is lower than allowed, make dy lower to compensate this
-- (+ 0.5 to force proper rounding).
dy = dx / min + 0.5
geom.width = dx + real_basew
geom.height = dy + real_baseh
elseif ratio > max then
-- dx is too high, lower it (+0.5 for proper rounding)
dx = dy * max + 0.5
geom.width = dx + real_basew
geom.height = dy + real_baseh;
end
-- Make sure these are integers
geom.width = math.floor(geom.width)
geom.height = math.floor(geom.height)
end
end
end
-- Handle the minimum size
local minw, minh
if hints.min_width then
minw, minh = hints.min_width, hints.min_height
elseif hints.base_width then
-- min size is substituted with base size if not specified
minw, minh = hints.base_width, hints.base_height
else
minw, minh = 0, 0
end
if minw ~= nil and minw > 0 and geom.width < minw then
geom.width = minw
end
if minh ~= nil and minh > 0 and geom.height < minh then
geom.height = minh
end
-- Handle the maximum size
if hints.max_width ~= nil and hints.max_width > 0 and hints.max_width < geom.width then
geom.width = hints.max_width
end
if hints.max_height ~= nil and hints.max_height > 0 and hints.max_height < geom.height then
geom.height = hints.max_height
end
-- Handle the size increment
if hints.width_inc and hints.width_inc > 0 then
local function apply_inc(size, inc, base)
local i = size - base
if i < 0 then i = 0 end
-- Round size down to a multiple of inc, ignoring the base size
return size - math.fmod(i, inc)
end
geom.width = apply_inc(geom.width, hints.width_inc, basew)
geom.height = apply_inc(geom.height, hints.height_inc, baseh)
end
c:geometry({ width = geom.width + titlebar.width, height = geom.height + titlebar.height })
size_hints_lock = false
end
client.connect_signal("property::width", apply_size_hints)
client.connect_signal("property::height", apply_size_hints)
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -28,7 +28,6 @@ return
startup_notification = require("awful.startup_notification"); startup_notification = require("awful.startup_notification");
tooltip = require("awful.tooltip"); tooltip = require("awful.tooltip");
ewmh = require("awful.ewmh"); ewmh = require("awful.ewmh");
icccm = require("awful.icccm");
titlebar = require("awful.titlebar"); titlebar = require("awful.titlebar");
} }

View File

@ -585,29 +585,154 @@ HANDLE_GEOM(height)
lua_pop(globalconf.L, 1); lua_pop(globalconf.L, 1);
} }
static void
client_remove_titlebar_geometry(client_t *c, area_t *geometry)
{
geometry->x += c->titlebar[CLIENT_TITLEBAR_LEFT].size;
geometry->y += c->titlebar[CLIENT_TITLEBAR_TOP].size;
geometry->width -= c->titlebar[CLIENT_TITLEBAR_LEFT].size;
geometry->width -= c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
geometry->height -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
geometry->height -= c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
}
static void
client_add_titlebar_geometry(client_t *c, area_t *geometry)
{
geometry->x -= c->titlebar[CLIENT_TITLEBAR_LEFT].size;
geometry->y -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
geometry->width += c->titlebar[CLIENT_TITLEBAR_LEFT].size;
geometry->width += c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
geometry->height += c->titlebar[CLIENT_TITLEBAR_TOP].size;
geometry->height += c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
}
/** Send a synthetic configure event to a window. /** Send a synthetic configure event to a window.
*/ */
void void
client_send_configure(client_t *c) client_send_configure(client_t *c)
{ {
area_t geometry = c->geometry; area_t geometry = c->geometry;
geometry.x += c->titlebar[CLIENT_TITLEBAR_LEFT].size;
geometry.y += c->titlebar[CLIENT_TITLEBAR_TOP].size;
geometry.width -= c->titlebar[CLIENT_TITLEBAR_LEFT].size;
geometry.width -= c->titlebar[CLIENT_TITLEBAR_RIGHT].size;
geometry.height -= c->titlebar[CLIENT_TITLEBAR_TOP].size;
geometry.height -= c->titlebar[CLIENT_TITLEBAR_BOTTOM].size;
client_remove_titlebar_geometry(c, &geometry);
xwindow_configure(c->window, geometry, c->border_width); xwindow_configure(c->window, geometry, c->border_width);
} }
/** Apply size hints to the client's new geometry.
*/
static area_t
client_apply_size_hints(client_t *c, area_t geometry)
{
int32_t minw = 0, minh = 0;
int32_t basew = 0, baseh = 0, real_basew = 0, real_baseh = 0;
if (c->fullscreen)
return geometry;
/* Size hints are applied to the window without any decoration */
client_remove_titlebar_geometry(c, &geometry);
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
{
basew = c->size_hints.base_width;
baseh = c->size_hints.base_height;
real_basew = basew;
real_baseh = baseh;
}
else if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
{
/* base size is substituted with min size if not specified */
basew = c->size_hints.min_width;
baseh = c->size_hints.min_height;
}
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MIN_SIZE)
{
minw = c->size_hints.min_width;
minh = c->size_hints.min_height;
}
else if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_SIZE)
{
/* min size is substituted with base size if not specified */
minw = c->size_hints.base_width;
minh = c->size_hints.base_height;
}
/* Handle the size aspect ratio */
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_ASPECT
&& c->size_hints.min_aspect_den > 0
&& c->size_hints.max_aspect_den > 0
&& geometry.height > real_baseh
&& geometry.width > real_basew)
{
/* ICCCM mandates:
* If a base size is provided along with the aspect ratio fields, the base size should be subtracted from the
* window size prior to checking that the aspect ratio falls in range. If a base size is not provided, nothing
* should be subtracted from the window size. (The minimum size is not to be used in place of the base size for
* this purpose.)
*/
double dx = geometry.width - real_basew;
double dy = geometry.height - real_baseh;
double ratio = dx / dy;
double min = c->size_hints.min_aspect_num / (double) c->size_hints.min_aspect_den;
double max = c->size_hints.max_aspect_num / (double) c->size_hints.max_aspect_den;
if(max > 0 && min > 0 && ratio > 0)
{
if(ratio < min)
{
/* dx is lower than allowed, make dy lower to compensate this (+ 0.5 to force proper rounding). */
dy = dx / min + 0.5;
geometry.width = dx + real_basew;
geometry.height = dy + real_baseh;
} else if(ratio > max)
{
/* dx is too high, lower it (+0.5 for proper rounding) */
dx = dy * max + 0.5;
geometry.width = dx + real_basew;
geometry.height = dy + real_baseh;
}
}
}
/* Handle the minimum size */
geometry.width = MAX(geometry.width, minw);
geometry.height = MAX(geometry.height, minh);
/* Handle the maximum size */
if(c->size_hints.flags & XCB_ICCCM_SIZE_HINT_P_MAX_SIZE)
{
if(c->size_hints.max_width)
geometry.width = MIN(geometry.width, c->size_hints.max_width);
if(c->size_hints.max_height)
geometry.height = MIN(geometry.height, c->size_hints.max_height);
}
/* Handle the size increment */
if(c->size_hints.flags & (XCB_ICCCM_SIZE_HINT_P_RESIZE_INC | XCB_ICCCM_SIZE_HINT_BASE_SIZE)
&& c->size_hints.width_inc && c->size_hints.height_inc)
{
uint16_t t1 = geometry.width, t2 = geometry.height;
unsigned_subtract(t1, basew);
unsigned_subtract(t2, baseh);
geometry.width -= t1 % c->size_hints.width_inc;
geometry.height -= t2 % c->size_hints.height_inc;
}
client_add_titlebar_geometry(c, &geometry);
return geometry;
}
static void static void
client_resize_do(client_t *c, area_t geometry, bool force_notice) client_resize_do(client_t *c, area_t geometry, bool force_notice, bool honor_hints)
{ {
bool send_notice = force_notice; bool send_notice = force_notice;
bool hide_titlebars = c->fullscreen; bool hide_titlebars = c->fullscreen;
screen_t *new_screen = screen_getbycoord(geometry.x, geometry.y); screen_t *new_screen = screen_getbycoord(geometry.x, geometry.y);
if (honor_hints)
geometry = client_apply_size_hints(c, geometry);
if(c->geometry.width == geometry.width if(c->geometry.width == geometry.width
&& c->geometry.height == geometry.height) && c->geometry.height == geometry.height)
send_notice = true; send_notice = true;
@ -710,11 +835,11 @@ client_resize_do(client_t *c, area_t geometry, bool force_notice)
* The sizes given as parameters are with borders! * The sizes given as parameters are with borders!
* \param c Client to resize. * \param c Client to resize.
* \param geometry New window geometry. * \param geometry New window geometry.
* \param hints Use size hints. * \param honor_hints Use size hints.
* \return true if an actual resize occurred. * \return true if an actual resize occurred.
*/ */
bool bool
client_resize(client_t *c, area_t geometry) client_resize(client_t *c, area_t geometry, bool honor_hints)
{ {
area_t area; area_t area;
@ -743,7 +868,7 @@ client_resize(client_t *c, area_t geometry)
|| c->geometry.width != geometry.width || c->geometry.width != geometry.width
|| c->geometry.height != geometry.height) || c->geometry.height != geometry.height)
{ {
client_resize_do(c, geometry, false); client_resize_do(c, geometry, false, honor_hints);
return true; return true;
} }
@ -842,7 +967,7 @@ client_set_fullscreen(lua_State *L, int cidx, bool s)
luaA_object_emit_signal(L, abs_cidx, "request::fullscreen", 1); luaA_object_emit_signal(L, abs_cidx, "request::fullscreen", 1);
luaA_object_emit_signal(L, abs_cidx, "property::fullscreen", 0); luaA_object_emit_signal(L, abs_cidx, "property::fullscreen", 0);
/* Force a client resize, so that titlebars get shown/hidden */ /* Force a client resize, so that titlebars get shown/hidden */
client_resize_do(c, c->geometry, true); client_resize_do(c, c->geometry, true, false);
stack_windows(); stack_windows();
} }
} }
@ -1445,7 +1570,7 @@ titlebar_resize(client_t *c, client_titlebar_t bar, int size)
} }
c->titlebar[bar].size = size; c->titlebar[bar].size = size;
client_resize_do(c, geometry, true); client_resize_do(c, geometry, true, false);
} }
#define HANDLE_TITLEBAR(name, index) \ #define HANDLE_TITLEBAR(name, index) \
@ -1501,7 +1626,7 @@ luaA_client_geometry(lua_State *L)
geometry.height = luaA_getopt_number(L, 2, "height", c->geometry.height); geometry.height = luaA_getopt_number(L, 2, "height", c->geometry.height);
} }
client_resize(c, geometry); client_resize(c, geometry, true);
} }
return luaA_pusharea(L, c->geometry); return luaA_pusharea(L, c->geometry);

View File

@ -141,7 +141,7 @@ void client_ban(client_t *);
void client_ban_unfocus(client_t *); void client_ban_unfocus(client_t *);
void client_unban(client_t *); void client_unban(client_t *);
void client_manage(xcb_window_t, xcb_get_geometry_reply_t *, bool); void client_manage(xcb_window_t, xcb_get_geometry_reply_t *, bool);
bool client_resize(client_t *, area_t); bool client_resize(client_t *, area_t, bool);
void client_unmanage(client_t *, bool); void client_unmanage(client_t *, bool);
void client_kill(client_t *); void client_kill(client_t *);
void client_set_sticky(lua_State *, int, bool); void client_set_sticky(lua_State *, int, bool);

View File

@ -376,7 +376,7 @@ screen_client_moveto(client_t *c, screen_t *new_screen, bool doresize)
new_geometry.y = to.y + to.height - new_geometry.height; new_geometry.y = to.y + to.height - new_geometry.height;
/* move / resize the client */ /* move / resize the client */
client_resize(c, new_geometry); client_resize(c, new_geometry, false);
luaA_object_push(globalconf.L, c); luaA_object_push(globalconf.L, c);
luaA_object_emit_signal(globalconf.L, -1, "property::screen", 0); luaA_object_emit_signal(globalconf.L, -1, "property::screen", 0);
lua_pop(globalconf.L, 1); lua_pop(globalconf.L, 1);