Merge pull request #3343 from Elv13/awful_wallpaper

Rewrite the wallpaper API
This commit is contained in:
Emmanuel Lepage Vallée 2021-10-02 13:38:21 -07:00 committed by GitHub
commit 2bca64b89c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 3146 additions and 245 deletions

View File

@ -77,7 +77,7 @@ mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon,
menubar.utils.terminal = terminal -- Set the terminal for applications that require it menubar.utils.terminal = terminal -- Set the terminal for applications that require it
-- }}} -- }}}
-- {{{ Tag -- {{{ Tag layout
-- @DOC_LAYOUT@ -- @DOC_LAYOUT@
-- Table of layouts to cover with awful.layout.inc, order matters. -- Table of layouts to cover with awful.layout.inc, order matters.
tag.connect_signal("request::default_layouts", function() tag.connect_signal("request::default_layouts", function()
@ -99,6 +99,27 @@ tag.connect_signal("request::default_layouts", function()
end) end)
-- }}} -- }}}
-- {{{ Wallpaper
-- @DOC_WALLPAPER@
screen.connect_signal("request::wallpaper", function(s)
awful.wallpaper {
screen = s,
widget = {
{
image = beautiful.wallpaper,
upscale = true,
downscale = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
end)
-- }}}
-- {{{ Wibar -- {{{ Wibar
-- Keyboard map indicator and switcher -- Keyboard map indicator and switcher
@ -107,19 +128,6 @@ mykeyboardlayout = awful.widget.keyboardlayout()
-- Create a textclock widget -- Create a textclock widget
mytextclock = wibox.widget.textclock() mytextclock = wibox.widget.textclock()
-- @DOC_WALLPAPER@
screen.connect_signal("request::wallpaper", function(s)
-- Wallpaper
if beautiful.wallpaper then
local wallpaper = beautiful.wallpaper
-- If wallpaper is a function, call it with the screen
if type(wallpaper) == "function" then
wallpaper = wallpaper(s)
end
gears.wallpaper.maximized(wallpaper, s, true)
end
end)
-- @DOC_FOR_EACH_SCREEN@ -- @DOC_FOR_EACH_SCREEN@
screen.connect_signal("request::desktop_decoration", function(s) screen.connect_signal("request::desktop_decoration", function(s)
-- Each screen has its own tag table. -- Each screen has its own tag table.

View File

@ -124,6 +124,10 @@ to an object such as the mouse.
The `naughty.layout.box` allows to provide custom widgets to use within the The `naughty.layout.box` allows to provide custom widgets to use within the
notifications. notifications.
The `awful.wallpaper` provides a non-intereactive "backgroud" for one or more
`screen`. While it uses normal widget, it will not automatically be repainted
if they change. It will also not provide any mouse events.
Finally, the `awful.titlebar`, while not technically a real `wibox`, acts Finally, the `awful.titlebar`, while not technically a real `wibox`, acts
exactly the same way and allows to attach widgets on each side of clients. exactly the same way and allows to attach widgets on each side of clients.

View File

@ -72,6 +72,17 @@ variables such as `bg_normal`. To get a list of all official variables, see
the [appearance guide](../documentation/06-appearance.md.html). the [appearance guide](../documentation/06-appearance.md.html).
]] ]]
sections.DOC_WALLPAPER = [[
The AwesomeWM wallpaper module, `awful.wallpaper` support both per-screen wallpaper
and wallpaper across multiple screens. In the default configuration, the `"request::wallpaper"` signal
is emitted everytime a screen is added, moved, resized or when the bars
(`awful.wibar`) are moved.
This is will suited for single-screen wallpapers. If you wish to use multi-screen wallpaper,
it is better to create a global wallpaper object and edit it when the screen change. See
the `add_screen`/`remove_screens` methods and the `screens` property of `awful.wallpaper` for
examples.
]]
sections.DOC_DEFAULT_APPLICATIONS = [[ sections.DOC_DEFAULT_APPLICATIONS = [[
   

View File

@ -38,7 +38,8 @@
# local use_li = ldoc.use_li # local use_li = ldoc.use_li
# local display_name = ldoc.display_name # local display_name = ldoc.display_name
# local iter = ldoc.modules.iter # local iter = ldoc.modules.iter
# local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end # local function un_cmake(s) return s:gsub("&#59", ";"):gsub("&#34", '"') end
# local function M(txt,item) return ldoc.markup(txt and un_cmake(txt) or nil,item,ldoc.plain) end
# local nowrap = ldoc.wrap and '' or 'nowrap' # local nowrap = ldoc.wrap and '' or 'nowrap'
# local html_space = function(s) return s:gsub(" ", "%%20") end # local html_space = function(s) return s:gsub(" ", "%%20") end
# local no_underscores = function(s) return s:gsub("_", " ") end # local no_underscores = function(s) return s:gsub("_", " ") end
@ -189,7 +190,7 @@
<h3>Usage:</h3> <h3>Usage:</h3>
<ul> <ul>
# for usage in iter(module.usage) do # for usage in iter(module.usage) do
$(li)<pre class="example">$(ldoc.escape(usage))</pre>$(il) $(li)<pre class="example">$(ldoc.escape(un_cmake(usage)))</pre>$(il)
# end -- for # end -- for
</ul> </ul>
# end -- if usage # end -- if usage
@ -331,7 +332,7 @@
# end # end
# if kitem.usage then # if kitem.usage then
<h3>Usage:</h3> <h3>Usage:</h3>
<pre class="example">$(ldoc.prettify(kitem.usage[1]))</pre> <pre class="example">$(ldoc.prettify(un_cmake(kitem.usage[1])))</pre>
# end # end
# end # end
# if not kind:match("^ldoc_skip") then # if not kind:match("^ldoc_skip") then
@ -450,7 +451,7 @@
<h3>Usage:</h3> <h3>Usage:</h3>
<ul> <ul>
# for usage in iter(item.usage) do # for usage in iter(item.usage) do
$(li)<pre class="example">$(ldoc.prettify(usage))</pre>$(il) $(li)<pre class="example">$(ldoc.prettify(un_cmake(usage)))</pre>$(il)
# end -- for # end -- for
</ul> </ul>
# end -- if usage # end -- if usage

View File

@ -58,6 +58,28 @@ do
assert(root.object == root_object) assert(root.object == root_object)
end end
--- The old wallpaper only took native surfaces.
--
-- This was a problem for the test backend. The new function takes both
-- native surfaces and LGI-ified Cairo surfaces.
function root.wallpaper(pattern)
if not pattern then return root._wallpaper() end
-- Checking for type will either potentially `error()` or always
-- return `userdata`. This check will error() when the surface is
-- already native.
local err = pcall(function() return pattern._native end)
-- The presence of `root._write_string` means the test backend is
-- used. Avoid passing the native surface.
if err and not root._write_string then
return root._wallpaper(pattern._native)
else
return root._wallpaper(pattern)
end
end
-- root.bottons() used to be a capi function. However this proved confusing -- root.bottons() used to be a capi function. However this proved confusing
-- as rc.lua used `awful.button` and `root.buttons()` used capi.button. There -- as rc.lua used `awful.button` and `root.buttons()` used capi.button. There
-- was a little documented hack to "flatten" awful.button into a pair of -- was a little documented hack to "flatten" awful.button into a pair of

View File

@ -35,6 +35,7 @@ local ret = {
tooltip = require("awful.tooltip"); tooltip = require("awful.tooltip");
permissions = require("awful.permissions"); permissions = require("awful.permissions");
titlebar = require("awful.titlebar"); titlebar = require("awful.titlebar");
wallpaper = require("awful.wallpaper");
rules = require("awful.rules"); rules = require("awful.rules");
popup = require("awful.popup"); popup = require("awful.popup");
spawn = require("awful.spawn"); spawn = require("awful.spawn");

862
lib/awful/wallpaper.lua Normal file
View File

@ -0,0 +1,862 @@
---------------------------------------------------------------------------
--- Allows to use the wibox widget system to draw the wallpaper.
--
-- Rather than simply having a function to set an image
-- (stretched, centered or tiled) like most wallpaper tools, this module
-- leverage the full widget system to draw the wallpaper. Note that the result
-- is **not** interactive. If you want an interactive wallpaper, better use
-- a `wibox` object with the `below` property set to `true` and maximized
-- using `awful.placement.maximized`.
--
-- It is possible to create an `awful.wallpaper` object from any places, but
-- it is recommanded to do it from the `request::wallpaper` signal handler.
-- That signal is called everytime something which could affect the wallpaper
-- rendering changes, such as new screens.
--
-- Single image
-- ============
--
-- This is the default `rc.lua` wallpaper format. It fills the whole screen
-- and stretches the image while keeping the aspect ratio.
--
--@DOC_awful_wallpaper_mazimized1_EXAMPLE@
--
-- If the image aspect ratio doesn't match, the `bg` property can be used to
-- fill the empty area:
--
--@DOC_awful_wallpaper_mazimized2_EXAMPLE@
--
-- It is also possible to stretch the image:
--
--@DOC_awful_wallpaper_mazimized3_EXAMPLE@
--
-- Finally, it is also possible to use simpler "branding" in a corner using
-- `awful.placement`:
--
--@DOC_awful_wallpaper_corner1_EXAMPLE@
--
-- Tiled
-- =====
--
-- This example tiles an image:
--
--@DOC_awful_wallpaper_tiled1_EXAMPLE@
--
-- This one tiles a shape using the `wibox.widget.separator` widget:
--
--@DOC_awful_wallpaper_tiled2_EXAMPLE@
--
-- See the `wibox.container.tile` for more advanced tiling configuration
-- options.
--
-- Solid colors and gradients
-- ==========================
--
-- Solid colors can be set using the `bg` property mentionned above. It
-- is also possible to set gradients:
--
--@DOC_awful_wallpaper_gradient1_EXAMPLE@
--
--@DOC_awful_wallpaper_gradient2_EXAMPLE@
--
-- Widgets
-- =======
--
-- It is possible to create a wallpaper using any widgets. However, keep
-- in mind that the wallpaper surface is not interactive, so some widgets
-- like the sliders will render, but will not behave correctly. Also, it
-- is not recommanded to update the wallpaper too often. This is very slow.
--
--@DOC_awful_wallpaper_widget2_EXAMPLE@
--
-- Cairo graphics API
-- ==================
--
-- AwesomeWM widgets are backed by Cairo. So it is always possible to get
-- access to the Cairo context directly to do some vector art:
--
--@DOC_awful_wallpaper_widget1_EXAMPLE@
--
--
-- SVG vector images
-- =================
--
-- SVG are supported if `librsvg` is installed. Please note that `librsvg`
-- doesn't implement all filters you might find in the latest version of
-- your web browser. It is possible some advanced SVG will not look exactly
-- as they do in a web browser or even Inkscape. However, for most images,
-- it should look identical.
--
-- Our SVG support goes beyond simple rendering. It is possible to set a
-- custom CSS stylesheet (see `wibox.widget.imagebox.stylesheet`):
--
--@DOC_awful_wallpaper_svg_EXAMPLE@
--
-- Note that in the example above, it is raw SVG code, but it is also possible
-- to use a file path. If you have a `.svgz`, you need to uncompress it first
-- using `gunzip` or a software like Inkscape.
--
-- Multiple screen
-- ===============
--
-- The default `rc.lua` creates a new wallpaper everytime `request::wallpaper`
-- is emitted. This is well suited for having a single wallpaper per screen.
-- It is also much simpler to implement slideshows and add/remove screens.
--
-- However, it isn't wall suited for wallpaper rendered across multiple screens.
-- For this case, it is better to capture the return value of `awful.wallpaper {}`
-- as a global variable. Then manually call `add_screen` and `remove_screen` when
-- needed. A shortcut can be to do:
--
-- @DOC_text_awful_wallpaper_multi_screen_EXAMPLE@
--
-- Slideshow
-- =========
--
-- Slideshows (changing the wallpaper after a few minutes) can be implemented
-- directly using a timer and callback, but it is more elegant to simply request
-- a new wallpaper, then get a random image from within the request handler. This
-- way, corner cases such as adding and removing screens are handled:
--
--@DOC_awful_wallpaper_slideshow1_EXAMPLE@
--
-- @author Emmanuel Lepage Vallee &lt;elv1313@gmail.com&gt;
-- @copyright 2019 Emmanuel Lepage Vallee
-- @popupmod awful.wallpaper
---------------------------------------------------------------------------
require("awful._compat")
local gtable = require( "gears.table" )
local gobject = require( "gears.object" )
local gcolor = require( "gears.color" )
local gtimer = require( "gears.timer" )
local surface = require( "gears.surface" )
local base = require( "wibox.widget.base" )
local background = require( "wibox.container.background")
local beautiful = require( "beautiful" )
local cairo = require( "lgi" ).cairo
local draw = require( "wibox.widget" ).draw_to_cairo_context
local grect = require( "gears.geometry" ).rectangle
local capi = { screen = screen, root = root }
local module = {}
local function get_screen(s)
return s and capi.screen[s]
end
-- Screen as key, wallpaper as values.
local pending_repaint = setmetatable({}, {__mode = 'k'})
local backgrounds = setmetatable({}, {__mode = 'k'})
local panning_modes = {}
-- Get a list of all screen areas.
local function get_rectangles(screens, honor_workarea, honor_padding)
local ret = {}
for _, s in ipairs(screens) do
table.insert(ret, s:get_bounding_geometry {
honor_padding = honor_padding,
honor_workarea = honor_workarea
})
end
return ret
end
-- Outer perimeter of all rectangles.
function panning_modes.outer(self)
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
local p1, p2 = {x = math.huge, y = math.huge}, {x = 0, y = 0}
for _, rect in ipairs(rectangles) do
p1.x, p1.y = math.min(p1.x, rect.x), math.min(p1.y, rect.y)
p2.x, p2.y = math.max(p2.x, rect.x + rect.width), math.max(p2.y, rect.y + rect.height)
end
-- Never try to paint this, it would freeze the system.
assert(p1.x ~= math.huge and p1.y ~= math.huge, "Setting wallpaper failed"..#self.screens)
return {
x = p1.x,
y = p1.y,
width = p2.x - p1.x,
height = p2.y - p1.y,
}
end
-- Horizontal inner perimeter of all rectangles.
function panning_modes.inner_horizontal(self)
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
local p1, p2 = {x = math.huge, y = 0}, {x = 0, y = math.huge}
for _, rect in ipairs(rectangles) do
p1.x, p1.y = math.min(p1.x, rect.x), math.max(p1.y, rect.y)
p2.x, p2.y = math.max(p2.x, rect.x + rect.width), math.min(p2.y, rect.y + rect.height)
end
-- Never try to paint this, it would freeze the system.
assert(p1.x ~= math.huge and p2.y ~= math.huge, "Setting wallpaper failed")
return {
x = p1.x,
y = p1.y,
width = p2.x - p1.x,
height = p2.y - p1.y,
}
end
-- Vertical inner perimeter of all rectangles.
function panning_modes.inner_vertical(self)
local rectangles = get_rectangles(self.screens, self.honor_workarea, self.honor_padding)
local p1, p2 = {x = 0, y = math.huge}, {x = math.huge, y = 0}
for _, rect in ipairs(rectangles) do
p1.x, p1.y = math.max(p1.x, rect.x), math.min(p1.y, rect.y)
p2.x, p2.y = math.min(p2.x, rect.x + rect.width), math.max(p2.y, rect.y + rect.height)
end
-- Never try to paint this, it would freeze the system.
assert(p1.y ~= math.huge and p2.a ~= math.huge, "Setting wallpaper failed")
return {
x = p1.x,
y = p1.y,
width = p2.x - p1.x,
height = p2.y - p1.y,
}
end
-- Best or vertical and horizontal "inner" modes.
function panning_modes.inner(self)
local vert = panning_modes.inner_vertical(self)
local hori = panning_modes.inner_horizontal(self)
if vert.width <= 0 or vert.height <= 0 then return hori end
if hori.width <= 0 or hori.height <= 0 then return vert end
if vert.width * vert.height > hori.width * hori.height then
return vert
else
return hori
end
end
local function paint()
if not next(pending_repaint) then return end
local root_width, root_height = capi.root.size()
-- Get the current wallpaper content.
local source = surface(root.wallpaper())
local target, cr
-- It's possible that a wallpaper for 1 screen is set using another tool, so make
-- sure we copy the current content.
if source then
target = source:create_similar(cairo.Content.COLOR, root_width, root_height)
cr = cairo.Context(target)
-- Copy the old wallpaper to the new one
cr:save()
cr.operator = cairo.Operator.SOURCE
cr:set_source_surface(source, 0, 0)
for s in screen do
cr:rectangle(
s.geometry.x,
s.geometry.y,
s.geometry.width,
s.geometry.height
)
end
cr:clip()
cr:paint()
cr:restore()
else
target = cairo.ImageSurface(cairo.Format.RGB32, root_width, root_height)
cr = cairo.Context(target)
end
local walls = {}
for _, wall in pairs(backgrounds) do
walls[wall] = true
end
-- Not supposed to happen, but there is enough API surface for
-- it to be a side effect of some signals. Calling the panning
-- mode callback with zero screen is not supported.
if not next(walls) then
return
end
for wall in pairs(walls) do
local geo = type(wall._private.panning_area) == "function" and
wall._private.panning_area(wall) or
panning_modes[wall._private.panning_area](wall)
-- If false, this panning area isn't well suited for the screen geometry.
if geo.width > 0 or geo.height > 0 then
local uncovered_areas = grect.area_remove(get_rectangles(wall.screens, false, false), geo)
cr:save()
-- Prevent overwrite then there is multiple non-continuous screens.
for _, s in ipairs(wall.screens) do
cr:rectangle(
s.geometry.x,
s.geometry.y,
s.geometry.width,
s.geometry.height
)
end
cr:clip()
-- The older surface might contain garbage, optionally clean it.
if wall.uncovered_areas_color then
cr:set_source(gcolor(wall.uncovered_areas_color))
for _, area in ipairs(uncovered_areas) do
cr:rectangle(area.x, area.y, area.width, area.height)
cr:fill()
end
end
if not wall._private.container then
wall._private.container = background()
wall._private.container.bg = wall._private.bg or beautiful.wallpaper_bg or "#000000"
wall._private.container.fg = wall._private.fg or beautiful.wallpaper_fg or "#ffffff"
wall._private.container.widget = wall.widget
end
local a_context = {
dpi = wall._private.context.dpi
}
-- Pick the lowest DPI.
if not a_context.dpi then
a_context.dpi = math.huge
for _, s in ipairs(wall.screens) do
a_context.dpi = math.min(
s.dpi and s.dpi or s.preferred_dpi, a_context.dpi
)
end
end
-- Fallback.
if not a_context.dpi then
a_context.dpi = 96
end
cr:translate(geo.x, geo.y)
draw(wall._private.container, cr, geo.width, geo.height, a_context)
cr:restore()
end
end
-- Set the wallpaper.
local pattern = cairo.Pattern.create_for_surface(target)
capi.root.wallpaper(pattern)
-- Limit some potential GC induced increase in memory usage.
-- But really, is someone is trying to apply wallpaper changes more
-- often than the GC is executed, they are doing it wrong.
target:finish()
end
local mutex = false
-- Uploading the surface to X11 is *very* resource intensive. Given the updates
-- will often happen in batch (like startup), make sure to only do one "real"
-- update.
local function update()
if mutex then return end
mutex = true
gtimer.delayed_call(function()
-- Remove the mutex first in case `paint()` raises an exception.
mutex = false
paint()
end)
end
capi.screen.connect_signal("removed", function(s)
if not backgrounds[s] then return end
backgrounds[s]:remove_screen(s)
update()
end)
capi.screen.connect_signal("property::geometry", function(s)
if not backgrounds[s] then return end
backgrounds[s]:repaint()
end)
--- The wallpaper widget.
--
-- When set, instead of using the `image_path` or `surface` properties, the
-- wallpaper will be defined as a normal `wibox` widget tree.
--
-- @property widget
-- @tparam wibox.widget widget
-- @see wibox.widget.imagebox
-- @see wibox.container.tile
--- The wallpaper DPI (dots per inch).
--
-- Each screen has a DPI. This value will be used by default, but sometime it
-- is useful to override the screen DPI and use a custom one. This makes
-- possible, for example, to draw the widgets bigger than they would otherwise
-- be.
--
-- If not DPI is defined, it will use the smallest DPI from any of the screen.
--
-- In this example, there is 3 screens with DPI of 100, 200 and 300. As you can
-- see, only the text size is affected. Many widgetds are DPI aware, but not all
-- of them. This is either because DPI isn't relevant to them or simply because it
-- isn't supported (like `wibox.widget.graph`).
--
-- @DOC_awful_wallpaper_dpi1_EXAMPLE@
--
-- @property dpi
-- @tparam[opt=screen.dpi] number dpi
-- @see screen
-- @see screen.dpi
--- The wallpaper screen.
--
-- Note that there can only be one wallpaper per screen. If there is more, one
-- will be chosen and all other ignored.
--
-- @property screen
-- @tparam screen screen
-- @see screens
-- @see add_screen
-- @see remove_screen
--- A list of screen for this wallpaper.
--
--@DOC_awful_wallpaper_screens1_EXAMPLE@
--
-- Some large wallpaper are made to span multiple screens.
-- @property screens
-- @tparam table screens
-- @see screen
-- @see add_screen
-- @see remove_screen
-- @see detach
--- The background color.
--
-- It will be used as the "fill" color if the `image` doesn't take all the
-- screen space. It will also be the default background for the `widget.
--
-- As usual with colors in `AwesomeWM`, it can also be a gradient or a pattern.
--
-- @property bg
-- @tparam gears.color bg
-- @see gears.color
--- The foreground color.
--
-- This will be used by the `widget` (if any).
--
-- As usual with colors in `AwesomeWM`, it can also be a gradient or a pattern.
--
-- @property fg
-- @tparam gears.color fg
-- @see gears.color
--- The default wallpaper background color.
-- @beautiful beautiful.wallpaper_bg
-- @tparam gears.color wallpaper_bg
-- @see bg
--- The default wallpaper foreground color.
--
-- This is useful when using widgets or text in the wallpaper. A wallpaper
-- created from a single image wont use this.
--
-- @beautiful beautiful.wallpaper_fg
-- @tparam gears.color wallpaper_fg
-- @see bg
--- Honor the workarea.
--
-- When set to `true`, the wallpaper will only fill the workarea space instead
-- of the entire screen. This means it wont be drawn below the `awful.wibar` or
-- docked clients. This is useful when using opaque bars. Note that it can cause
-- aspect ratio issues for the wallpaper `image` and add bars colored with the
-- `bg` color on the sides.
--
--@DOC_awful_wallpaper_workarea1_EXAMPLE@
--
-- @property honor_workarea
-- @tparam[opt=false] boolean honor_workarea
-- @see honor_padding
-- @see uncovered_areas
--- Honor the screen padding.
--
-- When set, this will look at the `screen.padding` property to restrict the
-- area where the wallpaper is rendered.
--
-- @DOC_awful_wallpaper_padding1_EXAMPLE@
--
-- @property honor_padding
-- @tparam boolean honor_padding
-- @see honor_workarea
-- @see uncovered_areas
--- Returns the list of screen(s) area which won't be covered by the wallpaper.
--
-- When `honor_workarea`, `honor_padding` or panning are used, some section of
-- the screen won't have a wallpaper. This returns a list of areas tables. Each
-- table has a `x`, `y`, `width` and `height` key.
--
-- @property uncovered_areas
-- @tparam table uncovered_areas
-- @see honor_workarea
-- @see honor_padding
-- @see uncovered_areas_color
--- The color for the uncovered areas.
--
-- Some application rely on the wallpaper for "fake" transparency. Even if an
-- area is hidden under a wibar (or other clients), its background can still
-- become visible. If you use such application and change your screen geometry
-- often enough, it is possible some areas would become filled with the remains
-- of previous wallpapers. This property allows to clean those areas with a solid
-- color or a gradient.
--
-- @property uncovered_areas_color
-- @tparam gears.color uncovered_areas_color
-- @see uncovered_areas
--- Defines where the wallpaper is placed when there is multiple screens.
--
-- When there is more than 1 screen, it is possible they don't have the same
-- resolution, position or orientation. Panning the wallpaper over them may look
-- better if a continuous rectangle is used rather than creating a virtual rectangle
-- around all screens.
--
-- The default algorithms are:
--
-- **outer:** *(default)*
--
-- Draw an imaginary rectangle around all screens.
--
-- @DOC_awful_wallpaper_panning_outer_EXAMPLE@
--
-- **inner:**
--
-- Take the largest area or either `inner_horizontal` or `inner_vertical`.
--
-- @DOC_awful_wallpaper_panning_inner_EXAMPLE@
--
-- **inner_horizontal:**
--
-- Take the smallest `x` value, the largest `x+width`, the smallest `y`
-- and the smallest `y+height`.
--
-- @DOC_awful_wallpaper_panning_inner_horizontal_EXAMPLE@
--
-- **inner_vertical:**
--
-- Take the smallest `y` value, the largest `y+height`, the smallest `x`
-- and the smallest `x+width`.
--
-- @DOC_awful_wallpaper_panning_inner_vertical_EXAMPLE@
--
-- **Custom function:**
--
-- It is also possible to define a custom function.
--
-- @DOC_awful_wallpaper_panning_custom_EXAMPLE@
--
-- @property panning_area
-- @tparam function|string panning_area
-- @see uncovered_areas
function module:set_panning_area(value)
value = value or "outer"
assert(type(value) == "function" or panning_modes[value], "Invalid panning mode: "..tostring(value))
self._private.panning_area = value
self:repaint()
self:emit_signal("property::panning_area", value)
end
function module:set_widget(w)
self._private.widget = base.make_widget_from_value(w)
if self._private.container then
self._private.container.widget = self._private.widget
end
self:repaint()
end
function module:get_widget()
return self._private.widget
end
function module:set_dpi(dpi)
self._private.context.dpi = dpi
self:repaint()
end
function module:get_dpi()
return self._private.context.dpi
end
function module:set_screen(s)
if not s then return end
self:_clear()
self:add_screen(s)
end
for _, prop in ipairs {"bg", "fg"} do
module["set_"..prop] = function(self, color)
if self._private.container then
self._private.container[prop] = color
end
self._private[prop] = color
self:repaint()
end
end
function module:get_uncovered_areas()
local geo = type(self._private.panning_area) == "function" and
self._private.panning_area(self) or
panning_modes[self._private.panning_area](self)
return grect.area_remove(get_rectangles(self.screens, false, false), geo)
end
function module:set_screens(screens)
local to_rem = {}
-- All screens.
-- The copy is needed because it's a metatable, `ipairs` doesn't work
-- correctly in all Lua versions.
if screens == capi.screen then
screens = {}
for s in capi.screen do
table.insert(screens, s)
end
end
for _, s in ipairs(screens) do
to_rem[get_screen(s)] = true
end
for _, s in ipairs(self.screens) do
to_rem[get_screen(s)] = nil
end
for _, s in ipairs(screens) do
s = get_screen(s)
self:add_screen(s)
to_rem[s] = nil
end
for s, remove in pairs(to_rem) do
if remove then
self:remove_screen(s)
end
end
end
function module:get_screens()
return self._private.screens
end
--- Add another screen (enable panning).
--
-- **Before:**
--
--@DOC_awful_wallpaper_add_screen1_EXAMPLE@
--
-- **After:**
--
--@DOC_awful_wallpaper_add_screen2_EXAMPLE@
--
-- Also note that adding a non-continuous screen might not work well,
-- but will not automatically add the screens in between:
--
--@DOC_awful_wallpaper_add_screen3_EXAMPLE@
--
-- @method add_screen
-- @tparam screen screen The screen object.
-- @see remove_screen
function module:add_screen(s)
s = get_screen(s)
for _, s2 in ipairs(self._private.screens) do
if s == s2 then return end
end
table.insert(self._private.screens, s)
if backgrounds[s] and backgrounds[s] ~= self then
backgrounds[s]:remove_screen(s)
end
backgrounds[s] = self
self:repaint()
end
--- Detach the wallpaper from all screens.
--
-- Adding a new wallpaper to a screen will automatically
-- detach the older one. However there is some case when
-- it is useful to call this manually. For example, when
-- adding a new panned wallpaper, it is possible that 2
-- wallpaper will have an overlap.
--
-- @method detach
-- @see remove_screen
-- @see add_screen
function module:detach()
local screens = gtable.clone(self.screens)
for _, s in ipairs(screens) do
self:remove_screen(s)
end
end
function module:_clear()
self._private.screens = setmetatable({}, {__mode = "v"})
update()
end
--- Repaint the wallpaper.
--
-- By default, even if the widget changes, the wallpaper will **NOT** be
-- automatically repainted. Repainting the native X11 wallpaper is slow and
-- it would be too easy to accidentally cause a performance problem. If you
-- really need to repaint the wallpaper, call this method.
--
-- @method repaint
function module:repaint()
for _, s in ipairs(self._private.screens) do
pending_repaint[s] = true
end
update()
end
--- Remove a screen.
--
-- Calling this will remove a screen, but will **not** repaint its area.
-- In this example, the wallpaper was spanning all 3 screens and the
-- first screen was removed:
--
-- @DOC_awful_wallpaper_remove_screen1_EXAMPLE@
--
-- As you can see, the content of screen 1 still looks like it is part of
-- the 3 screen wallpaper. The only use case for calling this method is if
-- you use a 3rd party tools to change the wallpaper.
--
-- If you wish to simply remove a screen and not have leftover content, it is
-- simpler to just create a new wallpaper for that screen:
--
-- @DOC_awful_wallpaper_remove_screen2_EXAMPLE@
--
-- @method remove_screen
-- @tparam screen screen The screen to remove.
-- @see detach
-- @see add_screen
-- @see screens
function module:remove_screen(s)
s = get_screen(s)
for k, s2 in ipairs(self._private.screens) do
if s == s2 then
table.remove(self._private.screens, k)
end
end
backgrounds[s] = nil
self:repaint()
end
--- Create a wallpaper.
--
-- Note that all parameters are not required. Please refer to the
-- module description and examples to understand parameters usages.
--
-- @constructorfct awful.wallpaper
-- @tparam table args
-- @tparam[opt] wibox.widget args.widget The wallpaper widget.
-- @tparam[opt] number args.dpi The wallpaper DPI (dots per inch).
-- @tparam[opt] screen args.screen The wallpaper screen.
-- @tparam[opt] table args.screens A list of screen for this wallpaper.
-- Use this parameter as a remplacement for `args.screen` to manage multiscreen wallpaper.
-- (Note: the expected table should be an array-like table `{screen1, screen2, ...}`)
-- @tparam[opt] gears.color args.bg The background color.
-- @tparam[opt] gears.color args.fg The foreground color.
-- @tparam[opt] gears.color args.uncovered_areas_color The color for the uncovered areas.
-- @tparam[opt] boolean args.honor_workarea Honor the workarea.
-- @tparam[opt] boolean args.honor_padding Honor the screen padding.
-- @tparam[opt] table args.uncovered_areas Returns the list of screen(s) area which won't be covered by the wallpaper.
-- @tparam[opt] function|string args.panning_area Defines where the wallpaper is placed when there is multiple screens.
local function new(_, args)
args = args or {}
local ret = gobject {
enable_auto_signals = true,
enable_properties = true,
}
rawset(ret, "_private", {})
ret._private.context = {}
ret._private.panning_area = "outer"
gtable.crush(ret, module, true)
ret:_clear()
-- Set the screen or screens first to avoid a race condition
-- with the other setters.
local args_screen, args_screens = args.screen, args.screens
if args_screen then
ret.screen = args_screen
elseif args_screens then
ret.screens = args_screens
end
-- Avoid crushing `screen` and `screens` twice.
args.screen, args.screens = nil, nil
gtable.crush(ret, args, false)
args.screen, args.screens = args_screen, args_screens
return ret
end
return setmetatable(module, {__call = new})

View File

@ -172,28 +172,39 @@ end
-- @tparam string path The directory to search. -- @tparam string path The directory to search.
-- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }` -- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }`
-- If ommited, all files are considered. -- If ommited, all files are considered.
-- @tparam[opt=false] boolean absolute_path Return the absolute path instead of the filename.
-- @treturn string|nil A randomly selected filename from the specified path (with -- @treturn string|nil A randomly selected filename from the specified path (with
-- a specified extension if required) or nil if no suitable file is found. -- a specified extension if required) or nil if no suitable file is found. If `absolute_path`
-- is set, then a path is returned instead of a file name.
-- @staticfct gears.filesystem.get_random_file_from_dir -- @staticfct gears.filesystem.get_random_file_from_dir
function filesystem.get_random_file_from_dir(path, exts) function filesystem.get_random_file_from_dir(path, exts, absolute_path)
local files, valid_exts = {}, {} local files, valid_exts = {}, {}
-- Transforms { "jpg", ... } into { [jpg] = #, ... } -- Transforms { "jpg", ... } into { [jpg] = #, ... }
if exts then for i, j in ipairs(exts) do valid_exts[j:lower()] = i end end if exts then for i, j in ipairs(exts) do valid_exts[j:lower():gsub("^[.]", "")] = i end end
-- Build a table of files from the path with the required extensions -- Build a table of files from the path with the required extensions
local file_list = Gio.File.new_for_path(path):enumerate_children("standard::*", 0) local file_list = Gio.File.new_for_path(path):enumerate_children("standard::*", 0)
-- This will happen when the directory doesn't exist.
if not file_list then return nil end
for file in function() return file_list:next_file() end do for file in function() return file_list:next_file() end do
if file:get_file_type() == "REGULAR" then if file:get_file_type() == "REGULAR" then
local file_name = file:get_display_name() local file_name = file:get_display_name()
if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then
table.insert(files, file_name) table.insert(files, file_name)
end end
end end
end end
if #files == 0 then return nil end
-- Return a randomly selected filename from the file table -- Return a randomly selected filename from the file table
return #files > 0 and files[math.random(#files)] or nil local file = files[math.random(#files)]
return absolute_path and (path:gsub("[/]*$", "") .. "/" .. file) or file
end end
return filesystem return filesystem

View File

@ -1,22 +1,7 @@
--------------------------------------------------------------------------- ---------------------------------------------------------------------------
-- Functions for setting the wallpaper. -- Functions for setting the wallpaper.
-- --
-- There are two levels of functionality provided by this module: -- This module is deprecated, please use `awful.wallpaper`.
--
-- The low-level functionality consists of two functions.
-- @{set} an already-prepared wallpaper on all screens and @{prepare_context}
-- prepares things to draw a new wallpaper.
--
-- The low-level API can for example be used to set solid red as a wallpaper
-- (see @{gears.color} for details on the supported syntax):
--
-- gears.wallpaper.set("#ff0000")
--
-- Ontop of these low-level functions, the remaining functions implement more
-- useful functionality. For example, given a screen object `s`, an image can be
-- set as the wallpaper as follows:
--
-- gears.wallpaper.maximized("path/to/image.png", s)
-- --
-- @author Uli Schlachter -- @author Uli Schlachter
-- @copyright 2012 Uli Schlachter -- @copyright 2012 Uli Schlachter
@ -51,8 +36,10 @@ end
-- @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[1] The available geometry (table with entries width and height) -- @return[1] The available geometry (table with entries width and height)
-- @return[1] A cairo context that the wallpaper should be drawn to. -- @return[1] A cairo context that the wallpaper should be drawn to.
-- @staticfct gears.wallpaper.prepare_context -- @deprecated gears.wallpaper.prepare_context
function wallpaper.prepare_context(s) function wallpaper.prepare_context(s)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
s = get_screen(s) s = get_screen(s)
local root_width, root_height = root.size() local root_width, root_height = root.size()
@ -110,8 +97,10 @@ end
-- @param pattern The wallpaper that should be set. This can be a cairo surface, -- @param pattern The wallpaper that should be set. This can be a cairo surface,
-- a description for gears.color or a cairo pattern. -- a description for gears.color or a cairo pattern.
-- @see gears.color -- @see gears.color
-- @staticfct gears.wallpaper.set -- @deprecated gears.wallpaper.set
function wallpaper.set(pattern) function wallpaper.set(pattern)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
if cairo.Surface:is_type_of(pattern) then if cairo.Surface:is_type_of(pattern) then
pattern = cairo.Pattern.create_for_surface(pattern) pattern = cairo.Pattern.create_for_surface(pattern)
end end
@ -121,7 +110,7 @@ function wallpaper.set(pattern)
if not cairo.Pattern:is_type_of(pattern) then if not cairo.Pattern:is_type_of(pattern) then
error("wallpaper.set() called with an invalid argument") error("wallpaper.set() called with an invalid argument")
end end
root.wallpaper(pattern._native) root.wallpaper(pattern)
end end
--- Set a centered wallpaper. --- Set a centered wallpaper.
@ -132,8 +121,10 @@ end
-- gears.color. The default is black. -- gears.color. The default is black.
-- @param scale The scale factor for the wallpaper. Default is 1 (original size). -- @param scale The scale factor for the wallpaper. Default is 1 (original size).
-- @see gears.color -- @see gears.color
-- @staticfct gears.wallpaper.centered -- @deprecated gears.wallpaper.centered
function wallpaper.centered(surf, s, background, scale) function wallpaper.centered(surf, s, background, scale)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local geom, cr = wallpaper.prepare_context(s) local geom, cr = wallpaper.prepare_context(s)
local original_surf = surf local original_surf = surf
surf = surface.load_uncached(surf) surf = surface.load_uncached(surf)
@ -172,8 +163,10 @@ end
-- @param s The screen whose wallpaper should be set. Can be nil, in which case -- @param s The screen whose wallpaper should be set. Can be nil, in which case
-- 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.
-- @staticfct gears.wallpaper.tiled -- @deprecated gears.wallpaper.tiled
function wallpaper.tiled(surf, s, offset) function wallpaper.tiled(surf, s, offset)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local _, cr = wallpaper.prepare_context(s) local _, cr = wallpaper.prepare_context(s)
if offset then if offset then
@ -202,8 +195,10 @@ end
-- @param ignore_aspect If this is true, the image's aspect ratio is ignored. -- @param ignore_aspect If this is true, the image's aspect ratio is ignored.
-- 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.
-- @staticfct gears.wallpaper.maximized -- @deprecated gears.wallpaper.maximized
function wallpaper.maximized(surf, s, ignore_aspect, offset) function wallpaper.maximized(surf, s, ignore_aspect, offset)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local geom, cr = wallpaper.prepare_context(s) local geom, cr = wallpaper.prepare_context(s)
local original_surf = surf local original_surf = surf
surf = surface.load_uncached(surf) surf = surface.load_uncached(surf)
@ -243,8 +238,10 @@ 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.
-- @see gears.color -- @see gears.color
-- @staticfct gears.wallpaper.fit -- @deprecated gears.wallpaper.fit
function wallpaper.fit(surf, s, background) function wallpaper.fit(surf, s, background)
debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5})
local geom, cr = wallpaper.prepare_context(s) local geom, cr = wallpaper.prepare_context(s)
local original_surf = surf local original_surf = surf
surf = surface.load_uncached(surf) surf = surface.load_uncached(surf)

View File

@ -31,6 +31,7 @@ function module:draw(context, cr, width, height)
if not self._private.surface then if not self._private.surface then
self._private.surface = cairo.ImageSurface(cairo.Format.ARGB32, w+hspace, h+vspace) self._private.surface = cairo.ImageSurface(cairo.Format.ARGB32, w+hspace, h+vspace)
self._private.cr = cairo.Context(self._private.surface) self._private.cr = cairo.Context(self._private.surface)
self._private.cr:set_source(cr:get_source())
self._private.pattern = cairo.Pattern.create_for_surface(self._private.surface) self._private.pattern = cairo.Pattern.create_for_surface(self._private.surface)
self._private.pattern.extend = cairo.Extend.REPEAT self._private.pattern.extend = cairo.Extend.REPEAT
self._private.cr:translate(math.ceil(hspace), math.ceil(vspace)) self._private.cr:translate(math.ceil(hspace), math.ceil(vspace))

View File

@ -48,28 +48,128 @@ end
local imagebox = { mt = {} } local imagebox = { mt = {} }
local rsvg_handle_cache = setmetatable({}, { __mode = 'v' }) local rsvg_handle_cache = setmetatable({}, { __mode = 'k' })
---Load rsvg handle form image file ---Load rsvg handle form image file
---@tparam string file Path to svg file. -- @tparam string file Path to svg file.
---@return Rsvg handle -- @return Rsvg handle
-- @treturn table A table where cached data can be stored.
local function load_rsvg_handle(file) local function load_rsvg_handle(file)
if not Rsvg then return end if not Rsvg then return end
local cache = rsvg_handle_cache[file] local cache = (rsvg_handle_cache[file] or {})["handle"]
if cache then if cache then
return cache return cache, rsvg_handle_cache[file]
end
local handle, err
if file:match("<[?]?xml") or file:match("<svg") then
handle, err = Rsvg.Handle.new_from_data(file)
else
handle, err = Rsvg.Handle.new_from_file(file)
end end
local handle, err = Rsvg.Handle.new_from_file(file)
if not err then if not err then
rsvg_handle_cache[file] = handle rsvg_handle_cache[file] = rsvg_handle_cache[file] or {}
return handle rsvg_handle_cache[file]["handle"] = handle
return handle, rsvg_handle_cache[file]
end
end
---Apply cairo surface for given imagebox widget
local function set_surface(ib, surf)
local is_surf_valid = surf.width > 0 and surf.height > 0
if not is_surf_valid then return false end
ib._private.default = { width = surf.width, height = surf.height }
ib._private.handle = nil
ib._private.image = surf
return true
end
---Apply RsvgHandle for given imagebox widget
local function set_handle(ib, handle, cache)
local dim = handle:get_dimensions()
local is_handle_valid = dim.width > 0 and dim.height > 0
if not is_handle_valid then return false end
ib._private.default = { width = dim.width, height = dim.height }
ib._private.handle = handle
ib._private.cache = cache
ib._private.image = nil
return true
end
---Try to load some image object from file then apply it to imagebox.
---@tparam table ib Imagebox
---@tparam string file Image file name
---@tparam function image_loader Function to load image object from file
---@tparam function image_setter Function to set image object to imagebox
---@treturn boolean True if image was successfully applied
local function load_and_apply(ib, file, image_loader, image_setter)
local image_applied
local object, cache = image_loader(file)
if object then
image_applied = image_setter(ib, object, cache)
end
return image_applied
end
---Update the cached size depending on the stylesheet and dpi.
--
-- It's necessary because a single RSVG handle can be used by
-- many imageboxes. So DPI and Stylesheet need to be set each time.
local function update_dpi(self, ctx)
if not self._private.handle then return end
local dpi = self._private.auto_dpi and
ctx.dpi or
self._private.dpi or
nil
local need_dpi = dpi and
self._private.last_dpi ~= dpi
local need_style = self._private.handle.set_stylesheet and
self._private.stylesheet
local old_size = self._private.default and self._private.default.width
if dpi and dpi ~= self._private.cache.dpi then
if type(dpi) == "table" then
self._private.handle:set_dpi_x_y(dpi.x, dpi.y)
else
self._private.handle:set_dpi(dpi)
end
end
if need_style and self._private.cache.stylesheet ~= self._private.stylesheet then
self._private.handle:set_stylesheet(self._private.stylesheet)
end
-- Reload the size.
if need_dpi or (need_style and self._private.stylesheet ~= self._private.last_stylesheet) then
set_handle(self, self._private.handle, self._private.cache)
end
self._private.last_dpi = dpi
self._private.cache.dpi = dpi
self._private.last_stylesheet = self._private.stylesheet
self._private.cache.stylesheet = self._private.stylesheet
-- This can happen in the constructor when `dpi` is set after `image`.
if old_size and old_size ~= self._private.default.width then
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
end end
end end
-- Draw an imagebox with the given cairo context in the given geometry. -- Draw an imagebox with the given cairo context in the given geometry.
function imagebox:draw(_, cr, width, height) function imagebox:draw(ctx, cr, width, height)
if width == 0 or height == 0 or not self._private.default then return end if width == 0 or height == 0 or not self._private.default then return end
-- For valign = "top" and halign = "left" -- For valign = "top" and halign = "left"
@ -78,9 +178,11 @@ function imagebox:draw(_, cr, width, height)
y = 0, y = 0,
} }
update_dpi(self, ctx)
local w, h = self._private.default.width, self._private.default.height local w, h = self._private.default.width, self._private.default.height
if not self._private.resize_forbidden then if self._private.resize then
-- That's for the "fit" policy. -- That's for the "fit" policy.
local aspects = { local aspects = {
w = width / w, w = width / w,
@ -93,10 +195,14 @@ function imagebox:draw(_, cr, width, height)
} }
for _, aspect in ipairs {"w", "h"} do for _, aspect in ipairs {"w", "h"} do
if policy[aspect] == "auto" then if self._private.upscale == false and (w < width and h < height) then
aspects[aspect] = math.min(width / w, height / h) aspects[aspect] = 1
elseif self._private.downscale == false and (w >= width and h >= height) then
aspects[aspect] = 1
elseif policy[aspect] == "none" then elseif policy[aspect] == "none" then
aspects[aspect] = 1 aspects[aspect] = 1
elseif policy[aspect] == "auto" then
aspects[aspect] = math.min(width / w, height / h)
end end
end end
@ -165,11 +271,22 @@ function imagebox:draw(_, cr, width, height)
end end
-- Fit the imagebox into the given geometry -- Fit the imagebox into the given geometry
function imagebox:fit(_, width, height) function imagebox:fit(ctx, width, height)
if not self._private.default then return 0, 0 end if not self._private.default then return 0, 0 end
update_dpi(self, ctx)
local w, h = self._private.default.width, self._private.default.height local w, h = self._private.default.width, self._private.default.height
if not self._private.resize_forbidden or w > width or h > height then if w <= width and h <= height and self._private.upscale == false then
return w, h
end
if (w < width or h < height) and self._private.downscale == false then
return w, h
end
if self._private.resize or w > width or h > height then
local aspect = math.min(width / w, height / h) local aspect = math.min(width / w, height / h)
return w * aspect, h * aspect return w * aspect, h * aspect
end end
@ -177,44 +294,6 @@ function imagebox:fit(_, width, height)
return w, h return w, h
end end
---Apply cairo surface for given imagebox widget
local function set_surface(ib, surf)
local is_surd_valid = surf.width > 0 and surf.height > 0
if not is_surd_valid then return end
ib._private.default = { width = surf.width, height = surf.height }
ib._private.handle = nil
ib._private.image = surf
return true
end
---Apply RsvgHandle for given imagebox widget
local function set_handle(ib, handle)
local dim = handle:get_dimensions()
local is_handle_valid = dim.width > 0 and dim.height > 0
if not is_handle_valid then return end
ib._private.default = { width = dim.width, height = dim.height }
ib._private.handle = handle
ib._private.image = nil
return true
end
---Try to load some image object from file then apply it to imagebox.
---@tparam table ib Imagebox
---@tparam string file Image file name
---@tparam function image_loader Function to load image object from file
---@tparam function image_setter Function to set image object to imagebox
---@treturn boolean True if image was successfully applied
local function load_and_apply(ib, file, image_loader, image_setter)
local image_applied
local object = image_loader(file)
if object then
image_applied = image_setter(ib, object)
end
return image_applied
end
--- The image rendered by the `imagebox`. --- The image rendered by the `imagebox`.
-- --
-- It can can be any of the following: -- It can can be any of the following:
@ -242,7 +321,10 @@ end
function imagebox:set_image(image) function imagebox:set_image(image)
local setup_succeed local setup_succeed
if type(image) == "userdata" then -- Keep the original to prevent the cache from being GCed.
self._private.original_image = image
if type(image) == "userdata" and not (Rsvg and Rsvg.Handle:is_type_of(image)) then
-- This function is not documented to handle userdata objects, but -- This function is not documented to handle userdata objects, but
-- historically it did, and it did by just assuming they refer to a -- historically it did, and it did by just assuming they refer to a
-- cairo surface. -- cairo surface.
@ -259,7 +341,8 @@ function imagebox:set_image(image)
end end
elseif Rsvg and Rsvg.Handle:is_type_of(image) then elseif Rsvg and Rsvg.Handle:is_type_of(image) then
-- try to apply given rsvg handle -- try to apply given rsvg handle
setup_succeed = set_handle(self, image) rsvg_handle_cache[image] = rsvg_handle_cache[image] or {}
setup_succeed = set_handle(self, image, rsvg_handle_cache[image])
elseif cairo.Surface:is_type_of(image) then elseif cairo.Surface:is_type_of(image) then
-- try to apply given cairo surface -- try to apply given cairo surface
setup_succeed = set_surface(self, image) setup_succeed = set_surface(self, image)
@ -311,23 +394,126 @@ function imagebox:set_clip_shape(clip_shape, ...)
end end
--- Should the image be resized to fit into the available space? --- Should the image be resized to fit into the available space?
--
-- Note that `upscale` and `downscale` can affect the value of `resize`.
-- If conflicting values are passed to the constructor, then the result
-- is undefined.
--
-- @DOC_wibox_widget_imagebox_resize_EXAMPLE@ -- @DOC_wibox_widget_imagebox_resize_EXAMPLE@
-- @property resize -- @property resize
-- @propemits true false -- @propemits true false
-- @tparam boolean resize -- @tparam boolean resize
--- Should the image be resized to fit into the available space? --- Allow the image to be upscaled (made bigger).
-- @tparam boolean allowed If `false`, the image will be clipped, else it will --
-- be resized to fit into the available space. -- Note that `upscale` and `downscale` can affect the value of `resize`.
-- @method set_resize -- If conflicting values are passed to the constructor, then the result
-- @hidden -- is undefined.
--
-- @DOC_wibox_widget_imagebox_upscale_EXAMPLE@
-- @property upscale
-- @tparam boolean upscale
-- @see downscale
-- @see resize
--- Allow the image to be downscaled (made smaller).
--
-- Note that `upscale` and `downscale` can affect the value of `resize`.
-- If conflicting values are passed to the constructor, then the result
-- is undefined.
--
-- @DOC_wibox_widget_imagebox_downscale_EXAMPLE@
-- @property downscale
-- @tparam boolean downscale
-- @see upscale
-- @see resize
--- Set the SVG CSS stylesheet.
--
-- If the image is an SVG (vector graphics), this property allows to set
-- a CSS stylesheet. It can be used to set colors and much more.
--
-- Note that this property is a string, not a path. If the stylesheet is
-- stored on disk, read the content first.
--
--@DOC_wibox_widget_imagebox_stylesheet_EXAMPLE@
--
-- @property stylesheet
-- @tparam string stylesheet
-- @propemits true false
--- Set the SVG DPI (dot per inch).
--
-- Force a specific DPI when rendering the `.svg`. For other file formats,
-- this does nothing.
--
-- It can either be a number of a table containing the `x` and `y` keys.
--
-- Please note that DPI and `resize` can "fight" each other and end up
-- making the image smaller instead of bigger.
--
--@DOC_wibox_widget_imagebox_dpi_EXAMPLE@
--
-- @property dpi
-- @tparam number|table dpi
-- @propemits true false
-- @see auto_dpi
--- Use the object DPI when rendering the SVG.
--
-- By default, the SVG are interpreted as-is. When this property is set,
-- the screen DPI will be passed to the SVG renderer. Depending on which
-- tool was used to create the `.svg`, this may do nothing at all. However,
-- for example, if the `.svg` uses `<text>` elements and doesn't have an
-- hardcoded stylesheet, the result will differ.
--
-- @property auto_dpi
-- @tparam[opt=false] boolean auto_dpi
-- @propemits true false
-- @see dpi
for _, prop in ipairs {"stylesheet", "dpi", "auto_dpi"} do
imagebox["set_" .. prop] = function(self, value)
-- It will be set in :fit and :draw. The handle is shared
-- by multiple imagebox, so it cannot be set just once.
self._private[prop] = value
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
self:emit_signal("property::" .. prop)
end
end
function imagebox:set_resize(allowed) function imagebox:set_resize(allowed)
self._private.resize_forbidden = not allowed self._private.resize = allowed
if allowed then
self._private.downscale = true
self._private.upscale = true
self:emit_signal("property::downscale", allowed)
self:emit_signal("property::upscale", allowed)
end
self:emit_signal("widget::redraw_needed") self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed") self:emit_signal("widget::layout_changed")
self:emit_signal("property::resize", allowed) self:emit_signal("property::resize", allowed)
end end
for _, prop in ipairs {"downscale", "upscale" } do
imagebox["set_" .. prop] = function(self, allowed)
self._private[prop] = allowed
if self._private.resize ~= (self._private.upscale or self._private.downscale) then
self._private.resize = self._private.upscale or self._private.downscale
self:emit_signal("property::resize", self._private.resize)
end
self:emit_signal("widget::redraw_needed")
self:emit_signal("widget::layout_changed")
self:emit_signal("property::"..prop, allowed)
end
end
--- Set the horizontal fit policy. --- Set the horizontal fit policy.
-- --
-- Valid values are: -- Valid values are:
@ -506,12 +692,14 @@ local function new(image, resize_allowed, clip_shape, ...)
local ret = base.make_widget(nil, nil, {enable_properties = true}) local ret = base.make_widget(nil, nil, {enable_properties = true})
gtable.crush(ret, imagebox, true) gtable.crush(ret, imagebox, true)
ret._private.resize = true
if image then if image then
ret:set_image(image) ret:set_image(image)
end end
if resize_allowed ~= nil then if resize_allowed ~= nil then
ret:set_resize(resize_allowed) ret.resize = resize_allowed
end end
ret._private.clip_shape = clip_shape ret._private.clip_shape = clip_shape

7
root.c
View File

@ -481,6 +481,11 @@ luaA_root_wallpaper(lua_State *L)
{ {
if(lua_gettop(L) == 1) if(lua_gettop(L) == 1)
{ {
/* Avoid `error()s` down the line. If this happens during
* initialization, AwesomeWM can be stuck in an infinite loop */
if(lua_isnil(L, -1))
return 0;
cairo_pattern_t *pattern = (cairo_pattern_t *)lua_touserdata(L, -1); cairo_pattern_t *pattern = (cairo_pattern_t *)lua_touserdata(L, -1);
lua_pushboolean(L, root_set_wallpaper(pattern)); lua_pushboolean(L, root_set_wallpaper(pattern));
/* Don't return the wallpaper, it's too easy to get memleaks */ /* Don't return the wallpaper, it's too easy to get memleaks */
@ -602,7 +607,7 @@ const struct luaL_Reg awesome_root_methods[] =
{ "cursor", luaA_root_cursor }, { "cursor", luaA_root_cursor },
{ "fake_input", luaA_root_fake_input }, { "fake_input", luaA_root_fake_input },
{ "drawins", luaA_root_drawins }, { "drawins", luaA_root_drawins },
{ "wallpaper", luaA_root_wallpaper }, { "_wallpaper", luaA_root_wallpaper },
{ "size", luaA_root_size }, { "size", luaA_root_size },
{ "size_mm", luaA_root_size_mm }, { "size_mm", luaA_root_size_mm },
{ "tags", luaA_root_tags }, { "tags", luaA_root_tags },

View File

@ -115,22 +115,40 @@ describe("gears.filesystem", function()
assert.is_true(test_b == "a.png" assert.is_true(test_b == "a.png"
or test_b == "b.jpg") or test_b == "b.jpg")
-- Any file found (selected extensions)
local test_c = gfs.get_random_file_from_dir(root .. "filesystem_tests", {".png", ".jpg"})
assert.is_true(test_c == "a.png"
or test_c == "b.jpg")
-- Test absolute paths.
local test_d = gfs.get_random_file_from_dir(root .. "filesystem_tests", {".png", ".jpg"}, true)
assert.is_true(test_d == root .. "filesystem_tests/a.png"
or test_d == root .. "filesystem_tests/b.jpg")
-- Make sure the paths are generated correctly.
assert.is_nil(test_d:match("//"))
-- "." in filename test cases with extensions -- "." in filename test cases with extensions
local test_c = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {"ext"}) local test_e = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {"ext"})
assert.is_true(test_c == "filename.ext" assert.is_true(test_e == "filename.ext"
or test_c == ".filename.ext" or test_e == ".filename.ext"
or test_c == "file.name.ext" or test_e == "file.name.ext"
or test_c == ".file.name.ext") or test_e == ".file.name.ext")
-- "." in filename test cases with no extensions -- "." in filename test cases with no extensions
local test_d = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {""}) local test_f = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {""})
assert.is_true(test_d == "filename" assert.is_true(test_f == "filename"
or test_d == "filename." or test_f == "filename."
or test_d == "filename.ext." or test_f == "filename.ext."
or test_d == ".filename" or test_f == ".filename"
or test_d == ".filename." or test_f == ".filename."
or test_d == "file.name.ext." or test_f == "file.name.ext."
or test_d == ".file.name.ext.") or test_f == ".file.name.ext.")
-- Test invalid directories.
local test_g = gfs.get_random_file_from_dir(root .. "filesystem_tests/fake_dir")
assert.is_nil(test_g)
end end
end) end)
end) end)

View File

@ -85,7 +85,9 @@ function(escape_string variable content escaped_content line_prefix)
if(variable MATCHES "--DOC_HIDE_ALL") if(variable MATCHES "--DOC_HIDE_ALL")
return() return()
endif() endif()
string(REGEX REPLACE "\n" ";" var_lines "${variable}")
string(REGEX REPLACE ";" "&#59" var_lines "${variable}")
string(REGEX REPLACE "\n" ";" var_lines "${var_lines}")
set(tmp_output ${content}) set(tmp_output ${content})
set(section OFF) set(section OFF)
@ -101,7 +103,7 @@ function(escape_string variable content escaped_content line_prefix)
# Pick lines that are not specified to be hidden # Pick lines that are not specified to be hidden
if((NOT LINE MATCHES "^.*--DOC_[A-Z]+") AND (NOT section)) if((NOT LINE MATCHES "^.*--DOC_[A-Z]+") AND (NOT section))
set(tmp_output ${tmp_output}\n${DOC_LINE_PREFIX}${line_prefix}${LINE}) set(tmp_output "${tmp_output}\n${DOC_LINE_PREFIX}${line_prefix}${LINE}")
endif() endif()
# If we found a start marker previously, look for an end marker. # If we found a start marker previously, look for an end marker.
@ -256,6 +258,12 @@ function(run_test test_path namespace escaped_content)
"\n${expected_output}" "\n${expected_output}"
"${TEST_DOC_CONTENT}" TEST_DOC_CONTENT " " "${TEST_DOC_CONTENT}" TEST_DOC_CONTENT " "
) )
# Add a <br/> to prevent markdown from merging the usage and output
# into a single block.
if(tmp_content MATCHES "--DOC_NO_USAGE")
set(TEST_DOC_CONTENT "${TEST_DOC_CONTENT}\n${DOC_LINE_PREFIX} **Usage example:**")
endif()
else() else()
escape_string( escape_string(
"\n${expected_output}" "\n${expected_output}"
@ -347,7 +355,7 @@ foreach(file ${test_files})
# variable during the pre-processing. # variable during the pre-processing.
# While at it, replace \" created by CMake by ', # While at it, replace \" created by CMake by ',
# &quot; wont work in <code>. # &quot; wont work in <code>.
string(REPLACE "\"" "'" ${TEST_NAME} ${ESCAPED_CODE_EXAMPLE}) string(REPLACE "\"" "&#34" ${TEST_NAME} ${ESCAPED_CODE_EXAMPLE})
endif() endif()
endforeach() endforeach()

View File

@ -8,6 +8,7 @@ local gears = require("gears")
local naughty = require("naughty") local naughty = require("naughty")
local wibox = require("wibox") local wibox = require("wibox")
local beautiful = require("beautiful") --DOC_HIDE local beautiful = require("beautiful") --DOC_HIDE
local assets = require("beautiful.theme_assets")
local look = require("_default_look") local look = require("_default_look")
screen[1]._resize {width = 640, height = 480} screen[1]._resize {width = 640, height = 480}
@ -17,6 +18,78 @@ screen[1]._resize {width = 640, height = 480}
local c = client.gen_fake {hide_first=true} local c = client.gen_fake {hide_first=true}
-- Don't use `awful.wallpaper` to avoid rasterizing the background.
local wallpaper = wibox {
below = true,
shape = gears.shape.octogon,
visible = true,
bg = "#00000000",
}
awful.placement.maximize(wallpaper)
wallpaper.widget = wibox.widget {
fit = function(_, _, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:translate(width - 80, 60)
assets.gen_awesome_name(
cr, height - 40, "#ffffff", beautiful.bg_normal, beautiful.bg_normal
)
end,
widget = wibox.widget.base.make_widget()
}
local p = awful.popup {
widget = wibox.widget {
{ text = "Item", widget = wibox.widget.textbox },
{
{
text = "Selected",
widget = wibox.widget.textbox
},
bg = beautiful.bg_highlight,
widget = wibox.container.background
},
{ text = "Item", widget = wibox.widget.textbox },
forced_width = 100,
widget = wibox.layout.fixed.vertical
},
border_color = beautiful.border_color,
border_width = 1,
x = 50,
y = 200,
}
p:_apply_size_now()
require("gears.timer").run_delayed_calls_now()
p._drawable._do_redraw()
require("gears.timer").delayed_call(function()
-- Get the 4th textbox
local list = p:find_widgets(30, 40)
mouse.coords {x= 120, y=225}
mouse.push_history()
local textboxinstance = list[#list]
local p2 = awful.popup {
widget = wibox.widget {
text = "Submenu",
forced_height = 20,
forced_width = 70,
widget = wibox.widget.textbox
},
border_color = beautiful.border_color,
preferred_positions = "right",
border_width = 1,
}
p2:move_next_to(textboxinstance, "right")
end)
c:geometry { c:geometry {
x = 50, x = 50,
y = 350, y = 350,
@ -97,6 +170,7 @@ wibox {
width = 50, width = 50,
height = 50, height = 50,
shape = gears.shape.octogon, shape = gears.shape.octogon,
visible = true,
color = "#0000ff", color = "#0000ff",
x = 570, x = 570,
y = 410, y = 410,
@ -188,13 +262,17 @@ create_info("awful.tooltip", 30, 130, 100, 30)
create_info("awful.popup", 450, 240, 100, 30) create_info("awful.popup", 450, 240, 100, 30)
create_info("naughty.layout.box", 255, 110, 130, 30) create_info("naughty.layout.box", 255, 110, 130, 30)
create_info("Standard `wibox`", 420, 420, 130, 30) create_info("Standard `wibox`", 420, 420, 130, 30)
create_info("awful.wallpaper", 420, 330, 110, 30)
create_info("awful.menu", 60, 270, 80, 30)
create_line(150, 10, 150, 55) create_line(150, 10, 150, 55)
create_line(75, 100, 75, 135) create_line(75, 100, 75, 135)
create_line(545, 432, 575, 432) create_line(545, 432, 575, 432)
create_line(500, 165, 500, 245) create_line(500, 165, 500, 245)
create_line(390, 250, 450, 250) create_line(380, 250, 450, 250)
create_line(190, 365, 255, 365) create_line(190, 365, 255, 365)
create_line(320, 60, 320, 110) create_line(320, 60, 320, 110)
create_line(525, 345, 570, 345)
create_line(100, 240, 100, 270)
--DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 --DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -58,11 +58,23 @@ if not rawget(screen, "no_outline") then
end end
end end
-- Draw the wallpaper (if any).
if root._wallpaper_pattern then
cr:set_source_rgb(1,0,0)
cr:set_line_width(2)
cr:rectangle(0, 0, root.size())
cr:stroke()
cr:set_source(root._wallpaper_pattern)
cr:rectangle(0, 0, root.size())
cr:fill()
cr:paint()
end
cr:set_line_width(beautiful.border_width/2) cr:set_line_width(beautiful.border_width/2)
cr:set_source(color(beautiful.border_color)) cr:set_source(color(beautiful.border_color))
local rect = {x1 = 0 ,y1 = 0 , x2 = 0 , y2 = 0} local rect = {x1 = 0 ,y1 = 0 , x2 = 0 , y2 = 0}
-- Get the region with wiboxes -- Get the region with wiboxes

View File

@ -0,0 +1,48 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
awful.wallpaper {
screen = screen[1],
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
require("gears.timer").run_delayed_calls_now()

View File

@ -0,0 +1,50 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
local wall = awful.wallpaper {
screen = screen[1],
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
wall:add_screen(screen[2])
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,51 @@
--DOC_GEN_IMAGE
--DOC_HIDE_ALL
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
local wall = awful.wallpaper {
screen = screen[1],
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
wall:add_screen(screen[3])
require("gears.timer").run_delayed_calls_now()

View File

@ -0,0 +1,30 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { --DOC_HIDE
wallpaper = require("awful.wallpaper"), --DOC_HIDE
placement = require("awful.placement"), --DOC_HIDE
} --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
bg = "#000000",
widget = {
{
{
image = beautiful.awesome_icon,
resize = false,
point = awful.placement.bottom_right,
widget = wibox.widget.imagebox,
},
widget = wibox.layout.manual,
},
margins = 5,
widget = wibox.container.margin
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,57 @@
--DOC_GEN_IMAGE
--DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
--DOC_HIDE_END
for s in screen do
local dpi = s.index * 100
--DOC_NEWLINE
awful.wallpaper {
screen = s,
dpi = dpi,
widget = wibox.widget {
--DOC_HIDE_START
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
--DOC_HIDE_END
text = "DPI: " .. dpi,
valign = "center",
align = "center",
widget = wibox.widget.textbox,
}, --DOC_HIDE
widget = wibox.layout.stack --DOC_HIDE
} --DOC_HIDE
}
end
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,21 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
bg = {
type = "linear" ,
from = { 0, 0 },
to = { 0, 240 },
stops = {
{ 0, "#0000ff" },
{ 1, "#ff0000" }
}
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,22 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
bg = {
type = "radial",
from = { 160, 98, 20 },
to = { 160, 98, 120 },
stops = {
{ 0 , "#ff0000" },
{ 0.5, "#00ff00" },
{ 1 , "#0000ff" },
}
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,24 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
widget = {
{
image = beautiful.wallpaper,
resize = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,26 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
bg = "#0000ff",
widget = {
{
image = beautiful.wallpaper,
resize = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,20 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
widget = {
horizontal_fit_policy = "fit",
vertical_fit_policy = "fit",
image = beautiful.wallpaper,
widget = wibox.widget.imagebox,
},
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,58 @@
--DOC_GEN_IMAGE --DOC_GEN_OUTPUT
--DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 320, height = 196}
require("_default_look")
--DOC_HIDE_END
-- Add some padding to the first screen.
screen[1].padding = {
left = 30,
right = 10,
}
--DOC_NEWLINE
local wall = awful.wallpaper {
screen = screen[1],
honor_workarea = true,
honor_padding = true,
bg = "#222222",
uncovered_areas_color = "#ff0000",
widget = wibox.widget {
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
local radius = math.min(width, height)/2
cr:arc(width/2, height/2, radius, 0, 2 * math.pi)
cr:set_source(gears.color {
type = "radial",
from = { width/2, radius, 20 },
to = { width/2, radius, 120 },
stops = {
{ 0, "#0000ff" },
{ 1, "#ff0000" },
{ 1, "#000000" },
}
})
cr:fill()
end,
widget = wibox.widget.base.make_widget()
}
}
--DOC_HIDE_START
require("gears.timer").run_delayed_calls_now()
--DOC_HIDE_END
--DOC_NEWLINE
-- Areas due to the padding and the wibar (workarea).
for _, area in ipairs(wall.uncovered_areas) do
print("Uncovered area:", area.x, area.y, area.width, area.height)
end

View File

@ -0,0 +1,3 @@
Uncovered area: 0 0 30 196
Uncovered area: 0 0 320 14
Uncovered area: 310 0 10 196

View File

@ -0,0 +1,86 @@
--DOC_GEN_IMAGE --DOC_GEN_OUTPUT --DOC_NO_USAGE
--DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 97, width = 96, height = 160}
screen._add_screen {x = 97, y = 129, width = 160, height = 96}
screen._add_screen {x = 258, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 0,width = 160, height = 96}
screen._add_screen {x = 402, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 258,width = 160, height = 96}
require("_default_look")
local walls = {}
--DOC_HIDE_END
local function custom_panning_area(wallpaper)
return {
x = wallpaper.screens[1].geometry.x + 50,
y = wallpaper.screens[2].geometry.y + 50,
width = 96,
height = 96,
}
end
--DOC_HIDE_START
for i=0, 1 do
walls[i+1] = awful.wallpaper {
screens = {
screen[i*3 + 1],
screen[i*3 + 2],
screen[i*3 + 3],
},
bg = "#222222",
panning_area = custom_panning_area,
uncovered_areas_color = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
end
require("gears.timer").run_delayed_calls_now()
--DOC_HIDE_END
--DOC_NEWLINE
-- Areas due to the padding and the wibar (workarea).
for k, wall in ipairs(walls) do
for _, area in ipairs(wall.uncovered_areas) do
print("Uncovered wallpaper #".. k .." area:", area.x, area.y, area.width, area.height)
end
end

View File

@ -0,0 +1,60 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL --DOC_NO_USAGE
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 97, width = 96, height = 160}
screen._add_screen {x = 97, y = 129, width = 160, height = 96}
screen._add_screen {x = 258, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 0,width = 160, height = 96}
screen._add_screen {x = 402, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 258,width = 160, height = 96}
require("_default_look")
for i=0, 1 do
awful.wallpaper {
screens = {
screen[i*3 + 1],
screen[i*3 + 2],
screen[i*3 + 3],
},
bg = "#222222",
panning_area = "inner",
uncovered_areas_color = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
end
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,60 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL --DOC_NO_USAGE
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 97, width = 96, height = 160}
screen._add_screen {x = 97, y = 129, width = 160, height = 96}
screen._add_screen {x = 258, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 0,width = 160, height = 96}
screen._add_screen {x = 402, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 258,width = 160, height = 96}
require("_default_look")
for i=0, 1 do
awful.wallpaper {
screens = {
screen[i*3 + 1],
screen[i*3 + 2],
screen[i*3 + 3],
},
bg = "#222222",
panning_area = "inner_horizontal",
uncovered_areas_color = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
end
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,60 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL --DOC_NO_USAGE
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 97, width = 96, height = 160}
screen._add_screen {x = 97, y = 129, width = 160, height = 96}
screen._add_screen {x = 258, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 0,width = 160, height = 96}
screen._add_screen {x = 402, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 258,width = 160, height = 96}
require("_default_look")
for i=0, 1 do
awful.wallpaper {
screens = {
screen[i*3 + 1],
screen[i*3 + 2],
screen[i*3 + 3],
},
bg = "#222222",
panning_area = "inner_vertical",
uncovered_areas_color = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
end
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,58 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL --DOC_NO_USAGE
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 97, width = 96, height = 160}
screen._add_screen {x = 97, y = 129, width = 160, height = 96}
screen._add_screen {x = 258, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 0,width = 160, height = 96}
screen._add_screen {x = 402, y = 97,width = 96, height = 160}
screen._add_screen {x = 370, y = 258,width = 160, height = 96}
require("_default_look")
for i=0, 1 do
awful.wallpaper {
screens = {
screen[i*3 + 1],
screen[i*3 + 2],
screen[i*3 + 3],
},
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
end
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,53 @@
--DOC_GEN_IMAGE --DOC_NO_USAGE
--DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
-- There is 3 screens. This will add the wallpaper to the last 2.
local wall = awful.wallpaper {
screens = screen,
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
require("gears.timer").run_delayed_calls_now()
--DOC_HIDE_END
wall:remove_screen(screen[1])
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,58 @@
--DOC_GEN_IMAGE --DOC_NO_USAGE
--DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
-- There is 3 screens. This will add the wallpaper to the last 2.
awful.wallpaper {
screens = screen,
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
require("gears.timer").run_delayed_calls_now()
--DOC_HIDE_END
awful.wallpaper {
screen = screen[1],
bg = "#00ffff",
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,54 @@
--DOC_GEN_IMAGE
--DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 160, height = 96}
screen._add_screen {x = 161, y = 0, width = 160, height = 96}
screen._add_screen {x = 322, y = 0, width = 160, height = 96}
require("_default_look")
--DOC_HIDE_END
-- There is 3 screens. This will add the wallpaper to the last 2.
awful.wallpaper {
screens = {
screen[2],
screen[3],
},
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color("#0000ff"))
cr:line_to(width, height)
cr:line_to(width, 0)
cr:line_to(0, 0)
cr:close_path()
cr:fill()
cr:set_source(gears.color("#ff00ff"))
cr:move_to(0, 0)
cr:line_to(0, height)
cr:line_to(width, height)
cr:close_path()
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "Center",
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,45 @@
--DOC_NO_USAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local gears = {timer = require("gears.timer"), filesystem = require("gears.filesystem")}--DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
-- The "request::wallpaper" section is already in the default
-- `rc.lua`, replace it with this.
screen.connect_signal("request::wallpaper", function(s)
awful.wallpaper {
screen = s,
bg = "#0000ff",
widget = {
{
image = gears.filesystem.get_random_file_from_dir(
"path/to/dir",
{".jpg", ".png", ".svg"},
true
),
resize = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
end)
--DOC_NEWLINE
-- **Somewhere else** in the code, **not** in the `request::wallpaper` handler.
gears.timer {
timeout = 1800,
autostart = true,
callback = function()
for s in screen do
s:emit_signal("request::wallpaper")
end
end,
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,36 @@
--DOC_GEN_IMAGE --DOC_NO_USAGE --DOC_HIDE_START
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
screen[1]._resize {x = 0, y = 0, width = 320, height = 196}
--DOC_HIDE_END
local image = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'..
'<svg width="190" height="60">'..
'<rect x="10" y="10" width="50" height="50" />'..
'<rect x="70" y="10" width="50" height="50" class="my_class" />'..
'<rect x="130" y="10" width="50" height="50" id="my_id" />'..
'</svg>'
--DOC_NEWLINE
local stylesheet = "" ..
"rect { fill: #ffff00; } "..
".my_class { fill: #00ff00; } "..
"#my_id { fill: #0000ff; }"
--DOC_NEWLINE
awful.wallpaper {
screen = screen[1], --DOC_HIDE
widget = wibox.widget {
forced_height = 60, --DOC_HIDE
forced_width = 190, --DOC_HIDE
stylesheet = stylesheet,
image = image,
widget = wibox.widget.imagebox
}
}
--DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,27 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local beautiful = require("beautiful") --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
bg = "#0000ff",
widget = {
{
image = beautiful.awesome_icon,
resize = false,
widget = wibox.widget.imagebox,
},
horizontal_spacing = 5,
vertical_spacing = 5,
valign = "top",
halign = "left",
tiled = true,
widget = wibox.container.tile,
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,30 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local gears = { shape = require("gears.shape") } --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
bg = "#0000ff",
widget = {
{
shape = gears.shape.star,
forced_width = 30,
forced_height = 30,
widget = wibox.widget.separator,
},
horizontal_spacing = 5,
vertical_spacing = 5,
vertical_crop = true,
horizontal_crop = true,
valign = "center",
halign = "center",
tiled = true,
widget = wibox.container.tile,
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,82 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local gears = {color = require("gears.color") } --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local s = screen[1] --DOC_HIDE
awful.wallpaper {
screen = s,
widget = wibox.widget {
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
cr:set_source(gears.color {
type = 'linear',
from = { 0, 0 },
to = { 0, height },
stops = {
{ 0 , '#030d27' },
{ 0.75, '#3a183f' },
{ 0.75, '#000000' },
{ 1 , '#222222' }
}
})
cr:paint()
-- Clip the first 33% of the screen
cr:rectangle(0,0, width, height/3)
--DOC_NEWLINE
-- Clip-out some increasingly large sections of add the sun "bars"
for i=0, 6 do
cr:rectangle(0, height*.28 + i*(height*.055 + i/2), width, height*.055)
end
cr:clip()
--DOC_NEWLINE
-- Draw the sun
cr:set_source(gears.color {
type = 'linear' ,
from = { 0, 0 },
to = { 0, height },
stops = {
{ 0, '#f0d64f' },
{ 1, '#e484c6' }
}
})
cr:arc(width/2, height/2, height*.35, 0, math.pi*2)
cr:fill()
--DOC_NEWLINE
-- Draw the grid
local lines = width/8
cr:reset_clip()
cr:set_line_width(0.5)
cr:set_source(gears.color("#8922a3"))
--DOC_NEWLINE
for i=1, lines do
cr:move_to((-width) + i* math.sin(i * (math.pi/(lines*2)))*30, height)
cr:line_to(width/4 + i*((width/2)/lines), height*0.75 + 2)
cr:stroke()
end
--DOC_NEWLINE
for i=1, 5 do
cr:move_to(0, height*0.75 + i*10 + i*2)
cr:line_to(width, height*0.75 + i*10 + i*2)
cr:stroke()
end
end,
}
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,48 @@
--DOC_NO_USAGE --DOC_GEN_IMAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
math.randomseed(1) --DOC_HIDE
screen[1]._resize {x = 0, y = 0, width = 320, height = 196} --DOC_HIDE
local function binary()
local ret = {}
for _=1, 15 do
for _=1, 57 do
table.insert(ret, math.random() > 0.5 and 1 or 0)
end
table.insert(ret, "\n")
end
return table.concat(ret)
end
--DOC_NEWLINE
awful.wallpaper {
screen = screen[1], --DOC_HIDE
bg = "#000000",
fg = "#55ff5577",
widget = wibox.widget {
{
{
markup = "<tt><b>[SYSTEM FAILURE]</b></tt>",
valign = "center",
align = "center",
widget = wibox.widget.textbox
},
fg = "#00ff00",
widget = wibox.container.background
},
{
wrap = "word",
text = binary(),
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
},
}
require("gears.timer").run_delayed_calls_now() --DOC_HIDE

View File

@ -0,0 +1,56 @@
--DOC_GEN_IMAGE --DOC_HIDE_ALL
local awful = { wallpaper = require("awful.wallpaper") }
local wibox = require("wibox")
local gears = {color = require("gears.color") }
screen._track_workarea = true
screen[1]._resize {x = 0, y = 0, width = 320, height = 196}
screen._add_screen {x = 330, y = 0, width = 320, height = 196}
require("_default_look")
screen.connect_signal("request::wallpaper", function(s)
awful.wallpaper {
screen = s,
honor_workarea = s.index == 2,
bg = "#222222",
widget = wibox.widget {
{
fit = function(_, width, height)
return width, height
end,
draw = function(_, _, cr, width, height)
local radius = math.min(width, height)/2
cr:arc(width/2, height/2, radius, 0, 2 * math.pi)
cr:set_source(gears.color {
type = "radial",
from = { width/2, radius, 20 },
to = { width/2, radius, 120 },
stops = {
{ 0, "#0000ff" },
{ 1, "#ff0000" },
{ 1, "#000000" },
}
})
cr:fill()
end,
widget = wibox.widget.base.make_widget()
},
{
text = "honor_workarea = " .. (s.index == 2 and "true" or "false"),
valign = "center",
align = "center",
widget = wibox.widget.textbox,
},
widget = wibox.layout.stack
}
}
end)
require("gears.timer").run_delayed_calls_now()
for s in screen do
s:emit_signal("request::wallpaper", s)
end
require("gears.timer").run_delayed_calls_now()

View File

@ -3,87 +3,98 @@ local wibox = require("wibox")
local beautiful = require("beautiful") local beautiful = require("beautiful")
require("_date") require("_date")
-- Create the same number of tags as the default config local ret = nil
awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, screen[1], awful.layout.layouts[1])
local mykeyboardlayout = awful.widget.keyboardlayout()
local mytextclock = wibox.widget.textclock()
local mylayoutbox = awful.widget.layoutbox(screen[1])
local mytaglist = awful.widget.taglist(screen[1], awful.widget.taglist.filter.all, {})
local mytasklist = awful.widget.tasklist(screen[1], awful.widget.tasklist.filter.currenttags, {})
local mypromptbox = wibox.widget.textbox("")
local wb = awful.wibar { position = "top" } for i = 1, screen.count() do
wb:setup { local s = screen[i]
layout = wibox.layout.align.horizontal,
{
layout = wibox.layout.fixed.horizontal,
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
mytaglist,
mypromptbox,
},
mytasklist,
{
layout = wibox.layout.fixed.horizontal,
mykeyboardlayout,
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
mytextclock,
mylayoutbox,
},
}
-- Create the same number of tags as the default config
awful.tag({ "1", "2", "3", "4", "5", "6", "7", "8", "9" }, s, awful.layout.layouts[1])
local mykeyboardlayout = awful.widget.keyboardlayout()
local mytextclock = wibox.widget.textclock()
local mylayoutbox = awful.widget.layoutbox(s)
local mytaglist = awful.widget.taglist(s, awful.widget.taglist.filter.all, {})
local mytasklist = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, {})
local mypromptbox = wibox.widget.textbox("")
client.connect_signal("request::titlebars", function(c) local wb = awful.wibar {
local top_titlebar = awful.titlebar(c, { position = "top",
height = 20, screen = s
bg_normal = beautiful.bg_normal,
})
top_titlebar : setup {
{ -- Left
awful.titlebar.widget.iconwidget(c),
layout = wibox.layout.fixed.horizontal
},
{ -- Middle
{ -- Title
align = "center",
widget = awful.titlebar.widget.titlewidget(c)
},
layout = wibox.layout.flex.horizontal
},
{ -- Right
awful.titlebar.widget.floatingbutton (c),
awful.titlebar.widget.maximizedbutton(c),
awful.titlebar.widget.stickybutton (c),
awful.titlebar.widget.ontopbutton (c),
awful.titlebar.widget.closebutton (c),
layout = wibox.layout.fixed.horizontal()
},
layout = wibox.layout.align.horizontal
} }
end)
wb:setup {
layout = wibox.layout.align.horizontal,
{
layout = wibox.layout.fixed.horizontal,
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
mytaglist,
mypromptbox,
},
mytasklist,
{
layout = wibox.layout.fixed.horizontal,
mykeyboardlayout,
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
{
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox,
},
mytextclock,
mylayoutbox,
},
}
client.connect_signal("request::titlebars", function(c)
local top_titlebar = awful.titlebar(c, {
height = 20,
bg_normal = beautiful.bg_normal,
})
top_titlebar : setup {
{ -- Left
awful.titlebar.widget.iconwidget(c),
layout = wibox.layout.fixed.horizontal
},
{ -- Middle
{ -- Title
align = "center",
widget = awful.titlebar.widget.titlewidget(c)
},
layout = wibox.layout.flex.horizontal
},
{ -- Right
awful.titlebar.widget.floatingbutton (c),
awful.titlebar.widget.maximizedbutton(c),
awful.titlebar.widget.stickybutton (c),
awful.titlebar.widget.ontopbutton (c),
awful.titlebar.widget.closebutton (c),
layout = wibox.layout.fixed.horizontal()
},
layout = wibox.layout.align.horizontal
}
end)
ret = ret or {
mykeyboardlayout = mykeyboardlayout,
mytextclock = mytextclock ,
mylayoutbox = mylayoutbox ,
mytaglist = mytaglist ,
mytasklist = mytasklist ,
mywibox = wb ,
mypromptbox = mypromptbox ,
}
end
require("gears.timer").run_delayed_calls_now() require("gears.timer").run_delayed_calls_now()
return { return ret
mykeyboardlayout = mykeyboardlayout,
mytextclock = mytextclock ,
mylayoutbox = mylayoutbox ,
mytaglist = mytaglist ,
mytasklist = mytasklist ,
mywibox = wb ,
mypromptbox = mypromptbox ,
}

View File

@ -118,6 +118,10 @@ awesome._modifiers = {
}, },
} }
function awesome._get_key_name(key)
return key
end
awesome._active_modifiers = {} awesome._active_modifiers = {}
return awesome return awesome

View File

@ -1,6 +1,8 @@
local root = {_tags={}} local root = {_tags={}}
local gtable = require("gears.table") local gtable = require("gears.table")
local cairo = require( "lgi" ).cairo
function root:tags() function root:tags()
return root._tags return root._tags
@ -169,6 +171,24 @@ function root._write_string(string, c)
end end
end end
function root._wallpaper(pattern)
if not pattern then return root._wallpaper_surface end
-- Make a copy because `:finish()` is called by `root.wallpaper` to avoid
-- a memory leak in the "real" backend.
local target = cairo.ImageSurface(cairo.Format.RGB32, root.size())
local cr = cairo.Context(target)
cr:set_source(pattern)
cr:rectangle(0, 0, root.size())
cr:fill()
root._wallpaper_pattern = cairo.Pattern.create_for_surface(target)
root._wallpaper_surface = target
return target
end
function root.set_newindex_miss_handler(h) function root.set_newindex_miss_handler(h)
rawset(root, "_ni_handler", h) rawset(root, "_ni_handler", h)

View File

@ -0,0 +1,13 @@
--DOC_NO_USAGE
local awful = { wallpaper = require("awful.wallpaper") } --DOC_HIDE
local global_wallpaper = awful.wallpaper {
-- [...] your content
}
--DOC_NEWLINE
screen.connect_signal("request::wallpaper", function()
-- `screen` is the global screen module. It is also a list of all screens.
global_wallpaper.screens = screen
end)

View File

@ -0,0 +1,54 @@
--DOC_HIDE_ALL
--DOC_GEN_IMAGE
local parent = ...
local wibox = require( "wibox" )
local beautiful = require( "beautiful" )
local function cell_centered_widget(widget)
return wibox.widget {
widget,
valign = 'center',
halign = 'center',
content_fill_vertical = false,
content_fill_horizontal = false,
widget = wibox.container.place
}
end
local function build_ib(size, resize)
return cell_centered_widget(wibox.widget {
{
downscale = resize,
upscale = true,
forced_height = size,
forced_width = size,
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox
},
forced_width = size + 2,
forced_height = size + 2,
color = beautiful.border_color,
margins = 1,
widget = wibox.container.margin
})
end
local l = wibox.widget {
homogeneous = false,
spacing = 5,
layout = wibox.layout.grid,
}
parent:add(l)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox('downscale = true')), 1, 1)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox('downscale = false')), 2, 1)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox('imagebox size')), 3, 1)
for i,size in ipairs({16, 32, 64}) do
l:add_widget_at(build_ib(size, true), 1, i + 1)
l:add_widget_at(build_ib(size, false), 2, i + 1)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox(size..'x'..size)), 3, i + 1)
end
--DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,48 @@
--DOC_GEN_IMAGE --DOC_HIDE
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local l = wibox.layout { --DOC_HIDE
forced_width = 360, --DOC_HIDE
spacing = 5, --DOC_HIDE
layout = wibox.layout.flex.vertical --DOC_HIDE
} --DOC_HIDE
local image = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'..
'<svg width="2in" height="1in">'..
'<rect height="0.1in" width="0.1in" style="fill:red;" />'..
'<text x="10" y="32" width="150" style="font-size: 0.1in;">Hello world!</text>'..
'</svg>'
--DOC_NEWLINE
for _, dpi in ipairs {100, 200, 300} do
local row = wibox.layout { --DOC_HIDE
spacing = 5, --DOC_HIDE
layout = wibox.layout.fixed.horizontal --DOC_HIDE
} --DOC_HIDE
row:add(wibox.widget { --DOC_HIDE
markup = "<b>dpi = "..dpi.."</b>", --DOC_HIDE
forced_width = 80, --DOC_HIDE
widget = wibox.widget.textbox --DOC_HIDE
}) --DOC_HIDE
local w = wibox.widget {
image = image,
dpi = dpi,
resize = false,
forced_height = 70,
forced_width = 150,
widget = wibox.widget.imagebox
}
row:add(w) --DOC_HIDE
l:add(row) --DOC_HIDE
end
parent:add(l) --DOC_HIDE
--DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,31 @@
--DOC_GEN_IMAGE --DOC_HIDE
local parent = ... --DOC_HIDE
local wibox = require("wibox") --DOC_HIDE
local image = '<?xml version="1.0" encoding="UTF-8" standalone="no"?>'..
'<svg width="190" height="60">'..
'<rect x="10" y="10" width="50" height="50" />'..
'<rect x="70" y="10" width="50" height="50" class="my_class" />'..
'<rect x="130" y="10" width="50" height="50" id="my_id" />'..
'</svg>'
--DOC_NEWLINE
local stylesheet = "" ..
"rect { fill: #ffff00; } "..
".my_class { fill: #00ff00; } "..
"#my_id { fill: #0000ff; }"
--DOC_NEWLINE
local w = wibox.widget {
forced_height = 60, --DOC_HIDE
forced_width = 190, --DOC_HIDE
stylesheet = stylesheet,
image = image,
widget = wibox.widget.imagebox
}
parent:add(w) --DOC_HIDE
--DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -0,0 +1,54 @@
--DOC_HIDE_ALL
--DOC_GEN_IMAGE
local parent = ...
local wibox = require( "wibox" )
local beautiful = require( "beautiful" )
local function cell_centered_widget(widget)
return wibox.widget {
widget,
valign = 'center',
halign = 'center',
content_fill_vertical = false,
content_fill_horizontal = false,
widget = wibox.container.place
}
end
local function build_ib(size, resize)
return cell_centered_widget(wibox.widget {
{
upscale = resize,
downscale = true,
forced_height = size,
forced_width = size,
image = beautiful.awesome_icon,
widget = wibox.widget.imagebox
},
forced_width = size + 2,
forced_height = size + 2,
color = beautiful.border_color,
margins = 1,
widget = wibox.container.margin
})
end
local l = wibox.widget {
homogeneous = false,
spacing = 5,
layout = wibox.layout.grid,
}
parent:add(l)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox('upscale = true')), 1, 1)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox('upscale = false')), 2, 1)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox('imagebox size')), 3, 1)
for i,size in ipairs({16, 32, 64}) do
l:add_widget_at(build_ib(size, true), 1, i + 1)
l:add_widget_at(build_ib(size, false), 2, i + 1)
l:add_widget_at(cell_centered_widget(wibox.widget.textbox(size..'x'..size)), 3, i + 1)
end
--DOC_HIDE vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -68,7 +68,10 @@ local function add_client(args)
local c = data.c local c = data.c
if not c then if not c then
c = client.get()[1] c = client.get()[1]
assert(c.name == name) assert(c.name == name,
"Expected "..name.." got "..c.name.." there is "
..#client.get().." clients"
)
data.c = c data.c = c
end end
local test = args.test or default_test local test = args.test or default_test

View File

@ -5,6 +5,13 @@ local wp = require("gears.wallpaper")
local color = require("gears.color") local color = require("gears.color")
local cairo = require( "lgi" ).cairo local cairo = require( "lgi" ).cairo
local surface = require("gears.surface") local surface = require("gears.surface")
local awall = require("awful.wallpaper")
local beautiful = require("beautiful")
local wibox = require("wibox")
local gdebug = require("gears.debug")
-- This test suite is for a deprecated module.
gdebug.deprecate = function() end
local steps = {} local steps = {}
@ -54,85 +61,236 @@ table.insert(steps, function()
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.fit(img, screen[1], "#00ff00") wp.fit(img, screen[1], "#00ff00")
-- There is a delayed call for the last call, let it be processed before -- There is a delayed call for the last call, let it be processed before
-- adding more -- adding more
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.centered(img, nil, nil) wp.centered(img, nil, nil)
wp.centered(img, screen[1], nil) wp.centered(img, screen[1], nil)
-- There is a delayed call for the last call, let it be processed before -- There is a delayed call for the last call, let it be processed before
-- adding more -- adding more
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.centered(img, screen[1], "#00ff00") wp.centered(img, screen[1], "#00ff00")
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.maximized(img, nil, nil, nil) wp.maximized(img, nil, nil, nil)
-- There is a delayed call for the last call, let it be processed before -- There is a delayed call for the last call, let it be processed before
-- adding more -- adding more
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.maximized(img, screen[1], nil, nil) wp.maximized(img, screen[1], nil, nil)
-- There is a delayed call for the last call, let it be processed before -- There is a delayed call for the last call, let it be processed before
-- adding more -- adding more
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.maximized(img, screen[1], false, nil)
wp.maximized(img, screen[1], true, nil)
wp.maximized(img, screen[1], false, nil) -- There is a delayed call for the last call, let it be processed before
wp.maximized(img, screen[1], true, nil) -- adding more
return true
-- There is a delayed call for the last call, let it be processed before
-- adding more
return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.maximized(img, screen[1], false, {x=10, y= 10})
wp.maximized(img, screen[1], true, {x=10, y= 10})
wp.maximized(img, screen[1], false, {x=10, y= 10}) return true
wp.maximized(img, screen[1], true, {x=10, y= 10})
return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.tiled(img, nil, nil) wp.tiled(img, nil, nil)
wp.tiled(img, screen[1], nil) wp.tiled(img, screen[1], nil)
-- There is a delayed call for the last call, let it be processed before -- There is a delayed call for the last call, let it be processed before
-- adding more -- adding more
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
wp.tiled(img, screen[1], {x=10, y= 10}) wp.tiled(img, screen[1], {x=10, y= 10})
return true return true
end) end)
table.insert(steps, function() table.insert(steps, function()
for _, c in ipairs(colors) do for _, c in ipairs(colors) do
wp.set(c) wp.set(c)
end end
return true return true
end) end)
-- Make sure passing `nil` doesn't crash Awesome.
table.insert(steps, function()
root._wallpaper(nil)
root.wallpaper(nil)
return true
end)
local walls = setmetatable({}, {__mode = "v"})
-- Test awful.wallpaper garbage collection.
table.insert(steps, function()
walls["first"] = awall {
screen = screen[1],
widget = {
{
image = beautiful.wallpaper,
upscale = true,
downscale = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
collectgarbage("collect")
return true
end)
table.insert(steps, function(count)
if count < 3 then
collectgarbage("collect")
return
end
assert(walls["first"])
walls["second"] = awall {
screen = screen[1],
widget = {
{
image = beautiful.wallpaper,
upscale = true,
downscale = true,
widget = wibox.widget.imagebox,
},
valign = "center",
halign = "center",
tiled = false,
widget = wibox.container.tile,
}
}
return true
end)
local repaint_called, paint_width, paint_height = false, nil, nil
-- Test setting "after the fact"
table.insert(steps, function(count)
if count < 3 then
collectgarbage("collect")
return
end
assert(walls["second"])
assert(not walls["first"])
local real_repaint = walls["second"].repaint
walls["second"].repaint = function(self)
repaint_called = true
real_repaint(self)
end
walls["second"].widget = wibox.widget {
fit = function(_, w, h)
return w, h
end,
draw = function(self, ctx, cr2, width, height)
cr2:set_source_rgba(1, 0, 0, 0.5)
cr2:rectangle(0, 0, width/2, height/2)
cr2:fill()
paint_width, paint_height = width, height
assert((not self.dpi) and ctx.dpi)
end
}
walls["second"].bg = "#0000ff"
walls["second"].fg = "#00ff00"
assert(repaint_called)
-- This needs to happen after this event loop to avoid
-- painting the wallpaper many time in a row.
assert(not paint_width)
return true
end)
table.insert(steps, function()
assert(walls["second"])
assert(paint_width == screen[1].geometry.width)
assert(paint_height == screen[1].geometry.height)
repaint_called = false
paint_width, paint_height = nil, nil
-- Disable new wallpaper creation to prevent request::wallpaper from
-- replacing the wall.
local constructor = getmetatable(awall).__call
setmetatable(awall, {__call = function() end})
screen[1]:fake_resize(
10, 10, math.floor(screen[1].geometry.width/2), math.floor(screen[1].geometry.height/2)
)
assert(paint_height ~= screen[1].geometry.height)
assert(repaint_called)
setmetatable(awall, {__call = constructor})
return true
end)
table.insert(steps, function(count)
if count == 1 then return end
print(paint_width, paint_height, screen[1].geometry.width, screen[1].geometry.height)
assert(paint_width == screen[1].geometry.width)
assert(paint_height == screen[1].geometry.height)
walls["second"].dpi = 123
assert(walls["second"].dpi == 123)
walls["second"]:detach()
return true
end)
table.insert(steps, function(count)
if count < 3 then
collectgarbage("collect")
return
end
assert(not walls["second"])
return true
end)
runner.run_steps(steps) runner.run_steps(steps)

View File

@ -8,6 +8,7 @@ local xresources = require("beautiful.xresources")
local rnotification = require("ruled.notification") local rnotification = require("ruled.notification")
local dpi = xresources.apply_dpi local dpi = xresources.apply_dpi
local xrdb = xresources.get_current_theme() local xrdb = xresources.get_current_theme()
local gdebug = require("gears.debug")
local gfs = require("gears.filesystem") local gfs = require("gears.filesystem")
local themes_path = gfs.get_themes_dir() local themes_path = gfs.get_themes_dir()
@ -126,10 +127,40 @@ local wallpaper_alt_fg = xrdb.color12
if not is_dark_bg then if not is_dark_bg then
wallpaper_bg, wallpaper_fg = wallpaper_fg, wallpaper_bg wallpaper_bg, wallpaper_fg = wallpaper_fg, wallpaper_bg
end end
theme.wallpaper = function(s)
return theme_assets.wallpaper(wallpaper_bg, wallpaper_fg, wallpaper_alt_fg, s) local rsvg = pcall(function() return require("lgi").Rsvg end)
if rsvg then
local handle = require("lgi").Rsvg.Handle.new_from_file(
themes_path.."xresources/wallpaper.svg"
)
if handle then
handle:set_stylesheet([[
.normal {
fill: ]]..wallpaper_fg..[[;
}
.background {
fill: ]]..wallpaper_bg..[[;
stroke: ]]..wallpaper_bg..[[;
}
.logo {
fill: ]]..wallpaper_alt_fg..[[;
}
]])
theme.wallpaper = handle
end
else
gdebug.print_warning("Could not load the wallpaper: librsvg is not installed.")
end end
if not theme.wallpaper then
theme.wallpaper = themes_path.."xresources/wallpaper.svg"
end
theme.wallpaper_bg = wallpaper_bg
-- Set different colors for urgent notifications. -- Set different colors for urgent notifications.
rnotification.connect_signal('request::rules', function() rnotification.connect_signal('request::rules', function()
rnotification.append_rule { rnotification.append_rule {

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1280pt" height="720pt" viewBox="0 0 1280 720" version="1.1">
<rect x="0" y="0" width="1280" height="720" style="stroke:none;" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;fill:rgb(27.843137%,47.843137%,70.196078%);fill-opacity:1;" d="M 1152 72 L 1224 72 L 1224 144 L 1152 144 Z M 1152 72 " class="logo"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 0 24 L 48 24 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;" d="M 1152 156 L 1224 156 L 1224 228 L 1152 228 Z M 1152 156 " class="normal"/>
<path style="fill:none !important;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 48 L 48 48 L 48 72 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 84 L 24 132 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 48 84 L 48 132 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;" d="M 1152 240 L 1224 240 L 1224 312 L 1152 312 Z M 1152 240 " class="normal"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 192 L 72 192 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 216 L 72 216 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;" d="M 1152 324 L 1224 324 L 1224 396 L 1152 396 Z M 1152 324 " class="normal"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 276 L 72 276 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 0 300 L 48 300 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;" d="M 1152 408 L 1224 408 L 1224 480 L 1152 480 Z M 1152 408 " class="normal"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 36 360 L 36 384 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;" d="M 1152 492 L 1224 492 L 1224 564 L 1152 564 Z M 1152 492 " class="normal"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 444 L 24 492 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 48 444 L 48 492 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style=" stroke:none;fill-rule:nonzero;" d="M 1152 576 L 1224 576 L 1224 648 L 1152 648 Z M 1152 576 " class="normal"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 528 L 72 528 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
<path style="fill:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;" d="M 24 552 L 72 552 " transform="matrix(1,0,0,1,1152,72)" class="background"/>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB