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

contrib.kbdlayout: asynchronous
[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     -- Toggle display
76     if self.visible then
77         client.hidden = false
78         client:raise()
79         self.last_tag = self.screen.selected_tag
80         client:tags({self.screen.selected_tag})
81         capi.client.focus = client
82    else
83         client.hidden = true
84         local ctags = client:tags()
85         for i, t in pairs(ctags) do
86             ctags[i] = nil
87         end
88         client:tags(ctags)
89     end
90
91     return client
92 end
93
94 function quake:compute_size()
95     -- skip if we already have a geometry for this screen
96     if not self.geometry[self.screen] then
97         local geom
98         if not self.overlap then
99             geom = screen[self.screen].workarea
100         else
101             geom = screen[self.screen].geometry
102         end
103         local width, height = self.width, self.height
104         if width  <= 1 then width = math.floor(geom.width * width) - 2 * self.border end
105         if height <= 1 then height = math.floor(geom.height * height) end
106         local x, y
107         if     self.horiz == "left"  then x = geom.x
108         elseif self.horiz == "right" then x = geom.width + geom.x - width
109         else   x = geom.x + (geom.width - width)/2 end
110         if     self.vert == "top"    then y = geom.y
111         elseif self.vert == "bottom" then y = geom.height + geom.y - height
112         else   y = geom.y + (geom.height - height)/2 end
113         self.geometry[self.screen] = { x = x, y = y, width = width, height = height }
114     end
115     return self.geometry[self.screen]
116 end
117
118 function quake:new(config)
119     local conf = config or {}
120
121     conf.app        = conf.app       or "xterm"    -- application to spawn
122     conf.name       = conf.name      or "QuakeDD"  -- window name
123     conf.argname    = conf.argname   or "-name %s" -- how to specify window name
124     conf.extra      = conf.extra     or ""         -- extra arguments
125     conf.border     = conf.border    or 1          -- client border width
126     conf.visible    = conf.visible   or false      -- initially not visible
127     conf.followtag  = conf.followtag or false      -- spawn on currently focused screen
128     conf.overlap    = conf.overlap   or false      -- overlap wibox
129     conf.screen     = conf.screen    or awful.screen.focused()
130
131     -- If width or height <= 1 this is a proportion of the workspace
132     conf.height     = conf.height    or 0.25       -- height
133     conf.width      = conf.width     or 1          -- width
134     conf.vert       = conf.vert      or "top"      -- top, bottom or center
135     conf.horiz      = conf.horiz     or "left"     -- left, right or center
136     conf.geometry   = {}                           -- internal use
137
138     local dropdown = setmetatable(conf, { __index = quake })
139
140     capi.client.connect_signal("manage", function(c)
141         if c.instance == dropdown.name and c.screen == dropdown.screen then
142             dropdown:display()
143         end
144     end)
145     capi.client.connect_signal("unmanage", function(c)
146         if c.instance == dropdown.name and c.screen == dropdown.screen then
147             dropdown.visible = false
148         end
149      end)
150
151     return dropdown
152 end
153
154 function quake:toggle()
155      if self.followtag then self.screen = awful.screen.focused() end
156      local current_tag = self.screen.selected_tag
157      if current_tag and self.last_tag ~= current_tag and self.visible then
158          self:display():move_to_tag(current_tag)
159      else
160          self.visible = not self.visible
161          self:display()
162      end
163 end
164
165 return setmetatable(quake, { __call = function(_, ...) return quake:new(...) end })