diff options
Diffstat (limited to '.config/awesome/signals/naughty.lua')
-rw-r--r-- | .config/awesome/signals/naughty.lua | 292 |
1 files changed, 246 insertions, 46 deletions
diff --git a/.config/awesome/signals/naughty.lua b/.config/awesome/signals/naughty.lua index 90594c1..cc096e7 100644 --- a/.config/awesome/signals/naughty.lua +++ b/.config/awesome/signals/naughty.lua @@ -1,54 +1,254 @@ -local awful = require "awful" -local beautiful = require "beautiful" +local gdebug = require "gears.debug" local naughty = require "naughty" -local qvars = require "quarrel.vars" -local wibox = require "wibox" - -naughty.connect_signal("request::display", function(n) - naughty.layout.box { - notification = n, - placement = function(d) - return awful.placement.right(d, { - margins = beautiful.useless_gap * 2 - }) - end, - widget_template = { - widget = wibox.container.margin, - margins = qvars.big_padding, - { - { - widget = naughty.widget.icon, - notification = n - }, - { - widget = wibox.container.place, - valign = "center", - halign = "center", - { - { - widget = naughty.widget.title, - notification = n - }, - { - widget = naughty.widget.message, - notification = n - }, - -- spacing = 4, - layout = wibox.layout.fixed.vertical, - } - }, - fill_space = true, - spacing = n.icon and qvars.big_padding or 0, - layout = wibox.layout.fixed.horizontal - } - } +local ndbus = require "naughty.dbus" +local qdelegate = require "quarrel.delegate" +local wicked = require "ui.wicked" + +---@diagnostic disable-next-line:redundant-parameter +ndbus._notif_methods.Notify = qdelegate(function(env, sender, object_path, interface, method, parameters, invocation) + local appname, replaces_id, app_icon, title, text, actions, hints, expire = env.unpack(parameters.value) + + local args = {} + if text ~= "" then + args.message = text + if title ~= "" then + args.title = title + end + else + if title ~= "" then + args.message = title + else + -- FIXME: We have to reply *something* to the DBus invocation. + -- Right now this leads to a memory leak, I think. + return + end + end + + if appname ~= "" then + args.appname = appname --TODO v6 Remove this. + args.app_name = appname + end + + local preset = args.preset or env.cst.config.defaults + local notification + if actions then + args.actions = {} + + for i = 1, #actions, 2 do + local action_id = actions[i] + local action_text = actions[i + 1] + + if action_id == "default" then + args.run = function() + env.sendActionInvoked(notification.id, "default") + notification:destroy(env.cst.notification_closed_reason.dismissed_by_user) + end + elseif action_id ~= nil and action_text ~= nil then + local a = env.naction { + name = action_text, + id = action_id, + position = (i - 1) / 2 + 1, + } + + -- Right now `gears` doesn't have a great icon implementation + -- and `naughty` doesn't depend on `menubar`, so delegate the + -- icon "somewhere" using a request. + if hints["action-icons"] and action_id ~= "" then + naughty.emit_signal("request::action_icon", a, "dbus", { id = action_id }) + end + + a:connect_signal("invoked", function() + env.sendActionInvoked(notification.id, action_id) + + if not notification.resident then + notification:destroy(env.cst.notification_closed_reason.dismissed_by_user) + end + end) + + table.insert(args.actions, a) + end + end + end + args.destroy = function(reason) + env.sendNotificationClosed(notification.id, reason) + end + local legacy_data = { -- This data used to be generated by AwesomeWM's C code + type = "method_call", + interface = interface, + path = object_path, + member = method, + sender = sender, + bus = "session", } -end) + if + not preset.callback + or ( + type(preset.callback) == "function" + and preset.callback(legacy_data, appname, replaces_id, app_icon, title, text, actions, hints, expire) + ) + then + if app_icon ~= "" then + args.app_icon = app_icon + end + + if hints.icon_data or hints.image_data or hints["image-data"] then + -- Icon data is a bit complex and hence needs special care: + -- .value breaks with the array of bytes (ay) that we get here. + -- So, bypass it and look up the needed value differently + local icon_condidates = {} + for k, v in parameters:get_child_value(7 - 1):pairs() do + if k == "image-data" then + icon_condidates[1] = v -- not deprecated + break + elseif k == "image_data" then -- deprecated + icon_condidates[2] = v + elseif k == "icon_data" then -- deprecated + icon_condidates[3] = v + end + end + + -- The order is mandated by the spec. + local icon_data = icon_condidates[1] or icon_condidates[2] or icon_condidates[3] + + -- icon_data is an array: + -- 1 -> width + -- 2 -> height + -- 3 -> rowstride + -- 4 -> has alpha + -- 5 -> bits per sample + -- 6 -> channels + -- 7 -> data + + -- Get the value as a GVariant and then use LGI's special + -- GVariant.data to get that as an LGI byte buffer. That one can + -- then by converted to a string via its __tostring metamethod. + local data = tostring(icon_data:get_child_value(7 - 1).data) + args.image = env.convert_icon(icon_data[1], icon_data[2], icon_data[3], icon_data[6], data) + + -- Convert all animation frames. + if naughty.image_animations_enabled then + args.images = { args.image } + + if #icon_data > 7 then + for frame = 8, #icon_data do + data = tostring(icon_data:get_child_value(frame - 1).data) + + table.insert( + args.images, + env.convert_icon(icon_data[1], icon_data[2], icon_data[3], icon_data[6], data) + ) + end + end + end + end + + -- Alternate ways to set the icon. The specs recommends to allow both + -- the icon and image to co-exist since they serve different purpose. + -- However in case the icon isn't specified, use the image. + args.image = args.image + or hints["image-path"] -- not deprecated + or hints["image_path"] -- deprecated + + if naughty.image_animations_enabled then + args.images = args.images or {} + end + + if replaces_id and replaces_id ~= "" and replaces_id ~= 0 then + args.replaces_id = replaces_id + end + if expire and expire > -1 then + args.timeout = expire / 1000 + end + + args.freedesktop_hints = hints + + -- Not very pretty, but given the current format is documented in the + -- public API... well, whatever... + if hints and hints.urgency then + for name, key in pairs(env.urgency) do + local b = string.char(hints.urgency) + if key == b then + args.urgency = name + end + end + end + + args.urgency = args.urgency or "normal" + + -- Try to update existing objects when possible + notification = naughty.get_by_id(replaces_id) + + if notification then + if not notification._private._unique_sender then + -- If this happens, the notification is either trying to + -- highjack content created within AwesomeWM or it is garbage + -- to begin with. + gdebug.print_warning( + "A notification has been received, but tried to update " + .. "the content of a notification it does not own." + ) + elseif notification._private._unique_sender ~= sender then + -- Nothing says you cannot and some scripts may do it + -- accidentally, but this is rather unexpected. + gdebug.print_warning( + "Notification " + .. notification.title + .. " is being updated" + .. "by a different DBus connection (" + .. sender + .. "), this is " + .. "suspicious. The original connection was " + .. notification._private._unique_sender + ) + end + + for k, v in pairs(args) do + if k == "destroy" then + k = "destroy_cb" + end + notification[k] = v + end + + -- Update the icon if necessary. + if app_icon ~= notification._private.app_icon then + notification._private.app_icon = app_icon + + naughty._emit_signal_if("request::icon", function() + if notification._private.icon then + return true + end + end, notification, "dbus_clear", {}) + end + + -- Even if no property changed, restart the timeout. + notification:reset_timeout() + else + -- Only set the sender for new notifications. + args._unique_sender = sender + args._foreign = true + + notification = env.nnotif(args) + + notification:connect_signal("destroyed", function(_, r) + args.destroy(r) + end) + end + + invocation:return_value(env.GLib.Variant("(u)", { notification.id })) + return + end + + invocation:return_value(env.GLib.Variant("(u)", { env.nnotif._gen_next_id() })) +end, ndbus._notif_methods.Notify) + +naughty.connect_signal("request::display", wicked) naughty.connect_signal("request::display_error", function(message, startup) naughty.notification { urgency = "critical", - title = "Oops, an error happened"..(startup and " during startup!" or "!"), - message = message + title = "Oops, an error happened" .. (startup and " during startup!" or "!"), + message = message, } + + gdebug.print_error("[Error" .. (startup and "during startup" or "") .. "]: " .. message) end) |