Merge branch 'surface-errors' of https://github.com/psychon/awesome

This commit is contained in:
Uli Schlachter 2016-01-17 16:01:03 +01:00
commit 3793c339ce
9 changed files with 92 additions and 39 deletions

15
draw.c
View File

@ -229,23 +229,18 @@ draw_dup_image_surface(cairo_surface_t *surface)
/** Load the specified path into a cairo surface /** Load the specified path into a cairo surface
* \param L Lua state * \param L Lua state
* \param path file to load * \param path file to load
* \param error A place to store an error message, if needed
* \return A cairo image surface or NULL on error. * \return A cairo image surface or NULL on error.
*/ */
cairo_surface_t * cairo_surface_t *
draw_load_image(lua_State *L, const char *path) draw_load_image(lua_State *L, const char *path, GError **error)
{ {
GError *error = NULL;
cairo_surface_t *ret; cairo_surface_t *ret;
GdkPixbuf *buf = gdk_pixbuf_new_from_file(path, &error); GdkPixbuf *buf = gdk_pixbuf_new_from_file(path, error);
if (!buf) { if (!buf)
luaL_where(L, 1); /* error was set above */
lua_pushstring(L, error->message);
lua_concat(L, 2);
g_error_free(error);
lua_error(L);
return NULL; return NULL;
}
ret = draw_surface_from_pixbuf(buf); ret = draw_surface_from_pixbuf(buf);
g_object_unref(buf); g_object_unref(buf);

3
draw.h
View File

@ -25,6 +25,7 @@
#include <xcb/xcb.h> #include <xcb/xcb.h>
#include <cairo.h> #include <cairo.h>
#include <lua.h> #include <lua.h>
#include <glib.h> /* for GError */
#include "common/util.h" #include "common/util.h"
@ -70,7 +71,7 @@ a_iso2utf8(const char *str, ssize_t len, char **dest, ssize_t *dlen)
cairo_surface_t *draw_surface_from_data(int width, int height, uint32_t *data); cairo_surface_t *draw_surface_from_data(int width, int height, uint32_t *data);
cairo_surface_t *draw_dup_image_surface(cairo_surface_t *surface); cairo_surface_t *draw_dup_image_surface(cairo_surface_t *surface);
cairo_surface_t *draw_load_image(lua_State *L, const char *path); cairo_surface_t *draw_load_image(lua_State *L, const char *path, GError **error);
xcb_visualtype_t *draw_find_visual(const xcb_screen_t *s, xcb_visualid_t visual); xcb_visualtype_t *draw_find_visual(const xcb_screen_t *s, xcb_visualid_t visual);
xcb_visualtype_t *draw_default_visual(const xcb_screen_t *s); xcb_visualtype_t *draw_default_visual(const xcb_screen_t *s);

View File

@ -558,7 +558,7 @@ function menu.entry(parent, args)
if type(args.cmd) == "table" then if type(args.cmd) == "table" then
if args.theme.submenu_icon then if args.theme.submenu_icon then
submenu = wibox.widget.imagebox() submenu = wibox.widget.imagebox()
submenu:set_image(surface.load(args.theme.submenu_icon)) submenu:set_image(args.theme.submenu_icon)
else else
submenu = wibox.widget.textbox() submenu = wibox.widget.textbox()
submenu:set_font(args.theme.font) submenu:set_font(args.theme.font)

View File

@ -107,9 +107,7 @@ function taglist.taglist_label(t, args)
text = text .. "</span>" text = text .. "</span>"
end end
if not taglist_disable_icon then if not taglist_disable_icon then
if tag.geticon(t) and type(tag.geticon(t)) == "image" then if tag.geticon(t) then
icon = tag.geticon(t)
elseif tag.geticon(t) then
icon = surface.load(tag.geticon(t)) icon = surface.load(tag.geticon(t))
end end
end end

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

View File

@ -562,13 +562,7 @@ function naughty.notify(args)
end end
-- is the icon file readable? -- is the icon file readable?
local success, res = pcall(function() return surface.load_uncached(icon) end) local icon = surface.load_uncached(icon)
if success then
icon = res
else
io.stderr:write(string.format("naughty: Couldn't load image '%s': %s\n", tostring(icon), res))
icon = nil
end
-- if we have an icon, use it -- if we have an icon, use it
if icon then if icon then

View File

@ -72,17 +72,16 @@ end
--- Set an imagebox' image --- Set an imagebox' image
-- @param image Either a string or a cairo image surface. A string is -- @param image Either a string or a cairo image surface. A string is
-- interpreted as the path to a png image file. -- interpreted as the path to a png image file.
-- @return true on success, false if the image cannot be used
function imagebox:set_image(image) function imagebox:set_image(image)
local image = image local image = image
if type(image) == "string" then if type(image) == "string" then
local success, result = pcall(surface.load, image) image = surface.load(image)
if not success then if not image then
print("Error while reading '" .. image .. "': " .. result)
print(debug.traceback()) print(debug.traceback())
return false return false
end end
image = result
end end
image = surface.load(image) image = surface.load(image)

16
luaa.c
View File

@ -147,16 +147,24 @@ luaA_restart(lua_State *L)
/** Load an image from a given path. /** Load an image from a given path.
* *
* @param name The file name. * @param name The file name.
* @return A cairo surface as light user datum. * @return[1] A cairo surface as light user datum.
* @return[2] nil
* @treturn[2] string Error message
* @function load_image * @function load_image
*/ */
static int static int
luaA_load_image(lua_State *L) luaA_load_image(lua_State *L)
{ {
GError *error = NULL;
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
cairo_surface_t *surface = draw_load_image(L, filename); cairo_surface_t *surface = draw_load_image(L, filename, &error);
if (!surface) if (!surface) {
return 0; lua_pushnil(L);
lua_pushstring(L, error->message);
g_error_free(error);
return 2;
}
/* lua has to make sure to free the ref or we have a leak */ /* lua has to make sure to free the ref or we have a leak */
lua_pushlightuserdata(L, surface); lua_pushlightuserdata(L, surface);
return 1; return 1;

View File

@ -124,8 +124,11 @@ drawable_set_geometry(lua_State *L, int didx, area_t geom)
static int static int
luaA_drawable_get_surface(lua_State *L, drawable_t *drawable) luaA_drawable_get_surface(lua_State *L, drawable_t *drawable)
{ {
/* Lua gets its own reference which it will have to destroy */ if (drawable->surface)
lua_pushlightuserdata(L, cairo_surface_reference(drawable->surface)); /* Lua gets its own reference which it will have to destroy */
lua_pushlightuserdata(L, cairo_surface_reference(drawable->surface));
else
lua_pushnil(L);
return 1; return 1;
} }