Add better error handling to gears.surface

Previously, a Lua error was thrown when loading a file failed. Most callers are
not prepared for this and the result is less than optimal.

This commit makes the functions print the errors and return nil instead. For
callers that want to handle errors themselves, "_silent" variants of the
functions are introduced which just return errors to the caller.

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2016-01-15 18:27:22 +01:00
parent 2e58a9c6eb
commit 58ecd92b8e
1 changed files with 62 additions and 7 deletions

View File

@ -9,6 +9,7 @@ local setmetatable = setmetatable
local type = type local type = type
local capi = { awesome = awesome } local capi = { awesome = awesome }
local cairo = require("lgi").cairo local cairo = require("lgi").cairo
local gdebug = require("gears.debug")
-- Keep this in sync with build-utils/lgi-check.sh! -- Keep this in sync with build-utils/lgi-check.sh!
local ver_major, ver_minor, ver_patch = string.match(require('lgi.version'), '(%d)%.(%d)%.(%d)') local ver_major, ver_minor, ver_patch = string.match(require('lgi.version'), '(%d)%.(%d)%.(%d)')
@ -19,13 +20,22 @@ end
local surface = { mt = {} } local surface = { mt = {} }
local surface_cache = setmetatable({}, { __mode = 'v' }) local surface_cache = setmetatable({}, { __mode = 'v' })
local function get_empty_surface()
return cairo.ImageSurface(cairo.Format.ARGB32, 0, 0)
end
--- Try to convert the argument into an lgi cairo surface. --- Try to convert the argument into an lgi cairo surface.
-- This is usually needed for loading images by file name. -- This is usually needed for loading images by file name.
function surface.load_uncached(_surface) -- @param _surface The surface to load or nil
-- @param default The default value to return on error; when nil, then a surface
-- in an error state is returned.
-- @return The loaded surface, or the replacement default
-- @return An error message, or nil on success
function surface.load_uncached_silently(_surface, default)
local file local file
-- Nil is not changed -- On nil, return an empty surface
if not _surface then if not _surface then
return nil return get_empty_surface()
end end
-- Remove from cache if it was cached -- Remove from cache if it was cached
surface_cache[_surface] = nil surface_cache[_surface] = nil
@ -35,8 +45,15 @@ function surface.load_uncached(_surface)
end end
-- Strings are assumed to be file names and get loaded -- Strings are assumed to be file names and get loaded
if type(_surface) == "string" then if type(_surface) == "string" then
local err
file = _surface file = _surface
_surface = capi.awesome.load_image(file) _surface, err = capi.awesome.load_image(file)
if not _surface then
if type(default) == 'nil' then
default = get_empty_surface()
end
return default, err
end
end end
-- Everything else gets forced into a surface -- Everything else gets forced into a surface
_surface = cairo.Surface(_surface, true) _surface = cairo.Surface(_surface, true)
@ -47,14 +64,53 @@ function surface.load_uncached(_surface)
return _surface return _surface
end end
function surface.load(_surface) --- Try to convert the argument into an lgi cairo surface.
-- This is usually needed for loading images by file name and uses a cache.
-- In contrast to `load()`, errors are returned to the caller.
-- @param _surface The surface to load or nil
-- @param default The default value to return on error; when nil, then a surface
-- in an error state is returned.
-- @return The loaded surface, or the replacement default, or nil if called with
-- nil.
-- @return An error message, or nil on success
function surface.load_silently(_surface, default)
if type(_surface) == "string" then if type(_surface) == "string" then
local cache = surface_cache[_surface] local cache = surface_cache[_surface]
if cache then if cache then
return cache return cache
end end
end end
return surface.load_uncached(_surface) return surface.load_uncached_silently(_surface, default)
end
local function do_load_and_handle_errors(_surface, func)
if type(_surface) == 'nil' then
return get_empty_surface()
end
local result, err = func(_surface, false)
if result then
return result
end
gdebug.print_error("Failed to load '" .. tostring(_surface) .. "': " .. tostring(err))
return get_empty_surface()
end
--- Try to convert the argument into an lgi cairo surface.
-- This is usually needed for loading images by file name. Errors are handled
-- via `gears.debug.print_error`.
-- @param _surface The surface to load or nil
-- @return The loaded surface, or nil
function surface.load_uncached(_surface)
return do_load_and_handle_errors(_surface, surface.load_uncached_silently)
end
--- Try to convert the argument into an lgi cairo surface.
-- This is usually needed for loading images by file name. Errors are handled
-- via `gears.debug.print_error`.
-- @param _surface The surface to load or nil
-- @return The loaded surface, or nil
function surface.load(_surface)
return do_load_and_handle_errors(_surface, surface.load_silently)
end end
function surface.mt:__call(...) function surface.mt:__call(...)
@ -70,7 +126,6 @@ function surface.get_size(surf)
return w - x, h - y return w - x, h - y
end end
--- Create a copy of a cairo surface. --- Create a copy of a cairo surface.
-- The surfaces returned by `surface.load` are cached and must not be -- The surfaces returned by `surface.load` are cached and must not be
-- modified to avoid unintended side-effects. This function allows to create -- modified to avoid unintended side-effects. This function allows to create