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

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