Add support for spawing with Tyrannical arguments (Finally!!!)

It is now possible to spawn with an array of properties, see the README.md
for more details. It require Awesome 3.5.3+, some patch will get into 3.5.6
to make this even better. I also added the 'new_tag' property to be used
with spawn.

* Also fix issue #37, sorry about that one...
This commit is contained in:
Emmanuel Lepage Vallee 2014-05-05 22:36:11 -04:00
parent c22a1e528c
commit d97eff6ffa
4 changed files with 115 additions and 20 deletions

View File

@ -52,7 +52,7 @@ can also to other value by using the class name as table key:
Tyrannical focus model is very fine tuned. It is possible to add rules on how Tyrannical focus model is very fine tuned. It is possible to add rules on how
the focus will be attributes to clients and tags. the focus will be attributes to clients and tags.
**block_transient_for_focus_stealing:** **block_children_focus_stealing:**
This is a fancy X11 name for something very common: modal dialogs and popups. This is a fancy X11 name for something very common: modal dialogs and popups.
If this is set to `true`, then a dialog wont be able to steal the focus from If this is set to `true`, then a dialog wont be able to steal the focus from
whatever your doing. This is useful for some misbehaving apps such as Firefox whatever your doing. This is useful for some misbehaving apps such as Firefox
@ -277,7 +277,7 @@ Then edit this section to fit your needs.
| Property | Description | Type | | Property | Description | Type |
| -------------------------------------- | --------------------------------------------------- |:----------------:| | -------------------------------------- | --------------------------------------------------- |:----------------:|
| **block_transient_for_focus_stealing** | Prevent popups from stealing focus | boolean | | **block_children_focus_stealing** | Prevent popups from stealing focus | boolean |
| **default_layout** | The default layout for tags | layout | | **default_layout** | The default layout for tags | layout |
| **group_children** | Add dialogs to the same tags as their parent client | boolean | | **group_children** | Add dialogs to the same tags as their parent client | boolean |
| **mwfact** | The default master/slave ratio | float (0-1) | | **mwfact** | The default master/slave ratio | float (0-1) |
@ -320,6 +320,63 @@ name"].name,tyrannical.tags_by_name["your tag name"])```. Tyrannical's purpose
is not to duplicate or change ```awful.tag``` behavior, it is simply a is not to duplicate or change ```awful.tag``` behavior, it is simply a
configuration wrapper. configuration wrapper.
#### Is it possible to directly launch clients in the current tag or a new one?
This feature is mostly available for Awesome 3.5.3+, 3.5.6+ is recommanded.
Tyrannical will use the "startup notification" feild in clients that support it
to track a spawn request. Some applications, such as GVim and XTerm, doesn't
support this. URxvt, Konsole and Gnome terminal does.
Here are some example:
```lua
-- Spawn in a new tag
awful.util.spawn("urxvt",{new_tag=true})
-- Or for more advanced use case, you can use a full tag definition too
awful.util.spawn("urxvt",{ new_tag= {
name = "MyNewTag",
exclusive = true,
})
-- Spawn in the current tag, floating and on top
awful.util.spawn(terminal,{intrusive=true, floating=true, ontop=true})
```
For Awesome 3.5.6+, it is possible to replace the default mod4+r keybinding with
a more powerful one:
```lua
awful.key({ modkey }, "r",
function ()
awful.prompt.run({ prompt = "Run: ", hooks = {
{{ },"Return",function(command)
local result = awful.util.spawn(command)
mypromptbox[mouse.screen].widget:set_text(type(result) == "string" and result or "")
return true
end},
{{"Mod1" },"Return",function(command)
local result = awful.util.spawn(command,{intrusive=true})
mypromptbox[mouse.screen].widget:set_text(type(result) == "string" and result or "")
return true
end},
{{"Shift" },"Return",function(command)
local result = awful.util.spawn(command,{intrusive=true,ontop=true,floating=true})
mypromptbox[mouse.screen].widget:set_text(type(result) == "string" and result or "")
return true
end}
}},
mypromptbox[mouse.screen].widget,nil,
awful.completion.shell,
awful.util.getdir("cache") .. "/history")
end),
```
When using this, instead of pressing `Return` to spawn the application, you can
use `Alt+Return` to launch it as an `intrusive` client. You can add more sections
to support more use case (such as `Shift+Return` to launch as `floating` as shown
above)
#### What is Tyrannical license? #### What is Tyrannical license?
Tyrannical is licensed under the [2 clause BSD](http://opensource.org/licenses/BSD-2-Clause) Tyrannical is licensed under the [2 clause BSD](http://opensource.org/licenses/BSD-2-Clause)

View File

@ -28,3 +28,34 @@ awful.tag.swap = function(tag1,tag2)
awful.tag.setscreen(tag1,scr2) awful.tag.setscreen(tag1,scr2)
awful.tag.move(idx2,tag1) awful.tag.move(idx2,tag1)
end end
-- Check if adding support for sn-based spawn is necessary
if not awful.spawn then
awful.spawn = {snid_buffer={}}
function awful.util.spawn(cmd,sn_rules,callback)
if cmd and cmd ~= "" then
local enable_sn = (sn_rules ~= false or callback) and true or true
if not sn_rules and callback then
sn_rules = {callback=callback}
elseif callback then
sn_rules.callback = callback
end
local pid,snid = capi.awesome.spawn(cmd, enable_sn)
-- The snid will be nil in case of failure
if snid and type(sn_rules) == "table" then
awful.spawn.snid_buffer[snid] = sn_rules
end
return pid,snid
end
-- For consistency
return "Error: No command to execute"
end
local function on_canceled(sn)
awful.spawn.snid_buffer[sn] = nil
end
capi.awesome.connect_signal("spawn::canceled" , on_canceled )
capi.awesome.connect_signal("spawn::timeout" , on_canceled )
else
-- Then if it's there, disable the part we don't want
capi.client.disconnect_signal("manage",awful.spawn.on_snid_callback)
end

View File

@ -4,17 +4,22 @@ local tyrannical = nil
-- Use Tyrannical policies instead of the default ones -- Use Tyrannical policies instead of the default ones
capi.client.disconnect_signal("request::activate",ewmh.activate) capi.client.disconnect_signal("request::activate",ewmh.activate)
capi.client.connect_signal("request::activate",function(c) capi.client.connect_signal("request::activate",function(c,reason)
if not tyrannical then if not tyrannical then
tyrannical = require("tyrannical") tyrannical = require("tyrannical")
end end
local sel_tags = nil --TODO check if the current tag prevent _out stealing -- Always grant those request as it probably mean that it is a modal dialog
local tags = c:tags() if c.transient_for and capi.client.focus == c.transient_for then
-- for k,t in ipairs(tags) do
--TODO check if one of them is selected
-- end
capi.client.focus = c capi.client.focus = c
c:raise() c:raise()
-- If it is not modal, then use the normal code path
elseif reason == "rule" or reason == "ewmh" then
tyrannical.focus_client(c)
-- Tyrannical doesn't have enough information, grant the request
else
capi.client.focus = c
c:raise()
end
end) end)

View File

@ -6,6 +6,8 @@ local awful = require("awful")
local capi = {client = client , tag = tag , awesome = awesome, local capi = {client = client , tag = tag , awesome = awesome,
screen = screen , mouse = mouse } screen = screen , mouse = mouse }
-- Patch Awesome < 3.5.3
require("tyrannical.extra.legacy")
-------------------------------INIT------------------------------ -------------------------------INIT------------------------------
@ -14,7 +16,7 @@ local signals,module,class_client,tags_hash,settings,sn_callback,fallbacks,prop
"locked" , "class" , "spawn" , "position" , "force_screen" , "locked" , "class" , "spawn" , "position" , "force_screen" ,
"max_clients" , "exec_once" , "clone_on" , "clone_of" , "no_focus_stealing", "max_clients" , "exec_once" , "clone_on" , "clone_of" , "no_focus_stealing",
"fallback" , "no_focus_stealing_out","no_focus_stealing_in" "fallback" , "no_focus_stealing_out","no_focus_stealing_in"
},{},{},{},{},{},{},awful.tag.getproperty },{},{},{},{},awful.spawn and awful.spawn.snid_buffer or {},{},awful.tag.getproperty
for _,sig in ipairs(signals) do for _,sig in ipairs(signals) do
capi.tag.add_signal("property::"..sig) capi.tag.add_signal("property::"..sig)
@ -76,18 +78,19 @@ end
--Check all focus policies then change focus (Awesome 3.5.3+) --Check all focus policies then change focus (Awesome 3.5.3+)
function module.focus_client(c,properties) function module.focus_client(c,properties)
local properties = properties or (class_client[string.lower(c.class or "N/A")] or {}).properties or {} local properties = properties or (class_client[string.lower(c.class or "N/A")] or {}).properties or {}
if (((not c.transient_for) or (not settings.block_transient_for_focus_stealing)) and (not properties.no_autofocus)) then if (((not c.transient_for) or (c.transient_for==capi.client.focus) or (not settings.block_children_focus_stealing)) and (not properties.no_autofocus)) then
if not awful.util.table.hasitem(c:tags(), awful.tag.selected(c.screen or 1)) and (not prop(tags[1],"no_focus_stealing_in")) then if not awful.util.table.hasitem(c:tags(), awful.tag.selected(c.screen or 1)) and (not prop(c:tags()[1],"no_focus_stealing_in")) then
awful.tag.viewonly(c:tags()[1]) awful.tag.viewonly(c:tags()[1])
end end
capi.client.focus = c capi.client.focus = c
c:raise()
end end
end end
--Apply all properties --Apply all properties
local function apply_properties(c,override,normal) local function apply_properties(c,override,normal)
local props,ret = awful.util.table.join(normal,override),nil local props,ret = awful.util.table.join(normal,override),nil
--Set all 'c.something' properties --Set all 'c.something' properties, --TODO maybe eventually move to awful.rules.execute
for k,_ in pairs(props) do for k,_ in pairs(props) do
if override[k] ~= nil then props[k] = override[k] else props[k] = normal[k] end if override[k] ~= nil then props[k] = override[k] else props[k] = normal[k] end
c[k] = props[k] c[k] = props[k]
@ -104,8 +107,10 @@ local function apply_properties(c,override,normal)
if props.slave == true or props.master == true then if props.slave == true or props.master == true then
awful.client["set"..(props.slave and "slave" or "master")](c, true) awful.client["set"..(props.slave and "slave" or "master")](c, true)
end end
if props.new_tag then
ret = c:tags({awful.tag.add(type(props.new_tag)=="table" and props.new_tag.name or c.class,type(props.new_tag)=="table" and props.new_tag or {})})
--Add to the current tag if the client is intrusive, ignore exclusive --Add to the current tag if the client is intrusive, ignore exclusive
if props.intrusive == true or (settings.force_odd_as_intrusive and c.type ~= "normal") then elseif props.intrusive == true or (settings.force_odd_as_intrusive and c.type ~= "normal") then
local tag = awful.tag.selected(c.screen) or awful.tag.viewonly(awful.tag.gettags(c.screen)[1]) or awful.tag.selected(c.screen) local tag = awful.tag.selected(c.screen) or awful.tag.viewonly(awful.tag.gettags(c.screen)[1]) or awful.tag.selected(c.screen)
if tag then --Can be false if there is no tags if tag then --Can be false if there is no tags
ret = c:tags({tag}) ret = c:tags({tag})
@ -118,7 +123,7 @@ end
local function match_client(c, startup) local function match_client(c, startup)
if not c then return end if not c then return end
local startup = startup == nil and capi.awesome.startup or startup local startup = startup == nil and capi.awesome.startup or startup
local props = (c.startup_id and sn_callback[c.startup_id]) and sn_callback[c.startup_id](c,startup) or {} local props = c.startup_id and sn_callback[tostring(c.startup_id)] or {}
local low,tags = string.lower(c.class or "N/A"),props.tags or {props.tag} local low,tags = string.lower(c.class or "N/A"),props.tags or {props.tag}
local rules = class_client[low] local rules = class_client[low]
@ -139,7 +144,7 @@ local function match_client(c, startup)
tag.screen = (tag.force_screen ~= true and c_src) or (has_screen and c_src or type(tag.screen)=="table" and tag.screen[1] or tag.screen) tag.screen = (tag.force_screen ~= true and c_src) or (has_screen and c_src or type(tag.screen)=="table" and tag.screen[1] or tag.screen)
tag.screen,match = (tag.screen <= capi.screen.count()) and tag.screen or mouse_s,tag.instances[tag.screen] tag.screen,match = (tag.screen <= capi.screen.count()) and tag.screen or mouse_s,tag.instances[tag.screen]
if (not match and not (fav_scr == true and mouse_s ~= tag.screen)) or (match and (prop(match,"max_clients") or 999) <= #match:clients()) then if (not match and not (fav_scr == true and mouse_s ~= tag.screen)) or (match and (prop(match,"max_clients") or 999) <= #match:clients()) then
awful.tag.add(tag.name,tag) awful.tag.setproperty(awful.tag.add(tag.name,tag),"volatile",match and (prop(match,"max_clients") ~= nil) or tag.volatile)
end end
tags_src[tag.screen],fav_scr = tags_src[tag.screen] or {},fav_scr or (tag.screen == mouse_s) --Reset if a better screen is found tags_src[tag.screen],fav_scr = tags_src[tag.screen] or {},fav_scr or (tag.screen == mouse_s) --Reset if a better screen is found
tags_src[tag.screen][#tags_src[tag.screen]+1] = tag.instances[tag.screen] tags_src[tag.screen][#tags_src[tag.screen]+1] = tag.instances[tag.screen]
@ -221,9 +226,6 @@ awful.tag.add,awful.tag._setscreen,awful.tag._viewonly = function(tag,props)
return t return t
end,awful.tag.setscreen,awful.tag.viewonly end,awful.tag.setscreen,awful.tag.viewonly
-- Patch Awesome < 3.5.3
require("tyrannical.extra.legacy")
awful.tag.viewonly = function(t) awful.tag.viewonly = function(t)
if not t then return end if not t then return end
if not awful.tag.getscreen(t) then awful.tag.setscreen(capi.mouse.screen) end if not awful.tag.getscreen(t) then awful.tag.setscreen(capi.mouse.screen) end