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

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