Set the wallpaper more intelligently

Usually users want a wallpaper on all their screens. With the current code, this
resulted in a loop of upload-wallpaper, {download-wallpaper, add-new-part,
upload-wallpaper}*.

Fix this by being more intelligent: Instead of setting the wallpaper
immediately, this now uses gears.timer.delayed_call() to set the wallpaper. All
following modifications which come in before the delayed call runs will still be
part of the current update. This should mean that during startup, there is just
a single upload of a wallpaper.

(The above is what happens if there is no wallpaper yet. If there is already
one, we use :create_similar() and thus should only upload the part of the
wallpaper that changed, but this doesn't really make a difference.)

As a side-effect, the new code no longer draws to the old wallpaper to modify
it, but always creates a copy of it. This means that:

Fixes https://github.com/awesomeWM/awesome/issues/288.
Closes https://github.com/awesomeWM/awesome/pull/530.

Signed-off-by: Uli Schlachter <psychon@znc.in>
This commit is contained in:
Uli Schlachter 2015-10-14 16:28:17 +02:00 committed by Daniel Hahler
parent 57e3927cbd
commit 112ce3b419
1 changed files with 45 additions and 25 deletions

View File

@ -8,6 +8,7 @@
local cairo = require("lgi").cairo local cairo = require("lgi").cairo
local color = require("gears.color") local color = require("gears.color")
local surface = require("gears.surface") local surface = require("gears.surface")
local timer = require("gears.timer")
local wallpaper = { mt = {} } local wallpaper = { mt = {} }
@ -27,29 +28,56 @@ do
end end
end end
--- Prepare the needed state for setting a wallpaper -- A cairo surface that we still want to set as the wallpaper
local pending_wallpaper = nil
--- Prepare the needed state for setting a wallpaper.
-- This function returns a cairo context through which a wallpaper can be drawn.
-- The context is only valid for a short time and should not be saved in a
-- global variable.
-- @param s The screen to set the wallpaper on or nil for all screens -- @param s The screen to set the wallpaper on or nil for all screens
-- @return The available geometry (table with entries width and height), a -- @return[1] The available geometry (table with entries width and height)
-- that should be used for setting the wallpaper and a cairo context for -- @return[1] A cairo context that the wallpaper should be drawn to
-- drawing to this surface function wallpaper.prepare_context(s)
local function prepare_wallpaper(s)
local geom = s and screen[s].geometry or root_geom local geom = s and screen[s].geometry or root_geom
local img = surface(root.wallpaper()) local cr
if not img then if not pending_wallpaper then
-- No wallpaper yet, create an image surface for just the part we need -- Prepare a pending wallpaper
img = cairo.ImageSurface(cairo.Format.RGB24, geom.width, geom.height) local wp = surface(root.wallpaper())
img:set_device_offset(-geom.x, -geom.y)
if not wp then
-- No wallpaper yet
wp = cairo.ImageSurface(cairo.Format.RGB24, 0, 0)
end
pending_wallpaper = wp:create_similar(cairo.Content.COLOR, root_geom.width, root_geom.height)
-- Copy the old wallpaper to the new one
cr = cairo.Context(pending_wallpaper)
cr:save()
cr.operator = cairo.Operator.SOURCE
cr:set_source_surface(wp, 0, 0)
cr:paint()
cr:restore()
-- Set the wallpaper (delayed)
timer.delayed_call(function()
local wp = pending_wallpaper
pending_wallpaper = nil
wallpaper.set(wp)
end)
else
-- Draw to the already-pending wallpaper
cr = cairo.Context(pending_wallpaper)
end end
local cr = cairo.Context(img)
-- Only draw to the selected area -- Only draw to the selected area
cr:translate(geom.x, geom.y) cr:translate(geom.x, geom.y)
cr:rectangle(0, 0, geom.width, geom.height) cr:rectangle(0, 0, geom.width, geom.height)
cr:clip() cr:clip()
return geom, img, cr return geom, cr
end end
--- Set the current wallpaper. --- Set the current wallpaper.
@ -75,7 +103,7 @@ end
-- @param background The background color that should be used. Gets handled via -- @param background The background color that should be used. Gets handled via
-- gears.color. The default is black. -- gears.color. The default is black.
function wallpaper.centered(surf, s, background) function wallpaper.centered(surf, s, background)
local geom, img, cr = prepare_wallpaper(s) local geom, cr = wallpaper.prepare_context(s)
local surf = surface(surf) local surf = surface(surf)
local background = color(background) local background = color(background)
@ -91,8 +119,6 @@ function wallpaper.centered(surf, s, background)
cr:clip() cr:clip()
cr:set_source_surface(surf, 0, 0) cr:set_source_surface(surf, 0, 0)
cr:paint() cr:paint()
wallpaper.set(img)
end end
--- Set a tiled wallpaper. --- Set a tiled wallpaper.
@ -101,7 +127,7 @@ end
-- all screens are set. -- all screens are set.
-- @param offset This can be set to a table with entries x and y. -- @param offset This can be set to a table with entries x and y.
function wallpaper.tiled(surf, s, offset) function wallpaper.tiled(surf, s, offset)
local geom, img, cr = prepare_wallpaper(s) local geom, cr = wallpaper.prepare_context(s)
if offset then if offset then
cr:translate(offset.x, offset.y) cr:translate(offset.x, offset.y)
@ -112,8 +138,6 @@ function wallpaper.tiled(surf, s, offset)
cr.source = pattern cr.source = pattern
cr.operator = cairo.Operator.SOURCE cr.operator = cairo.Operator.SOURCE
cr:paint() cr:paint()
wallpaper.set(img)
end end
--- Set a maximized wallpaper. --- Set a maximized wallpaper.
@ -124,7 +148,7 @@ end
-- The default is to honor the aspect ratio. -- The default is to honor the aspect ratio.
-- @param offset This can be set to a table with entries x and y. -- @param offset This can be set to a table with entries x and y.
function wallpaper.maximized(surf, s, ignore_aspect, offset) function wallpaper.maximized(surf, s, ignore_aspect, offset)
local geom, img, cr = prepare_wallpaper(s) local geom, cr = wallpaper.prepare_context(s)
local surf = surface(surf) local surf = surface(surf)
local w, h = surface.get_size(surf) local w, h = surface.get_size(surf)
local aspect_w = geom.width / w local aspect_w = geom.width / w
@ -147,8 +171,6 @@ function wallpaper.maximized(surf, s, ignore_aspect, offset)
cr:set_source_surface(surf, 0, 0) cr:set_source_surface(surf, 0, 0)
cr.operator = cairo.Operator.SOURCE cr.operator = cairo.Operator.SOURCE
cr:paint() cr:paint()
wallpaper.set(img)
end end
--- Set a fitting wallpaper. --- Set a fitting wallpaper.
@ -158,7 +180,7 @@ end
-- @param background The background color that should be used. Gets handled via -- @param background The background color that should be used. Gets handled via
-- gears.color. The default is black. -- gears.color. The default is black.
function wallpaper.fit(surf, s, background) function wallpaper.fit(surf, s, background)
local geom, img, cr = prepare_wallpaper(s) local geom, cr = wallpaper.prepare_context(s)
local surf = surface(surf) local surf = surface(surf)
local background = color(background) local background = color(background)
@ -179,8 +201,6 @@ function wallpaper.fit(surf, s, background)
cr:scale(scale, scale) cr:scale(scale, scale)
cr:set_source_surface(surf, 0, 0) cr:set_source_surface(surf, 0, 0)
cr:paint() cr:paint()
wallpaper.set(img)
end end
return wallpaper return wallpaper