init
This commit is contained in:
commit
d2743fe41d
22 changed files with 2290 additions and 0 deletions
136
lib/async.lua
Normal file
136
lib/async.lua
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
-- This source code form is subject to the terms of the MPL-v2 https://mozilla.org/MPL/2.0/ --
|
||||
--* .name Asynchronous callbacks for anything that needs it
|
||||
local C = require("c")
|
||||
local X = require("x")
|
||||
local Conf = require("conf")
|
||||
local ev = require("ev") -- https://github.com/brimworks/lua-ev
|
||||
local Async = {ev = ev}
|
||||
|
||||
--* fd(int fd, int events, function callback) Raw file descriptor callbacks
|
||||
function Async.fd(fd, evs, cb)
|
||||
ev.IO.new(cb, fd, evs):start(ev.Loop.default)
|
||||
end
|
||||
function Async.idle(cb)
|
||||
local id
|
||||
id = ev.Idle.new(function()
|
||||
id:stop(ev.Loop.default)
|
||||
cb()
|
||||
X.sync()
|
||||
end)
|
||||
id:start(ev.Loop.default)
|
||||
end
|
||||
|
||||
--* timer(float after, [float repeat], function callback) Calls **callback** after **after** seconds, and then repeats on a **repeat** second loop if it's provided
|
||||
function Async.timer(after, rep, cb)
|
||||
if not cb then rep, cb = cb, rep end
|
||||
if after <= 0 then after = 0.00000001 end
|
||||
ev.Timer.new(cb, after, rep):start(ev.Loop.default)
|
||||
end
|
||||
|
||||
--* .desc The *spawn** functions return a table with the **pid**, as well as *write*(*str* **data**) and *close*() for stdin
|
||||
local StdinWrapper = {
|
||||
write = function(self, text)
|
||||
C.write(self.stdin, text)
|
||||
return self
|
||||
end,
|
||||
close = function(self)
|
||||
C.close(self.stdin)
|
||||
end
|
||||
}
|
||||
StdinWrapper.__index = StdinWrapper
|
||||
--* table stdin spawn(str program, [str arg, ...], [function callback]) Spawns a program and calls **callback** once it exits. Parameters passed to **callback** are (*str* **stdout**, *str* **stderr**, *int* **exitStatus**)
|
||||
function Async.spawn(...)
|
||||
local args = {...}
|
||||
local cb
|
||||
if #args == 0 then error("Need a program") end
|
||||
if type(args[#args]) == "function" then cb = table.remove(args, #args) end
|
||||
local pid, stdin, stdout, stderr
|
||||
if cb then
|
||||
pid, stdin, stdout, stderr = C.spawn(args)
|
||||
if not pid then return nil, "Failed to spawn" end
|
||||
local childWatcher
|
||||
childWatcher = ev.Child.new(function(loop, child)
|
||||
childWatcher:stop(loop)
|
||||
local out = C.read(stdout)
|
||||
local err = C.read(stderr)
|
||||
C.close(stdin, stdout, stderr)
|
||||
cb(out, err, child:getstatus().exit_status)
|
||||
end, pid, false)
|
||||
childWatcher:start(ev.Loop.default)
|
||||
else
|
||||
pid, stdin, stdout, stderr = C.spawn(args)
|
||||
if not pid then return nil, "Failed to spawn" end
|
||||
end
|
||||
return setmetatable({ pid = pid, stdin = stdin }, StdinWrapper)
|
||||
end
|
||||
--* table stdin spawnShell(str command, [str shell], [function callback]) Runs **command** in a shell instace. The shell is the first of **shell**, **Conf.shell**, **$SHELL**, "/bin/sh". Parameters passed to **callback** are the same as with *spawn*
|
||||
function Async.spawnShell(str, shell, cb)
|
||||
if not cb then cb, shell = shell, cb end
|
||||
return Async.spawn(shell or Conf.shell or os.getenv("SHELL") or "/bin/sh", "-c", str, cb)
|
||||
end
|
||||
--* table stdin spawnRead(str program, [str arg, ...], function dataCallback, [function exitCallback]) Spawns a program and calls **dataCallback** when the program writes to stdout or stderr, then **exitCallback** once it exits. Parameters to **dataCallback** are (*str* **stdout**, *str* **stderr**). Paramaters to **exitCallback** are (*int* **exitStatus**)
|
||||
function Async.spawnRead(...)
|
||||
local args = {...}
|
||||
if #args < 2 then error("Need a program and callback") end
|
||||
local outcb = table.remove(args, #args)
|
||||
if type(outcb) ~= "function" then error("Need a callback") end
|
||||
local exitcb
|
||||
if type(args[#args]) == "function" then
|
||||
if #args < 2 then error("Need a program") end
|
||||
outcb, exitcb = table.remove(args, #args), outcb
|
||||
if type(outcb) ~= "function" then error("Need a callback") end
|
||||
end
|
||||
local pid, stdin, stdout, stderr = C.spawn(args)
|
||||
C.setNonBlocking(stdout)
|
||||
C.setNonBlocking(stderr)
|
||||
local stdoutWatcher = ev.IO.new(function(loop, io, revents)
|
||||
local d = C.read(stdout)
|
||||
print(d, #d)
|
||||
if d and #d > 0 then
|
||||
outcb(d, nil)
|
||||
end
|
||||
end, stdout, ev.READ)
|
||||
stdoutWatcher:start(ev.Loop.default)
|
||||
local exitWatcher
|
||||
exitWatcher = ev.Child.new(function(loop, child, revents)
|
||||
local out = C.read(stdout)
|
||||
local err = C.read(stderr)
|
||||
C.close(stdout, stderr, stdin)
|
||||
stdoutWatcher:stop(loop)
|
||||
exitWatcher:stop(loop)
|
||||
outcb(out, err)
|
||||
if exitcb then exitcb(child:getstatus().exit_status) end
|
||||
end, pid, false)
|
||||
exitWatcher:start(ev.Loop.default)
|
||||
return setmetatable({pid = pid, stdin = stdin}, StdinWrapper)
|
||||
end
|
||||
--* table stdin spawnReadShell(str command, [str shell], function dataCallback, [function exitCallback]) Combination of *spawnRead* and *spawnShell*
|
||||
function Async.spawnReadShell(str, shell, outcb, exitcb)
|
||||
if type(shell) == "function" then
|
||||
shell, outcb, exitcb = nil, shell, outcb
|
||||
end
|
||||
return Async.spawnRead(shell or Conf.shell or os.getenv("SHELL") or "/bin/sh", "-c", str, outcb, exitcb)
|
||||
end
|
||||
function Async.readFile(path, cb)
|
||||
local stdout = C.getFD(assert(io.open(path, "r")))
|
||||
C.setNonBlocking(stdout)
|
||||
local chunks = {}
|
||||
local stdoutWatcher
|
||||
stdoutWatcher = ev.IO.new(function()
|
||||
local c = C.read(stdout)
|
||||
if c and #c > 0 then
|
||||
table.insert(chunks, c)
|
||||
else
|
||||
C.close(stdout)
|
||||
stdoutWatcher:stop(ev.Loop.default)
|
||||
cb(table.concat(chunks))
|
||||
end
|
||||
end, stdout, ev.READ)
|
||||
stdoutWatcher:start(ev.Loop.default)
|
||||
end
|
||||
|
||||
function Async.loop()
|
||||
ev.Loop.default:loop()
|
||||
end
|
||||
|
||||
return Async
|
||||
46
lib/conf.lua
Normal file
46
lib/conf.lua
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
-- This source code form is subject to the terms of the MPL-v2 https://mozilla.org/MPL/2.0/ --
|
||||
-- (re)loads the config file and stores settings
|
||||
local X = require("x")
|
||||
local Conf = {}
|
||||
|
||||
function Conf.onKeyPress(key, mod, cb)
|
||||
if type(key) ~= "string" or type(mod) ~= "number" or type(cb) ~= "function" then
|
||||
error("Parameters are wrong")
|
||||
end
|
||||
local n, err = X.grabKey(key, mod)
|
||||
assert(not err, err)
|
||||
local id = key..mod
|
||||
if Conf.keyPressHandlers[id] then
|
||||
error(string.format("Key %s with mod %i already bound", key, mod))
|
||||
end
|
||||
Conf.keyPressHandlers[id] = cb
|
||||
end
|
||||
|
||||
function Conf.on(t, cb)
|
||||
Conf.eventHandlers[t] = cb
|
||||
end
|
||||
|
||||
function Conf.init(path)
|
||||
Conf.keyPressHandlers = {}
|
||||
Conf.keyReleaseHandlers = {}
|
||||
Conf.eventHandlers = {}
|
||||
path = path or Conf.cmdArgs.config or (os.getenv("XDG_CONFIG_HOME") or (os.getenv("HOME").."/.config")).."/thornWM/config.lua"
|
||||
local func = loadfile(path)
|
||||
if func then
|
||||
local s, err = pcall(func)
|
||||
if s then
|
||||
return
|
||||
end
|
||||
print(string.format("Config %s failed:%s", path, err))
|
||||
os.exit(1)
|
||||
else
|
||||
print(string.format("Config %s not found", path))
|
||||
end
|
||||
local s, err = pcall((os.getenv("XDG_CONFIG_DIRS") or "/etc/xdg") .. "thornWM/config.lua")
|
||||
if not s then
|
||||
print(string.format("Config %s failed:%s", path, err))
|
||||
os.exit(1)
|
||||
end
|
||||
end
|
||||
|
||||
return Conf
|
||||
40
lib/event.lua
Normal file
40
lib/event.lua
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
-- This source code form is subject to the terms of the MPL-v2 https://mozilla.org/MPL/2.0/ --
|
||||
-- Event handling
|
||||
local X = require("x")
|
||||
local Conf = require("conf")
|
||||
local Tree = require("tree")
|
||||
local Event = {}
|
||||
|
||||
local xEventHandlers = {}
|
||||
xEventHandlers[X.MapRequest] = function(ev)
|
||||
if Tree.windows[ev.id] then return print("got duplicate map request") end
|
||||
local win = Tree.newWindow(ev)
|
||||
local handler = Conf.eventHandlers.newWindow
|
||||
if handler and win then handler(win) end
|
||||
end
|
||||
xEventHandlers[X.DestroyNotify] = function(ev)
|
||||
local win = Tree.windows[ev.id]
|
||||
local handler = Conf.eventHandlers.delWindow
|
||||
if handler and win then handler(win) end
|
||||
if win then
|
||||
win:unmanage()
|
||||
end
|
||||
end
|
||||
xEventHandlers[X.KeyPress] = function(ev)
|
||||
local handler = Conf.keyPressHandlers[ev.key..ev.mask]
|
||||
if handler then handler(ev) end
|
||||
end
|
||||
|
||||
function Event.handleXEvents()
|
||||
while X.pending() > 0 do
|
||||
local ev = X.nextEvent()
|
||||
if xEventHandlers[ev.type] then
|
||||
xEventHandlers[ev.type](ev)
|
||||
end
|
||||
if Conf.eventHandlers[ev.type] then
|
||||
pcall(Conf.eventHandlers[ev.type], ev)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return Event
|
||||
61
lib/ewmh.lua
Normal file
61
lib/ewmh.lua
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
-- This source code form is subject to the terms of the MPL-v2 https://mozilla.org/MPL/2.0/ --
|
||||
--* Handles Extended Window Manager Hint (https://specifications.freedesktop.org/wm-spec/latest/) stuff that doesn't really fit in Tree
|
||||
local Tree
|
||||
local X = require("x")
|
||||
local EWMH = {}
|
||||
|
||||
local pid = tonumber(io.open("/proc/self/stat"):read(8):match("%d+"))
|
||||
|
||||
--* init(str name) Sets up properties to the root and support check windows
|
||||
function EWMH.init(name)
|
||||
Tree = require("tree")
|
||||
local supported = {
|
||||
X.getAtoms(
|
||||
"_NET_SUPPORTED", "_NET_SUPPORTING_WM_CHECK", "_NET_WM_NAME", "_NET_WM_PID", "_NET_WM_WINDOW_TYPE",
|
||||
"_NET_WM_WINDOW_TYPE_DESKTOP", "_NET_WM_WINDOW_TYPE_DOCK", "_NET_WM_WINDOW_TYPE_TOOLBAR", "_NET_WM_WINDOW_TYPE_MENU",
|
||||
"_NET_WM_WINDOW_TYPE_UTILITY", "_NET_WM_WINDOW_TYPE_SPLASH", "_NET_WM_WINDOW_TYPE_DIALOG", "_NET_WM_WINDOW_TYPE_NORMAL"
|
||||
)
|
||||
}
|
||||
local supportWin = X.createWindow()
|
||||
X.setProperty(supportWin, X.getAtoms("WM_CLASS"), X.getAtoms("STRING"), 8, X.PropModeReplace, { name, name })
|
||||
X.setProperty(supportWin, X.getAtoms("_NET_WM_NAME"), X.getAtoms("UTF8_STRING"), 8, X.PropModeReplace, name)
|
||||
X.setProperty(supportWin, X.getAtoms("_NET_SUPPORTING_WM_CHECK"), X.getAtoms("WINDOW"), 32, X.PropModeReplace, supportWin)
|
||||
X.setProperty(supportWin, X.getAtoms("_NET_WM_PID"), X.getAtoms("CARDINAL"), 32, X.PropModeReplace, pid)
|
||||
X.setProperty(X.root, X.getAtoms("_NET_SUPPORTING_WM_CHECK"), X.getAtoms("WINDOW"), 32, X.PropModeReplace, supportWin)
|
||||
X.setProperty(X.root, X.getAtoms("_NET_SUPPORTED"), X.getAtoms("ATOM"), 32, X.PropModeReplace, supported)
|
||||
end
|
||||
|
||||
--* bool should shouldManage(table win) Returns a boolean on whether the WM should manage the window
|
||||
function EWMH.shouldManage(win)
|
||||
local winTypeAtom = X.getProperty(win.id, X.getAtoms("_NET_WM_WINDOW_TYPE"), 0, X.getAtoms("ATOM"))
|
||||
if not winTypeAtom then return true end
|
||||
local winType = X.getAtomNames(winTypeAtom)
|
||||
if winType == "_NET_WM_WINDOW_TYPE_DOCK" then
|
||||
local attrs = X.getWindowAttributes(win.id)
|
||||
win.winType = "dock"
|
||||
if attrs.y == 0 then
|
||||
-- print(inspect(Tree))
|
||||
local m = Tree.focusedMonitor.margins
|
||||
-- print("HHIHIHII", inspect(m))
|
||||
-- print("m", inspect(attrs))
|
||||
Tree.focusedMonitor.dockMargins = { top = attrs.height, bottom = 0, left = 0, right = 0 }
|
||||
Tree.focusedMonitor:setMargins(Tree.focusedMonitor.margins)
|
||||
end
|
||||
return false
|
||||
elseif winType == "_NET_WM_WINDOW_TYPE_SPLASH" then
|
||||
return false
|
||||
else
|
||||
win.winType = winType or "normal"
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--* bool should shouldTile(table win) Returns a boolean on whether the WM should tile the window by default
|
||||
function EWMH.shouldTile(win)
|
||||
if win.winType == "_NET_WM_WINDOW_TYPE_DIALOG" then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
return EWMH
|
||||
85
lib/tree/init.lua
Normal file
85
lib/tree/init.lua
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
-- This source code form is subject to the terms of the MPL-v2 https://mozilla.org/MPL/2.0/ --
|
||||
--* .name Manages the tree structure that holds all monitors, containers, windows, and tags
|
||||
local X = require("x")
|
||||
local EWMH = require("ewmh")
|
||||
local Conf = require("conf")
|
||||
local Tree = {
|
||||
windows = {},
|
||||
monitors = {},
|
||||
focusedWindow = nil,
|
||||
focusedMonitor = nil,
|
||||
-- tags = {},
|
||||
}
|
||||
|
||||
-- local Tag = require("tree.tag")
|
||||
local Window = require("tree.window")
|
||||
|
||||
function Tree.newWindow(ev)
|
||||
local win = Window.new(ev.id)
|
||||
if EWMH.shouldManage(win) then
|
||||
Tree.windows[win.id] = win
|
||||
win.managed = true
|
||||
if EWMH.shouldTile(win) then
|
||||
table.insert(Tree.focusedMonitor.stack, win)
|
||||
win.monitor = Tree.focusedMonitor
|
||||
-- Tree.focusedMonitor:updateTiling()
|
||||
else
|
||||
win:float()
|
||||
end
|
||||
win:show()
|
||||
else
|
||||
win.managed = false
|
||||
win:show()
|
||||
return
|
||||
end
|
||||
return win
|
||||
end
|
||||
|
||||
----* table tag Tree.newTag(any id, [table preset]) Creates a new tag that can then be added to tables. New tags are also initialised automatically with *Window*:*addTag*()
|
||||
--function Tree.newTag(id, tbl)
|
||||
-- if Tree.tags[id] then return Tree.tags[id] end
|
||||
-- local tag = Tag.new(tbl)
|
||||
-- tag.id = id
|
||||
-- Tree.tags[id] = tag
|
||||
-- return tag
|
||||
--end
|
||||
--* Tree.init() Sets up the tree
|
||||
function Tree.init()
|
||||
Window.init()
|
||||
for m,monitor in pairs(X.getMonitors()) do
|
||||
Tree.monitors[m] = monitor
|
||||
monitor.number = m
|
||||
monitor.stack = {}
|
||||
monitor.dockMargins = { top = 0, bottom = 0, left = 0, right = 0 }
|
||||
monitor.margins = { top = 5, bottom = 5, left = 5, right = 5 }
|
||||
function monitor:updateTiling()
|
||||
if self.tiler then
|
||||
self.tiler:tile(self.stack, self)
|
||||
end
|
||||
end
|
||||
function monitor:setMargins(m)
|
||||
print("привет", inspect(m))
|
||||
self.margins.top = m.top + self.dockMargins.top
|
||||
self.margins.bottom = m.bottom + self.dockMargins.bottom
|
||||
self.margins.right = m.right + self.dockMargins.right
|
||||
self.margins.left = m.left + self.dockMargins.left
|
||||
end
|
||||
if Conf.eventHandlers.newMonitor then
|
||||
Conf.eventHandlers.newMonitor(monitor)
|
||||
end
|
||||
end
|
||||
Tree.focusedMonitor = Tree.monitors[1]
|
||||
-- Tree.newTag("focused")
|
||||
end
|
||||
-- function Tree.updateDirty()
|
||||
-- if Tree.dirtyGeometry then
|
||||
-- for w,win in pairs(Tree.windows) do
|
||||
-- if win.dirtyGeometry then
|
||||
-- win:applyGeometry()
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
return Tree
|
||||
|
||||
56
lib/tree/tag.lua
Normal file
56
lib/tree/tag.lua
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
---- TAG --
|
||||
--local Tag = {}
|
||||
--Tag.__index = Tag
|
||||
--function Tag.new(tag)
|
||||
-- tag = tag or {}
|
||||
-- tag.windows = {}
|
||||
-- return setmetatable(tag, Tag)
|
||||
--end
|
||||
----* Tag:setBorderColour(int colour) Sets the tag's window's border colour. **colour** is in the form of 0xRRGGBB
|
||||
--function Tag:setBorderColour(col)
|
||||
-- self.borderColour = col
|
||||
-- for w,win in pairs(self.windows) do win:updateTag(self) end
|
||||
--end
|
||||
--function Tag:setBorderWidth(width)
|
||||
-- self.borderWidth = width
|
||||
-- for w,win in pairs(self.windows) do win:updateTag(self) end
|
||||
--end
|
||||
--function Tag:setMargin(top, right, bottom, left)
|
||||
-- if type(top) == "table" then
|
||||
-- self.margin = top
|
||||
-- else
|
||||
-- self.margin = {top = top, right = right, bottom = bottom, left = left}
|
||||
-- end
|
||||
-- for w,win in pairs(self.windows) do win:updateTag(self) end
|
||||
--end
|
||||
----* Tag:set(table props) Sets a table worth of properties
|
||||
--function Tag:set(tbl)
|
||||
-- for prop,value in pairs(tbl) do
|
||||
-- if prop == "borderColour" then
|
||||
-- self:setBorderColour(value)
|
||||
-- elseif prop == "borderWidth" then
|
||||
-- self:setBorderWidth(value)
|
||||
-- elseif prop == "visible" then
|
||||
-- if value then self:show() else self:hide() end
|
||||
-- elseif prop == "margin" then
|
||||
-- self:setMargin(value)
|
||||
-- end
|
||||
-- end
|
||||
--end
|
||||
----* Tag:show() Unhides windows from the screen
|
||||
--function Tag:show()
|
||||
-- self.visible = true
|
||||
-- for w,win in pairs(self.windows) do
|
||||
-- win:updateTag(self)
|
||||
-- end
|
||||
--end
|
||||
----* Tag:hide() Hides windows from the screen
|
||||
--function Tag:hide()
|
||||
-- self.visible = false
|
||||
-- for w,win in pairs(self.windows) do
|
||||
-- win:updateTag(self)
|
||||
-- end
|
||||
--end
|
||||
|
||||
--return Tag
|
||||
|
||||
91
lib/tree/tilers.lua
Normal file
91
lib/tree/tilers.lua
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
local Tilers = {}
|
||||
local inspect = require("inspect")
|
||||
|
||||
local dirs = {
|
||||
left = function(m, w, h, stack, r, gap)
|
||||
local masterBase = {
|
||||
w = w * r - gap/2,
|
||||
h = h,
|
||||
x = m.left,
|
||||
y = m.top
|
||||
}
|
||||
local childBase = {
|
||||
w = (w * (1-r)) - gap/2,
|
||||
h = (masterBase.h / (#stack-1)) - gap + gap / (#stack-1),
|
||||
x = masterBase.x + masterBase.w + gap,
|
||||
y = m.top
|
||||
}
|
||||
local childOffset = {
|
||||
x = 0, y = childBase.h + gap
|
||||
}
|
||||
return masterBase, childBase, childOffset
|
||||
end,
|
||||
top = function(m, w, h, stack, r, gap)
|
||||
local masterBase = {
|
||||
w = w,
|
||||
h = h * r - gap,
|
||||
x = m.left,
|
||||
y = m.top
|
||||
}
|
||||
local childBase = {
|
||||
w = (masterBase.w / (#stack-1)) - gap + gap / (#stack-1),
|
||||
h = (h * (1-r)) - gap,
|
||||
x = m.left,
|
||||
y = masterBase.y + masterBase.h + gap,
|
||||
}
|
||||
local childOffset = {
|
||||
x = childBase.w + gap, y = 0
|
||||
}
|
||||
return masterBase, childBase, childOffset
|
||||
end
|
||||
}
|
||||
dirs.right = function(...)
|
||||
local masterBase, childBase, childOffset = dirs.left(...)
|
||||
masterBase.x, childBase.x = childBase.x, masterBase.x
|
||||
masterBase.w, childBase.w = childBase.w, masterBase.w
|
||||
return masterBase, childBase, childOffset
|
||||
end
|
||||
dirs.bottom = function(...)
|
||||
local masterBase, childBase, childOffset = dirs.top(...)
|
||||
masterBase.y, childBase.y = childBase.y, masterBase.y
|
||||
masterBase.h, childBase.h = childBase.h, masterBase.h
|
||||
return masterBase, childBase, childOffset
|
||||
end
|
||||
|
||||
local function makeTiler(getOff)
|
||||
return {
|
||||
r = 0.5,
|
||||
tile = function(self, stack, monitor)
|
||||
local m = monitor.margins
|
||||
local w = monitor.width - m.left - m.right
|
||||
local h = monitor.height - m.top - m.bottom
|
||||
if #stack == 1 then
|
||||
stack[1]:setGeometry(m.left, m.top, w, h)
|
||||
elseif #stack >= 2 then
|
||||
local masterBase, childBase, childOffset = getOff(m, w, h, stack, self.r, self.gap)
|
||||
stack[1]:setGeometry(masterBase.x, masterBase.y, masterBase.w, masterBase.h)
|
||||
for i=2,#stack do
|
||||
stack[i]:setGeometry(childBase.x, childBase.y, childBase.w, childBase.h)
|
||||
childBase.x = childBase.x + childOffset.x
|
||||
childBase.y = childBase.y + childOffset.y
|
||||
end
|
||||
end
|
||||
end,
|
||||
setGap = function(self, gap)
|
||||
assert(type(gap) == "number")
|
||||
self.gap = gap
|
||||
end,
|
||||
gap = 0
|
||||
}
|
||||
end
|
||||
|
||||
Tilers = {
|
||||
masterTop = makeTiler(dirs.top),
|
||||
masterLeft = makeTiler(dirs.left),
|
||||
masterRight = makeTiler(dirs.right),
|
||||
masterBottom = makeTiler(dirs.bottom),
|
||||
}
|
||||
|
||||
return Tilers
|
||||
|
||||
|
||||
122
lib/tree/window.lua
Normal file
122
lib/tree/window.lua
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
local Tree
|
||||
local X = require("x")
|
||||
local Util = require("util")
|
||||
local Async = require("async")
|
||||
local Tag = require("tree.tag")
|
||||
|
||||
local function round(n)
|
||||
return math.floor(n+0.5)
|
||||
end
|
||||
|
||||
local Window = {
|
||||
x = 0, y = 0,
|
||||
w = 0, h = 0,
|
||||
bx = 0, by = 0,
|
||||
bw = 0, bh = 0,
|
||||
margin = {top = 0, right = 0, bottom = 0, left = 0},
|
||||
borderWidth = 0
|
||||
}
|
||||
Window.__index = Window
|
||||
function Window.new(id)
|
||||
local window = {
|
||||
id = id,
|
||||
monitor = Tree.focusedMonitor,
|
||||
-- tags = {
|
||||
-- main = Tag.new()
|
||||
-- }
|
||||
}
|
||||
setmetatable(window, Window)
|
||||
return window
|
||||
end
|
||||
function Window:getClass()
|
||||
if self.class == nil then
|
||||
local c1, c2 = X.getProperty(self.id, X.getAtoms("WM_CLASS"), 0, X.AnyPropertyType)
|
||||
if c1 then
|
||||
self.class = {c1, c2}
|
||||
else
|
||||
self.class = false
|
||||
end
|
||||
end
|
||||
return self.class
|
||||
end
|
||||
function Window:getName()
|
||||
self.name = X.getProperty(self.id, X.getAtoms("_NET_WM_NAME"), 0, X.getAtoms("UTF8_STRING"))
|
||||
if not self.name then
|
||||
self.name = X.getProperty(self.id, X.getAtoms("WM_NAME"), 0, X.AnyPropertyType)
|
||||
end
|
||||
return self.name
|
||||
end
|
||||
function Window:float()
|
||||
end
|
||||
function Window:setGeometry(x, y, w, h)
|
||||
if self.x ~= x or self.y ~= y or self.width ~= w or self.h ~= h then
|
||||
self.x, self.y, self.width, self.height = x, y, w, h
|
||||
self:applyGeometry()
|
||||
end
|
||||
-- self.monitor.dirtyGeometry = true
|
||||
-- self.dirtyGeometry = true
|
||||
-- Tree.dirtyGeometry = true
|
||||
-- self.dirtyGeometry = true
|
||||
-- print("set")
|
||||
-- Async.idle(function()
|
||||
-- if self.dirtyGeometry then
|
||||
-- self:applyGeometry()
|
||||
-- end
|
||||
-- print("ran")
|
||||
-- self.dirtyGeometry = false
|
||||
-- end)
|
||||
end
|
||||
function Window:applyGeometry()
|
||||
X.setWindowGeometry(self.id, self.x, self.y, self.width, self.height)
|
||||
end
|
||||
function Window:show()
|
||||
self.visible = true
|
||||
X.mapWindow(self.id)
|
||||
Tree.focusedMonitor:updateTiling()
|
||||
end
|
||||
function Window:hide()
|
||||
self.visible = false
|
||||
X.unmapWindow(self.id)
|
||||
Tree.focusedMonitor:updateTiling()
|
||||
end
|
||||
--* Window:focus() Moves keyboard focus to window, appends it to the focus history (**Tree**.**focusHistory**), and adds the "focused" tag
|
||||
function Window:unmanage()
|
||||
local i = self:getIndex()
|
||||
table.remove(self.monitor.stack, i)
|
||||
if self.focused then
|
||||
Tree.focusedWindow = nil
|
||||
self.focused = nil
|
||||
end
|
||||
Tree.windows[self.id] = nil
|
||||
Tree.focusedMonitor:updateTiling()
|
||||
end
|
||||
function Window:getIndex()
|
||||
return Util.indexOf(self.monitor.stack, self)
|
||||
end
|
||||
function Window:getNext(d)
|
||||
d = d or 1
|
||||
local stack = Tree.focusedMonitor.stack
|
||||
local w = self:getIndex()
|
||||
local i = (((w-1) + d) % #stack) + 1
|
||||
return stack[i]
|
||||
end
|
||||
function Window:focus()
|
||||
self.focused = true
|
||||
if Tree.focusedWindow then
|
||||
Tree.focusedWindow.focused = false
|
||||
end
|
||||
Tree.focusedWindow = self
|
||||
Tree.focusedContainer = self.parent
|
||||
Tree.focusedMonitor.focusedContainer = self.parent
|
||||
X.setInputFocus(self.id)
|
||||
end
|
||||
function Window:getStackIndex()
|
||||
return Util.indexOf(self, self.monitor.stack)
|
||||
end
|
||||
|
||||
function Window.init()
|
||||
Tree = require("tree")
|
||||
end
|
||||
|
||||
return Window
|
||||
|
||||
17
lib/util.lua
Normal file
17
lib/util.lua
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
local Util = {}
|
||||
|
||||
function Util.indexOf(haystack, needle)
|
||||
for b,blade in pairs(haystack) do
|
||||
if blade == needle then return b end
|
||||
end
|
||||
end
|
||||
|
||||
function Util.getNext(stack, item, d)
|
||||
local w = Util.indexOf(stack, item)
|
||||
if not w then return nil end
|
||||
local i = (((w-1) + d) % #stack) + 1
|
||||
return stack[i]
|
||||
end
|
||||
|
||||
return Util
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue