quake.lua 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. --[[
  2. Licensed under GNU General Public License v2
  3. * (c) 2016, Luca CPZ
  4. * (c) 2015, unknown
  5. --]]
  6. local awful = require("awful")
  7. local capi = { client = client }
  8. local math = math
  9. local string = string
  10. local pairs = pairs
  11. local screen = screen
  12. local setmetatable = setmetatable
  13. -- Quake-like Dropdown application spawn
  14. local quake = {}
  15. -- If you have a rule like "awful.client.setslave" for your terminals,
  16. -- ensure you use an exception for QuakeDD. Otherwise, you may
  17. -- run into problems with focus.
  18. function quake:display()
  19. if self.followtag then self.screen = awful.screen.focused() end
  20. -- First, we locate the client
  21. local client = nil
  22. local i = 0
  23. for c in awful.client.iterate(function (c)
  24. -- c.name may be changed!
  25. return c.instance == self.name
  26. end)
  27. do
  28. i = i + 1
  29. if i == 1 then
  30. client = c
  31. else
  32. -- Additional matching clients, let's remove the sticky bit
  33. -- which may persist between awesome restarts. We don't close
  34. -- them as they may be valuable. They will just turn into
  35. -- normal clients.
  36. c.sticky = false
  37. c.ontop = false
  38. c.above = false
  39. end
  40. end
  41. if not client and not self.visible then return end
  42. if not client then
  43. -- The client does not exist, we spawn it
  44. local cmd = string.format("%s %s %s", self.app,
  45. string.format(self.argname, self.name), self.extra)
  46. awful.spawn(cmd, { tag = self.screen.selected_tag })
  47. return
  48. end
  49. -- Set geometry
  50. client.floating = true
  51. client.border_width = self.border
  52. client.size_hints_honor = false
  53. local maximized = client.maximized
  54. local fullscreen = client.fullscreen
  55. client:geometry(self.geometry[self.screen.index] or self:compute_size())
  56. -- Set not sticky and on top
  57. client.sticky = false
  58. client.ontop = true
  59. client.above = true
  60. client.skip_taskbar = true
  61. -- Additional user settings
  62. if self.settings then self.settings(client) end
  63. -- Toggle display
  64. if self.visible then
  65. client.hidden = false
  66. client.maximized = self.maximized
  67. client.fullscreen = self.fullscreen
  68. client:raise()
  69. self.last_tag = self.screen.selected_tag
  70. client:tags({self.screen.selected_tag})
  71. capi.client.focus = client
  72. else
  73. self.maximized = maximized
  74. self.fullscreen = fullscreen
  75. client.maximized = false
  76. client.fullscreen = false
  77. client.hidden = true
  78. local ctags = client:tags()
  79. for j, _ in pairs(ctags) do
  80. ctags[j] = nil
  81. end
  82. client:tags(ctags)
  83. end
  84. return client
  85. end
  86. function quake:compute_size()
  87. -- skip if we already have a geometry for this screen
  88. if not self.geometry[self.screen.index] then
  89. local geom
  90. if not self.overlap then
  91. geom = screen[self.screen.index].workarea
  92. else
  93. geom = screen[self.screen.index].geometry
  94. end
  95. local width, height = self.width, self.height
  96. if width <= 1 then width = math.floor(geom.width * width) - 2 * self.border end
  97. if height <= 1 then height = math.floor(geom.height * height) end
  98. local x, y
  99. if self.horiz == "left" then x = geom.x
  100. elseif self.horiz == "right" then x = geom.width + geom.x - width
  101. else x = geom.x + (geom.width - width)/2 end
  102. if self.vert == "top" then y = geom.y
  103. elseif self.vert == "bottom" then y = geom.height + geom.y - height
  104. else y = geom.y + (geom.height - height)/2 end
  105. self.geometry[self.screen.index] = { x = x, y = y, width = width, height = height }
  106. end
  107. return self.geometry[self.screen.index]
  108. end
  109. function quake:toggle()
  110. if self.followtag then self.screen = awful.screen.focused() end
  111. local current_tag = self.screen.selected_tag
  112. if current_tag and self.last_tag ~= current_tag and self.visible then
  113. local c=self:display()
  114. if c then
  115. c:move_to_tag(current_tag)
  116. end
  117. else
  118. self.visible = not self.visible
  119. self:display()
  120. end
  121. end
  122. function quake.new(conf)
  123. conf = conf or {}
  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. -- 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. conf.maximized = false
  141. conf.fullscreen = false
  142. local dropdown = setmetatable(conf, { __index = quake })
  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. return dropdown
  154. end
  155. return setmetatable(quake, { __call = function(_, ...) return quake.new(...) end })