|
@ -1,32 +1,93 @@ |
|
|
import x11/xlib, x11/xutil, x11/x, x11/keysym |
|
|
import x11/xlib, x11/xutil, x11/x, x11/keysym |
|
|
|
|
|
import strformat, os, options, tables, random |
|
|
|
|
|
|
|
|
|
|
|
type XDirection = enum left, right |
|
|
|
|
|
type YDirection = enum up, down |
|
|
|
|
|
|
|
|
|
|
|
type Window = ref object of RootObj |
|
|
|
|
|
x : cint |
|
|
|
|
|
y : cint |
|
|
|
|
|
width : cint |
|
|
|
|
|
height : cint |
|
|
|
|
|
speed : cint |
|
|
|
|
|
win : TWindow |
|
|
|
|
|
xDirection : XDirection |
|
|
|
|
|
yDirection : YDirection |
|
|
|
|
|
|
|
|
|
|
|
var root : TWindow |
|
|
|
|
|
var windowState : Table[TWindow, Window] = initTable[TWindow, Window]() |
|
|
|
|
|
|
|
|
|
|
|
var invalidWindows : seq[TWindow] |
|
|
|
|
|
|
|
|
|
|
|
proc getWindowName(display : PDisplay, window : TWindow) : Option[string] = |
|
|
|
|
|
var name : cstring |
|
|
|
|
|
if display.XFetchName(window, name.addr) == BadWindow: |
|
|
|
|
|
return none(string) |
|
|
|
|
|
some($name) |
|
|
|
|
|
|
|
|
|
|
|
proc setWindowName(display : PDisplay, window : TWindow, name : cstring) : Option[string] = |
|
|
|
|
|
if display.XStoreName(window, name) == BadWindow: |
|
|
|
|
|
return none(string) |
|
|
|
|
|
some($name) |
|
|
|
|
|
|
|
|
|
|
|
proc gcWindows(display : PDisplay) = |
|
|
|
|
|
for xid, window in windowState.pairs: |
|
|
|
|
|
discard display.getWindowName(xid) |
|
|
|
|
|
for xid in invalidWindows: |
|
|
|
|
|
windowState.del(xid) |
|
|
|
|
|
invalidWindows = @[] |
|
|
|
|
|
|
|
|
|
|
|
proc ignoreBadWindows(display : PDisplay, ev : PXErrorEvent) : cint {.cdecl.} = |
|
|
|
|
|
# resourceID maps to the Window's XID |
|
|
|
|
|
invalidWindows &= @[ev.resourceID] |
|
|
|
|
|
0 |
|
|
|
|
|
|
|
|
proc getDisplay : PDisplay = |
|
|
proc getDisplay : PDisplay = |
|
|
result = XOpenDisplay(nil) |
|
|
result = XOpenDisplay(nil) |
|
|
if result == nil: |
|
|
if result == nil: |
|
|
quit("Failed to open display") |
|
|
quit("Failed to open display") |
|
|
|
|
|
|
|
|
proc grabMouse(display : PDisplay, button : int) = |
|
|
iterator getChildren(display : PDisplay, rootHeight : int, rootWidth : int) : Window = |
|
|
discard XGrabButton(display, |
|
|
var currentWindow : PWindow |
|
|
button.cuint, |
|
|
var rootReturn : TWindow |
|
|
Mod1Mask.cuint, |
|
|
var parentReturn : TWindow |
|
|
DefaultRootWindow(display), |
|
|
var childrenReturn : PWindow |
|
|
1.cint, |
|
|
var nChildrenReturn : cuint |
|
|
ButtonPressMask or ButtonReleaseMask or PointerMotionMask, |
|
|
|
|
|
GrabModeAsync, |
|
|
# Seed the RNG |
|
|
GrabModeAsync, |
|
|
randomize() |
|
|
None, |
|
|
|
|
|
None) |
|
|
discard XQueryTree(display, |
|
|
|
|
|
root, |
|
|
proc grabKeys(display : PDisplay) = |
|
|
rootReturn.addr, |
|
|
let keyModifier = "F1" |
|
|
parentReturn.addr, |
|
|
discard XGrabKey(display, |
|
|
childrenReturn.addr, |
|
|
XKeySymToKeyCode(display, |
|
|
nChildrenReturn.addr) |
|
|
XStringToKeySym(keyModifier.cstring)).cint, |
|
|
|
|
|
Mod1Mask.cuint, |
|
|
|
|
|
DefaultRootWindow(display), |
|
|
for i in 0..(nChildrenReturn.int - 1): |
|
|
1.cint, |
|
|
var attr : TXWindowAttributes |
|
|
GrabModeAsync.cint, |
|
|
|
|
|
GrabModeAsync.cint) |
|
|
currentWindow = cast[PWindow]( |
|
|
|
|
|
cast[uint](childrenReturn) + cast[uint](i * currentWindow[].sizeof) |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
if display.XGetWindowAttributes(currentWindow[], attr.addr) == BadWindow: |
|
|
|
|
|
windowState.del(currentWindow[]) |
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
yield Window( |
|
|
|
|
|
x: rand(0..rootWidth).cint, |
|
|
|
|
|
y: rand(0..rootHeight).cint, |
|
|
|
|
|
xDirection: right, |
|
|
|
|
|
yDirection: down, |
|
|
|
|
|
width: attr.width, |
|
|
|
|
|
height: attr.height, |
|
|
|
|
|
win: currentWindow[], |
|
|
|
|
|
speed: 10 |
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
discard XFree(childrenReturn) |
|
|
|
|
|
|
|
|
when isMainModule: |
|
|
when isMainModule: |
|
|
var start : TXButtonEvent |
|
|
var start : TXButtonEvent |
|
@ -35,39 +96,51 @@ when isMainModule: |
|
|
|
|
|
|
|
|
let display = getDisplay() |
|
|
let display = getDisplay() |
|
|
|
|
|
|
|
|
display.grabKeys |
|
|
root = DefaultRootWindow(display) |
|
|
display.grabMouse(1) |
|
|
|
|
|
display.grabMouse(3) |
|
|
|
|
|
|
|
|
|
|
|
start.subWindow = None |
|
|
start.subWindow = None |
|
|
|
|
|
|
|
|
while true: |
|
|
discard XSetErrorHandler(ignoreBadWindows) |
|
|
# TODO refactor using XPending ? |
|
|
discard display.XGetWindowAttributes(root, attr.addr) |
|
|
discard XNextEvent(display, ev.addr) |
|
|
|
|
|
|
|
|
|
|
|
# subwindow is because we grabbed the root window |
|
|
|
|
|
# and we want events in its children |
|
|
|
|
|
|
|
|
|
|
|
if (ev.theType == KeyPress) and (ev.xKey.subWindow != None): |
|
|
let rootWidth = attr.width |
|
|
discard XRaiseWindow(display, ev.xKey.subWindow) |
|
|
let rootHeight = attr.height |
|
|
|
|
|
|
|
|
elif (ev.theType == ButtonPress) and (ev.xButton.subWindow != None): |
|
|
while true: |
|
|
discard XGetWindowAttributes(display, ev.xButton.subWindow, attr.addr) |
|
|
sleep(10) |
|
|
start = ev.xButton |
|
|
|
|
|
|
|
|
|
|
|
elif (ev.theType == MotionNotify) and (start.subWindow != None): |
|
|
for window in getChildren(display, rootHeight, rootWidth): |
|
|
var xDiff : int = ev.xButton.xRoot - start.xRoot |
|
|
# go through each window, add it to the state |
|
|
var yDiff : int = ev.xButton.yRoot - start.yRoot |
|
|
discard windowState.hasKeyOrPut(window.win, window) |
|
|
|
|
|
|
|
|
discard XMoveResizeWindow(display, |
|
|
display.gcWindows() |
|
|
start.subWindow, |
|
|
|
|
|
attr.x + (if start.button == 1: xDiff else: 0).cint, |
|
|
|
|
|
attr.y + (if start.button == 1: yDiff else: 0).cint, |
|
|
|
|
|
max(1, attr.width + (if start.button == 3: xDiff else: 0)).cuint, |
|
|
|
|
|
max(1, attr.height + (if start.button == 3: yDiff else: 0)).cuint) |
|
|
|
|
|
|
|
|
|
|
|
elif ev.theType == ButtonRelease: |
|
|
# Go through each window and move them, update the state, etc |
|
|
start.subWindow = None |
|
|
for window in windowState.values: |
|
|
|
|
|
if window.xDirection == right: |
|
|
|
|
|
if window.x == (rootWidth - window.width) or window.x > rootWidth: |
|
|
|
|
|
windowState[window.win].xDirection = left |
|
|
|
|
|
windowState[window.win].x -= 1 |
|
|
|
|
|
else: |
|
|
|
|
|
windowState[window.win].x += 1 |
|
|
|
|
|
else: |
|
|
|
|
|
if window.x <= 0: |
|
|
|
|
|
windowState[window.win].xDirection = right |
|
|
|
|
|
windowState[window.win].x += 1 |
|
|
|
|
|
else: |
|
|
|
|
|
windowState[window.win].x -= 1 |
|
|
|
|
|
|
|
|
|
|
|
if window.yDirection == up: |
|
|
|
|
|
if window.y <= 0: |
|
|
|
|
|
windowState[window.win].yDirection = down |
|
|
|
|
|
windowState[window.win].y += 1 |
|
|
else: |
|
|
else: |
|
|
continue |
|
|
windowState[window.win].y -= 1 |
|
|
|
|
|
else: |
|
|
|
|
|
if window.y >= (rootHeight - window.height): |
|
|
|
|
|
windowState[window.win].yDirection = up |
|
|
|
|
|
windowState[window.win].y -= 1 |
|
|
|
|
|
else: |
|
|
|
|
|
windowState[window.win].y += 1 |
|
|
|
|
|
|
|
|
|
|
|
discard display.XMoveWindow(window.win, windowState[window.win].x, windowState[window.win].y) |
|
|
|
|
|
discard display.XSync(0) |
|
|