diff --git a/event.c b/event.c index dfe046dd3..6c1eff06c 100644 --- a/event.c +++ b/event.c @@ -335,7 +335,7 @@ event_handle_configurerequest(xcb_configure_request_event_t *ev) 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 */ client_send_configure(c); } diff --git a/lib/awful/icccm.lua.in b/lib/awful/icccm.lua.in deleted file mode 100644 index 98a5edac3..000000000 --- a/lib/awful/icccm.lua.in +++ /dev/null @@ -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 diff --git a/lib/awful/init.lua.in b/lib/awful/init.lua.in index 7f120b02d..cb64420c4 100644 --- a/lib/awful/init.lua.in +++ b/lib/awful/init.lua.in @@ -28,7 +28,6 @@ return startup_notification = require("awful.startup_notification"); tooltip = require("awful.tooltip"); ewmh = require("awful.ewmh"); - icccm = require("awful.icccm"); titlebar = require("awful.titlebar"); } diff --git a/objects/client.c b/objects/client.c index d7ec17f5b..1a32dc7da 100644 --- a/objects/client.c +++ b/objects/client.c @@ -585,29 +585,154 @@ HANDLE_GEOM(height) 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. */ void client_send_configure(client_t *c) { 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); } +/** 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 -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 hide_titlebars = c->fullscreen; 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 && c->geometry.height == geometry.height) 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! * \param c Client to resize. * \param geometry New window geometry. - * \param hints Use size hints. + * \param honor_hints Use size hints. * \return true if an actual resize occurred. */ bool -client_resize(client_t *c, area_t geometry) +client_resize(client_t *c, area_t geometry, bool honor_hints) { area_t area; @@ -743,7 +868,7 @@ client_resize(client_t *c, area_t geometry) || c->geometry.width != geometry.width || c->geometry.height != geometry.height) { - client_resize_do(c, geometry, false); + client_resize_do(c, geometry, false, honor_hints); 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, "property::fullscreen", 0); /* 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(); } } @@ -1445,7 +1570,7 @@ titlebar_resize(client_t *c, client_titlebar_t bar, int size) } c->titlebar[bar].size = size; - client_resize_do(c, geometry, true); + client_resize_do(c, geometry, true, false); } #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); } - client_resize(c, geometry); + client_resize(c, geometry, true); } return luaA_pusharea(L, c->geometry); diff --git a/objects/client.h b/objects/client.h index 2f24a764d..43c6f3e43 100644 --- a/objects/client.h +++ b/objects/client.h @@ -141,7 +141,7 @@ void client_ban(client_t *); void client_ban_unfocus(client_t *); void client_unban(client_t *); 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_kill(client_t *); void client_set_sticky(lua_State *, int, bool); diff --git a/screen.c b/screen.c index 81e063ee4..cf22b4c21 100644 --- a/screen.c +++ b/screen.c @@ -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; /* move / resize the client */ - client_resize(c, new_geometry); + client_resize(c, new_geometry, false); luaA_object_push(globalconf.L, c); luaA_object_emit_signal(globalconf.L, -1, "property::screen", 0); lua_pop(globalconf.L, 1);