Shifty

From awesome
Jump to: navigation, search

Introduction[edit]

Shifty is an Awesome 3 extension that implements dynamic tagging.


It also implements a client matching configuration that simplifies tag-client matching.

Here are a few ways of how shifty makes awesome awesomer:

  • On-the-fly tag creation and disposal
  • Advanced client matching
  • Easy moving of clients between tags
  • Tag add/rename prompt in taglist completion
  • Reordering tags and configurable positioning
  • Tag name guessing, automagic no-config client grouping
  • Customizable keybindings per client and tag
  • Simple yet powerful configuration

Note: Shifty is Awesome and served us well. Awesome now support dynamic tags by default (since mid-3.4 cycle). If you use Shifty only for dynamic tags, you can use awful.tag.add() and awful.tag.remove() directly. If you use only basic Shifty configuration system, then Tyrannicalis a newer (and faster) configuration system mostly compatible with Shifty but without all the more advanced features.

Videos[edit]

To see an early version of Shifty in action check: http://garoth.com/awesome/shifty.ogv

TODO Get somebody to do some video...

Installation[edit]

This absolute simplest way to get started is using the example.rc.lua included with shifty.

  1. Go to your configuration directory (usually ~/.config/awesome).
  2. Clone repository
   # for 3.4:
   git clone git://github.com/masterkorp/awesome-shifty.git
   # for 3.5
   git clone git://github.com/cdump/awesome-shifty
  1. Move the example rc.lua file into your configuration directory.
   cp shifty/example.rc.lua rc.lua

WARNING: If you already have a custom rc.lua, move it first.

  1. Restart awesome and enjoy.

Configuration[edit]

You can (and should) look in the example rc.lua to see how all this flows together. The example is the meant to be the simplest implementation that follows the default rc.lua. While its not absolutely necessary to start from the example configuration, it may be easier start with for the new user.

The author highly recommends starting with the example rc.lua. It will really save you some time and heartache.

Tags[edit]

Predefined tags are configured through the shifty.config.tags variable. Note that this does not imply the tags will be created at startup. These tags may not exist until a client that 'needs' them is spawned.

shifty.config.tags = {
    ["1:sys"] = {
        init     = true, -- Create this tag at startup and be persistent.
        position = 1,
        screen   = 1,   -- Only create this tag on screen 1.
        mwfact   = 0.60 -- Sets the master window size factor to 0.60 (60%)
                        -- in the tag.
    },
    ["2:term"] = {
        persist  = true,  -- Don't delete after last client closes.
        position = 2,
    },
    ["3:www"] = {
        position    = 3,
        spawn       = "firefox" -- Open firefox when created.
        exclusive   = true, -- Only clients matched from config.apps will
                            -- be allowed here.
        max_clients = 1,    -- If more than one client is started, then a
                            -- new tag is made.
    },
    ["ardour"] = {
        nopopup     = true, -- Prevents focusing on creation.
        leave_kills = true, -- Don't destroy after closing the last client
                            -- until switching another tag.
    },
    ["p2p"] = {
        icon_only = true,               -- only show the icon
        icon      = "~/.icons/p2p.png", -- path to icon for taglist
    },
    ["gimp"] = {
        layout = "tile",
        mwfact = 0.18,
        icon   = "~/.icons/gimp.png", -- Display an icon, but will also
                                        -- have text.
    },
    ["fs"] = {
        rel_index = 1, -- always open next to the current tag and not at the
                        -- end. (0 would create it before current tag)
    },
}
  • screen

TIP: screen = math.max(screen.count(), 2) will assign a tag to the second screen but only when it is attached.

  • position

causes tag sys to always be inserted as 1st in the list, term as 2nd and www as 3rd.

  • nopopup
    • Do not focus on creation.

Valid keys for config.tags and config.defaults:

   layout        = func   -- a layout from awful.layout.suit (e.g. awful.layout.suit.tile)
   mwfact        = float  -- how big the master window is
   nmaster       = int    -- how many columns for master windows
   ncol          = int    -- how many columns for non-master windows
   exclusive     = bool   -- if true, only clients from rules (config.apps) allowed in this tag
   persist       = bool   -- persist after no apps are in it
   nopopup       = bool   -- do not focus on creation
   leave_kills   = bool   -- if true, tag won't be deleted until unselected
   position      = int    -- determines position in taglist (then what does index do?)
   icon          = string -- image file for icon
   icon_only     = bool   -- if true, no text (just icon)
   init          = bool   -- if true, create on startup (implies persist)
   sweep_delay   = int    -- ???
   keys          = {}     -- a table of keys, which are associated with the tag
   overload_keys = {}     -- ???
   index         = int    -- ???
   rel_index     = int    -- ???
   run           = func   -- a lua function which is execute on tag creation
   spawn         = string -- shell command which is execute on tag creation (ex. a programm)
   screen        = int    -- which screen to spawn on (see above)
   max_clients   = int    -- if more than this many clients are started, then a new tag is made

Notes init implies persist

Persistent tags are never deleted automatically (they can be deleted by shifty.delete) and tags with leave_kills set will not be deleted until the tag is unselected.

Running shifty.init() after config variables are set creates tags configured with init flag.

The run key defines a function that will be run on tag creation.

Applications[edit]

Example application configuration table:

shifty.config.apps = {
    {
        match  = {"htop", "Wicd", "jackctl"},
        tag    = "1:sys",
        screen = 1,
    },
    {
        match = {"Iceweasel.*", "Firefox.*"},
        tag   = "3:www",
    },
    {
        match  = {"urxvt"},
        tag    = "2:term",
        screen = 1,
    },
    {
        match   = {"Ardour.*", "Jamin"},
        tag     = "ardour",
        nopopup = true,
    },
    {
        match = {"Gimp","Ufraw"},
        tag = {"graph", "gimp"},  -- Both tags will be applied.
    },
    {
        match = {"gimp%-image%-window"},
        slave = true,                    -- Client is started as a slave.
    },
    {
        match = {"gcolor2", "xmag", "MPlayer"},
        float = true,
        intrusive = true,   -- Disregard a tag's exclusive property.
    },
    {
        match = {"gcolor2"},
        geometry = {100, 100, nil, nil},
    },
    {
        match = {""},   -- Matches all clients to provide button behaviors.
        buttons = {
            button({}, 1, function (c) client.focus = c; c:raise() end),
            button({modkey}, 1, function (c) awful.mouse.client.move() end),
            button({modkey}, 3, awful.mouse.client.resize),
        },
    },
}

Valid keys for config.apps:

   above          = bool
   below          = bool
   border_width   = int
   buttons        = {}
   dockable       = bool
   float          = bool
   fullscreen     = bool
   geometry       = {x, y, w, h}
   hidden         = bool
   honorsizehints = bool
   intrusive      = bool
   keys           = {}
   kill           = bool
   minimized      = bool
   nofocus        = bool
   nopopup        = bool
   ontop          = bool
   opacity        = float
   props          = {}
   run            = func
   screen         = int
   skip_taskbar   = bool
   slave          = bool
   startup        = bool
   sticky         = bool
   struts         = {}
   tag            = string
   titlebar       = bool
   urgent         = bool
   wfact          = float

Client tag matching[edit]

Its possible to match for specific attributes like class, instance, name, role and type. Example:

  {
     match  = {
       class= "URxvt", 
       name="root. "   --Notice the dot         
       },
     tag    = "5:admin",
     screen = 1,
   },

This will keep all urxvt windows that are running as root on the 5th tag.

If you dont specify any type all options are searched:

  {
     match = {
       class = {
          "Do",
       },
       "clock%-applet",
     },
     float = true,
     intrusive = true,
  },

The pattern is lua pattern matching. To check the proprieties use xprop and click in the desired window.

Notes:

In tag field of config.apps you can even provide tag names that don't have presets in config.tags, they'll be created with the name from config.apps and according to config.defaults (if any).

Take notice of the { match = "" }, ... entry in config.apps - you need that if you want to have client buttons.

The geometry table causes client window to be created at specified coordinates and of a prescribed size. Table format is {x, y, width, height}.

Defaults[edit]

Fallback values used when a preset is not found in the first two configuration tables.

shifty.config.defaults = {
    layout = "max",
    mwfact = 0.5,
    run = function(tag) naughty.notify({text = tag.name}) end,
}

Here new tags that are not preconfigured get 'max' layout, a mwfact of 0.5 and will show a desktop notification.

Keybindings[edit]

Example keybindings. These are the very basics, consider this just a launching point to get started.

In the 'big' table of globalkeys we keep these:

awful.key({}, "XF86Back", awful.tag.viewprev),
awful.key({}, "XF86Forward", awful.tag.viewnext),
awful.key({modkey}, "XF86Back", shifty.shift_prev),
awful.key({modkey}, "XF86Forward", shifty.shift_next),
awful.key({modkey}, "t", function() shifty.add({ rel_index = 1 }) end),
awful.key({modkey, "Control"},
            "t",
            function() shifty.add({ rel_index = 1, nopopup = true }) end
            ),
awful.key({modkey, "Shift"}, "r", shifty.rename),
awful.key({modkey}, "w", shifty.del),

And as a replacement for the stock numerical keybindings:

for i=1,9 do
    globalkeys = awful.util.table.join(
                        globalkeys,
                        awful.key({modkey}, i,
                            function()
                                awful.tag.viewonly(shifty.getpos(i))
                            end))
    globalkeys = awful.util.table.join(
                        globalkeys,
                        awful.key({modkey, "Control"}, i,
                            function ()
                                local t = shifty.getpos(i)
                                t.selected = not t.selected
                            end))
    globalkeys = awful.util.table.join(globalkeys,
                                awful.key({modkey, "Control", "Shift"}, i,
                function ()
                    if client.focus then
                        awful.client.toggletag(shifty.getpos(i))
                    end
                end))
    -- move clients to other tags
    globalkeys = awful.util.table.join(
                    globalkeys,
                    awful.key({modkey, "Shift"}, i,
                        function ()
                            if client.focus then
                                local t = shifty.getpos(i)
                                awful.client.movetotag(t)
                                awful.tag.viewonly(t)
                            end
                        end))
end

NOTE: Shifty operates numerical bindings according to attribute position with a function called getpos()

Function shifty.getpos() is meant to handle mod+i combination by mapping tags' position attribute.

  • tag with position == i exists, switches to it
  • more than one tag with position == i, cycle through all of of them
  • position i tag doesn't exist, create a new tag

Globals[edit]

   config.guess_name = true

If set to true (default) shifty will attempt to guess new tag name from client's class. This has effect only when a client is unmatched and being opened when there's no tags or current tag is solitary or exclusive.

   config.guess_position = true

If set to true (default) shifty will check first character of a tag name for being a number and set tag's position according to that. Providing position explicitly overrides this.

   config.remember_index = true

If set to true (default) shifty will keep track of tag's taglist index and if closed reopen the tag at the same place. Specifying position, index or rel_index overrides this.

   config.layouts = {}

If set (to a table of layout functions), enables setting layouts by short name

   config.clientkeys = {}

Default table of client keys, this is usually just your clientkeys.

   config.globalkeys = nil

Default table of global keys, this is usually just your globalkeys.

   config.prompt_sources = {
       "config_tags",
       "config_apps",
       "existing",
       "history"
   }

Table where the keys are used as strings for prompt completion.

   config.prompt_matchers = {"^", ":", ""}

Config for prompt completion matching, a table of strings (symbols)

Advanced[edit]

If you don't want to start from the example.rc.lua then here are some steps that will help you integrate shifty in your existing configuration.

1. Remove:

  • Anything related to tag creation
  • your manage function

2. Require the module

   require('shifty')   -- Near the top of rc.lua

3. Define the shifty.config variables (detailed above)

4. After taglist definitions in rc.lua, add this (note that mytaglist must be a table of taglists, not a single taglist object):

shifty.taglist = mytaglist

5. Add some keybindings to rc.lua.

6. Replace mod+i (numerical) keybindings loop with the example above.

10. Following keytables' (globalkeys, clientkeys) definitions tell shifty what's going on:

root.keys(globalkeys)
shifty.config.globalkeys = globalkeys
shifty.config.clientkeys = clientkeys

11. You made it! Now when you start Awesome there'll be no tags created unless you preconfigured some with init flag. In former case, opening a window that doesn't have a preset will create a new tag named after client's class (or "new" if shifty.config.guess is unset/false).

Support[edit]

Help is best found in this order:

1. Web search, e.g. Google is your friend...

2. #awesome on irc.oftc.net is good for immediate aid, especially with configuration questions and such.

3. Messaging through github

4. Mail the awesome mailing list

5. Mail the author.

Bugs[edit]

Please file all bugs and feature requests using the issue tracker on github.

See also[edit]

tyrannical - An alternative dynamic tagging system, which is less intrusive and make use of the builtin tagging features of awesome 3.