From d97eff6ffaae7334374f317f6e1c2f55a8d6ba41 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Mon, 5 May 2014 22:36:11 -0400 Subject: [PATCH] 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... --- README.md | 61 +++++++++++++++++++++++++++++++++++++++++++++-- extra/legacy.lua | 31 ++++++++++++++++++++++++ extra/request.lua | 21 +++++++++------- init.lua | 22 +++++++++-------- 4 files changed, 115 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 286a05f..54b9f52 100644 --- a/README.md +++ b/README.md @@ -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 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. 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 @@ -277,7 +277,7 @@ Then edit this section to fit your needs. | 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 | | **group_children** | Add dialogs to the same tags as their parent client | boolean | | **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 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? Tyrannical is licensed under the [2 clause BSD](http://opensource.org/licenses/BSD-2-Clause) diff --git a/extra/legacy.lua b/extra/legacy.lua index eefec80..2c71899 100644 --- a/extra/legacy.lua +++ b/extra/legacy.lua @@ -27,4 +27,35 @@ awful.tag.swap = function(tag1,tag2) awful.tag.move(idx1,tag2) awful.tag.setscreen(tag1,scr2) awful.tag.move(idx2,tag1) +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 \ No newline at end of file diff --git a/extra/request.lua b/extra/request.lua index acd2e1d..20adefc 100644 --- a/extra/request.lua +++ b/extra/request.lua @@ -4,17 +4,22 @@ local tyrannical = nil -- Use Tyrannical policies instead of the default ones 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 tyrannical = require("tyrannical") end - local sel_tags = nil --TODO check if the current tag prevent _out stealing - local tags = c:tags() --- for k,t in ipairs(tags) do - --TODO check if one of them is selected --- end - capi.client.focus = c - c:raise() + -- Always grant those request as it probably mean that it is a modal dialog + if c.transient_for and capi.client.focus == c.transient_for then + capi.client.focus = c + 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) diff --git a/init.lua b/init.lua index 5fb74b2..2afc2fb 100755 --- a/init.lua +++ b/init.lua @@ -6,6 +6,8 @@ local awful = require("awful") local capi = {client = client , tag = tag , awesome = awesome, screen = screen , mouse = mouse } +-- Patch Awesome < 3.5.3 +require("tyrannical.extra.legacy") -------------------------------INIT------------------------------ @@ -14,7 +16,7 @@ local signals,module,class_client,tags_hash,settings,sn_callback,fallbacks,prop "locked" , "class" , "spawn" , "position" , "force_screen" , "max_clients" , "exec_once" , "clone_on" , "clone_of" , "no_focus_stealing", "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 capi.tag.add_signal("property::"..sig) @@ -76,18 +78,19 @@ end --Check all focus policies then change focus (Awesome 3.5.3+) function module.focus_client(c,properties) 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 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 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(c:tags()[1],"no_focus_stealing_in")) then awful.tag.viewonly(c:tags()[1]) end capi.client.focus = c + c:raise() end end --Apply all properties local function apply_properties(c,override,normal) 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 if override[k] ~= nil then props[k] = override[k] else props[k] = normal[k] end c[k] = props[k] @@ -104,8 +107,10 @@ local function apply_properties(c,override,normal) if props.slave == true or props.master == true then awful.client["set"..(props.slave and "slave" or "master")](c, true) 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 - 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) if tag then --Can be false if there is no tags ret = c:tags({tag}) @@ -118,7 +123,7 @@ end local function match_client(c, startup) if not c then return end 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 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,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 - 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 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] @@ -221,9 +226,6 @@ awful.tag.add,awful.tag._setscreen,awful.tag._viewonly = function(tag,props) return t end,awful.tag.setscreen,awful.tag.viewonly --- Patch Awesome < 3.5.3 -require("tyrannical.extra.legacy") - awful.tag.viewonly = function(t) if not t then return end if not awful.tag.getscreen(t) then awful.tag.setscreen(capi.mouse.screen) end