awesome-launch/init.lua

312 lines
8.9 KiB
Lua
Raw Normal View History

2020-09-16 19:21:01 +02:00
-- This project is licensed under the MIT License (see LICENSE).
2019-03-28 15:39:13 +01:00
--- Launch clients with single instance IDs using wm-launch.
--
-- @author James Reed <jcrd@tuta.io>
2020-09-16 19:21:01 +02:00
-- @copyright 2019-2020 James Reed
2019-07-03 01:22:20 +02:00
-- @module awesome-launch
2019-03-28 15:39:13 +01:00
local awful = require("awful")
local gears = require("gears")
local ruled = require("ruled")
2019-08-14 02:52:22 +02:00
local wibox = require("wibox")
local beautiful = require("beautiful")
local uuid = require("uuid")
2019-09-09 22:09:09 +02:00
local shared = require("awesome-launch.shared")
2019-03-28 15:39:13 +01:00
2019-09-09 22:09:09 +02:00
uuid.seed()
2019-08-14 02:52:22 +02:00
local launch = {}
2019-09-09 22:09:09 +02:00
launch.widget = require("awesome-launch.widget")
2019-03-28 15:39:13 +01:00
awesome.register_xproperty("WM_LAUNCH_ID", "string")
local function get_data(c)
local id = c:get_xproperty("WM_LAUNCH_ID")
if id and id ~= "" then
return shared.pending[id]
end
for _, data in pairs(shared.pending) do
if data.rule and ruled.client.match(c, data.rule) then
return data
end
end
end
2019-03-28 15:39:13 +01:00
awful.rules.add_rule_source("launch",
function (c, props, callbacks)
local data = get_data(c)
2020-12-21 01:11:35 +01:00
if not data then
return
end
2019-03-28 15:39:13 +01:00
data.timer:stop()
if data.props.tag and not data.props.tag.activated then
data.props.tag = awful.screen.focused().selected_tag
end
2019-03-28 15:39:13 +01:00
gears.table.crush(props, data.props)
if data.callback then
table.insert(callbacks, data.callback)
2019-03-28 15:39:13 +01:00
end
if data.factory then
c:connect_signal("request::unmanage", function ()
awful.spawn("wm-launchd -check " .. data.factory)
end)
end
shared.pending[data.id] = nil
2019-09-09 22:09:09 +02:00
launch.widget.update_widgets()
2019-03-28 15:39:13 +01:00
end)
awful.client.property.persist("cmdline", "string")
launch.client = {}
2019-03-28 15:39:13 +01:00
local function get_ids()
local ids = {}
for _, c in ipairs(client.get()) do
2020-12-21 01:11:35 +01:00
if c.single_instance_id then
ids[c.single_instance_id] = c
end
2019-03-28 15:39:13 +01:00
end
return ids
end
--- Get a launched client by its ID.
2019-03-28 15:39:13 +01:00
--
-- @param id The ID.
2019-04-11 04:15:17 +02:00
-- @param filter Function to filter clients that are considered.
2019-03-28 15:39:13 +01:00
-- @return The client.
2019-04-11 04:15:17 +02:00
-- @function client.by_id
function launch.client.by_id(id, filter)
for _, c in ipairs(client.get()) do
if (not filter or filter(c)) and c.single_instance_id == id then
return c
end
end
end
--- Get a launched client by its command line.
--
2019-04-11 04:15:17 +02:00
-- @param cmd The command line.
-- @param filter Function to filter clients that are considered.
-- @return The client.
2019-04-11 04:15:17 +02:00
-- @function client.by_cmdline
function launch.client.by_cmdline(cmd, filter)
for _, c in ipairs(client.get()) do
if (not filter or filter(c)) and c.cmdline == cmd then
return c
end
end
2019-03-28 15:39:13 +01:00
end
--- Spawn a client with wm-launch.
--
-- @param cmd The command.
-- @param args Table containing the single instance ID and additional arguments
-- @param args.id Single instance ID.
-- @param args.props Properties to apply to the client.
-- @param args.pwd Pathname to the working directory for new clients.
-- @param args.timeout Seconds after which to stop waiting for a client to spawn.
-- @param args.callback Function to call with client when it spawns.
-- @param args.factory The factory to use (see wm-launch's -f flag).
-- @param args.systemd If true, run cmd with systemd-run.
-- @param args.firejail If true, run cmd with firejail.
-- @param args.rule Fallback client rule used if setting the ID fails.
2019-03-28 15:39:13 +01:00
-- @return The client's ID.
-- @function launch.spawn
local function spawn(cmd, args)
args = args or {}
local id = args.id or uuid()
2019-03-28 15:39:13 +01:00
local data = {
id = id,
2019-03-28 15:39:13 +01:00
props = args.props or {},
pwd = args.pwd,
callback = args.callback,
2019-08-14 02:52:22 +02:00
timeout = math.ceil(args.timeout or 10),
rule = args.rule,
2019-03-28 15:39:13 +01:00
}
gears.table.crush(data.props, {
2020-12-21 01:11:35 +01:00
single_instance_id = id,
cmdline = cmd,
})
2019-03-28 15:39:13 +01:00
2019-08-14 02:52:22 +02:00
local step = 1/2
2019-03-28 15:39:13 +01:00
data.timer = gears.timer {
2019-08-14 02:52:22 +02:00
timeout = step,
callback = function ()
data.timeout = data.timeout - step
if data.timeout == 0 then
2019-09-09 22:09:09 +02:00
shared.pending[id] = nil
launch.widget.update_widgets()
2019-08-14 02:52:22 +02:00
return false
else
data.widget.id_const.id_margin.id_progress.value = data.timeout
end
return true
end,
2019-03-28 15:39:13 +01:00
}
2019-09-09 22:09:09 +02:00
if launch.widget.active() then
data.widget = launch.widget.new(cmd, data)
2019-08-14 02:52:22 +02:00
end
2019-09-10 00:13:35 +02:00
local launch_cmd = "wm-launch"
if args.factory then
data.factory = args.factory
2019-09-10 00:13:35 +02:00
launch_cmd = launch_cmd .. " -f " .. args.factory
end
if args.systemd then
launch_cmd = launch_cmd .. " -s"
end
if args.firejail then
2019-09-10 00:13:35 +02:00
launch_cmd = launch_cmd .. " -j"
end
2019-09-10 00:13:35 +02:00
launch_cmd = string.format("%s %s %s", launch_cmd, id, cmd)
2019-03-28 15:39:13 +01:00
if data.pwd then
2019-09-10 00:13:35 +02:00
awful.spawn.with_shell(string.format("cd %s; %s", data.pwd, launch_cmd))
2019-03-28 15:39:13 +01:00
else
2019-09-10 00:13:35 +02:00
awful.spawn(launch_cmd)
2019-03-28 15:39:13 +01:00
end
2019-09-09 22:09:09 +02:00
shared.pending[id] = data
launch.widget.update_widgets()
2019-03-28 15:39:13 +01:00
data.timer:start()
return id
end
launch.spawn = {}
setmetatable(launch.spawn, {__call = function (_, ...) spawn(...) end})
--- Spawn a command if an instance is not already running.
--
-- @param cmd The command.
-- @param args Table containing the single instance ID and additional arguments for spawn
-- @param args.id Single instance ID.
-- @param args.props Properties to apply to the client.
-- @param args.pwd Pathname to the working directory for new clients.
-- @param args.timeout Seconds after which to stop waiting for a client to spawn.
-- @param args.callback Function to call with client when it spawns.
-- @param args.factory The factory to use (see wm-launch's -f flag).
-- @param args.systemd If true, run cmd with systemd-run.
-- @param args.firejail If true, run cmd with firejail.
-- @param args.rule Fallback client rule used if setting the ID fails.
-- @param args.filter Function to filter clients that are considered.
2019-03-28 15:39:13 +01:00
-- @return The client's ID.
-- @function spawn.single_instance
function launch.spawn.single_instance(cmd, args)
local c
if args.id then
c = launch.client.by_id(args.id, args.filter)
else
c = launch.client.by_cmdline(cmd, args.filter)
end
2020-12-21 01:11:35 +01:00
if not c then
return spawn(cmd, args)
end
2019-03-28 15:39:13 +01:00
return args.id
end
--- Raise a client if it exists or spawn a new one then raise it.
--
-- @param cmd The command.
-- @param args Table containing the single instance ID and additional arguments for spawn
-- @param args.id Single instance ID.
-- @param args.props Properties to apply to the client.
-- @param args.pwd Pathname to the working directory for new clients.
-- @param args.timeout Seconds after which to stop waiting for a client to spawn.
-- @param args.callback Function to call with client when it spawns.
-- @param args.raise_callback Function to call with client when it spawns or is raised.
-- @param args.factory The factory to use (see wm-launch's -f flag).
-- @param args.systemd If true, run cmd with systemd-run.
-- @param args.firejail If true, run cmd with firejail.
-- @param args.rule Fallback client rule used if setting the ID fails.
-- @param args.filter Function to filter clients that are considered.
2019-03-28 15:39:13 +01:00
-- @return The client's ID.
-- @function spawn.raise_or_spawn
function launch.spawn.raise_or_spawn(cmd, args)
local c
if args.id then
c = launch.client.by_id(args.id, args.filter)
else
c = launch.client.by_cmdline(cmd, args.filter)
end
2019-03-28 15:39:13 +01:00
if c then
c:emit_signal("request::activate", "launch.spawn.raise_or_spawn",
2020-12-21 01:11:35 +01:00
{raise = true})
if args.raise_callback then
args.raise_callback(c)
end
2019-03-28 15:39:13 +01:00
return args.id
end
if args.raise_callback then
local cb = args.callback
args.callback = function (c)
2020-12-21 01:11:35 +01:00
if cb then
cb(c)
end
args.raise_callback(c)
end
end
2019-03-28 15:39:13 +01:00
return spawn(cmd, args)
end
2019-09-15 03:46:21 +02:00
--- Spawn clients on a tag.
--
-- Usage: `launch.spawn.here().spawn("xterm")`
--
-- @param tag_func Optional function that returns the tag, defaults to
-- `awful.screen.focused().selected_tag`.
-- @return A table with the functions: spawn, single_instance, raise_or_spawn.
-- @function spawn.here
function launch.spawn.here(tag_func)
local here = {}
local function with_tag(func, cmd, args)
local tag
if tag_func then
tag = tag_func()
else
tag = awful.screen.focused().selected_tag
end
local a = {
2020-12-21 01:11:35 +01:00
filter = function (c)
return c:isvisible()
end,
2019-09-15 03:46:21 +02:00
props = {tag = tag},
}
gears.table.crush(a, args or {})
func(cmd, a)
end
function here.spawn(...)
with_tag(launch.spawn, ...)
end
function here.single_instance(...)
with_tag(launch.spawn.single_instance, ...)
end
function here.raise_or_spawn(...)
with_tag(launch.spawn.raise_or_spawn, ...)
end
return here
end
2019-03-28 15:39:13 +01:00
return launch