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

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