diff --git a/common/xutil.h b/common/xutil.h index 0864feaa..5ee3b08d 100644 --- a/common/xutil.h +++ b/common/xutil.h @@ -28,6 +28,11 @@ #include "common/atoms.h" #include "common/util.h" +#define MAX_X11_COORDINATE INT16_MAX +#define MIN_X11_COORDINATE INT16_MIN +#define MAX_X11_SIZE UINT16_MAX +#define MIN_X11_SIZE 1 + static inline char * xutil_get_text_property_from_reply(xcb_get_property_reply_t *reply) { diff --git a/lib/awful/layout/init.lua b/lib/awful/layout/init.lua index b1da60e6..3d32ef08 100644 --- a/lib/awful/layout/init.lua +++ b/lib/awful/layout/init.lua @@ -172,8 +172,8 @@ function layout.arrange(screen) p.geometries = setmetatable({}, {__mode = "k"}) layout.get(screen).arrange(p) for c, g in pairs(p.geometries) do - g.width = g.width - c.border_width * 2 - useless_gap * 2 - g.height = g.height - c.border_width * 2 - useless_gap * 2 + g.width = math.max(1, g.width - c.border_width * 2 - useless_gap * 2) + g.height = math.max(1, g.height - c.border_width * 2 - useless_gap * 2) g.x = g.x + useless_gap g.y = g.y + useless_gap c:geometry(g) diff --git a/lib/awful/layout/suit/tile.lua b/lib/awful/layout/suit/tile.lua index 802fdce9..fce17e01 100644 --- a/lib/awful/layout/suit/tile.lua +++ b/lib/awful/layout/suit/tile.lua @@ -171,7 +171,7 @@ local function tile_group(gs, cls, wa, orientation, fact, group) end total_fact = total_fact + fact[i] end - size = math.min(size, available) + size = math.max(1, math.min(size, available)) local coord = wa[y] local used_size = 0 @@ -181,7 +181,7 @@ local function tile_group(gs, cls, wa, orientation, fact, group) local hints = {} local i = c - group.first +1 geom[width] = size - geom[height] = math.floor(unused * fact[i] / total_fact) + geom[height] = math.max(1, math.floor(unused * fact[i] / total_fact)) geom[x] = group.coord geom[y] = coord gs[cls[c]] = geom diff --git a/luaa.c b/luaa.c index e32544ed..35c93879 100644 --- a/luaa.c +++ b/luaa.c @@ -181,7 +181,7 @@ luaA_load_image(lua_State *L) static int luaA_set_preferred_icon_size(lua_State *L) { - globalconf.preferred_icon_size = luaL_checknumber(L, 1); + globalconf.preferred_icon_size = luaA_checkinteger_range(L, 1, 0, UINT32_MAX); return 0; } diff --git a/luaa.h b/luaa.h index 42404eeb..c26b4d6d 100644 --- a/luaa.h +++ b/luaa.h @@ -75,6 +75,18 @@ luaA_typerror(lua_State *L, int narg, const char *tname) return luaL_argerror(L, narg, msg); } +static inline int +luaA_rangerror(lua_State *L, int narg, double min, double max) +{ + const char *msg = lua_pushfstring(L, "value in [%f, %f] expected, got %f", + min, max, (double) lua_tonumber(L, narg)); +#if LUA_VERSION_NUM >= 502 + luaL_traceback(L, L, NULL, 2); + lua_concat(L, 2); +#endif + return luaL_argerror(L, narg, msg); +} + static inline void luaA_getuservalue(lua_State *L, int idx) { @@ -141,10 +153,37 @@ luaA_getopt_number(lua_State *L, int idx, const char *name, lua_Number def) return def; } +static inline lua_Number +luaA_checknumber_range(lua_State *L, int n, lua_Number min, lua_Number max) +{ + lua_Number result = lua_tonumber(L, n); + if (result < min || result > max) + luaA_rangerror(L, n, min, max); + return result; +} + +static inline lua_Number +luaA_optnumber_range(lua_State *L, int narg, lua_Number def, lua_Number min, lua_Number max) +{ + if (lua_isnoneornil(L, narg)) + return def; + return luaA_checknumber_range(L, narg, min, max); +} + +static inline lua_Number +luaA_getopt_number_range(lua_State *L, int idx, const char *name, lua_Number def, lua_Number min, lua_Number max) +{ + lua_getfield(L, idx, name); + if (lua_isnil(L, -1) || lua_isnumber(L, -1)) + def = luaA_optnumber_range(L, -1, def, min, max); + lua_pop(L, 1); + return def; +} + static inline int luaA_checkinteger(lua_State *L, int n) { - double d = lua_tonumber(L, n); + lua_Number d = lua_tonumber(L, n); if (d != (int)d) luaA_typerror(L, n, "integer"); return d; @@ -166,6 +205,33 @@ luaA_getopt_integer(lua_State *L, int idx, const char *name, lua_Integer def) return def; } +static inline int +luaA_checkinteger_range(lua_State *L, int n, int min, int max) +{ + int result = luaA_checkinteger(L, n); + if (result < min || result > max) + luaA_rangerror(L, n, min, max); + return result; +} + +static inline lua_Integer +luaA_optinteger_range(lua_State *L, int narg, lua_Integer def, int min, int max) +{ + if (lua_isnoneornil(L, narg)) + return def; + return luaA_checkinteger_range(L, narg, min, max); +} + +static inline int +luaA_getopt_integer_range(lua_State *L, int idx, const char *name, lua_Integer def, int min, int max) +{ + lua_getfield(L, idx, name); + if (lua_isnil(L, -1) || lua_isnumber(L, -1)) + def = luaA_optinteger_range(L, -1, def, min, max); + lua_pop(L, 1); + return def; +} + /** Push a area type to a table on stack. * \param L The Lua VM state. * \param geometry The area geometry to push. diff --git a/mouse.c b/mouse.c index 75301b56..78557dfd 100644 --- a/mouse.c +++ b/mouse.c @@ -31,6 +31,7 @@ #include "mouse.h" #include "common/util.h" +#include "common/xutil.h" #include "globalconf.h" #include "objects/client.h" #include "objects/drawin.h" @@ -111,7 +112,7 @@ mouse_query_pointer_root(int16_t *x, int16_t *y, xcb_window_t *child, uint16_t * * \param y Y-coordinate inside window. */ static inline void -mouse_warp_pointer(xcb_window_t window, int x, int y) +mouse_warp_pointer(xcb_window_t window, int16_t x, int16_t y) { xcb_warp_pointer(globalconf.connection, XCB_NONE, window, 0, 0, 0, 0, x, y); @@ -223,8 +224,8 @@ luaA_mouse_coords(lua_State *L) if(!mouse_query_pointer_root(&mouse_x, &mouse_y, NULL, &mask)) return 0; - x = luaA_getopt_number(L, 1, "x", mouse_x); - y = luaA_getopt_number(L, 1, "y", mouse_y); + x = luaA_getopt_integer_range(L, 1, "x", mouse_x, MIN_X11_COORDINATE, MAX_X11_COORDINATE); + y = luaA_getopt_integer_range(L, 1, "y", mouse_y, MIN_X11_COORDINATE, MAX_X11_COORDINATE); if(ignore_enter_notify) client_ignore_enterleave_events(); diff --git a/objects/client.c b/objects/client.c index d530376e..8ec2118d 100644 --- a/objects/client.c +++ b/objects/client.c @@ -98,6 +98,8 @@ #include "systray.h" #include "xwindow.h" +#include "math.h" + #include #include #include @@ -2538,7 +2540,7 @@ luaA_client_titlebar_ ## name(lua_State *L) \ if (lua_isnil(L, 2)) \ titlebar_resize(L, 1, c, index, 0); \ else \ - titlebar_resize(L, 1, c, index, luaL_checknumber(L, 2)); \ + titlebar_resize(L, 1, c, index, ceil(luaA_checknumber_range(L, 2, 0, MAX_X11_SIZE))); \ } \ \ luaA_object_push_item(L, 1, titlebar_get_drawable(L, c, 1, index)); \ @@ -2566,8 +2568,8 @@ luaA_client_geometry(lua_State *L) area_t geometry; luaA_checktable(L, 2); - geometry.x = luaA_getopt_number(L, 2, "x", c->geometry.x); - geometry.y = luaA_getopt_number(L, 2, "y", c->geometry.y); + geometry.x = round(luaA_getopt_number_range(L, 2, "x", c->geometry.x, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + geometry.y = round(luaA_getopt_number_range(L, 2, "y", c->geometry.y, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); if(client_isfixed(c)) { geometry.width = c->geometry.width; @@ -2575,8 +2577,8 @@ luaA_client_geometry(lua_State *L) } else { - geometry.width = luaA_getopt_number(L, 2, "width", c->geometry.width); - geometry.height = luaA_getopt_number(L, 2, "height", c->geometry.height); + geometry.width = ceil(luaA_getopt_number_range(L, 2, "width", c->geometry.width, MIN_X11_SIZE, MAX_X11_SIZE)); + geometry.height = ceil(luaA_getopt_number_range(L, 2, "height", c->geometry.height, MIN_X11_SIZE, MAX_X11_SIZE)); } client_resize(c, geometry, c->size_hints_honor); @@ -2600,8 +2602,8 @@ luaA_client_apply_size_hints(lua_State *L) area_t geometry = c->geometry; if(!client_isfixed(c)) { - geometry.width = luaL_checknumber(L, 2); - geometry.height = luaL_checknumber(L, 3); + geometry.width = ceil(luaA_checknumber_range(L, 2, MIN_X11_SIZE, MAX_X11_SIZE)); + geometry.height = ceil(luaA_checknumber_range(L, 3, MIN_X11_SIZE, MAX_X11_SIZE)); } if (c->size_hints_honor) diff --git a/objects/drawin.c b/objects/drawin.c index b550e050..7e4bd415 100644 --- a/objects/drawin.c +++ b/objects/drawin.c @@ -34,6 +34,7 @@ #include "drawin.h" #include "common/atoms.h" #include "common/xcursor.h" +#include "common/xutil.h" #include "event.h" #include "ewmh.h" #include "objects/client.h" @@ -41,6 +42,8 @@ #include "systray.h" #include "xwindow.h" +#include "math.h" + #include #include @@ -406,10 +409,10 @@ luaA_drawin_geometry(lua_State *L) area_t wingeom; luaA_checktable(L, 2); - wingeom.x = luaA_getopt_number(L, 2, "x", drawin->geometry.x); - wingeom.y = luaA_getopt_number(L, 2, "y", drawin->geometry.y); - wingeom.width = luaA_getopt_number(L, 2, "width", drawin->geometry.width); - wingeom.height = luaA_getopt_number(L, 2, "height", drawin->geometry.height); + wingeom.x = round(luaA_getopt_number_range(L, 2, "x", drawin->geometry.x, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + wingeom.y = round(luaA_getopt_number_range(L, 2, "y", drawin->geometry.y, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + wingeom.width = ceil(luaA_getopt_number_range(L, 2, "width", drawin->geometry.width, MIN_X11_SIZE, MAX_X11_SIZE)); + wingeom.height = ceil(luaA_getopt_number_range(L, 2, "height", drawin->geometry.height, MIN_X11_SIZE, MAX_X11_SIZE)); if(wingeom.width > 0 && wingeom.height > 0) drawin_moveresize(L, 1, wingeom); @@ -426,7 +429,8 @@ LUA_OBJECT_EXPORT_PROPERTY(drawin, drawin_t, visible, lua_pushboolean) static int luaA_drawin_set_x(lua_State *L, drawin_t *drawin) { - drawin_moveresize(L, -3, (area_t) { .x = luaA_checkinteger(L, -1), + int x = round(luaA_checknumber_range(L, -1, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + drawin_moveresize(L, -3, (area_t) { .x = x, .y = drawin->geometry.y, .width = drawin->geometry.width, .height = drawin->geometry.height }); @@ -443,8 +447,9 @@ luaA_drawin_get_x(lua_State *L, drawin_t *drawin) static int luaA_drawin_set_y(lua_State *L, drawin_t *drawin) { + int y = round(luaA_checknumber_range(L, -1, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x, - .y = luaA_checkinteger(L, -1), + .y = y, .width = drawin->geometry.width, .height = drawin->geometry.height }); return 0; @@ -460,9 +465,7 @@ luaA_drawin_get_y(lua_State *L, drawin_t *drawin) static int luaA_drawin_set_width(lua_State *L, drawin_t *drawin) { - int width = luaA_checkinteger(L, -1); - if(width <= 0) - luaL_error(L, "invalid width"); + int width = ceil(luaA_checknumber_range(L, -1, MIN_X11_SIZE, MAX_X11_SIZE)); drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x, .y = drawin->geometry.y, .width = width, @@ -480,9 +483,7 @@ luaA_drawin_get_width(lua_State *L, drawin_t *drawin) static int luaA_drawin_set_height(lua_State *L, drawin_t *drawin) { - int height = luaA_checkinteger(L, -1); - if(height <= 0) - luaL_error(L, "invalid height"); + int height = ceil(luaA_checknumber_range(L, -1, MIN_X11_SIZE, MAX_X11_SIZE)); drawin_moveresize(L, -3, (area_t) { .x = drawin->geometry.x, .y = drawin->geometry.y, .width = drawin->geometry.width, diff --git a/objects/window.c b/objects/window.c index 5f229225..15b582c8 100644 --- a/objects/window.c +++ b/objects/window.c @@ -29,6 +29,7 @@ #include "objects/window.h" #include "common/atoms.h" +#include "common/xutil.h" #include "ewmh.h" #include "property.h" #include "xwindow.h" @@ -352,7 +353,7 @@ window_set_xproperty(lua_State *L, xcb_window_t window, int prop_idx, int value_ } else if(prop->type == PROP_NUMBER || prop->type == PROP_BOOLEAN) { if (prop->type == PROP_NUMBER) - number = luaL_checkinteger(L, value_idx); + number = luaA_checkinteger_range(L, value_idx, 0, UINT32_MAX); else number = luaA_checkboolean(L, value_idx); data = &number; @@ -475,7 +476,7 @@ window_translate_type(window_type_t type) static int luaA_window_set_border_width(lua_State *L, window_t *c) { - window_set_border_width(L, -3, luaL_checknumber(L, -1)); + window_set_border_width(L, -3, round(luaA_checknumber_range(L, -1, 0, MAX_X11_SIZE))); return 0; } diff --git a/objects/window.h b/objects/window.h index 3afcbb35..5386fafe 100644 --- a/objects/window.h +++ b/objects/window.h @@ -26,6 +26,7 @@ #include "common/luaclass.h" #include "objects/button.h" #include "strut.h" +#include "math.h" /** Windows type */ typedef enum diff --git a/root.c b/root.c index f1236e86..99ebdb1c 100644 --- a/root.c +++ b/root.c @@ -30,9 +30,12 @@ #include "common/atoms.h" #include "common/xcursor.h" +#include "common/xutil.h" #include "objects/button.h" #include "xwindow.h" +#include "math.h" + #include #include #include @@ -265,8 +268,8 @@ luaA_root_fake_input(lua_State *L) { type = XCB_MOTION_NOTIFY; detail = luaA_checkboolean(L, 2); /* relative to the current position or not */ - x = luaL_checkinteger(L, 3); - y = luaL_checkinteger(L, 4); + x = round(luaA_checknumber_range(L, 3, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + y = round(luaA_checknumber_range(L, 4, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); } else return 0; diff --git a/strut.c b/strut.c index 6300f435..8d46de34 100644 --- a/strut.c +++ b/strut.c @@ -21,6 +21,7 @@ #include "strut.h" #include "luaa.h" +#include "math.h" /** Push a strut type to a table on stack. * \param L The Lua VM state. @@ -51,10 +52,10 @@ void luaA_tostrut(lua_State *L, int idx, strut_t *strut) { luaA_checktable(L, idx); - strut->left = luaA_getopt_integer(L, idx, "left", strut->left); - strut->right = luaA_getopt_integer(L, idx, "right", strut->right); - strut->top = luaA_getopt_integer(L, idx, "top", strut->top); - strut->bottom = luaA_getopt_integer(L, idx, "bottom", strut->bottom); + strut->left = ceil(luaA_getopt_number_range(L, idx, "left", strut->left, 0, UINT16_MAX)); + strut->right = ceil(luaA_getopt_number_range(L, idx, "right", strut->right, 0, UINT16_MAX)); + strut->top = ceil(luaA_getopt_number_range(L, idx, "top", strut->top, 0, UINT16_MAX)); + strut->bottom = ceil(luaA_getopt_number_range(L, idx, "bottom", strut->bottom, 0, UINT16_MAX)); } // vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/systray.c b/systray.c index fea74449..cad77b5a 100644 --- a/systray.c +++ b/systray.c @@ -21,6 +21,7 @@ #include "systray.h" #include "common/atoms.h" +#include "common/xutil.h" #include "objects/drawin.h" #include "xwindow.h" #include "globalconf.h" @@ -324,13 +325,13 @@ luaA_systray(lua_State *L) { size_t bg_len; drawin_t *w = luaA_checkudata(L, 1, &drawin_class); - int x = luaL_checkinteger(L, 2); - int y = luaL_checkinteger(L, 3); - int base_size = luaL_checkinteger(L, 4); + int x = round(luaA_checknumber_range(L, 2, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + int y = round(luaA_checknumber_range(L, 3, MIN_X11_COORDINATE, MAX_X11_COORDINATE)); + int base_size = ceil(luaA_checknumber_range(L, 4, MIN_X11_SIZE, MAX_X11_SIZE)); bool horiz = lua_toboolean(L, 5); const char *bg = luaL_checklstring(L, 6, &bg_len); bool revers = lua_toboolean(L, 7); - int spacing = luaL_checkinteger(L, 8); + int spacing = ceil(luaA_checknumber_range(L, 8, 0, MAX_X11_COORDINATE)); color_t bg_color; bool force_redraw = false; diff --git a/tests/test-awful-layout.lua b/tests/test-awful-layout.lua new file mode 100644 index 00000000..bc8ced7d --- /dev/null +++ b/tests/test-awful-layout.lua @@ -0,0 +1,122 @@ +-- This test hit the client layout code paths to see if there is errors. +-- it doesn't check if the layout are correct. + +local awful = require("awful") + +local first_layout = nil + +local t = nil + +local has_spawned = false + +local steps = { + +-- Add enough clients +function(count) + if count <= 1 and not has_spawned then + for _=1, 5 do awful.spawn("xterm") end + has_spawned = true + elseif #client.get() >= 5 then + + first_layout = client.focus:tags()[1].layout + + t = client.focus:tags()[1] + + return true + end +end, + +} + +local function next_layout() + awful.layout.inc(1) + + assert(client.focus:tags()[1].layout ~= first_layout) + + return true +end + +-- Test most properties for each layouts +local common_steps = { + function() + assert(#t:clients() == 5) + + t.master_count = 2 + + return true + end, + function() + t.master_count = 0 + + return true + end, + function() + t.master_count = 6 --more than #client.get(1) + + return true + end, + function() + t.master_count = 1 + + return true + end, + function() + t.column_count = 2 + + return true + end, + function() + t.column_count = 6 --more than #client.get(1) + + return true + end, + function() + t.column_count = 1 + + return true + end, + function() + t.master_fill_policy = t.master_fill_policy == "mwfact" and + "expand" or "mwfact" + + return true + end, + function() + t.master_width_factor = 0.75 + + return true + end, + function() + t.master_width_factor = 0 + + return true + end, + function() + t.master_width_factor = 1 + + return true + end, + function() + t.master_width_factor = 0.5 + + return true + end, + function() + t.gap = t.gap == 0 and 5 or 0 + + return true + end, +} + +local first = false +for _ in ipairs(awful.layout.layouts) do + if not first then + first = true + else + awful.util.table.merge(steps, {next_layout}) + end + + awful.util.table.merge(steps, common_steps) +end + +require("_runner").run_steps(steps)