Shifty
| Languages: |
English • Français |
Contents |
Introduction
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
Videos
To see an early version of Shifty in action check: http://garoth.com/awesome/shifty.ogv
TODO Get somebody to do some video...
Installation
This absolute simplest way to get started is using the example.rc.lua included with shifty.
- Go to your configuration directory (usually ~/.config/awesome).
- 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
- 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.
- Restart awesome and enjoy.
Configuration
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
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
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
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
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
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
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
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
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
Please file all bugs and feature requests using the issue tracker on github.
See also
tyrannical - An alternative dynamic tagging system, which is less intrusive and make use of the builtin tagging features of awesome 3.