From 6615772d10b27f828dc2ba1713de131c7eb217fc Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 20:24:47 -0700 Subject: [PATCH 01/10] tests: Extend the test client to handle minimization. The goal is to make the coverage less flacky by explicitly handling some events. --- tests/_client.lua | 26 ++++++++++++++++++++++---- tests/test-maximize.lua | 32 +++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/tests/_client.lua b/tests/_client.lua index d86d9d25..5b58f792 100644 --- a/tests/_client.lua +++ b/tests/_client.lua @@ -10,10 +10,11 @@ end local test_client_source = [[ pcall(require, 'luarocks.loader') -local lgi = require 'lgi' -local Gdk = lgi.require('Gdk') -local Gtk = lgi.require('Gtk') -local Gio = lgi.require('Gio') +local lgi = require 'lgi' +local GLib = lgi.require('GLib') +local Gdk = lgi.require('Gdk') +local Gtk = lgi.require('Gtk') +local Gio = lgi.require('Gio') Gtk.init() local function open_window(class, title, options) @@ -40,6 +41,8 @@ local function open_window(class, title, options) end if options.maximize_before then window:maximize() + elseif options.unminimize_after then + window:iconify() end window:set_wmclass(class, class) window:show_all() @@ -47,6 +50,14 @@ local function open_window(class, title, options) window:maximize() elseif options.unmaximize_after then window:unmaximize() + elseif options.minimize_after then + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, function() + window:iconify() + end) + elseif options.unminimize_after then + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, function() + window:deiconify() + end) end if options.resize_after_width and options.resize_after_height then window:resize( @@ -152,6 +163,13 @@ return function(class, title, sn_rules, callback, resize_increment, args) if args.unmaximize_after then options = options .. "unmaximize_after," end + if args.unminimize_after then + options = options .. "unminimize_after," + end + if args.minimize_after then + options = options .. "minimize_after," + end + if args.size then options = table.concat { options, diff --git a/tests/test-maximize.lua b/tests/test-maximize.lua index 8f28b25d..2b478e81 100644 --- a/tests/test-maximize.lua +++ b/tests/test-maximize.lua @@ -371,8 +371,38 @@ gears.table.merge(steps, { if not c or counter ~= 2 then return end + c:kill() + + client.disconnect_signal("request::geometry", geometry_handler) + + test_client(nil,nil,nil,nil,nil,{minimize_after=true}) + return true - end + end, + function() + -- Test minimization. + if #client.get() ~= 1 or not client.get()[1].minimized then return end + + assert(client.get()[1].minimized) + client.get()[1]:kill() + + return true + end, + function() + if #client.get() > 0 then return end + + test_client(nil,nil,nil,nil,nil,{unminimize_after=true}) + + return true + end, + function() + if #client.get() ~= 1 or client.get()[1].minimized then return end + + assert(not client.get()[1].minimized) + client.get()[1]:kill() + + return true + end, }) runner.run_steps(steps) From 15f63c700c34386887be6fb161ed088e573613fc Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 20:25:35 -0700 Subject: [PATCH 02/10] tests: Add size changes to the `xeyes` test. It should trigger some shape related code in the core which wasn't hit reliably until now. --- tests/test-client-shape.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test-client-shape.lua b/tests/test-client-shape.lua index 5bb88a1d..84235e35 100644 --- a/tests/test-client-shape.lua +++ b/tests/test-client-shape.lua @@ -11,6 +11,18 @@ runner.run_steps{ assert(type(a) == "number", a) end if #client.get() == 1 then + local c = client.get()[1] + + local geo = c:geometry() + + -- Resize it to test shape change events. + c:geometry { + x = geo.x, + y = geo.y, + width = geo.width * 2, + height = geo.height * 2, + } + return true end end, @@ -27,8 +39,15 @@ runner.run_steps{ assert(not surface.load_silently(c.client_shape_clip, false)) assert(not surface.load_silently(c.shape_clip, false)) + return true + end, + + function() + client.get()[1]:kill() return true end + + } -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 6c1f2aac5b4d7cf0dd4d3441104022a73b915f39 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 20:26:33 -0700 Subject: [PATCH 03/10] tests: Emulate some clicks on the titlebars. Maybe it will make the test coverage more deterministic... or not. --- tests/test-titlebar.lua | 58 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/test-titlebar.lua b/tests/test-titlebar.lua index 4b0b8d4b..b299b778 100644 --- a/tests/test-titlebar.lua +++ b/tests/test-titlebar.lua @@ -29,7 +29,7 @@ window.decorated = false ]]) } -local found_font = nil +local found_font, events, next_pos = nil, {}, {} -- Use the test client props local dep = gdebug.deprecate @@ -44,6 +44,20 @@ local function kill_client(c) awesome.kill(c.pid, 9) end +local function click() + local x, y= next_pos.x, next_pos.y + mouse.coords{x=x, y=y} + assert(mouse.coords().x == x and mouse.coords().y == y) + root.fake_input("button_press", 1) + awesome.sync() + root.fake_input("button_release", 1) + awesome.sync() + next_pos = nil + + return true +end + + -- Too bad there's no way to disconnect the rc.lua request::titlebars function local steps = { @@ -183,6 +197,48 @@ local steps = { assert(found_font == "sans 10") + -- Test the events. + for _, event in ipairs { "button::press", "button::release", "mouse::move" } do + c:connect_signal(event, function() + table.insert(events, event) + end) + end + + next_pos = { x = c:geometry().x + 5, y = c:geometry().y + 5 } + + return true + end, + click, + function() + if #events == 0 then return end + + events = {} + + local c = client.get()[1] + + next_pos = { x = c:geometry().x + 5, y = c:geometry().y + c:geometry().height - 5 } + + return true + end, + click, + function() + if #events == 0 then return end + + local c = client.get()[1] + + next_pos = { + x = c:geometry().x + c:geometry().width/2, + y = c:geometry().y + c:geometry().height/2 + } + + return true + end, + click, + function() + if #events == 0 then return end + + local c = client.get()[1] + kill_client(c) return true From 4955ec584fa00aae5557d036bbc1752bf82a2d48 Mon Sep 17 00:00:00 2001 From: Uli Schlachter Date: Sun, 31 Oct 2021 13:36:17 -0700 Subject: [PATCH 04/10] tests: Add a systray program. --- CMakeLists.txt | 5 ++ tests/test-systray.c | 118 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 tests/test-systray.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f8b0668..a972655f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,6 +402,11 @@ add_custom_target(check DEPENDS check-integration) add_executable(test-gravity tests/test-gravity.c) target_link_libraries(test-gravity ${AWESOME_COMMON_REQUIRED_LDFLAGS} ${AWESOME_REQUIRED_LDFLAGS}) + +add_executable(test-systray tests/test-systray.c) +target_link_libraries(test-systray + ${AWESOME_COMMON_REQUIRED_LDFLAGS} ${AWESOME_REQUIRED_LDFLAGS}) + if(DO_COVERAGE) set(TESTS_RUN_ENV DO_COVERAGE=1) endif() diff --git a/tests/test-systray.c b/tests/test-systray.c new file mode 100644 index 00000000..971b50d2 --- /dev/null +++ b/tests/test-systray.c @@ -0,0 +1,118 @@ +/* + * A simple client icon that "does nothing". + * + * Copyright © 2021 Uli Schlachter + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "common/atoms.h" +#include "common/util.h" + +// Include all the C code we might need directly into this file - I am lazy +#include "common/atoms.c" +#include "common/util.c" + +#define SYSTEM_TRAY_REQUEST_DOCK 0 + +static xcb_atom_t systray_atom(xcb_connection_t *conn, int screen) { + char *atom_name; + xcb_intern_atom_reply_t *reply; + xcb_atom_t atom; + + atom_name = xcb_atom_name_by_screen("_NET_SYSTEM_TRAY", screen); + if(!atom_name) + fatal("Error getting systray atom name\n"); + + reply = xcb_intern_atom_reply(conn, xcb_intern_atom(conn, 0, strlen(atom_name), atom_name), NULL); + free(atom_name); + if (!reply) + fatal("Error interning systray icon\n"); + + atom = reply->atom; + free(reply); + return atom; +} + +static xcb_window_t find_systray(xcb_connection_t *conn, xcb_atom_t net_system_tray) { + xcb_get_selection_owner_reply_t* reply = xcb_get_selection_owner_reply(conn, xcb_get_selection_owner(conn, net_system_tray), NULL); + if (!reply) + fatal("Failed to request selection owner\n"); + xcb_window_t owner = reply->owner; + if (owner == XCB_NONE) + fatal("No systray running\n"); + free(reply); + return owner; +} + +static uint32_t get_color(xcb_connection_t *conn, xcb_screen_t *screen, uint16_t red, uint16_t green, uint16_t blue) { + xcb_alloc_color_reply_t *reply = xcb_alloc_color_reply(conn, xcb_alloc_color(conn, screen->default_colormap, red, green, blue), NULL); + if (!reply) + fatal("Error allocating color"); + uint32_t pixel = reply->pixel; + free(reply); + return pixel; +} + +int main() { + int default_screen; + xcb_connection_t* conn = xcb_connect(NULL, &default_screen); + if (xcb_connection_has_error(conn)) { + fatal("Could not connect to X11 server: %d\n", xcb_connection_has_error(conn)); + } + atoms_init(conn); + xcb_screen_t* screen = xcb_aux_get_screen(conn, default_screen); + + // Create a window for the systray icon + xcb_window_t window = xcb_generate_id(conn); + xcb_create_window(conn, screen->root_depth, window, screen->root, 0, 0, 10, 10, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, XCB_CW_BACK_PIXEL, (uint32_t[]) { get_color(conn, screen, 0xffff, 0x9999, 0x0000) }); + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, _XEMBED_INFO, _XEMBED_INFO, 32, 2, (uint32_t[]) { 0, 1 }); + + // Make our window a systray icon + xcb_window_t systray_owner = find_systray(conn, systray_atom(conn, default_screen)); + xcb_client_message_event_t ev; + + p_clear(&ev, 1); + ev.response_type = XCB_CLIENT_MESSAGE; + ev.window = systray_owner; + ev.format = 32; + ev.data.data32[0] = XCB_CURRENT_TIME; + ev.data.data32[1] = SYSTEM_TRAY_REQUEST_DOCK; + ev.data.data32[2] = window; + ev.data.data32[3] = 0; + ev.data.data32[4] = 0; + ev.type = _NET_SYSTEM_TRAY_OPCODE; + xcb_send_event(conn, false, systray_owner, XCB_EVENT_MASK_NO_EVENT, (char *) &ev); + + xcb_flush(conn); + xcb_generic_event_t *event; + while ((event = xcb_wait_for_event(conn)) != NULL) { + free(event); + } + + xcb_disconnect(conn); + return 0; +} + +// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From b4beb2308beda029ddbcb1ea0d459ee442f73d46 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 20:49:26 -0700 Subject: [PATCH 05/10] tests: Add a simple systray test. Compile a random little C program found on stack overflow to make a systray. The goal is to make the `event.c` tests less flacky. --- tests/test-systray.lua | 90 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 tests/test-systray.lua diff --git a/tests/test-systray.lua b/tests/test-systray.lua new file mode 100644 index 00000000..de4b5f93 --- /dev/null +++ b/tests/test-systray.lua @@ -0,0 +1,90 @@ +local spawn = require("awful.spawn") +local wibox = require("wibox") +local beautiful = require("beautiful") + +local steps, pid1, pid2, draw_w, st = {} + +table.insert(steps, function() + screen[1].mywibox:remove() + + local widget = wibox.widget.base.make_widget() + + function widget:fit(_, w, h) + return w, h + end + + function widget:draw(_, _, width) + draw_w = width + end + + local wb = wibox { + x = 0, + y = 0, + width = 100, + height = 20, + visible = true + } + + st = wibox.widget.systray() + + wb.widget = { + st, + widget, + layout = wibox.layout.fixed.horizontal + } + + pid1 = spawn("./test-systray") + + return true +end) + +table.insert(steps, function() + if draw_w ~= 80 then return end + + pid2 = spawn("./test-systray") + + return true +end) + +table.insert(steps, function() + if draw_w ~= 60 then return end + + st.reverse = true + st.horizontal = false + + return true +end) + +table.insert(steps, function() + st.horizontal = true + + beautiful.systray_icon_spacing = 10 + + return true +end) + + +table.insert(steps, function() + if draw_w ~= 50 then return end + + st.base_size = 5 + return true +end) + + +table.insert(steps, function() + if draw_w ~= 80 then return end + + awesome.kill(pid1, 9) + awesome.kill(pid2, 9) + + return true +end) + + +table.insert(steps, function() + if draw_w ~= 100 then return end + return true +end) + +require("_runner").run_steps(steps) From 90a2b003a2c28b6f5605bd5c898c0f8e4451e3cc Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 21:10:26 -0700 Subject: [PATCH 06/10] tests: Remove a noisy print --- tests/test-wallpaper.lua | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test-wallpaper.lua b/tests/test-wallpaper.lua index f86f7cb3..3c83da2b 100644 --- a/tests/test-wallpaper.lua +++ b/tests/test-wallpaper.lua @@ -270,7 +270,6 @@ 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) From a08191913e21727f97be92817fb64da8aa37e6b5 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 21:12:07 -0700 Subject: [PATCH 07/10] tests: Blind attempt at making a flacky test under luajit stable. I could not reproduce the issue locally. --- tests/_client.lua | 35 +++++++++++++++++++++++++++++++---- tests/test-input-binding.lua | 18 ++++++++++++++++-- 2 files changed, 47 insertions(+), 6 deletions(-) diff --git a/tests/_client.lua b/tests/_client.lua index 5b58f792..ac3f1b6c 100644 --- a/tests/_client.lua +++ b/tests/_client.lua @@ -1,4 +1,5 @@ local spawn = require("awful.spawn") +local gtimer = require("gears.timer") local lua_executable = os.getenv("LUA") if lua_executable == nil or lua_executable == "" then @@ -118,13 +119,15 @@ local lgi = require("lgi") local Gio = lgi.require("Gio") local initialized = false -local pipe +local pipe, pid + local function init() if initialized then return end initialized = true local cmd = { lua_executable, "-e", test_client_source } - local _, _, stdin, stdout, stderr = awesome.spawn(cmd, false, true, true, true) - pipe = Gio.UnixOutputStream.new(stdin, true) + local _pid, _, stdin, stdout, stderr = awesome.spawn(cmd, false, true, true, true) + pipe = Gio.UnixOutputStream.new(stdin, true) + pid = _pid stdout = Gio.UnixInputStream.new(stdout, true) stderr = Gio.UnixInputStream.new(stderr, true) spawn.read_lines(stdout, function(...) print("_client", ...) end) @@ -139,7 +142,29 @@ local function get_snid(sn_rules, callback) return snid end -return function(class, title, sn_rules, callback, resize_increment, args) +local module = {} + +function module.terminate() + if not initialized then return end + + for _, c in ipairs(client.get()) do + c:kill() + end + + pipe:close() + initialized, pipe = false, nil + + -- Make a copy to avoid the re-initialized race condition. + local original_pid = pid + + gtimer.delayed_call(function() + awesome.kill(original_pid, 9) + end) + + return true +end + +local function new(_, class, title, sn_rules, callback, resize_increment, args) args = args or {} class = class or "test_app" title = title or "Awesome test client" @@ -200,4 +225,6 @@ return function(class, title, sn_rules, callback, resize_increment, args) return snid end +return setmetatable(module, {__call = new }) + -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 diff --git a/tests/test-input-binding.lua b/tests/test-input-binding.lua index 99fdce49..a52a19da 100644 --- a/tests/test-input-binding.lua +++ b/tests/test-input-binding.lua @@ -243,8 +243,20 @@ for _, type_name in ipairs { "key", "button" } do end) -- Cleanup (otherwise there is a race with the root.buttons tests) - table.insert(steps, function() - if #mouse.screen.clients ~= 0 then return end + table.insert(steps, function(count) + if #mouse.screen.clients ~= 0 then + if count > 5 then + -- It's stuck, kill it. + test_client.terminate() + else + for _, c in pairs(client.get()) do + c:kill() + end + end + + + return + end return true end) @@ -372,6 +384,8 @@ table.insert(steps, function() return true end) +table.insert(steps, test_client.terminate) + runner.run_steps(steps) -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80 From 295538e389b1afa72586b824875ac10298a6dd03 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 22:12:52 -0700 Subject: [PATCH 08/10] tests: Force GTK3 for the client test. GTK4 removes iconify and deiconify and replaces them with minimize and unminimize. --- tests/_client.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/_client.lua b/tests/_client.lua index ac3f1b6c..3eb015f3 100644 --- a/tests/_client.lua +++ b/tests/_client.lua @@ -14,7 +14,7 @@ pcall(require, 'luarocks.loader') local lgi = require 'lgi' local GLib = lgi.require('GLib') local Gdk = lgi.require('Gdk') -local Gtk = lgi.require('Gtk') +local Gtk = lgi.require('Gtk', '3.0') local Gio = lgi.require('Gio') Gtk.init() From 6e8b0d5a8528e0bdf3396a7eb6d7f25302600380 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Thu, 28 Oct 2021 22:37:23 -0700 Subject: [PATCH 09/10] ci: Be faster. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 443a8b33..4b35482a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -253,7 +253,7 @@ jobs: grep -q 'May not access' lib/beautiful/init.lua - name: Build - run: cd "${{ github.workspace }}/build" && make + run: cd "${{ github.workspace }}/build" && make -j3 # Executable needs to be run once to provide coverage results - name: Install and run From 9dcfde32e0e73ce15cadb92fdc987ffc9eea36a5 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 31 Oct 2021 19:30:17 -0700 Subject: [PATCH 10/10] naughty: Never hold a strong reference to the notification in the box. luajit was failing to GC the notification about 5% of the time. This commit stores all widget notifications in a weak table and don't let any lambda access the parent object notification object. Each of those changes reduces the failure rate. There might still be a couple in there, but the test passed 200x on my laptop with 100% success rate. --- lib/naughty/container/background.lua | 19 ++++++----- lib/naughty/layout/box.lua | 51 ++++++++++++++++------------ lib/naughty/list/actions.lua | 13 ++++--- lib/naughty/widget/_default.lua | 6 +++- lib/naughty/widget/icon.lua | 16 ++++++--- lib/naughty/widget/message.lua | 31 +++++++++++------ lib/naughty/widget/title.lua | 31 +++++++++++------ 7 files changed, 105 insertions(+), 62 deletions(-) diff --git a/lib/naughty/container/background.lua b/lib/naughty/container/background.lua index 2d479634..535fae5a 100644 --- a/lib/naughty/container/background.lua +++ b/lib/naughty/container/background.lua @@ -46,22 +46,24 @@ end -- @propemits true false function background:set_notification(notif) - if self._private.notification == notif then return end + local old = self._private.notification[1] - if self._private.notification then - self._private.notification:disconnect_signal("property::bg", + if old == notif then return end + + if old then + old:disconnect_signal("property::bg", self._private.background_changed_callback) - self._private.notification:disconnect_signal("property::border_width", + old:disconnect_signal("property::border_width", self._private.background_changed_callback) - self._private.notification:disconnect_signal("property::border_color", + old:disconnect_signal("property::border_color", self._private.background_changed_callback) - self._private.notification:disconnect_signal("property::shape", + old:disconnect_signal("property::shape", self._private.background_changed_callback) end update_background(notif, self) - self._private.notification = notif + self._private.notification = setmetatable({notif}, {__mode="v"}) notif:connect_signal("property::bg" , self._private.background_changed_callback) notif:connect_signal("property::border_width", self._private.background_changed_callback) @@ -82,12 +84,13 @@ local function new(args) args = args or {} local bg = wbg() + bg._private.notification = {} bg:set_border_strategy("inner") gtable.crush(bg, background, true) function bg._private.background_changed_callback() - update_background(bg._private.notification, bg) + update_background(bg._private.notification[1], bg) end if args.notification then diff --git a/lib/naughty/layout/box.lua b/lib/naughty/layout/box.lua index 868cf83d..0c648247 100644 --- a/lib/naughty/layout/box.lua +++ b/lib/naughty/layout/box.lua @@ -47,14 +47,16 @@ local function init_screen(s) end local function disconnect(self) - if self._private.notification then - self._private.notification:disconnect_signal("destroyed", + local n = self._private.notification[1] + + if n then + n:disconnect_signal("destroyed", self._private.destroy_callback) - self._private.notification:disconnect_signal("property::margin", + n:disconnect_signal("property::margin", self._private.update) - self._private.notification:disconnect_signal("property::suspended", + n:disconnect_signal("property::suspended", self._private.hide) end end @@ -102,7 +104,7 @@ local function update_position(position, preset) honor_workarea = true, } if k == 1 then - args.offset = get_offset(position, preset) + args.offset = get_offset(position, preset) end -- The first entry is aligned to the workarea, then the following to the @@ -123,14 +125,13 @@ local function finish(self) end end - local preset - if self.private and self.private.args and self.private.args.notification then - preset = self.private.args.notification.preset - end + local preset = (self._private.notification[1] or {}).preset update_position(self.position, preset) disconnect(self) + + self._private.notification = {} end -- It isn't a good idea to use the `attach` `awful.placement` property. If the @@ -236,21 +237,19 @@ local function generate_widget(args, n) end -- Call `:set_notification` on all children - awcommon._set_common_property(w, "notification", n or args.notification) + awcommon._set_common_property(w, "notification", n) return w end local function init(self, notification) - local args = self._private.args - local preset = notification.preset or {} - local position = args.position or notification.position or + local position = self._private.position or notification.position or preset.position or beautiful.notification_position or "top_right" if not self.widget then - self.widget = generate_widget(self._private.args, notification) + self.widget = generate_widget(self._private, notification) end local bg = self._private.widget:get_children_by_id( "background_role" )[1] @@ -295,20 +294,26 @@ local function init(self, notification) end function box:set_notification(notif) - if self._private.notification == notif then return end + if self._private.notification[1] == notif then return end disconnect(self) init(self, notif) - self._private.notification = notif + self._private.notification = setmetatable({notif}, {__mode="v"}) self:emit_signal("property::notification", notif) end +function box:get_notification() + return self._private.notification[1] +end + function box:get_position() - if self._private.notification then - return self._private.notification:get_position() + local n = self._private.notification[1] + + if n then + return n:get_position() end return "top_right" @@ -357,7 +362,9 @@ local function new(args) if not new_args.widget then return nil end local ret = popup(new_args) - ret._private.args = new_args + ret._private.notification = {} + ret._private.widget_template = args.widget_template + ret._private.position = args.position gtable.crush(ret, box, true) @@ -371,8 +378,10 @@ local function new(args) --TODO remove local function hide() - if ret._private.notification then - ret._private.notification:destroy() + local n = ret._private.notification[1] + + if n then + n:destroy() end end diff --git a/lib/naughty/list/actions.lua b/lib/naughty/list/actions.lua index c7664ce8..46a66337 100644 --- a/lib/naughty/list/actions.lua +++ b/lib/naughty/list/actions.lua @@ -168,14 +168,16 @@ local function wb_label(action, self) end local function update(self) - if not self._private.layout or not self._private.notification then return end + local n = self._private.notification[1] + + if not self._private.layout or not n then return end awcommon.list_update( self._private.layout, self._private.default_buttons, function(o) return wb_label(o, self) end, self._private.data, - self._private.notification.actions, + n.actions, { widget_template = self._private.widget_template } @@ -229,7 +231,7 @@ local actionlist = {} function actionlist:set_notification(notif) - self._private.notification = notif + self._private.notification = setmetatable({notif}, {__mode="v"}) if not self._private.layout then self._private.layout = wibox.layout.fixed.horizontal() @@ -329,8 +331,9 @@ local function new(_, args) gtable.crush(wdg, actionlist, true) wdg._private.data = {} + wdg._private.notification = {} - gtable.crush(wdg, args) + gtable.crush(wdg, args, false) wdg._private.style = wdg._private.style or {} @@ -338,7 +341,7 @@ local function new(_, args) wdg._private.default_buttons = gtable.join( abutton({ }, 1, function(a) - local notif = wdg._private.notification or args.notification + local notif = wdg._private.notification[1] a:invoke(notif) end) ) diff --git a/lib/naughty/widget/_default.lua b/lib/naughty/widget/_default.lua index c964d9da..ebfd8ca0 100644 --- a/lib/naughty/widget/_default.lua +++ b/lib/naughty/widget/_default.lua @@ -22,7 +22,7 @@ local function notif_size() constraint:set_width(beautiful.notification_max_width or dpi(500)) rawset(constraint, "set_notification", function(_, notif) - constraint._private.notification = notif + constraint._private.notification = setmetatable({notif}, {__mode = "v"}) local s = false if notif.width and notif.width ~= beautiful.notification_max_width then @@ -37,6 +37,10 @@ local function notif_size() constraint.strategy = s and "exact" or "max" end) + rawset(constraint, "get_notification", function() + return constraint._private.notification[1] + end) + return constraint end diff --git a/lib/naughty/widget/icon.lua b/lib/naughty/widget/icon.lua index 7eeb7db4..f17c5c9d 100644 --- a/lib/naughty/widget/icon.lua +++ b/lib/naughty/widget/icon.lua @@ -83,10 +83,12 @@ end -- @propemits true false function icon:set_notification(notif) - if self._private.notification == notif then return end + local old = (self._private.notification or {})[1] - if self._private.notification then - self._private.notification:disconnect_signal("destroyed", + if old == notif then return end + + if old then + old:disconnect_signal("destroyed", self._private.icon_changed_callback) end @@ -96,7 +98,7 @@ function icon:set_notification(notif) self:set_image(icn) end - self._private.notification = notif + self._private.notification = setmetatable({notif}, {__mode="v"}) notif:connect_signal("property::icon", self._private.icon_changed_callback) self:emit_signal("property::notification", notif) @@ -154,10 +156,14 @@ local function new(args) local tb = imagebox() gtable.crush(tb, icon, true) + tb._private.notification = {} function tb._private.icon_changed_callback() + local n = tb._private.notification[1] - local icn = gsurface.load_silently(tb._private.notification.icon) + if not n then return end + + local icn = gsurface.load_silently(n.icon) if icn then tb:set_image(icn) diff --git a/lib/naughty/widget/message.lua b/lib/naughty/widget/message.lua index 0dac616e..9224f172 100644 --- a/lib/naughty/widget/message.lua +++ b/lib/naughty/widget/message.lua @@ -27,18 +27,20 @@ local message = {} -- @propemits true false function message:set_notification(notif) - if self._private.notification == notif then return end + local old = self._private.notification[1] - if self._private.notification then - self._private.notification:disconnect_signal("property::message", + if old == notif then return end + + if old then + old:disconnect_signal("property::message", self._private.message_changed_callback) - self._private.notification:disconnect_signal("property::fg", + old:disconnect_signal("property::fg", self._private.message_changed_callback) end markup(self, notif.message, notif.fg, notif.font) - self._private.notification = notif + self._private.notification = setmetatable({notif}, {__mode="v"}) notif:connect_signal("property::message", self._private.message_changed_callback) notif:connect_signal("property::fg" , self._private.message_changed_callback) @@ -57,16 +59,23 @@ local function new(args) local tb = textbox() tb:set_wrap("word") tb:set_font(beautiful.notification_font) + tb._private.notification = {} gtable.crush(tb, message, true) function tb._private.message_changed_callback() - markup( - tb, - tb._private.notification.message, - tb._private.notification.fg, - tb._private.notification.font - ) + local n = tb._private.notification[1] + + if n then + markup( + tb, + n.message, + n.fg, + n.font + ) + else + markup(tb, nil, nil) + end end if args.notification then diff --git a/lib/naughty/widget/title.lua b/lib/naughty/widget/title.lua index 6f35dc2b..dceba4f9 100644 --- a/lib/naughty/widget/title.lua +++ b/lib/naughty/widget/title.lua @@ -27,18 +27,20 @@ local title = {} -- @propemits true false function title:set_notification(notif) - if self._private.notification == notif then return end + local old = self._private.notification[1] - if self._private.notification then - self._private.notification:disconnect_signal("property::message", + if old == notif then return end + + if old then + old:disconnect_signal("property::message", self._private.title_changed_callback) - self._private.notification:disconnect_signal("property::fg", + old:disconnect_signal("property::fg", self._private.title_changed_callback) end markup(self, notif.title, notif.fg, notif.font) - self._private.notification = notif + self._private.notification = setmetatable({notif}, {__mode="v"}) self._private.title_changed_callback() notif:connect_signal("property::title", self._private.title_changed_callback) @@ -58,16 +60,23 @@ local function new(args) local tb = textbox() tb:set_wrap("word") tb:set_font(beautiful.notification_font) + tb._private.notification = {} gtable.crush(tb, title, true) function tb._private.title_changed_callback() - markup( - tb, - tb._private.notification.title, - tb._private.notification.fg, - tb._private.notification.font - ) + local n = tb._private.notification[1] + + if n then + markup( + tb, + n.title, + n.fg, + n.font + ) + else + markup("", nil, nil) + end end if args.notification then