From 66967c0fb20cebde1f752dd35e918de2d4f95a8b Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Wed, 20 Feb 2013 12:11:17 -0500 Subject: [PATCH] First commit --- README | 9 ++ init.lua | 182 +++++++++++++++++++++++++++ sample.rules.lua | 316 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 507 insertions(+) create mode 100644 README create mode 100644 init.lua create mode 100644 sample.rules.lua diff --git a/README b/README new file mode 100644 index 0000000..bbb49ca --- /dev/null +++ b/README @@ -0,0 +1,9 @@ +#Tyranic - A simple rule engine + +Shifty was great and served us well since the early days of the Awesome 3.* series, but just as +many kid aged TV stars, it havn't grown that well. Many of it's, once unique, features are now +supported by the default awful.tag engine, adding legacy complexity to the code base and affecting +performance. + +This is why Tyranic was created. It is a light rule engine offering pretty much the same rule system, +but without all the dynamic tag code. Note that dynamic tagging is now supported directly by awesome. \ No newline at end of file diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..9314fda --- /dev/null +++ b/init.lua @@ -0,0 +1,182 @@ +local setmetatable = setmetatable +local print , pairs = print , pairs +local ipairs , type = ipairs , type +local string = string +local awful = require("awful") + +local capi = {client = client , tag = tag , + screen = screen , mouse = mouse } + +module("tyranic") + +-------------------------------INIT------------------------------ + +for _,sig in ipairs({"property::exclusive","property::init","property::volatile","property::focus_on_new","property::instances","property::match","property::class"})do + capi.tag.add_signal(sig) +end + +-------------------------------DATA------------------------------ + +local class_client,matches_client = {},{} + +--------------------------TYRANIC LOGIC-------------------------- + +--Turn tags -> matches into matches -> tags +local function fill_tyranic(tab_in,tab_out,value) + if tab_in and tab_out then + for i=1,#tab_in do + local low = string.lower(tab_in[i]) + local tmp = tab_out[low] or {tags={},properties={}} + value.instances = value.instances or {} + tmp.tags[#tmp.tags+1] = value + tab_out[low] = tmp + end + end +end + +--Load tags, this cannot be undone +local function load_tags(tyranic_tags) + for k,v in ipairs(tyranic_tags) do + if v.init ~= false then + v.instances = {} + local stype = type(v.screen) + if stype == "table" then + for k2,v2 in pairs(v.screen) do + if v2 <= capi.screen.count() then + v.screen = v2 + v.instances[v2] = awful.tag.add(v.name,v) + end + end + elseif (v.screen or 1) <= capi.screen.count() then + v.instances[v.screen or 1] = awful.tag.add(v.name,v) + end + elseif v.volatile == nil then + v.volatile = true + end + fill_tyranic(v.class,class_client,v) + fill_tyranic(v.match,matches_client,v) + end +end + +--Load property +local function load_property(name,property) + for k2,v2 in pairs(property) do + local key_type = type(k2) + local low = string.lower(key_type == "number" and v2 or k2) + class_client[low] = class_client[low] or {name=low,tags={},properties={}} + class_client[low].properties[name] = key_type == "number" and true or v2 + end +end + +--Match client +local function match_client(c, startup) + if not c then return end + local low = string.lower(c.class or "N/A") + local rules = class_client[low] + if rules then + --Force floating state if necessary + if rules.properties.floating ~= nil then + awful.client.floating.set(c, rules.properties.floating) + end + --Set other properties + for k,v in pairs(rules.properties) do + c[k] = v + end + --Add to the current tag if the client is intrusive, ignore exclusive + if rules.properties.intrusive == true then + local tag = awful.tag.selected(c.screen) + if tag then + c:tags({tag}) + return + end + end + --TODO pre_match + --Add to matches + local tags = {} + for j=1,#(rules.tags or {}) do + local tag_tmp = rules.tags[j] + if not tag_tmp.instances[c.screen or 1] then + local cache = tag_tmp.screen + tag_tmp.screen = tag_tmp.force_screen == true and tag_tmp.screen or c.screen + c.screen = tag_tmp.screen + tag_tmp.instances[c.screen or 1] = awful.tag.add(tag_tmp.name,tag_tmp) + tag_tmp.screen = cache + end + tags[#tags+1] = tag_tmp.instances[c.screen or 1] + end + if #tags > 0 then + c:tags(tags) + if awful.tag.getproperty(tags[1],"focus_on_new") ~= false then + awful.tag.viewonly(tags[1]) + end + return + end + --TODO post_match + end + --Add to the current tag if not exclusive + --TODO + --Last resort, create a new tag + class_client[low] = class_client[low] or {tags={},properties={}} + local tmp,tag = class_client[low],awful.tag.add(c.class,{name=c.class,volatile=true,screen=c.screen}) + tmp.tags[#tmp.tags+1] = {name=c.class,instances = {tag},volatile=true,screen=c.screen} + c:tags({tag}) + if awful.tag.getproperty(tag,"focus_on_new") ~= false then + awful.tag.viewonly(tag) + end +end + +capi.client.connect_signal("manage", match_client) + +capi.client.connect_signal("untagged", function (c, t) + if awful.tag.getproperty(t,"volatile") == true and #t:clients() == 0 then + local rules = class_client[string.lower(c.class or "N/A")] + for j=1,#(rules and rules.tags or {}) do + rules.tags[j].instances[c.screen] = rules.tags[j].instances[c.screen] ~= t and rules.tags[j].instances[c.screen] or nil + end + awful.tag.delete(t) + end +end) + +-- awful.tag.withcurrent = function() end --Disable automatic tag insertion +awful.tag.withcurrent = function(c, startup) + local tags,old_tags = {},c:tags() + --Safety to prevent + for k, t in ipairs(old_tags) do + if awful.tag.getscreen(t) == c.screen then + tags[#tags+1] = t + end + end + --Necessary when dragging clients + if startup == nil and old_tags[1] and old_tags[1].screen ~= c.screen then --nil != false + local sellist = awful.tag.selectedlist(c.screen) + if #sellist > 0 then --Use already selected tag + tags = sellist + else --Select a tag + match_client(c, startup) + end + end + c:tags(tags) +end + +--------------------------OBJECT GEARS--------------------------- +local properties = {} +setmetatable(properties, {__newindex = function(table,k,v) load_property(k,v) end}) + +local function getter (table, key) + if key == "properties" then + return properties + end +end +local function setter (table, key,value) + if key == "tags" then + load_tags(value) + elseif key == "properties" then + properties = value + setmetatable(properties, {__newindex = function(table,k,v) load_property(k,v) end}) + for k,v in pairs(tyranic_properties) do + load_property(k,v) + end + end +end + +setmetatable(_M, { __call = function(_, ...) return end , __index = getter, __newindex = setter}) \ No newline at end of file diff --git a/sample.rules.lua b/sample.rules.lua new file mode 100644 index 0000000..88610ba --- /dev/null +++ b/sample.rules.lua @@ -0,0 +1,316 @@ +local awful = require("awful") + + +-- }}} + +local layouts = +{ + awful.layout.suit.floating, + awful.layout.suit.tile, + awful.layout.suit.tile.left, + awful.layout.suit.tile.bottom, + awful.layout.suit.tile.top, + awful.layout.suit.fair, + awful.layout.suit.fair.horizontal, + awful.layout.suit.spiral, + awful.layout.suit.spiral.dwindle, + awful.layout.suit.max, + awful.layout.suit.max.fullscreen, + awful.layout.suit.magnifier +} + +tags = {} --TODO remove + +tyranic.tags = { + { + name = "Term", + init = true , + exclusive = true , + icon = utils.tools.invertedIconPath("term.png") , + screen = {config.data().scr.pri, config.data().scr.sec} , + layout = awful.layout.suit.tile , + class = { + "xterm" , "urxvt" , "aterm","URxvt","XTerm" + }, + match = { + "konsole" + } + } , + { + name = "Internet", + init = true , + exclusive = true , + icon = utils.tools.invertedIconPath("net.png") , + screen = config.data().scr.pri , + layout = awful.layout.suit.max , + class = { + "Opera" , "Firefox" , "Rekonq" , "Dillo" , "Arora", + "Chromium" , "nightly" , "Nightly" , "minefield" , "Minefield" } + } , + { + name = "Files", + init = true , + exclusive = true , + icon = utils.tools.invertedIconPath("folder.png") , + screen = config.data().scr.pri , + layout = awful.layout.suit.tile , + class = { + "Thunar" , "Konqueror" , "Dolphin" , "ark" , "Nautilus", } + } , + { + name = "Develop", + init = true , + exclusive = true , +-- screen = {config.data().scr.pri, config.data().scr.sec} , + icon = utils.tools.invertedIconPath("bug.png") , + layout = awful.layout.suit.max , + class ={ + "Kate" , "KDevelop" , "Codeblocks", "Code::Blocks" , "DDD", "kate4" } + } , + { + name = "Edit", + init = true , + exclusive = true , +-- screen = {config.data().scr.pri, config.data().scr.sec} , + icon = utils.tools.invertedIconPath("editor.png") , + layout = awful.layout.suit.tile.bottom , + class = { + "KWrite" , "GVim" , "Emacs" , "Code::Blocks" , "DDD" } + } , + { + name = "Media", + init = true , + exclusive = true , + icon = utils.tools.invertedIconPath("media.png") , + layout = awful.layout.suit.max , + class = { + "Xine" , "xine Panel" , "xine*" , "MPlayer" , "GMPlayer", + "XMMS" } + } , + { + name = "Doc", + -- init = true , + exclusive = true , + icon = utils.tools.invertedIconPath("info.png") , +-- screen = config.data().scr.music , + layout = awful.layout.suit.max , + class = { + "Assistant" , "Okular" , "Evince" , "EPDFviewer" , "xpdf", + "Xpdf" , } + } , + + + -----------------VOLATILE TAGS----------------------- + { + name = "Imaging", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("image.png") , + layout = awful.layout.suit.max , + class = {"Inkscape" , "KolourPaint" , "Krita" , "Karbon" , "Karbon14"} + } , + { + name = "Picture", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("image.png") , + layout = awful.layout.suit.max , + class = {"Digikam" , "F-Spot" , "GPicView" , "ShowPhoto" , "KPhotoAlbum"} + } , + { + name = "Video", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("video.png") , + layout = awful.layout.suit.max , + class = {"KDenLive" , "Cinelerra" , "AVIDeMux" , "Kino"} + } , + { + name = "Movie", + init = false , + position = 12 , + exclusive = true , + icon = utils.tools.invertedIconPath("video.png") , + layout = awful.layout.suit.max , + class = {"VLC"} + } , + { + name = "3D", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("3d.png") , + layout = awful.layout.suit.max.fullscreen , + class = {"Blender" , "Maya" , "K-3D" , "KPovModeler" , } + } , + { + name = "Music", + init = false , + position = 10 , + exclusive = true , + screen = config.data().scr.music or config.data().scr.pri , + icon = utils.tools.invertedIconPath("media.png") , + layout = awful.layout.suit.max , + class = {"Amarok" , "SongBird" , "last.fm" ,} + } , + { + name = "Down", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("download.png") , + layout = awful.layout.suit.max , + class = {"Transmission" , "KGet"} + } , + { + name = "Office", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("office.png") , + layout = awful.layout.suit.max , + class = { + "OOWriter" , "OOCalc" , "OOMath" , "OOImpress" , "OOBase" , + "SQLitebrowser" , "Silverun" , "Workbench" , "KWord" , "KSpread" , + "KPres","Basket", "openoffice.org" , "OpenOffice.*" , } + } , + { + name = "RSS", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("rss.png") , + layout = awful.layout.suit.max , + class = {} + } , + { + name = "Chat", + init = false , + position = 10 , + exclusive = true , + screen = config.data().scr.sec or config.data().scr.sec , + icon = utils.tools.invertedIconPath("chat.png") , + layout = awful.layout.suit.tile , + class = {"Pidgin" , "Kopete" ,} + } , + { + name = "Burning", + init = false , + position = 10 , + exclusive = true , + icon = utils.tools.invertedIconPath("burn.png") , + layout = awful.layout.suit.tile , + class = {"k3b"} + } , + { + name = "Mail", + init = false , + position = 10 , + exclusive = true , +-- screen = config.data().scr.sec or config.data().scr.pri , + icon = utils.tools.invertedIconPath("mail2.png") , + layout = awful.layout.suit.max , + class = {"Thunderbird" , "kmail" , "evolution" ,} + } , + { + name = "IRC", + init = false , + position = 10 , + exclusive = true , + screen = config.data().scr.irc or config.data().scr.pri , + init = true , + spawn = "konversation" , + icon = utils.tools.invertedIconPath("irc.png") , + force_screen= true , + layout = awful.layout.suit.fair , + class = {"Konversation" , "Botch" , "WeeChat" , "weechat" , "irssi"} + } , + { + name = "Test", + init = false , + position = 99 , + exclusive = false , + screen = config.data().scr.sec or config.data().scr.pri , + leave_kills = true , + persist = true , + icon = utils.tools.invertedIconPath("tools.png") , + layout = awful.layout.suit.max , + class = {} + } , + { + name = "Config", + init = false , + position = 10 , + exclusive = false , + icon = utils.tools.invertedIconPath("tools.png") , + layout = awful.layout.suit.max , + class = {"Systemsettings", "Kcontrol" , "gconf-editor"} + } , + { + name = "Game", + init = false , + screen = config.data().scr.pri , + position = 10 , + exclusive = false , + icon = utils.tools.invertedIconPath("game.png") , + force_screen= true , + layout = awful.layout.suit.max , + class = {"sauer_client" , "Cube 2$" , "Cube 2: Sauerbraten" ,} + } , + { + name = "Gimp", + init = false , + position = 10 , + exclusive = false , + icon = utils.tools.invertedIconPath("image.png") , + layout = awful.layout.tile , + nmaster = 1 , + incncol = 10 , + ncol = 2 , + mwfact = 0.00 , + class = {} + } , + { + name = "Other", + init = true , + position = 15 , + exclusive = false , + icon = utils.tools.invertedIconPath("term.png") , + max_clients = 5 , + screen = {3, 4, 5} , + layout = awful.layout.suit.tile , + class = {} + } , + { + name = "MediaCenter", + init = true , + position = 15 , + exclusive = false , + icon = utils.tools.invertedIconPath("video.png") , + max_clients = 5 , + screen = config.data().scr.media or config.data().scr.pri , + init = "mythfrontend" , + layout = awful.layout.suit.tile , + class = {"mythfrontend" , "xbmc" ,} + } , + } +tyranic.properties.intrusive = { + "ksnapshot" , "pinentry" , "gtksu" , "kcalc" , "xcalc" , + "feh" , "Gradient editor", "About KDE" , "Paste Special", "Background color", + "kcolorchooser" , "plasmoidviewer" , "plasmaengineexplorer" , "Xephyr" , "kruler" , +} +tyranic.properties.floating = { + "MPlayer" , "pinentry" , "ksnapshot" , "pinentry" , "gtksu" , + "xine" , "feh" , "kmix" , "kcalc" , "xcalc" , + "yakuake" , "Select Color$" , "kruler" , "kcolorchooser", "Paste Special" , + "New Form" , "Insert Picture" , "kcharselect", "mythfrontend" , "plasmoidviewer" +} + +tyranic.properties.ontop = { + "Xephyr" , "ksnapshot" , "kruler" +} + +tyranic.properties.size_hints_honor = { xterm = false, URxvt = false, aterm = false, sauer_client = false, mythfrontend = false}