feat: add DWN browser automation demo and client library examples
Add three new example files demonstrating DWN window manager API usage: - dwn_browser_demo.wren: browser automation OCR demo that opens google.nl, searches for "ponies", and captures screenshots across multiple scrolls - dwn_client.wren: reusable DWNClient class wrapping WebSocket commands for workspace management, client focus, keyboard input, and window queries - dwn_demo.wren: comprehensive DWNDemo class with dashboard display, workspace switching, client listing, and layout formatting utilities
This commit is contained in:
Vendored
+318
@@ -0,0 +1,318 @@
|
||||
// retoor <retoor@molodetz.nl>
|
||||
|
||||
import "websocket" for WebSocket
|
||||
import "json" for Json
|
||||
|
||||
class DWNClient {
|
||||
construct new(host, port) {
|
||||
_host = host
|
||||
_port = port
|
||||
_ws = null
|
||||
}
|
||||
|
||||
construct new() {
|
||||
_host = "localhost"
|
||||
_port = 8777
|
||||
_ws = null
|
||||
}
|
||||
|
||||
connect() {
|
||||
var url = "ws://%(_host):%(_port)/ws"
|
||||
_ws = WebSocket.connect(url)
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
if (_ws != null) {
|
||||
_ws.close()
|
||||
_ws = null
|
||||
}
|
||||
}
|
||||
|
||||
isConnected { _ws != null && _ws.isOpen }
|
||||
|
||||
sendCommand(command) {
|
||||
if (_ws == null) Fiber.abort("Not connected")
|
||||
_ws.send(Json.stringify(command))
|
||||
var msg = _ws.receive()
|
||||
if (msg == null || !msg.isText) return null
|
||||
return Json.parse(msg.text)
|
||||
}
|
||||
|
||||
getStatus() {
|
||||
return sendCommand({"command": "get_status"})
|
||||
}
|
||||
|
||||
getWorkspaces() {
|
||||
return sendCommand({"command": "get_workspaces"})
|
||||
}
|
||||
|
||||
getClients() {
|
||||
return sendCommand({"command": "get_clients"})
|
||||
}
|
||||
|
||||
findClients(title, wmClass, workspace) {
|
||||
var cmd = {"command": "find_clients"}
|
||||
if (title != null) cmd["title"] = title
|
||||
if (wmClass != null) cmd["class"] = wmClass
|
||||
if (workspace != null) cmd["workspace"] = workspace
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
getFocusedClient() {
|
||||
return sendCommand({"command": "get_focused_client"})
|
||||
}
|
||||
|
||||
switchWorkspace(workspace) {
|
||||
return sendCommand({"command": "switch_workspace", "workspace": workspace})
|
||||
}
|
||||
|
||||
runCommand(exec) {
|
||||
return sendCommand({"command": "run_command", "exec": exec})
|
||||
}
|
||||
|
||||
focusClient(windowId) {
|
||||
return sendCommand({"command": "focus_client", "window": windowId})
|
||||
}
|
||||
|
||||
keyPress(keysym) {
|
||||
var cmd = {"command": "key_press"}
|
||||
if (keysym != null) cmd["keysym"] = keysym
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
keyRelease(keysym) {
|
||||
var cmd = {"command": "key_release"}
|
||||
if (keysym != null) cmd["keysym"] = keysym
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
keyTap(keysym) { keyTap(keysym, null) }
|
||||
|
||||
keyTap(keysym, modifiers) {
|
||||
var cmd = {"command": "key_tap"}
|
||||
if (keysym != null) cmd["keysym"] = keysym
|
||||
if (modifiers != null) cmd["modifiers"] = modifiers
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
keyType(text) {
|
||||
return sendCommand({"command": "key_type", "text": text})
|
||||
}
|
||||
|
||||
mouseMove(x, y) {
|
||||
return sendCommand({"command": "mouse_move", "x": x, "y": y})
|
||||
}
|
||||
|
||||
mouseMoveRelative(dx, dy) {
|
||||
return sendCommand({"command": "mouse_move_relative", "dx": dx, "dy": dy})
|
||||
}
|
||||
|
||||
mouseClick(button) { mouseClick(button, null, null) }
|
||||
|
||||
mouseClick(button, x, y) {
|
||||
var cmd = {"command": "mouse_click", "button": button}
|
||||
if (x != null && y != null) {
|
||||
cmd["x"] = x
|
||||
cmd["y"] = y
|
||||
}
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
mousePress(button) {
|
||||
return sendCommand({"command": "mouse_press", "button": button})
|
||||
}
|
||||
|
||||
mouseRelease(button) {
|
||||
return sendCommand({"command": "mouse_release", "button": button})
|
||||
}
|
||||
|
||||
mouseScroll(direction, amount) {
|
||||
return sendCommand({"command": "mouse_scroll", "direction": direction, "amount": amount})
|
||||
}
|
||||
|
||||
getMousePosition() {
|
||||
return sendCommand({"command": "get_mouse_position"})
|
||||
}
|
||||
|
||||
closeClient(windowId) {
|
||||
return sendCommand({"command": "close_client", "window": windowId})
|
||||
}
|
||||
|
||||
killClient(windowId) {
|
||||
return sendCommand({"command": "kill_client", "window": windowId})
|
||||
}
|
||||
|
||||
minimizeClient(windowId) {
|
||||
return sendCommand({"command": "minimize_client", "window": windowId})
|
||||
}
|
||||
|
||||
restoreClient(windowId) {
|
||||
return sendCommand({"command": "restore_client", "window": windowId})
|
||||
}
|
||||
|
||||
maximizeClient(windowId) { maximizeClient(windowId, null) }
|
||||
|
||||
maximizeClient(windowId, state) {
|
||||
var cmd = {"command": "maximize_client", "window": windowId}
|
||||
if (state != null) cmd["state"] = state
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
fullscreenClient(windowId) { fullscreenClient(windowId, null) }
|
||||
|
||||
fullscreenClient(windowId, state) {
|
||||
var cmd = {"command": "fullscreen_client", "window": windowId}
|
||||
if (state != null) cmd["state"] = state
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
floatClient(windowId) { floatClient(windowId, null) }
|
||||
|
||||
floatClient(windowId, state) {
|
||||
var cmd = {"command": "float_client", "window": windowId}
|
||||
if (state != null) cmd["state"] = state
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
moveClient(windowId, x, y) {
|
||||
return sendCommand({"command": "move_client", "window": windowId, "x": x, "y": y})
|
||||
}
|
||||
|
||||
resizeClient(windowId, width, height) {
|
||||
return sendCommand({"command": "resize_client", "window": windowId, "width": width, "height": height})
|
||||
}
|
||||
|
||||
moveClientToWorkspace(windowId, workspace) {
|
||||
return sendCommand({"command": "move_client_to_workspace", "window": windowId, "workspace": workspace})
|
||||
}
|
||||
|
||||
raiseClient(windowId) {
|
||||
return sendCommand({"command": "raise_client", "window": windowId})
|
||||
}
|
||||
|
||||
lowerClient(windowId) {
|
||||
return sendCommand({"command": "lower_client", "window": windowId})
|
||||
}
|
||||
|
||||
setLayout(layout) { setLayout(layout, null) }
|
||||
|
||||
setLayout(layout, workspace) {
|
||||
var cmd = {"command": "set_layout", "layout": layout}
|
||||
if (workspace != null) cmd["workspace"] = workspace
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
cycleLayout() { cycleLayout(null) }
|
||||
|
||||
cycleLayout(workspace) {
|
||||
var cmd = {"command": "cycle_layout"}
|
||||
if (workspace != null) cmd["workspace"] = workspace
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
getScreenInfo() {
|
||||
return sendCommand({"command": "get_screen_info"})
|
||||
}
|
||||
|
||||
showDesktop() { showDesktop(null) }
|
||||
|
||||
showDesktop(state) {
|
||||
var cmd = {"command": "show_desktop"}
|
||||
if (state != null) cmd["state"] = state
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
screenshot(mode) { screenshot(mode, null, null, null, null, null) }
|
||||
|
||||
screenshot(mode, window, x, y, width, height) {
|
||||
var cmd = {"command": "screenshot", "mode": mode}
|
||||
if (mode == "window" && window != null) {
|
||||
cmd["window"] = window
|
||||
} else if (mode == "area") {
|
||||
if (x != null) cmd["x"] = x
|
||||
if (y != null) cmd["y"] = y
|
||||
if (width != null) cmd["width"] = width
|
||||
if (height != null) cmd["height"] = height
|
||||
}
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
ocr(imageBase64) { ocr(imageBase64, "eng") }
|
||||
|
||||
ocr(imageBase64, language) {
|
||||
return sendCommand({"command": "ocr", "image": imageBase64, "language": language})
|
||||
}
|
||||
|
||||
listEvents() {
|
||||
return sendCommand({"command": "list_events"})
|
||||
}
|
||||
|
||||
subscribe(events) {
|
||||
var cmd = {"command": "subscribe"}
|
||||
if (events != null) {
|
||||
cmd["events"] = events
|
||||
} else {
|
||||
cmd["all"] = true
|
||||
}
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
unsubscribe(events) {
|
||||
var cmd = {"command": "unsubscribe"}
|
||||
if (events != null) {
|
||||
cmd["events"] = events
|
||||
} else {
|
||||
cmd["all"] = true
|
||||
}
|
||||
return sendCommand(cmd)
|
||||
}
|
||||
|
||||
static formatLayout(layoutId) {
|
||||
if (layoutId == 0) return "tiling"
|
||||
if (layoutId == 1) return "floating"
|
||||
if (layoutId == 2) return "monocle"
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
static toHex(num) {
|
||||
var digits = "0123456789abcdef"
|
||||
var result = ""
|
||||
var n = num
|
||||
while (n > 0) {
|
||||
result = digits[n % 16] + result
|
||||
n = (n / 16).floor
|
||||
}
|
||||
return result.count == 0 ? "0" : result
|
||||
}
|
||||
}
|
||||
|
||||
if (Fiber.current.isDone != true) {
|
||||
System.print("=== DWN Client Module ===")
|
||||
System.print("")
|
||||
System.print("This module provides DWNClient class for DWN window manager API.")
|
||||
System.print("")
|
||||
System.print("Usage:")
|
||||
System.print(" import \"dwn_client\" for DWNClient")
|
||||
System.print(" var client = DWNClient.new()")
|
||||
System.print(" client.connect()")
|
||||
System.print(" var status = client.getStatus()")
|
||||
System.print(" client.disconnect()")
|
||||
System.print("")
|
||||
System.print("Quick test - connecting to DWN API...")
|
||||
|
||||
var client = DWNClient.new()
|
||||
client.connect()
|
||||
System.print("Connected")
|
||||
|
||||
var status = client.getStatus()
|
||||
System.print("")
|
||||
System.print("Status:")
|
||||
System.print(" Version: %(status["version"])")
|
||||
System.print(" Workspace: %(status["current_workspace"] + 1)")
|
||||
System.print(" Clients: %(status["client_count"])")
|
||||
System.print(" Status: %(status["status"])")
|
||||
|
||||
client.disconnect()
|
||||
System.print("")
|
||||
System.print("Disconnected")
|
||||
}
|
||||
Reference in New Issue
Block a user