]> git.madduck.net Git - etc/awesome.git/blob - util/quake.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

pulsebar: corrected colors typo; closes #251
[etc/awesome.git] / util / quake.lua
1
2 --[[
3                                                    
4      Licensed under GNU General Public License v2  
5       * (c) 2016, Luke Bonham                      
6                                                    
7 --]]
8
9 local awful        = require("awful")
10 local capi         = { client = client,
11                        timer  = require("gears.timer") }
12 local math         = { floor  = math.floor }
13 local string       = string
14
15 local pairs        = pairs
16 local screen       = screen
17 local setmetatable = setmetatable
18 local tostring     = tostring
19
20 -- Quake-like Dropdown application spawn
21 -- Original version: https://awesomewm.org/wiki/Drop-down_terminal#Another_solution
22 local quake = {}
23
24 -- If you have a rule like "awful.client.setslave" for your terminals,
25 -- ensure you use an exception for QuakeDD. Otherwise, you may
26 -- run into problems with focus.
27
28 function quake:display()
29    -- First, we locate the client
30    local client = nil
31    local i = 0
32    for c in awful.client.iterate(function (c)
33        -- c.name may be changed!
34        return c.instance == self.name
35    end, nil, self.screen)
36    do
37        i = i + 1
38        if i == 1 then
39            client = c
40        else
41            -- Additional matching clients, let's remove the sticky bit
42            -- which may persist between awesome restarts. We don't close
43            -- them as they may be valuable. They will just turn into
44            -- normal clients.
45            c.sticky = false
46            c.ontop = false
47            c.above = false
48        end
49    end
50
51    if not client and not self.visible then return end
52
53    if not client then
54        -- The client does not exist, we spawn it
55        awful.util.spawn(string.format("%s %s %s", self.app,
56                         string.format(self.argname, self.name), self.extra),
57                         false, self.screen)
58        self.notexist = true
59        return
60    end
61
62    -- Resize
63    awful.client.floating.set(client, true)
64    client.border_width = self.border
65    client.size_hints_honor = false
66    if self.notexist then
67        self:compute_size()
68        client:geometry(self.geometry)
69        self.notexist = false
70    end
71
72    -- Not sticky and on top
73    client.sticky = false
74    client.ontop = true
75    client.above = true
76    client.skip_taskbar = true
77
78    -- Toggle display
79    if self.visible then
80        client.hidden = false
81        client:raise()
82        self.last_tag = awful.tag.selected(self.screen)
83        client:tags({awful.tag.selected(self.screen)})
84        capi.client.focus = client
85    else
86        client.hidden = true
87        local ctags = client:tags()
88        for i, t in pairs(ctags) do
89            ctags[i] = nil
90        end
91        client:tags(ctags)
92    end
93
94    return client
95 end
96
97 function quake:compute_size()
98    local geom = screen[self.screen].workarea
99    local width, height
100    if self.width  <= 1 then width = math.floor(geom.width * self.width) end
101    if self.height <= 1 then height = math.floor(geom.height * self.height) end
102    local x, y
103    if     self.horiz == "left"  then x = geom.x
104    elseif self.horiz == "right" then x = geom.width + geom.x - self.width
105    else   x = geom.x + (geom.width - self.width)/2 end
106    if     self.vert == "top"    then y = geom.y
107    elseif self.vert == "bottom" then y = geom.height + geom.y - self.height
108    else   y = geom.y + (geom.height - self.height)/2 end
109    self.geometry = { x = x, y = y, width = width, height = height }
110 end
111
112 function quake:new(config)
113    local conf = config or {}
114
115    conf.app       = conf.app       or "xterm"    -- application to spawn
116    conf.name      = conf.name      or "QuakeDD"  -- window name
117    conf.argname   = conf.argname   or "-name %s" -- how to specify window name
118    conf.extra     = conf.extra     or ""         -- extra arguments
119    conf.visible   = conf.visible   or false      -- initially not visible
120    conf.border    = conf.border    or 1          -- client border width
121    conf.followtag = conf.followtag or false      -- spawn on currently focused screen
122    conf.screen    = conf.screen    or awful.screen.focused()
123
124    -- If width or height <= 1 this is a proportion of the workspace
125    conf.height       = conf.height       or 0.25     -- height
126    conf.width        = conf.width        or 1        -- width
127    conf.vert         = conf.vert         or "top"    -- top, bottom or center
128    conf.horiz        = conf.horiz        or "center" -- left, right or center
129
130    local console = setmetatable(conf, { __index = quake })
131    capi.client.connect_signal("manage", function(c)
132        if c.instance == console.name and c.screen == console.screen then
133            console:display()
134        end
135    end)
136    capi.client.connect_signal("unmanage", function(c)
137        if c.instance == console.name and c.screen == console.screen then
138            console.visible = false
139        end
140     end)
141
142    -- "Reattach" currently running quake application. This is in case awesome is restarted.
143    local reattach = capi.timer { timeout = 0 }
144    reattach:connect_signal("timeout", function()
145        if self.followtag then
146            self.screen = awful.screen.focused()
147        end
148        reattach:stop()
149        console:display()
150    end)
151    reattach:start()
152
153    return console
154 end
155
156 function quake:toggle()
157     if self.followtag then
158         self.screen = awful.screen.focused()
159     end
160     local current_tag = awful.tag.selected(self.screen)
161     if self.last_tag ~= current_tag and self.visible then
162         awful.client.movetotag(current_tag, self:display())
163     else
164         self.visible = not self.visible
165         self:display()
166     end
167 end
168
169 setmetatable(quake, { __call = function(_, ...) return quake:new(...) end })
170
171 return quake