]> 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:

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