2008-10-17 22:38:01 +02:00
|
|
|
----------------------------------------------------------------------------
|
|
|
|
-- @author koniu <gkusnierz@gmail.com>
|
|
|
|
-- @copyright 2008 koniu
|
|
|
|
-- @release @AWESOME_VERSION@
|
|
|
|
----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Package environment
|
|
|
|
local pairs = pairs
|
|
|
|
local table = table
|
|
|
|
local wibox = wibox
|
|
|
|
local image = image
|
|
|
|
local hooks = require("awful.hooks")
|
|
|
|
local string = string
|
|
|
|
local widget = widget
|
|
|
|
local button = button
|
|
|
|
local capi = { screen = screen }
|
2008-11-13 11:53:41 +01:00
|
|
|
local bt = require("beautiful")
|
2008-11-20 17:13:23 +01:00
|
|
|
local screen = screen
|
2008-11-20 17:51:29 +01:00
|
|
|
local awful = awful
|
|
|
|
local dbus = dbus
|
2008-10-17 22:38:01 +02:00
|
|
|
|
|
|
|
--- Notification library
|
|
|
|
module("naughty")
|
|
|
|
|
|
|
|
--- Naughty configuration - a table containing common/default popup settings.
|
|
|
|
-- You can override some of these for individual popups using args to notify().
|
|
|
|
-- @name config
|
|
|
|
-- @field timeout Number of seconds after which popups disappear.
|
|
|
|
-- Set to 0 for no timeout. Default: 5
|
|
|
|
-- @field screen Screen on which the popups will appear number. Default: 1
|
|
|
|
-- @field position Corner of the workarea the popups will appear.
|
|
|
|
-- Valid values: 'top_right', 'top_left', 'bottom_right', 'bottom_left'.
|
|
|
|
-- Default: 'top_right'
|
2008-11-15 21:08:17 +01:00
|
|
|
-- @field padding Space between popups and edge of the workarea. Default: 4
|
2008-11-15 17:55:38 +01:00
|
|
|
-- @field height Height of a single line of text. Default: 16
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @field width Width of a popup. Default: 300
|
2008-11-15 21:08:17 +01:00
|
|
|
-- @field spacing Spacing between popups. Default: 1
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @field ontop Boolean forcing popups to display on top. Default: true
|
2008-11-15 21:08:17 +01:00
|
|
|
-- @field margin Space between popup edge and content. Default: 10
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @field font Popup font. Default: beautiful.font or "Verdana 8"
|
|
|
|
-- @field icon Popup icon. Default: nil
|
2008-11-21 18:09:11 +01:00
|
|
|
-- @field icon_size Size of the icon in pixels. Default: nil
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @field fg Foreground color. Default: beautiful.fg_focus or '#ffffff'
|
|
|
|
-- @field bg Background color. Default: beautiful.bg_focus or '#535d6c'
|
|
|
|
-- @field border_color Border color.
|
|
|
|
-- Default: beautiful.border_focus or '#535d6c'
|
|
|
|
-- @field border_width Border width. Default: 1
|
2008-11-04 20:35:18 +01:00
|
|
|
-- @field hover_timeout Delay in seconds after which hovered popup disappears.
|
|
|
|
-- Default: nil
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @class table
|
|
|
|
|
|
|
|
config = {}
|
|
|
|
config.timeout = 5
|
|
|
|
config.screen = 1
|
|
|
|
config.position = "top_right"
|
2008-11-15 21:08:17 +01:00
|
|
|
config.padding = 4
|
2008-10-17 22:38:01 +02:00
|
|
|
config.height = 16
|
|
|
|
config.width = 300
|
2008-11-15 21:08:17 +01:00
|
|
|
config.spacing = 1
|
2008-10-17 22:38:01 +02:00
|
|
|
config.ontop = true
|
2008-11-15 21:08:17 +01:00
|
|
|
config.margin = 10
|
2008-10-17 22:38:01 +02:00
|
|
|
config.icon = nil
|
2008-11-21 18:09:11 +01:00
|
|
|
config.icon_size = nil
|
2008-10-17 22:38:01 +02:00
|
|
|
config.border_width = 1
|
2008-11-04 20:35:18 +01:00
|
|
|
config.hover_timeout = nil
|
2008-10-17 22:38:01 +02:00
|
|
|
|
2008-11-20 17:51:29 +01:00
|
|
|
-- Counter for the notifications
|
|
|
|
-- Required for later access via DBUS
|
|
|
|
local counter = 1
|
|
|
|
|
2008-10-17 22:38:01 +02:00
|
|
|
--- Index of notifications. See config table for valid 'position' values.
|
|
|
|
-- Each element is a table consisting of:
|
|
|
|
-- @field box Wibox object containing the popup
|
2008-11-15 17:55:38 +01:00
|
|
|
-- @field height Popup height
|
2008-11-19 03:16:44 +01:00
|
|
|
-- @field width Popup width
|
2008-11-15 17:55:38 +01:00
|
|
|
-- @field die Function to be executed on timeout
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @name notifications[position]
|
|
|
|
-- @class table
|
|
|
|
|
2008-11-20 17:13:23 +01:00
|
|
|
notifications = {}
|
|
|
|
for s = 1, screen.count() do
|
|
|
|
notifications[s] = {
|
|
|
|
top_left = {},
|
|
|
|
top_right = {},
|
|
|
|
bottom_left = {},
|
|
|
|
bottom_right = {},
|
|
|
|
}
|
|
|
|
end
|
2008-10-17 22:38:01 +02:00
|
|
|
|
|
|
|
--- Evaluate desired position of the notification by index - internal
|
|
|
|
-- @param idx Index of the notification
|
|
|
|
-- @param position top_right | top_left | bottom_right | bottom_left
|
2008-11-15 17:55:38 +01:00
|
|
|
-- @param height Popup height
|
2008-11-19 03:16:44 +01:00
|
|
|
-- @param width Popup width (optional)
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @return Absolute position in {x, y} dictionary
|
|
|
|
|
2008-11-20 17:13:23 +01:00
|
|
|
local function get_offset(screen, position, idx, width, height)
|
|
|
|
local ws = capi.screen[screen].workarea
|
2008-10-17 22:38:01 +02:00
|
|
|
local v = {}
|
2008-11-20 17:13:23 +01:00
|
|
|
width = width or notifications[screen][position][idx].width or config.width
|
2008-10-17 22:38:01 +02:00
|
|
|
|
|
|
|
-- calculate x
|
|
|
|
if position:match("left") then
|
2008-11-15 21:08:17 +01:00
|
|
|
v.x = ws.x + config.padding
|
2008-10-17 22:38:01 +02:00
|
|
|
else
|
2008-11-19 03:16:44 +01:00
|
|
|
v.x = ws.x + ws.width - (width + config.border_width*2 + config.padding)
|
2008-10-17 22:38:01 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- calculate existing popups' height
|
|
|
|
local existing = 0
|
|
|
|
for i = 1, idx-1, 1 do
|
2008-11-20 17:13:23 +01:00
|
|
|
existing = existing + notifications[screen][position][i].height + config.spacing + config.border_width*2
|
2008-10-17 22:38:01 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
-- calculate y
|
|
|
|
if position:match("top") then
|
2008-11-15 21:08:17 +01:00
|
|
|
v.y = ws.y + config.padding + existing
|
2008-10-17 22:38:01 +02:00
|
|
|
else
|
2008-11-15 21:08:17 +01:00
|
|
|
v.y = ws.y + ws.height - (config.padding + config.border_width + height + existing)
|
2008-10-17 22:38:01 +02:00
|
|
|
end
|
|
|
|
|
2008-10-23 18:47:43 +02:00
|
|
|
-- if positioned outside workarea, destroy oldest popup and recalculate
|
2008-11-15 17:55:38 +01:00
|
|
|
if v.y + height > ws.y + ws.height or v.y < ws.y then
|
2008-10-23 18:47:43 +02:00
|
|
|
idx = idx - 1
|
2008-11-20 17:13:23 +01:00
|
|
|
destroy(notifications[screen][position][1])
|
|
|
|
v = get_offset(screen, position, idx, width, height)
|
2008-10-23 18:47:43 +02:00
|
|
|
end
|
|
|
|
|
2008-10-17 22:38:01 +02:00
|
|
|
return v
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Re-arrange notifications according to their position and index - internal
|
|
|
|
-- @return None
|
2008-11-20 17:13:23 +01:00
|
|
|
local function arrange(screen)
|
|
|
|
for p,pos in pairs(notifications[screen]) do
|
|
|
|
for i,notification in pairs(notifications[screen][p]) do
|
|
|
|
local offset = get_offset(screen, p, i, notification.width, notification.height)
|
2008-11-21 01:44:55 +01:00
|
|
|
notification.box:geometry({ x = offset.x, y = offset.y, width = notification.width, height = notification.height })
|
2008-10-19 20:58:50 +02:00
|
|
|
notification.idx = i
|
2008-10-17 22:38:01 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Destroy notification by index
|
2008-11-25 09:45:28 +01:00
|
|
|
-- @param notification Notification object to be destroyed
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @return True if the popup was successfully destroyed, nil otherwise
|
2008-10-19 20:58:50 +02:00
|
|
|
function destroy(notification)
|
|
|
|
if notification then
|
2008-11-20 17:13:23 +01:00
|
|
|
local scr = notification.box.screen
|
|
|
|
table.remove(notifications[notification.box.screen][notification.position], notification.idx)
|
2008-11-15 16:25:22 +01:00
|
|
|
hooks.timer.unregister(notification.die)
|
2008-11-20 17:13:23 +01:00
|
|
|
notification.box.screen = nil
|
|
|
|
arrange(scr)
|
2008-10-17 22:38:01 +02:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-11-20 17:51:29 +01:00
|
|
|
--- Get notification by ID
|
|
|
|
-- @param id ID of the notification
|
|
|
|
-- @return notification object if it was found, nil otherwise
|
|
|
|
local function getById(id)
|
|
|
|
-- iterate the notifications to get the notfications with the correct ID
|
|
|
|
for s = 1, screen.count() do
|
|
|
|
for p,pos in pairs(notifications[s]) do
|
|
|
|
for i,notification in pairs(notifications[s][p]) do
|
|
|
|
if notification.id == id then
|
|
|
|
return notification
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-10-17 22:38:01 +02:00
|
|
|
--- Create notification. args is a dictionary of optional arguments. For more information and defaults see respective fields in config table.
|
|
|
|
-- @param text Text of the notification
|
|
|
|
-- @param timeout Time in seconds after which popup expires
|
|
|
|
-- @param title Title of the notification
|
|
|
|
-- @param position Corner of the workarea the popups will appear
|
|
|
|
-- @param icon Path to icon
|
2008-10-20 22:02:22 +02:00
|
|
|
-- @param icon_size Desired icon size in px
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @param fg Foreground color
|
|
|
|
-- @param bg Background color
|
|
|
|
-- @param screen Target screen for the notification
|
|
|
|
-- @param ontop Target screen for the notification
|
2008-11-05 03:03:28 +01:00
|
|
|
-- @param run Function to run on left click
|
2008-11-19 03:16:44 +01:00
|
|
|
-- @param width The popup width
|
2008-11-20 17:51:29 +01:00
|
|
|
-- @param replaces_id Replace the notification with the given ID
|
2008-10-17 22:38:01 +02:00
|
|
|
-- @usage naughty.notify({ title = 'Achtung!', text = 'You\'re idling', timeout = 0 })
|
2008-11-25 09:45:28 +01:00
|
|
|
-- @return The notification object
|
2008-10-17 22:38:01 +02:00
|
|
|
function notify(args)
|
2008-11-15 17:55:38 +01:00
|
|
|
-- gather variables together
|
2008-10-17 22:38:01 +02:00
|
|
|
local timeout = args.timeout or config.timeout
|
|
|
|
local icon = args.icon or config.icon
|
2008-10-20 22:02:22 +02:00
|
|
|
local icon_size = args.icon_size or config.icon_size
|
2008-10-17 22:38:01 +02:00
|
|
|
local text = args.text or ""
|
|
|
|
local screen = args.screen or config.screen
|
|
|
|
local ontop = args.ontop or config.ontop
|
2008-11-19 03:16:44 +01:00
|
|
|
local width = args.width or config.width
|
2008-10-17 22:38:01 +02:00
|
|
|
|
2008-11-21 01:43:18 +01:00
|
|
|
-- beautiful
|
|
|
|
local beautiful = bt.get()
|
|
|
|
local font = args.font or config.font or beautiful.font or "Verdana 8"
|
|
|
|
local fg = args.fg or config.fg or beautiful.fg_normal or '#ffffff'
|
|
|
|
local bg = args.bg or config.bg or beautiful.bg_normal or '#535d6c'
|
|
|
|
local border_color = config.border_color or beautiful.bg_focus or '#535d6c'
|
2008-11-15 17:55:38 +01:00
|
|
|
local notification = {}
|
2008-11-20 17:51:29 +01:00
|
|
|
|
|
|
|
-- replace notification if needed
|
|
|
|
if args.replaces_id then
|
|
|
|
obj = getById(args.replaces_id)
|
|
|
|
if obj then
|
|
|
|
-- destroy this and ...
|
|
|
|
destroy(obj)
|
|
|
|
end
|
|
|
|
-- ... may use its ID
|
|
|
|
if args.replaces_id < counter then
|
|
|
|
notification.id = args.replaces_id
|
|
|
|
else
|
|
|
|
counter = counter + 1
|
|
|
|
notification.id = counter
|
|
|
|
end
|
|
|
|
else
|
|
|
|
-- get a brand new ID
|
|
|
|
counter = counter + 1
|
|
|
|
notification.id = counter
|
|
|
|
end
|
|
|
|
|
2008-11-15 17:55:38 +01:00
|
|
|
notification.position = args.position or config.position
|
2008-11-20 17:13:23 +01:00
|
|
|
notification.idx = #notifications[screen][notification.position] + 1
|
2008-10-17 22:38:01 +02:00
|
|
|
|
2008-11-15 17:55:38 +01:00
|
|
|
local title = ""
|
2008-11-15 19:39:26 +01:00
|
|
|
if args.title then title = args.title .. "\n" end
|
2008-10-19 20:58:50 +02:00
|
|
|
|
2008-11-15 17:55:38 +01:00
|
|
|
-- hook destroy
|
2008-11-05 03:18:01 +01:00
|
|
|
local die = function () destroy(notification) end
|
|
|
|
hooks.timer.register(timeout, die)
|
|
|
|
notification.die = die
|
2008-11-04 20:35:18 +01:00
|
|
|
|
2008-11-05 03:18:01 +01:00
|
|
|
local run = args.run or die
|
2008-11-05 03:03:28 +01:00
|
|
|
|
2008-11-04 20:35:18 +01:00
|
|
|
local hover_destroy = function ()
|
2008-11-05 03:18:01 +01:00
|
|
|
if config.hover_timeout == 0 then die()
|
|
|
|
else hooks.timer.register(config.hover_timeout, die) end
|
2008-11-04 20:35:18 +01:00
|
|
|
end
|
|
|
|
|
2008-11-15 17:55:38 +01:00
|
|
|
-- create textbox
|
2008-11-12 11:48:51 +01:00
|
|
|
local textbox = widget({ type = "textbox", align = "flex" })
|
2008-11-05 03:03:28 +01:00
|
|
|
textbox:buttons({ button({ }, 1, run),
|
2008-11-05 03:18:01 +01:00
|
|
|
button({ }, 3, die) })
|
2008-11-21 14:41:03 +01:00
|
|
|
textbox.text = string.format('<margin right="'..config.margin..'" left="'..config.margin..'"/><span font_desc="%s"><b>%s</b>%s</span>', font, title, text)
|
2008-11-04 20:35:18 +01:00
|
|
|
if config.hover_timeout then textbox.mouse_enter = hover_destroy end
|
2008-10-17 22:38:01 +02:00
|
|
|
|
2008-11-15 17:55:38 +01:00
|
|
|
-- create iconbox
|
2008-10-17 22:38:01 +02:00
|
|
|
local iconbox = nil
|
|
|
|
if icon then
|
2008-11-12 11:48:51 +01:00
|
|
|
iconbox = widget({ type = "imagebox", align = "left" })
|
2008-11-05 03:03:28 +01:00
|
|
|
iconbox:buttons({ button({ }, 1, run),
|
2008-11-05 03:18:01 +01:00
|
|
|
button({ }, 3, die) })
|
2008-10-20 22:02:22 +02:00
|
|
|
local img = image(icon)
|
2008-11-04 21:09:28 +01:00
|
|
|
if icon_size then
|
|
|
|
img = img:crop_and_scale(0,0,img.height,img.width,icon_size,icon_size)
|
|
|
|
end
|
2008-11-21 18:09:11 +01:00
|
|
|
iconbox.resize = false
|
2008-10-20 22:02:22 +02:00
|
|
|
iconbox.image = img
|
2008-11-04 20:35:18 +01:00
|
|
|
if config.hover_timeout then iconbox.mouse_enter = hover_destroy end
|
2008-10-17 22:38:01 +02:00
|
|
|
end
|
|
|
|
|
2008-11-15 17:55:38 +01:00
|
|
|
-- create container wibox
|
2008-11-12 11:48:51 +01:00
|
|
|
notification.box = wibox({ position = "floating",
|
2008-11-21 01:43:18 +01:00
|
|
|
fg = fg,
|
|
|
|
bg = bg,
|
2008-11-15 17:55:38 +01:00
|
|
|
border_color = config.border_color,
|
|
|
|
border_width = config.border_width })
|
|
|
|
|
|
|
|
-- position the wibox
|
|
|
|
local lines = 1; for i in string.gmatch(title..text, "\n") do lines = lines + 1 end
|
|
|
|
if iconbox and iconbox.image.height > lines * config.height then
|
|
|
|
notification.height = iconbox.image.height
|
|
|
|
else
|
|
|
|
notification.height = lines * config.height end
|
2008-11-19 03:16:44 +01:00
|
|
|
notification.width = width
|
2008-11-20 17:13:23 +01:00
|
|
|
local offset = get_offset(screen, notification.position, notification.idx, notification.width, notification.height)
|
|
|
|
notification.box:geometry({ width = notification.width,
|
2008-11-15 17:55:38 +01:00
|
|
|
height = notification.height,
|
|
|
|
x = offset.x,
|
|
|
|
y = offset.y })
|
|
|
|
notification.box.ontop = ontop
|
|
|
|
notification.box.screen = screen
|
|
|
|
|
|
|
|
-- populate widgets
|
|
|
|
notification.box.widgets = { iconbox, textbox }
|
2008-10-17 22:38:01 +02:00
|
|
|
|
|
|
|
-- insert the notification to the table
|
2008-11-25 09:45:28 +01:00
|
|
|
table.insert(notifications[screen][notification.position], notification)
|
2008-10-17 22:38:01 +02:00
|
|
|
|
2008-11-25 09:45:28 +01:00
|
|
|
-- return the notification
|
|
|
|
return notification
|
|
|
|
end
|
2008-11-20 17:51:29 +01:00
|
|
|
|
|
|
|
-- DBUS/Notification support
|
|
|
|
|
|
|
|
-- Notify
|
|
|
|
awful.hooks.dbus.register("org.freedesktop.Notifications", function (data, arg1, replaces_id, icon, title, text)
|
|
|
|
args = {}
|
|
|
|
if data.member == "Notify" then
|
|
|
|
if text ~= "" then
|
|
|
|
args.text = text
|
|
|
|
if title ~= "" then
|
|
|
|
args.title = title
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if title ~= "" then
|
|
|
|
args.text = title
|
|
|
|
else
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if icon ~= "" then
|
|
|
|
args.icon = icon
|
|
|
|
end
|
|
|
|
if replaces_id and replaces_id ~= "" and replaces_id ~= 0 then
|
|
|
|
args.replaces_id = replaces_id
|
|
|
|
end
|
|
|
|
local id = notify(args).id
|
|
|
|
return "i", id
|
|
|
|
elseif data.member == "CloseNotification" then
|
|
|
|
local obj = getById(arg1)
|
|
|
|
if obj then
|
|
|
|
destroy(obj)
|
|
|
|
end
|
|
|
|
elseif data.member == "GetServerInfo" or data.member == "GetServerInformation" then
|
|
|
|
-- name of notification app, name of project, version of notification app, project version
|
|
|
|
return "s", "naughty", "s", "awesome", "s", "1337", "s", "1337"
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
awful.hooks.dbus.register("org.freedesktop.DBus.Introspectable",
|
|
|
|
function (data, text)
|
|
|
|
if data.member == "Introspect" then
|
|
|
|
local xml = [=[<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object
|
|
|
|
Introspection 1.0//EN"
|
|
|
|
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
|
|
<node>
|
|
|
|
<interface name="org.freedesktop.DBus.Introspectable">
|
|
|
|
<method name="Introspect">
|
|
|
|
<arg name="data" direction="out" type="s"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
<interface name="org.freedesktop.Notifications">
|
|
|
|
<method name="CloseNotification">
|
|
|
|
<arg name="id" type="u" direction="in"/>
|
|
|
|
</method>
|
|
|
|
<method name="Notify">
|
|
|
|
<arg name="app_name" type="s" direction="in"/>
|
|
|
|
<arg name="id" type="u" direction="in"/>
|
|
|
|
<arg name="icon" type="s" direction="in"/>
|
|
|
|
<arg name="summary" type="s" direction="in"/>
|
|
|
|
<arg name="body" type="s" direction="in"/>
|
|
|
|
<arg name="actions" type="as" direction="in"/>
|
|
|
|
<arg name="hints" type="a{sv}" direction="in"/>
|
|
|
|
<arg name="timeout" type="i" direction="in"/>
|
|
|
|
<arg name="return_id" type="u" direction="out"/>
|
|
|
|
</method>
|
|
|
|
</interface>
|
|
|
|
</node>]=]
|
|
|
|
return "s", xml
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- listen for dbus notification requests
|
|
|
|
dbus.request_name("org.freedesktop.Notifications")
|
|
|
|
|
2008-10-17 22:38:01 +02:00
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
|