Search code examples
awesome-wm

Auto start programs on specific screen tags


My goal is to automatically start a few programs on specific tags when awesome first runs, e.g. starting Chromium on screen 1, tag 1; starting gVim on screen 2, tag 1. I've found a few places online that advocate using the following function:

function spawn_once(command, class, tag) 
  -- create move callback
  local callback 
  callback = function(c) 
    if c.class == class then 
      awful.client.movetotag(tag, c) 
      client.remove_signal("manage", callback) 
    end 
  end 
  client.add_signal("manage", callback) 
  -- now check if not already running!     
  local findme = command
  local firstspace = findme:find(" ")
  if firstspace then
    findme = findme:sub(0, firstspace-1)
  end
  -- finally run it
  awful.util.spawn_with_shell("pgrep -u $USER -x " .. findme .. " > /dev/null || (" .. command .. ")")
end

spawn_once("chromium", "Chromium", root.tags()[2][1])
spawn_once("gvim", "gVim", root.tags()[1][2])

Unfortunately, the function doesn't work on newer versions of awesome. No matter what tag I choose or minor update to the function I make, it places the program on the first tag. See comments: https://gist.github.com/Flowkap/8858434

Doesn't work properly. Same as dvd001. So the programs run at tag 1 regardless of the specified tag.

How would I go about accomplishing this in the newer versions of awesome (v4.2+)?

Here is my rc.lua:

-- libraries
local naughty = require("naughty")
local gears = require("gears")
local beautiful = require("beautiful")
local awful = require("awful")
    require("awful.autofocus")
local wibox = require("wibox")

-- errors
if awesome.startup_errors then
    naughty.notify({
        preset = naughty.config.presets.critical,
        title = "Startup errors",
        text = awesome.startup_errors})
end

do
    local in_error = false
    awesome.connect_signal("debug::error", function (err)
        if in_error then return end
        in_error = true
        naughty.notify({preset = naughty.config.presets.critical,
            title = "Error",
            text = tostring(err)})
        in_error = false
    end)
end

-- variables
local modkey = "Mod4"
local terminal = "xterm"

-- layouts
awful.layout.layouts = {
    awful.layout.suit.tile.left,
    awful.layout.suit.max,
    awful.layout.suit.floating,
}

-- helper functions
local function client_menu_toggle_fn()
    local instance = nil

    return function ()
        if instance and instance.wibox.visible then
            instance:hide()
            instance = nil
        else
            instance = awful.menu.clients({theme = {width = 250}})
        end
    end
end

local function set_wallpaper(s)
    if beautiful.wallpaper then
        local wallpaper = beautiful.wallpaper
        if type(wallpaper) == "function" then
            wallpaper = wallpaper(s)
        end
        gears.wallpaper.maximized(wallpaper, s, true)
    end
end


-- init theme
beautiful.init(gears.filesystem.get_configuration_dir() .. "themes/shane/theme.lua")

-- change wallpaper on screen resize
screen.connect_signal("property::geometry", set_wallpaper)

-- tag buttons
local tag_buttons = gears.table.join(
    awful.button({}, 1, function (t) t:view_only() end),
    awful.button({modkey}, 1, function (t)
        if client.focus then
            client.focus:move_to_tag(t)
        end
    end),
    awful.button({}, 3, function (t)
        if client.focus then
            client.focus:toggle_tag(t)
        end
    end),
    awful.button({}, 4, function (t) awful.tag.viewnext(t.screen) end),
    awful.button({}, 5, function (t) awful.tag.viewprev(t.screen) end)
)

-- task buttons
local task_buttons = gears.table.join(
    awful.button({}, 1, function (c)
        if c == client.focus then
            c.minimized = true
        else
            c.minimized = false
            if not c:isvisible() and c.first_tag then
                c.first_tag:view_only()
            end
            client.focus = c
            c:raise()
        end
    end),
    awful.button({}, 3, client_menu_toggle_fn()),
    awful.button({}, 4, function ()
        awful.client.focus.byidx(1)
    end),
    awful.button({}, 5, function ()
        awful.client.focus.byidx(-1)
    end)
)

-- create clock
clock = wibox.widget.textclock(" %A, %B %d %l:%M%p ")

-- init screen
awful.screen.connect_for_each_screen(function (s)
    -- set wallpaper
    set_wallpaper(s)

    -- create tags
    if (s.index == 1) then
        awful.tag({"  1. Browser  ", "  2. Database  ", "  3. Files  ", "  4. Break  ", "  5. Music  ", "  6. Misc  "},
            s, awful.layout.layouts[1])
    else
        awful.tag({"  1. Terminal  ", "  2. Edit  ", "  3. Passwords  ", "  4. Misc  "}, s, awful.layout.layouts[1])
    end

    -- create tag list
    s.taglist = awful.widget.taglist(s, awful.widget.taglist.filter.all, tag_buttons)

    -- create prompt
    s.prompt = awful.widget.prompt({prompt = " Run: ", prompt_fg = "#f0f"})

    -- create tasks
    s.tasks = awful.widget.tasklist(s, awful.widget.tasklist.filter.currenttags, task_buttons)

    -- create the main bar
    s.bar = awful.wibar({position = "top", screen = s, height = 32})
    s.bar:setup {
        layout = wibox.layout.align.horizontal,
        { layout = wibox.layout.fixed.horizontal,
            s.taglist,
            s.prompt },
        { layout = wibox.layout.fixed.horizontal },
        { layout = wibox.layout.fixed.horizontal,
            clock },
    }

    -- task bar
    s.taskbar = awful.wibar({position = "bottom", screen = s, height = 32})
    s.taskbar:setup {
        layout = wibox.layout.align.horizontal,
        s.tasks,
    }
end)

-- startup programs
function spawn_once(command, class, tag)
    -- create move callback
    local callback
    callback = function(c)
        if c.class == class then
            awful.client.movetotag(tag, c)
            client.remove_signal("manage", callback)
        end
    end
    client.add_signal("manage", callback)
    -- now check if not already running!
    local findme = command
    local firstspace = findme:find(" ")
    if firstspace then
        findme = findme:sub(0, firstspace-1)
    end
    -- finally run it
    awful.util.spawn_with_shell("pgrep -u $USER -x " .. findme .. " > /dev/null || (" .. command .. ")")
end

spawn_once("chromium", "Chromium", screen[1].tags[1])
spawn_once("gvim", "gVim", screen[2].tags[2])

-- keybindings
keybindings = gears.table.join(
    awful.key({modkey, "Shift"}, "q", awesome.quit,
        {description = "quit awesome", group = "awesome"}),
    awful.key({modkey, "Shift"}, "r", awesome.restart,
        {description = "restart awesome", group = "awesome"}),
    awful.key({modkey}, "r", function () awful.screen.focused().prompt:run() end,
        {description = "run prompt", group = "launcher"}),
    awful.key({modkey}, "Left", awful.tag.viewprev,
        {description = "previous tag", group = "tag"}),
    awful.key({modkey}, "Right", awful.tag.viewnext,
        {description = "next tag", group = "tag"}),
    awful.key({modkey}, "j", function () awful.client.focus.byidx(1) end,
        {description = "focus next window", group = "client"}),
    awful.key({modkey}, "k", function () awful.client.focus.byidx(-1) end,
        {description = "focus previous window", group = "client"}),
    awful.key({modkey, "Shift"}, "j", function () awful.client.swap.byidx(1) end,
        {description = "swap with next client", group = "client"}),
    awful.key({modkey, "Shift"}, "k", function () awful.client.swap.byidx(-1) end,
        {description = "swap with previous client", group = "client"}),
    awful.key({modkey, "Control"}, "j", function () awful.screen.focus_relative(1) end,
        {description = "focus next screen", group = "screen"}),
    awful.key({modkey, "Control"}, "k", function () awful.screen.focus_relative(-1) end,
        {description = "focus previous screen", group = "screen"}),
    awful.key({modkey}, "Tab", function ()
        awful.client.focus.history.previous()
        if client.focus then client.focus:raise() end end,
        {description = "focus last client", group = "client"}),
    awful.key({modkey}, "Return", function () awful.spawn(terminal) end,
        {description = "open a terminal", group = "launcher"}),
    awful.key({modkey}, "l", function () awful.tag.incmwfact(0.05) end,
        {description = "increase master width", group = "layout"}),
    awful.key({modkey}, "h", function () awful.tag.incmwfact(-0.05) end,
        {description = "decrease master width", group = "layout"}),
    awful.key({modkey, "Shift"}, "h", function () awful.tag.incnmaster(1, nil, true) end,
        {description = "increase number of master clients", group = "layout"}),
    awful.key({modkey, "Shift"}, "l", function () awful.tag.incnmaster(-1, nil, true) end,
        {description = "decrease number of master clients", group = "layout"}),
    awful.key({modkey, "Control"}, "h", function () awful.tag.incncol(1, nil, true) end,
        {description = "increase number of columns", group = "layout"}),
    awful.key({modkey, "Control"}, "l", function () awful.tag.incncol(-1, nil, true) end,
        {description = "decrease number of columns", group = "layout"}),
    awful.key({modkey}, "space", function () awful.layout.inc(1) end,
        {description = "next layout", group = "layout"}),
    awful.key({modkey, "Shift"}, "space", function () awful.layout.inc(-1) end,
        {description = "previous layout", group = "layout"}),
    awful.key({modkey}, "c", function () awful.spawn("chromium") end,
        {description = "start chrome", group = "launcher"}),
    awful.key({modkey}, "v", function () awful.spawn("gvim") end,
        {description = "start gvim", group = "launcher"}),
    awful.key({}, "XF86AudioRaiseVolume", function () awful.spawn("pactl set-sink-volume 2 +2%", false) end,
        {description = "raise volume", group = "launcher"}),
    awful.key({}, "XF86AudioLowerVolume", function () awful.spawn("pactl set-sink-volume 2 -2%", false) end,
        {description = "lower volume", group = "launcher"}),
    awful.key({}, "XF86AudioPlay", function () awful.spawn("lollypop -t", false) end,
        {description = "play track", group = "launcher"}),
    awful.key({}, "XF86AudioPause", function () awful.spawn("lollypop -t", false) end,
        {description = "pause track", group = "launcher"}),
    awful.key({}, "XF86AudioNext", function () awful.spawn("lollypop -n", false) end,
        {description = "next track", group = "launcher"}),
    awful.key({}, "XF86AudioPrev", function () awful.spawn("lollypop -p", false) end,
        {description = "previous track", group = "launcher"})
)

-- bind numbers to tags
for i = 1, 9 do
    keybindings = gears.table.join(keybindings,
        awful.key({modkey}, "#" .. i + 9,
            function ()
                local screen = awful.screen.focused()
                local tag = screen.tags[i]
                if tag then
                    tag:view_only()
                end
            end, {description = "view tag #" .. i, group = "tag"}),
        awful.key({modkey, "Control"}, "#" .. i + 9,
            function ()
                local screen = awful.screen.focused()
                local tag = screen.tags[i]
                if tag then
                    awful.tag.viewtoggle(tag)
                end
            end, {description = "toggle tag #" .. i, group = "tag"}),
        awful.key({modkey, "Shift"}, "#" .. i + 9,
            function ()
                if client.focus then
                    local tag = client.focus.screen.tags[i]
                    if tag then
                        client.focus:move_to_tag(tag)
                    end
                end
            end, {description = "move focused client to tag #" .. i, group = "tag"}),
        awful.key({modkey, "Control", "Shift"}, "#" .. i + 9,
            function ()
                if client.focus then
                    local tag = client.focus.screen.tags[i]
                    if tag then
                        client.focus:toggle_tag(tag)
                    end
                end
            end, {description = "toggle focused client on tag #" .. i, group = "tag"})
    )
end

root.keys(keybindings)

-- client keys
client_keybindings = gears.table.join(
    awful.key({modkey, "Shift"}, "c", function (c) c:kill() end,
        {description = "close", group = "client"}),
    awful.key({modkey}, "m", function (c)
        c.maximized = not c.maximized
        c:raise()
    end, {description = "toggle maximized", group = "client"})
)

-- client buttons
client_buttons = gears.table.join(
    awful.button({}, 1, function (c) client.focus = c; c:raise() end),
    awful.button({modkey}, 1, awful.mouse.client.move),
    awful.button({modkey}, 3, awful.mouse.client.resize)
)

-- client rules
awful.rules.rules = {
    {
        rule = {},
        properties = {
            border_width = beautiful.border_width,
            border_color = beautiful.border_normal,
            focus = awful.client.focus.filter,
            raise = true,
            keys = client_keybindings,
            buttons = client_buttons,
            screen = awful.screen.preferred,
            placement = awful.placement.no_overlap+awful.placement.no_offscreen,
            size_hints_honor = false
        }
    }
}

-- signals
client.connect_signal("manage", function (c)
    if awesome.startup
        and not c.size_hints.user_position
        and not c.size_hints.program_position then
            awful.placement.no_offscreen(c)
    end
end)

Solution

  • One more alternative here is to use the xproperties and startup notification backed spawn commands.

    First, you need to register an xproperty [1]. After that you write a function that loop across the running clients and check if your property is set on them. If it isn't you use the second argument of the awful.spawn to set your magic token. Please note that you can also add an awful.rules source to address the problem when restarting Awesome and setting the tag based on the magical token[2].

    For this solution, you need to use Awesome v4.3+ and apply this hack:

    Force applications to support the startup notifications

    [1] https://awesomewm.org/apidoc/classes/client.html#awful.client.property.persist

    [2] https://awesomewm.org/apidoc/libraries/awful.rules.html#add_rule_source