]> git.pld-linux.org Git - packages/awesome-plugin-rodentbane.git/blame - rodentbane.lua
- builder script needs %prep
[packages/awesome-plugin-rodentbane.git] / rodentbane.lua
CommitLineData
19813400 1----------------------------------------------------------------------------
2-- @author Lucas de Vries <lucas@tuple-typed.org>
3-- @copyright 2009-2010 Lucas de Vries
4-- Licensed under the WTFPL
5----------------------------------------------------------------------------
6
7-- Load awful
8require("awful")
9
10-- Load beautiful
11require("beautiful")
12
13---- {{{ Grab environment
14local ipairs = ipairs
15local pairs = pairs
16local print = print
17local type = type
18local tonumber = tonumber
19local tostring = tostring
20local unpack = unpack
21local math = math
22local table = table
23local awful = awful
24local os = os
25local io = io
26local string = string
27local awful = awful
28local beautiful = beautiful
29
30-- Grab C API
31local capi =
32{
33 root = root,
34 awesome = awesome,
35 screen = screen,
36 client = client,
37 mouse = mouse,
38 button = button,
39 titlebar = titlebar,
40 widget = widget,
41 hooks = hooks,
42 keygrabber = keygrabber,
43 wibox = wibox,
44 widget = widget,
45}
46
47-- }}}
48
49--- Utilities for controlling the cursor
50module("rodentbane")
51
52-- Local data
53local bindings = {}
54local history = {}
55local current = nil
56local wiboxes = nil
57
58--- Create the wiboxes to display.
59function init()
60 -- Wiboxes table
61 wiboxes = {}
62
63 -- Borders
64 local borders = {"horiz", "vert", "left", "right", "top", "bottom"}
65
66 -- Create wibox for each border
67 for i, border in ipairs(borders) do
68 wiboxes[border] = capi.wibox({
69 position = "floating",
70 bg = beautiful.rodentbane_bg or beautiful.border_focus or "#C50B0B",
71 ontop = true,
72 })
73 end
74end
75
76--- Draw the guidelines on screen using wiboxes.
77-- @param area The area of the screen to draw on, defaults to current area.
78function draw(area)
79 -- Default to current area
80 local ar = area or current
81
82 -- Get numbers
83 local rwidth = beautiful.rodentbane_width or 2
84
85 -- Stop if the area is too small
86 if ar.width < rwidth*3 or ar.height < rwidth*3 then
87 stop()
88 return false
89 end
90
91 -- Put the wiboxes on the correct screen
92 for border, box in pairs(wiboxes) do
93 box.screen = ar.screen
94 end
95
96 -- Horizontal border
97 wiboxes.horiz:geometry({
98 x = ar.x+rwidth,
99 y = ar.y+math.floor(ar.height/2),
100 height = rwidth,
101 width = ar.width-(rwidth*2),
102 })
103
104 -- Vertical border
105 wiboxes.vert:geometry({
106 x = ar.x+math.floor(ar.width/2),
107 y = ar.y+rwidth,
108 width = rwidth,
109 height = ar.height-(rwidth*2),
110 })
111
112 -- Left border
113 wiboxes.left:geometry({
114 x = ar.x,
115 y = ar.y,
116 width = rwidth,
117 height = ar.height,
118 })
119
120 -- Right border
121 wiboxes.right:geometry({
122 x = ar.x+ar.width-rwidth,
123 y = ar.y,
124 width = rwidth,
125 height = ar.height,
126 })
127
128 -- Top border
129 wiboxes.top:geometry({
130 x = ar.x,
131 y = ar.y,
132 height = rwidth,
133 width = ar.width,
134 })
135
136 -- Bottom border
137 wiboxes.bottom:geometry({
138 x = ar.x,
139 y = ar.y+ar.height-rwidth,
140 height = rwidth,
141 width = ar.width,
142 })
143end
144
145--- Cut the navigation area into a direction.
146-- @param dir Direction to cut to {"up", "right", "down", "left"}.
147function cut(dir)
148 -- Store previous area
149 table.insert(history, 1, awful.util.table.join(current))
150
151 -- Cut in a direction
152 if dir == "up" then
153 current.height = math.floor(current.height/2)
154 elseif dir == "down" then
155 current.y = current.y+math.floor(current.height/2)
156 current.height = math.floor(current.height/2)
157 elseif dir == "left" then
158 current.width = math.floor(current.width/2)
159 elseif dir == "right" then
160 current.x = current.x+math.floor(current.width/2)
161 current.width = math.floor(current.width/2)
162 end
163
164 -- Redraw the box
165 draw()
166end
167
168--- Move the navigation area in a direction.
169-- @param dir Direction to move to {"up", "right", "down", "left"}.
170-- @param ratio Ratio of movement, multiplied by the size of the current area,
171-- defaults to 0.5 (ie. half the area size.
172function move(dir, ratio)
173 -- Store previous area
174 table.insert(history, 1, awful.util.table.join(current))
175
176 -- Default to ratio 0.5
177 local rt = ratio or 0.5
178
179 -- Move to a direction
180 if dir == "up" then
181 current.y = current.y-math.floor(current.height*rt)
182 elseif dir == "down" then
183 current.y = current.y+math.floor(current.height*rt)
184 elseif dir == "left" then
185 current.x = current.x-math.floor(current.width*rt)
186 elseif dir == "right" then
187 current.x = current.x+math.floor(current.width*rt)
188 end
189
190 -- Redraw the box
191 draw()
192end
193
194--- Bind a key in rodentbane mode.
195-- @param modkeys Modifier key combination to bind to.
196-- @param key Main key to bind to.
197-- @param func Function to bind the keys to.
198function bind(modkeys, key, func)
199 -- Create binding
200 local bind = {modkeys, key, func}
201
202 -- Add to bindings table
203 table.insert(bindings, bind)
204end
205
206--- Callback function for the keygrabber.
207-- @param modkeys Modkeys that were pressed.
208-- @param key Main key that was pressed.
209-- @param evtype Pressed or released event.
210function keyevent(modkeys, key, evtype)
211 -- Ignore release events and modifier keys
212 if evtype == "release"
213 or key == "Shift_L"
214 or key == "Shift_R"
215 or key == "Control_L"
216 or key == "Control_R"
217 or key == "Super_L"
218 or key == "Super_R"
219 or key == "Hyper_L"
220 or key == "Hyper_R"
221 or key == "Alt_L"
222 or key == "Alt_R"
223 or key == "Meta_L"
224 or key == "Meta_R"
225 then
226 return true
227 end
228
229 -- Special cases for printable characters
230 -- HACK: Maybe we need a keygrabber that gives keycodes ?
231 if key == " " then
232 key = "Space"
233 end
234
235 -- Figure out what to call
236 for ind, bind in ipairs(bindings) do
237 if bind[2]:lower() == key:lower()
238 and table_equals(bind[1], modkeys)
239 then
240 -- Call the function
241 if type(bind[3]) == "table" then
242 -- Allow for easy passing of arguments
243 local func = bind[3][1]
244 local args = {}
245
246 -- Add the rest of the arguments
247 for i, arg in ipairs(bind[3]) do
248 if i > 1 then
249 table.insert(args, arg)
250 end
251 end
252
253 -- Call function with args
254 func(unpack(args))
255 else
256 -- Call function directly
257 bind[3]()
258 end
259
260 -- A bind was found, continue grabbing
261 return true
262 end
263 end
264
265 -- No key was found, stop grabbing
266 stop()
267 return false
268end
269
270--- Check if two tables have the same values.
271-- @param t1 First table to check.
272-- @param t2 Second table to check.
273-- @return True if the tables are equivalent, false otherwise.
274function table_equals(t1, t2)
275 -- Check first table
276 for i, item in ipairs(t1) do
277 if awful.util.table.hasitem(t2, item) == nil then
278 -- An unequal item was found
279 return false
280 end
281 end
282
283 -- Check second table
284 for i, item in ipairs(t2) do
285 if awful.util.table.hasitem(t1, item) == nil then
286 -- An unequal item was found
287 return false
288 end
289 end
290
291 -- All items were equal
292 return true
293end
294
295--- Warp the mouse to the center of the navigation area
296function warp()
297 capi.mouse.coords({
298 x = current.x+(current.width/2),
299 y = current.y+(current.height/2),
300 })
301end
302
303--- Click with a button
304-- @param button Button number to click with, defaults to left (1)
305function click(button)
306 -- Default to left click
307 local b = button or 1
308
309 -- TODO: Figure out a way to use fake_input for clicks
310 --capi.root.fake_input("button_press", button)
311 --capi.root.fake_input("button_release", button)
312
313 -- Use xdotool when available, otherwise try xte
314 command = "xdotool click "..b.." &> /dev/null"
315 .." || xte 'mouseclick "..b.."' &> /dev/null"
316 .." || echo 'W: rodentbane: either xdotool or xte"
317 .." is required to emulate mouse clicks, neither was found.'"
318
319 awful.util.spawn_with_shell(command)
320end
321
322--- Undo a change to the area
323function undo()
324 -- Restore area
325 if #history > 0 then
326 current = history[1]
327 table.remove(history, 1)
328
329 draw()
330 end
331end
332
333--- Convenience function to bind to default keys.
334function binddefault()
335 -- Cut with hjkl
336 bind({}, "h", {cut, "left"})
337 bind({}, "j", {cut, "down"})
338 bind({}, "k", {cut, "up"})
339 bind({}, "l", {cut, "right"})
340
341 -- Move with Shift+hjkl
342 bind({"Shift"}, "h", {move, "left"})
343 bind({"Shift"}, "j", {move, "down"})
344 bind({"Shift"}, "k", {move, "up"})
345 bind({"Shift"}, "l", {move, "right"})
346
347 -- Undo with u
348 bind({}, "u", undo)
349
350 -- Left click with space
351 bind({}, "Space", function ()
352 warp()
353 click()
354 stop()
355 end)
356
357 -- Double Left click with alt+space
358 bind({"Mod1"}, "Space", function ()
359 warp()
360 click()
361 click()
362 stop()
363 end)
364
365 -- Middle click with Control+space
366 bind({"Control"}, "Space", function ()
367 warp()
368 click(2)
369 stop()
370 end)
371
372 -- Right click with shift+space
373 bind({"Shift"}, "Space", function ()
374 warp()
375 click(3)
376 stop()
377 end)
378
379 -- Only warp with return
380 bind({}, "Return", function ()
381 warp()
382 end)
383end
384
385--- Start the navigation sequence.
386-- @param screen Screen to start navigation on, defaults to current screen.
387-- @param recall Whether the previous area should be recalled (defaults to
388-- false).
389function start(screen, recall)
390 -- Default to current screen
391 local scr = screen or capi.mouse.screen
392
393 -- Initialise if not already done
394 if wiboxes == nil then
395 -- Add default bindings if we have none ourselves
396 if #bindings == 0 then
397 binddefault()
398 end
399
400 -- Create the wiboxes
401 init()
402 end
403
404 -- Empty current area if needed
405 if not recall then
406 -- Start with a complete area
407 current = capi.screen[scr].workarea
408
409 -- Empty history
410 history = {}
411 end
412
413 -- Move to the right screen
414 current.screen = scr
415
416 -- Start the keygrabber
417 capi.keygrabber.run(keyevent)
418
419 -- Draw the box
420 draw()
421end
422
423--- Stop the navigation sequence without doing anything.
424function stop()
425 -- Stop the keygrabber
426 capi.keygrabber.stop()
427
428 -- Hide all wiboxes
429 for border, box in pairs(wiboxes) do
430 box.screen = nil
431 end
432end
This page took 0.093837 seconds and 4 git commands to generate.