diff --git a/awesomerc.lua b/awesomerc.lua index 5981f8a4..bdbdbaa7 100644 --- a/awesomerc.lua +++ b/awesomerc.lua @@ -77,7 +77,7 @@ mylauncher = awful.widget.launcher({ image = beautiful.awesome_icon, menubar.utils.terminal = terminal -- Set the terminal for applications that require it -- }}} --- {{{ Tag +-- {{{ Tag layout -- @DOC_LAYOUT@ -- Table of layouts to cover with awful.layout.inc, order matters. tag.connect_signal("request::default_layouts", function() @@ -99,6 +99,27 @@ tag.connect_signal("request::default_layouts", function() 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 -- Keyboard map indicator and switcher @@ -107,19 +128,6 @@ mykeyboardlayout = awful.widget.keyboardlayout() -- Create a textclock widget 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@ screen.connect_signal("request::desktop_decoration", function(s) -- Each screen has its own tag table. diff --git a/docs/03-declarative-layout.md b/docs/03-declarative-layout.md index 12143b54..1555a7c0 100644 --- a/docs/03-declarative-layout.md +++ b/docs/03-declarative-layout.md @@ -124,6 +124,10 @@ to an object such as the mouse. The `naughty.layout.box` allows to provide custom widgets to use within the 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 exactly the same way and allows to attach widgets on each side of clients. diff --git a/docs/05-awesomerc.md.lua b/docs/05-awesomerc.md.lua index 0fc005d0..50965a47 100644 --- a/docs/05-awesomerc.md.lua +++ b/docs/05-awesomerc.md.lua @@ -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). ]] +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 = [[   diff --git a/docs/ldoc.ltp b/docs/ldoc.ltp index 148ac6d2..01b260b9 100644 --- a/docs/ldoc.ltp +++ b/docs/ldoc.ltp @@ -38,7 +38,8 @@ # local use_li = ldoc.use_li # local display_name = ldoc.display_name # 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(";", ";"):gsub(""", '"') 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 html_space = function(s) return s:gsub(" ", "%%20") end # local no_underscores = function(s) return s:gsub("_", " ") end @@ -189,7 +190,7 @@

Usage:

# end -- if usage @@ -331,7 +332,7 @@ # end # if kitem.usage then

Usage:

-
$(ldoc.prettify(kitem.usage[1]))
+
$(ldoc.prettify(un_cmake(kitem.usage[1])))
# end # end # if not kind:match("^ldoc_skip") then @@ -450,7 +451,7 @@

Usage:

# end -- if usage diff --git a/lib/awful/_compat.lua b/lib/awful/_compat.lua index c25714f0..496b090b 100644 --- a/lib/awful/_compat.lua +++ b/lib/awful/_compat.lua @@ -58,6 +58,28 @@ do assert(root.object == root_object) 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 -- 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 diff --git a/lib/awful/init.lua b/lib/awful/init.lua index cff95db1..d7ebbf81 100644 --- a/lib/awful/init.lua +++ b/lib/awful/init.lua @@ -35,6 +35,7 @@ local ret = { tooltip = require("awful.tooltip"); permissions = require("awful.permissions"); titlebar = require("awful.titlebar"); + wallpaper = require("awful.wallpaper"); rules = require("awful.rules"); popup = require("awful.popup"); spawn = require("awful.spawn"); diff --git a/lib/awful/wallpaper.lua b/lib/awful/wallpaper.lua new file mode 100644 index 00000000..e9374d1b --- /dev/null +++ b/lib/awful/wallpaper.lua @@ -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 <elv1313@gmail.com> +-- @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}) diff --git a/lib/gears/filesystem.lua b/lib/gears/filesystem.lua index 42f94ec7..de58e1a7 100644 --- a/lib/gears/filesystem.lua +++ b/lib/gears/filesystem.lua @@ -172,28 +172,39 @@ end -- @tparam string path The directory to search. -- @tparam[opt] table exts Specific extensions to limit the search to. eg:`{ "jpg", "png" }` -- 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 --- 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 -function filesystem.get_random_file_from_dir(path, exts) +function filesystem.get_random_file_from_dir(path, exts, absolute_path) local files, valid_exts = {}, {} -- 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 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 if file:get_file_type() == "REGULAR" then local file_name = file:get_display_name() + if not exts or valid_exts[file_name:lower():match(".+%.(.*)$") or ""] then table.insert(files, file_name) end end end + if #files == 0 then return nil end + -- 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 return filesystem diff --git a/lib/gears/wallpaper.lua b/lib/gears/wallpaper.lua index 8b3f796a..3843a359 100644 --- a/lib/gears/wallpaper.lua +++ b/lib/gears/wallpaper.lua @@ -1,22 +1,7 @@ --------------------------------------------------------------------------- -- Functions for setting the wallpaper. -- --- There are two levels of functionality provided by this module: --- --- 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) +-- This module is deprecated, please use `awful.wallpaper`. -- -- @author 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 -- @return[1] The available geometry (table with entries width and height) -- @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) + debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5}) + s = get_screen(s) 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, -- a description for gears.color or a cairo pattern. -- @see gears.color --- @staticfct gears.wallpaper.set +-- @deprecated gears.wallpaper.set function wallpaper.set(pattern) + debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5}) + if cairo.Surface:is_type_of(pattern) then pattern = cairo.Pattern.create_for_surface(pattern) end @@ -121,7 +110,7 @@ function wallpaper.set(pattern) if not cairo.Pattern:is_type_of(pattern) then error("wallpaper.set() called with an invalid argument") end - root.wallpaper(pattern._native) + root.wallpaper(pattern) end --- Set a centered wallpaper. @@ -132,8 +121,10 @@ end -- gears.color. The default is black. -- @param scale The scale factor for the wallpaper. Default is 1 (original size). -- @see gears.color --- @staticfct gears.wallpaper.centered +-- @deprecated gears.wallpaper.centered function wallpaper.centered(surf, s, background, scale) + debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5}) + local geom, cr = wallpaper.prepare_context(s) local original_surf = 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 -- all screens are set. -- @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) + debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5}) + local _, cr = wallpaper.prepare_context(s) if offset then @@ -202,8 +195,10 @@ end -- @param ignore_aspect If this is true, the image's aspect ratio is ignored. -- The default is to honor the aspect ratio. -- @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) + debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5}) + local geom, cr = wallpaper.prepare_context(s) local original_surf = surf surf = surface.load_uncached(surf) @@ -243,8 +238,10 @@ end -- @param background The background color that should be used. Gets handled via -- gears.color. The default is black. -- @see gears.color --- @staticfct gears.wallpaper.fit +-- @deprecated gears.wallpaper.fit function wallpaper.fit(surf, s, background) + debug.deprecate("Use `awful.wallpaper`", {deprecated_in=5}) + local geom, cr = wallpaper.prepare_context(s) local original_surf = surf surf = surface.load_uncached(surf) diff --git a/lib/wibox/container/tile.lua b/lib/wibox/container/tile.lua index e2175cd4..46fdd391 100644 --- a/lib/wibox/container/tile.lua +++ b/lib/wibox/container/tile.lua @@ -31,6 +31,7 @@ function module:draw(context, cr, width, height) if not self._private.surface then self._private.surface = cairo.ImageSurface(cairo.Format.ARGB32, w+hspace, h+vspace) 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.extend = cairo.Extend.REPEAT self._private.cr:translate(math.ceil(hspace), math.ceil(vspace)) diff --git a/lib/wibox/widget/imagebox.lua b/lib/wibox/widget/imagebox.lua index 060d8c42..ace4eed7 100644 --- a/lib/wibox/widget/imagebox.lua +++ b/lib/wibox/widget/imagebox.lua @@ -48,28 +48,128 @@ end local imagebox = { mt = {} } -local rsvg_handle_cache = setmetatable({}, { __mode = 'v' }) +local rsvg_handle_cache = setmetatable({}, { __mode = 'k' }) ---Load rsvg handle form image file ----@tparam string file Path to svg file. ----@return Rsvg handle +-- @tparam string file Path to svg file. +-- @return Rsvg handle +-- @treturn table A table where cached data can be stored. local function load_rsvg_handle(file) if not Rsvg then return end - local cache = rsvg_handle_cache[file] + local cache = (rsvg_handle_cache[file] or {})["handle"] + if cache then - return cache + return cache, rsvg_handle_cache[file] + end + + local handle, err + + if file:match("<[?]?xml") or file:match(" 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 -- 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 -- For valign = "top" and halign = "left" @@ -78,9 +178,11 @@ function imagebox:draw(_, cr, width, height) y = 0, } + update_dpi(self, ctx) + 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. local aspects = { w = width / w, @@ -93,10 +195,14 @@ function imagebox:draw(_, cr, width, height) } for _, aspect in ipairs {"w", "h"} do - if policy[aspect] == "auto" then - aspects[aspect] = math.min(width / w, height / h) + if self._private.upscale == false and (w < width and h < height) then + aspects[aspect] = 1 + elseif self._private.downscale == false and (w >= width and h >= height) then + aspects[aspect] = 1 elseif policy[aspect] == "none" then aspects[aspect] = 1 + elseif policy[aspect] == "auto" then + aspects[aspect] = math.min(width / w, height / h) end end @@ -165,11 +271,22 @@ function imagebox:draw(_, cr, width, height) end -- 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 + + update_dpi(self, ctx) + 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) return w * aspect, h * aspect end @@ -177,44 +294,6 @@ function imagebox:fit(_, width, height) return w, h 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`. -- -- It can can be any of the following: @@ -242,7 +321,10 @@ end function imagebox:set_image(image) 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 -- historically it did, and it did by just assuming they refer to a -- cairo surface. @@ -259,7 +341,8 @@ function imagebox:set_image(image) end elseif Rsvg and Rsvg.Handle:is_type_of(image) then -- 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 -- try to apply given cairo surface setup_succeed = set_surface(self, image) @@ -311,23 +394,126 @@ function imagebox:set_clip_shape(clip_shape, ...) end --- 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@ -- @property resize -- @propemits true false -- @tparam boolean resize ---- Should the image be resized to fit into the available space? --- @tparam boolean allowed If `false`, the image will be clipped, else it will --- be resized to fit into the available space. --- @method set_resize --- @hidden +--- Allow the image to be upscaled (made bigger). +-- +-- 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_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 `` 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) - 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::layout_changed") self:emit_signal("property::resize", allowed) 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. -- -- 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}) gtable.crush(ret, imagebox, true) + ret._private.resize = true if image then ret:set_image(image) end + if resize_allowed ~= nil then - ret:set_resize(resize_allowed) + ret.resize = resize_allowed end ret._private.clip_shape = clip_shape diff --git a/root.c b/root.c index ae683756..4b5ebfbe 100644 --- a/root.c +++ b/root.c @@ -481,6 +481,11 @@ luaA_root_wallpaper(lua_State *L) { 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); lua_pushboolean(L, root_set_wallpaper(pattern)); /* 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 }, { "fake_input", luaA_root_fake_input }, { "drawins", luaA_root_drawins }, - { "wallpaper", luaA_root_wallpaper }, + { "_wallpaper", luaA_root_wallpaper }, { "size", luaA_root_size }, { "size_mm", luaA_root_size_mm }, { "tags", luaA_root_tags }, diff --git a/spec/gears/filesystem_spec.lua b/spec/gears/filesystem_spec.lua index d277aeac..3ad4f25c 100644 --- a/spec/gears/filesystem_spec.lua +++ b/spec/gears/filesystem_spec.lua @@ -115,22 +115,40 @@ describe("gears.filesystem", function() assert.is_true(test_b == "a.png" 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 - local test_c = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {"ext"}) - assert.is_true(test_c == "filename.ext" - or test_c == ".filename.ext" - or test_c == "file.name.ext" - or test_c == ".file.name.ext") + local test_e = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {"ext"}) + assert.is_true(test_e == "filename.ext" + or test_e == ".filename.ext" + or test_e == "file.name.ext" + or test_e == ".file.name.ext") -- "." in filename test cases with no extensions - local test_d = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {""}) - assert.is_true(test_d == "filename" - or test_d == "filename." - or test_d == "filename.ext." - or test_d == ".filename" - or test_d == ".filename." - or test_d == "file.name.ext." - or test_d == ".file.name.ext.") + local test_f = gfs.get_random_file_from_dir(root .. "filesystem_tests/y", {""}) + assert.is_true(test_f == "filename" + or test_f == "filename." + or test_f == "filename.ext." + or test_f == ".filename" + or test_f == ".filename." + or test_f == "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) diff --git a/tests/examples/CMakeLists.txt b/tests/examples/CMakeLists.txt index 328ad98d..71befc53 100644 --- a/tests/examples/CMakeLists.txt +++ b/tests/examples/CMakeLists.txt @@ -85,7 +85,9 @@ function(escape_string variable content escaped_content line_prefix) if(variable MATCHES "--DOC_HIDE_ALL") return() endif() - string(REGEX REPLACE "\n" ";" var_lines "${variable}") + + string(REGEX REPLACE ";" ";" var_lines "${variable}") + string(REGEX REPLACE "\n" ";" var_lines "${var_lines}") set(tmp_output ${content}) 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 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() # 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}" "${TEST_DOC_CONTENT}" TEST_DOC_CONTENT " " ) + + # Add a
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() escape_string( "\n${expected_output}" @@ -347,7 +355,7 @@ foreach(file ${test_files}) # variable during the pre-processing. # While at it, replace \" created by CMake by ', # " wont work in . - string(REPLACE "\"" "'" ${TEST_NAME} ${ESCAPED_CODE_EXAMPLE}) + string(REPLACE "\"" """ ${TEST_NAME} ${ESCAPED_CODE_EXAMPLE}) endif() endforeach() diff --git a/tests/examples/awful/popup/wiboxtypes.lua b/tests/examples/awful/popup/wiboxtypes.lua index c6fa28d9..ccbef7ba 100644 --- a/tests/examples/awful/popup/wiboxtypes.lua +++ b/tests/examples/awful/popup/wiboxtypes.lua @@ -8,6 +8,7 @@ local gears = require("gears") local naughty = require("naughty") local wibox = require("wibox") local beautiful = require("beautiful") --DOC_HIDE +local assets = require("beautiful.theme_assets") local look = require("_default_look") 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} +-- 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 { x = 50, y = 350, @@ -97,6 +170,7 @@ wibox { width = 50, height = 50, shape = gears.shape.octogon, + visible = true, color = "#0000ff", x = 570, y = 410, @@ -188,13 +262,17 @@ create_info("awful.tooltip", 30, 130, 100, 30) create_info("awful.popup", 450, 240, 100, 30) create_info("naughty.layout.box", 255, 110, 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(75, 100, 75, 135) create_line(545, 432, 575, 432) 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(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 diff --git a/tests/examples/awful/template.lua b/tests/examples/awful/template.lua index 758d2280..1a20a8b3 100644 --- a/tests/examples/awful/template.lua +++ b/tests/examples/awful/template.lua @@ -58,11 +58,23 @@ if not rawget(screen, "no_outline") then 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_source(color(beautiful.border_color)) - local rect = {x1 = 0 ,y1 = 0 , x2 = 0 , y2 = 0} -- Get the region with wiboxes diff --git a/tests/examples/awful/wallpaper/add_screen1.lua b/tests/examples/awful/wallpaper/add_screen1.lua new file mode 100644 index 00000000..570884d9 --- /dev/null +++ b/tests/examples/awful/wallpaper/add_screen1.lua @@ -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() diff --git a/tests/examples/awful/wallpaper/add_screen2.lua b/tests/examples/awful/wallpaper/add_screen2.lua new file mode 100644 index 00000000..b7fead0f --- /dev/null +++ b/tests/examples/awful/wallpaper/add_screen2.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/add_screen3.lua b/tests/examples/awful/wallpaper/add_screen3.lua new file mode 100644 index 00000000..b46b12ed --- /dev/null +++ b/tests/examples/awful/wallpaper/add_screen3.lua @@ -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() diff --git a/tests/examples/awful/wallpaper/corner1.lua b/tests/examples/awful/wallpaper/corner1.lua new file mode 100644 index 00000000..1faf70e5 --- /dev/null +++ b/tests/examples/awful/wallpaper/corner1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/dpi1.lua b/tests/examples/awful/wallpaper/dpi1.lua new file mode 100644 index 00000000..c3e129f3 --- /dev/null +++ b/tests/examples/awful/wallpaper/dpi1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/gradient1.lua b/tests/examples/awful/wallpaper/gradient1.lua new file mode 100644 index 00000000..923739c4 --- /dev/null +++ b/tests/examples/awful/wallpaper/gradient1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/gradient2.lua b/tests/examples/awful/wallpaper/gradient2.lua new file mode 100644 index 00000000..9c1f9edb --- /dev/null +++ b/tests/examples/awful/wallpaper/gradient2.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/mazimized1.lua b/tests/examples/awful/wallpaper/mazimized1.lua new file mode 100644 index 00000000..36cbb4a0 --- /dev/null +++ b/tests/examples/awful/wallpaper/mazimized1.lua @@ -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 + diff --git a/tests/examples/awful/wallpaper/mazimized2.lua b/tests/examples/awful/wallpaper/mazimized2.lua new file mode 100644 index 00000000..5dd7e36e --- /dev/null +++ b/tests/examples/awful/wallpaper/mazimized2.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/mazimized3.lua b/tests/examples/awful/wallpaper/mazimized3.lua new file mode 100644 index 00000000..52d35440 --- /dev/null +++ b/tests/examples/awful/wallpaper/mazimized3.lua @@ -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 + diff --git a/tests/examples/awful/wallpaper/padding1.lua b/tests/examples/awful/wallpaper/padding1.lua new file mode 100644 index 00000000..1e34e1be --- /dev/null +++ b/tests/examples/awful/wallpaper/padding1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/padding1.output.txt b/tests/examples/awful/wallpaper/padding1.output.txt new file mode 100644 index 00000000..e90d2665 --- /dev/null +++ b/tests/examples/awful/wallpaper/padding1.output.txt @@ -0,0 +1,3 @@ +Uncovered area: 0 0 30 196 +Uncovered area: 0 0 320 14 +Uncovered area: 310 0 10 196 diff --git a/tests/examples/awful/wallpaper/panning_custom.lua b/tests/examples/awful/wallpaper/panning_custom.lua new file mode 100644 index 00000000..4ca2828d --- /dev/null +++ b/tests/examples/awful/wallpaper/panning_custom.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/panning_inner.lua b/tests/examples/awful/wallpaper/panning_inner.lua new file mode 100644 index 00000000..f13b585a --- /dev/null +++ b/tests/examples/awful/wallpaper/panning_inner.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/panning_inner_horizontal.lua b/tests/examples/awful/wallpaper/panning_inner_horizontal.lua new file mode 100644 index 00000000..5afc8692 --- /dev/null +++ b/tests/examples/awful/wallpaper/panning_inner_horizontal.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/panning_inner_vertical.lua b/tests/examples/awful/wallpaper/panning_inner_vertical.lua new file mode 100644 index 00000000..75bb9abc --- /dev/null +++ b/tests/examples/awful/wallpaper/panning_inner_vertical.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/panning_outer.lua b/tests/examples/awful/wallpaper/panning_outer.lua new file mode 100644 index 00000000..b1a39850 --- /dev/null +++ b/tests/examples/awful/wallpaper/panning_outer.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/remove_screen1.lua b/tests/examples/awful/wallpaper/remove_screen1.lua new file mode 100644 index 00000000..18b97b3f --- /dev/null +++ b/tests/examples/awful/wallpaper/remove_screen1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/remove_screen2.lua b/tests/examples/awful/wallpaper/remove_screen2.lua new file mode 100644 index 00000000..7dab36b7 --- /dev/null +++ b/tests/examples/awful/wallpaper/remove_screen2.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/screens1.lua b/tests/examples/awful/wallpaper/screens1.lua new file mode 100644 index 00000000..6d3b535f --- /dev/null +++ b/tests/examples/awful/wallpaper/screens1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/slideshow1.lua b/tests/examples/awful/wallpaper/slideshow1.lua new file mode 100644 index 00000000..2ae793d8 --- /dev/null +++ b/tests/examples/awful/wallpaper/slideshow1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/svg.lua b/tests/examples/awful/wallpaper/svg.lua new file mode 100644 index 00000000..1f7073dd --- /dev/null +++ b/tests/examples/awful/wallpaper/svg.lua @@ -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 = ''.. + ''.. + ''.. + ''.. + ''.. + '' + + --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 diff --git a/tests/examples/awful/wallpaper/tiled1.lua b/tests/examples/awful/wallpaper/tiled1.lua new file mode 100644 index 00000000..68f8e65b --- /dev/null +++ b/tests/examples/awful/wallpaper/tiled1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/tiled2.lua b/tests/examples/awful/wallpaper/tiled2.lua new file mode 100644 index 00000000..76576e78 --- /dev/null +++ b/tests/examples/awful/wallpaper/tiled2.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/widget1.lua b/tests/examples/awful/wallpaper/widget1.lua new file mode 100644 index 00000000..26228e13 --- /dev/null +++ b/tests/examples/awful/wallpaper/widget1.lua @@ -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 diff --git a/tests/examples/awful/wallpaper/widget2.lua b/tests/examples/awful/wallpaper/widget2.lua new file mode 100644 index 00000000..adef018e --- /dev/null +++ b/tests/examples/awful/wallpaper/widget2.lua @@ -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 = "[SYSTEM FAILURE]", + 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 diff --git a/tests/examples/awful/wallpaper/workarea1.lua b/tests/examples/awful/wallpaper/workarea1.lua new file mode 100644 index 00000000..c8f7bd70 --- /dev/null +++ b/tests/examples/awful/wallpaper/workarea1.lua @@ -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() diff --git a/tests/examples/shims/_default_look.lua b/tests/examples/shims/_default_look.lua index c67927f7..3b0cee76 100644 --- a/tests/examples/shims/_default_look.lua +++ b/tests/examples/shims/_default_look.lua @@ -3,87 +3,98 @@ local wibox = require("wibox") local beautiful = require("beautiful") require("_date") --- Create the same number of tags as the default config -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 ret = nil -local wb = awful.wibar { position = "top" } -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, - }, -} +for i = 1, screen.count() do + local s = screen[i] + -- 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 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 + local wb = awful.wibar { + position = "top", + screen = s } -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() -return { - mykeyboardlayout = mykeyboardlayout, - mytextclock = mytextclock , - mylayoutbox = mylayoutbox , - mytaglist = mytaglist , - mytasklist = mytasklist , - mywibox = wb , - mypromptbox = mypromptbox , -} +return ret diff --git a/tests/examples/shims/awesome.lua b/tests/examples/shims/awesome.lua index b33d7230..40186685 100644 --- a/tests/examples/shims/awesome.lua +++ b/tests/examples/shims/awesome.lua @@ -118,6 +118,10 @@ awesome._modifiers = { }, } +function awesome._get_key_name(key) + return key +end + awesome._active_modifiers = {} return awesome diff --git a/tests/examples/shims/root.lua b/tests/examples/shims/root.lua index b22c878b..02e7c946 100644 --- a/tests/examples/shims/root.lua +++ b/tests/examples/shims/root.lua @@ -1,6 +1,8 @@ local root = {_tags={}} local gtable = require("gears.table") +local cairo = require( "lgi" ).cairo + function root:tags() return root._tags @@ -169,6 +171,24 @@ function root._write_string(string, c) 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) rawset(root, "_ni_handler", h) diff --git a/tests/examples/text/awful/wallpaper/multi_screen.lua b/tests/examples/text/awful/wallpaper/multi_screen.lua new file mode 100644 index 00000000..4d5c2b1e --- /dev/null +++ b/tests/examples/text/awful/wallpaper/multi_screen.lua @@ -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) diff --git a/tests/examples/wibox/widget/imagebox/downscale.lua b/tests/examples/wibox/widget/imagebox/downscale.lua new file mode 100644 index 00000000..7b0f4f2c --- /dev/null +++ b/tests/examples/wibox/widget/imagebox/downscale.lua @@ -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 diff --git a/tests/examples/wibox/widget/imagebox/dpi.lua b/tests/examples/wibox/widget/imagebox/dpi.lua new file mode 100644 index 00000000..1a11f1f6 --- /dev/null +++ b/tests/examples/wibox/widget/imagebox/dpi.lua @@ -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 = ''.. + ''.. + ''.. + 'Hello world!'.. + '' + +--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 = "dpi = "..dpi.."", --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 diff --git a/tests/examples/wibox/widget/imagebox/stylesheet.lua b/tests/examples/wibox/widget/imagebox/stylesheet.lua new file mode 100644 index 00000000..59a3bd3a --- /dev/null +++ b/tests/examples/wibox/widget/imagebox/stylesheet.lua @@ -0,0 +1,31 @@ +--DOC_GEN_IMAGE --DOC_HIDE +local parent = ... --DOC_HIDE +local wibox = require("wibox") --DOC_HIDE + + local image = ''.. + ''.. + ''.. + ''.. + ''.. + '' + + --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 diff --git a/tests/examples/wibox/widget/imagebox/upscale.lua b/tests/examples/wibox/widget/imagebox/upscale.lua new file mode 100644 index 00000000..4c0a510a --- /dev/null +++ b/tests/examples/wibox/widget/imagebox/upscale.lua @@ -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 diff --git a/tests/test-awful-placement.lua b/tests/test-awful-placement.lua index 007766de..19bec43a 100644 --- a/tests/test-awful-placement.lua +++ b/tests/test-awful-placement.lua @@ -68,7 +68,10 @@ local function add_client(args) local c = data.c if not c then 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 end local test = args.test or default_test diff --git a/tests/test-wallpaper.lua b/tests/test-wallpaper.lua index c337d9db..f86f7cb3 100644 --- a/tests/test-wallpaper.lua +++ b/tests/test-wallpaper.lua @@ -5,6 +5,13 @@ local wp = require("gears.wallpaper") local color = require("gears.color") local cairo = require( "lgi" ).cairo 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 = {} @@ -54,85 +61,236 @@ table.insert(steps, function() end) 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 --- adding more -return true + -- There is a delayed call for the last call, let it be processed before + -- adding more + return true end) table.insert(steps, function() -wp.centered(img, nil, nil) -wp.centered(img, screen[1], nil) + wp.centered(img, nil, nil) + wp.centered(img, screen[1], nil) --- There is a delayed call for the last call, let it be processed before --- adding more -return true + -- There is a delayed call for the last call, let it be processed before + -- adding more + return true end) table.insert(steps, function() -wp.centered(img, screen[1], "#00ff00") + wp.centered(img, screen[1], "#00ff00") -return true + return true end) 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 --- adding more -return true + -- There is a delayed call for the last call, let it be processed before + -- adding more + return true end) 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 --- adding more -return true + -- There is a delayed call for the last call, let it be processed before + -- adding more + return true end) 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) -wp.maximized(img, screen[1], true, nil) - --- There is a delayed call for the last call, let it be processed before --- adding more -return true + -- There is a delayed call for the last call, let it be processed before + -- adding more + return true end) 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}) -wp.maximized(img, screen[1], true, {x=10, y= 10}) - -return true + return true end) table.insert(steps, function() -wp.tiled(img, nil, nil) -wp.tiled(img, screen[1], nil) + wp.tiled(img, nil, nil) + wp.tiled(img, screen[1], nil) --- There is a delayed call for the last call, let it be processed before --- adding more -return true + -- There is a delayed call for the last call, let it be processed before + -- adding more + return true end) 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) table.insert(steps, function() -for _, c in ipairs(colors) do - wp.set(c) -end + for _, c in ipairs(colors) do + wp.set(c) + end -return true + return true 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) diff --git a/themes/xresources/theme.lua b/themes/xresources/theme.lua index 11edfe7d..b3be3c46 100644 --- a/themes/xresources/theme.lua +++ b/themes/xresources/theme.lua @@ -8,6 +8,7 @@ local xresources = require("beautiful.xresources") local rnotification = require("ruled.notification") local dpi = xresources.apply_dpi local xrdb = xresources.get_current_theme() +local gdebug = require("gears.debug") local gfs = require("gears.filesystem") local themes_path = gfs.get_themes_dir() @@ -126,10 +127,40 @@ local wallpaper_alt_fg = xrdb.color12 if not is_dark_bg then wallpaper_bg, wallpaper_fg = wallpaper_fg, wallpaper_bg 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 +if not theme.wallpaper then + theme.wallpaper = themes_path.."xresources/wallpaper.svg" +end + +theme.wallpaper_bg = wallpaper_bg + -- Set different colors for urgent notifications. rnotification.connect_signal('request::rules', function() rnotification.append_rule { diff --git a/themes/xresources/wallpaper.svg b/themes/xresources/wallpaper.svg new file mode 100644 index 00000000..5bcbc0b1 --- /dev/null +++ b/themes/xresources/wallpaper.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + +