build: add -ldl linker flag, recursive source discovery, and API test targets to Makefile
This commit is contained in:
+132
-81
@@ -13,7 +13,7 @@
|
||||
<aside class="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h1>DWN</h1>
|
||||
<span class="version">v1.0.0</span>
|
||||
<span class="version">v2.0.0</span>
|
||||
</div>
|
||||
|
||||
<div class="search-box">
|
||||
@@ -47,6 +47,8 @@
|
||||
<div class="nav-section">
|
||||
<div class="nav-section-title">Advanced</div>
|
||||
<a href="architecture.html" class="nav-link">Architecture</a>
|
||||
<a href="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
|
||||
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
|
||||
<a href="building.html" class="nav-link">Building from Source</a>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -65,6 +67,7 @@
|
||||
<li><a href="#python">Python Examples</a></li>
|
||||
<li><a href="#javascript">JavaScript Examples</a></li>
|
||||
<li><a href="#bash">Bash Examples</a></li>
|
||||
<li><a href="#fade-control">Fade Effect Control</a></li>
|
||||
<li><a href="#events">Event Subscription</a></li>
|
||||
<li><a href="#automation">Automation Recipes</a></li>
|
||||
<li><a href="#browser-ocr">Browser Automation with OCR</a></li>
|
||||
@@ -78,17 +81,17 @@
|
||||
<pre><code>import json
|
||||
import websocket
|
||||
|
||||
ws = websocket.create_connection("ws://localhost:8777")
|
||||
ws = websocket.create_connection("ws://localhost:8777/ws")
|
||||
|
||||
def send_command(command, **params):
|
||||
request = {"command": command, **params}
|
||||
ws.send(json.dumps(request))
|
||||
return json.loads(ws.recv())
|
||||
|
||||
# List all windows
|
||||
result = send_command("list_windows")
|
||||
for window in result["windows"]:
|
||||
print(f"{window['title']} ({window['class']})")
|
||||
# List all clients
|
||||
result = send_command("get_clients")
|
||||
for client in result["clients"]:
|
||||
print(f"{client['title']} ({client['class']})")
|
||||
|
||||
ws.close()</code></pre>
|
||||
</div>
|
||||
@@ -100,21 +103,21 @@ ws.close()</code></pre>
|
||||
client = DWNClient()
|
||||
client.connect()
|
||||
|
||||
# Get all windows
|
||||
windows = client.list_windows()
|
||||
print(f"Found {len(windows)} windows")
|
||||
# Get all clients
|
||||
clients = client.get_clients()
|
||||
print(f"Found {len(clients)} clients")
|
||||
|
||||
# Focus window by title
|
||||
for w in windows:
|
||||
if "Firefox" in w["title"]:
|
||||
client.focus_window(w["id"])
|
||||
# Focus client by title
|
||||
for c in clients:
|
||||
if "Firefox" in c["title"]:
|
||||
client.focus_client(c["window"])
|
||||
break
|
||||
|
||||
# Switch to workspace 3
|
||||
client.switch_workspace(2)
|
||||
|
||||
# Type some text
|
||||
client.type_text("Hello from Python!")
|
||||
client.key_type("Hello from Python!")
|
||||
|
||||
client.disconnect()</code></pre>
|
||||
</div>
|
||||
@@ -145,30 +148,30 @@ print(ocr_result["text"])
|
||||
client.disconnect()</code></pre>
|
||||
</div>
|
||||
|
||||
<h3>Window Arrangement Script</h3>
|
||||
<h3>Client Arrangement Script</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>from dwn_api_client import DWNClient
|
||||
|
||||
def arrange_coding_setup(client):
|
||||
"""Arrange windows for coding: editor left, terminal right"""
|
||||
windows = client.list_windows()
|
||||
"""Arrange clients for coding: editor left, terminal right"""
|
||||
clients = client.get_clients()
|
||||
|
||||
# Find VS Code and terminal
|
||||
vscode = None
|
||||
terminal = None
|
||||
for w in windows:
|
||||
if "code" in w["class"].lower():
|
||||
vscode = w
|
||||
elif "terminal" in w["class"].lower():
|
||||
terminal = w
|
||||
for c in clients:
|
||||
if "code" in c["class"].lower():
|
||||
vscode = c
|
||||
elif "terminal" in c["class"].lower():
|
||||
terminal = c
|
||||
|
||||
if vscode:
|
||||
client.move_window(vscode["id"], 0, 32)
|
||||
client.resize_window(vscode["id"], 960, 1048)
|
||||
client.move_client(vscode["window"], 0, 32)
|
||||
client.resize_client(vscode["window"], 960, 1048)
|
||||
|
||||
if terminal:
|
||||
client.move_window(terminal["id"], 960, 32)
|
||||
client.resize_window(terminal["id"], 960, 1048)
|
||||
client.move_client(terminal["window"], 960, 32)
|
||||
client.resize_client(terminal["window"], 960, 1048)
|
||||
|
||||
client = DWNClient()
|
||||
client.connect()
|
||||
@@ -183,15 +186,15 @@ import json
|
||||
import websockets
|
||||
|
||||
async def main():
|
||||
async with websockets.connect("ws://localhost:8777") as ws:
|
||||
async with websockets.connect("ws://localhost:8777/ws") as ws:
|
||||
# Send command
|
||||
await ws.send(json.dumps({"command": "list_windows"}))
|
||||
await ws.send(json.dumps({"command": "get_clients"}))
|
||||
|
||||
# Receive response
|
||||
response = json.loads(await ws.recv())
|
||||
|
||||
for window in response["windows"]:
|
||||
print(f"Window: {window['title']}")
|
||||
for client in response["clients"]:
|
||||
print(f"Client: {client['title']}")
|
||||
|
||||
asyncio.run(main())</code></pre>
|
||||
</div>
|
||||
@@ -201,7 +204,7 @@ asyncio.run(main())</code></pre>
|
||||
<h3>Browser WebSocket</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>class DWNClient {
|
||||
constructor(url = 'ws://localhost:8777') {
|
||||
constructor(url = 'ws://localhost:8777/ws') {
|
||||
this.url = url;
|
||||
this.ws = null;
|
||||
this.pending = new Map();
|
||||
@@ -228,16 +231,16 @@ asyncio.run(main())</code></pre>
|
||||
this.ws.send(JSON.stringify(request));
|
||||
}
|
||||
|
||||
async listWindows() {
|
||||
this.send('list_windows');
|
||||
async getClients() {
|
||||
this.send('get_clients');
|
||||
}
|
||||
|
||||
async focusWindow(windowId) {
|
||||
this.send('focus_window', { window: windowId });
|
||||
async focusClient(windowId) {
|
||||
this.send('focus_client', { window: windowId });
|
||||
}
|
||||
|
||||
async typeText(text) {
|
||||
this.send('type_text', { text });
|
||||
async keyType(text) {
|
||||
this.send('key_type', { text });
|
||||
}
|
||||
|
||||
async screenshot(mode = 'fullscreen') {
|
||||
@@ -248,28 +251,28 @@ asyncio.run(main())</code></pre>
|
||||
// Usage
|
||||
const client = new DWNClient();
|
||||
await client.connect();
|
||||
await client.listWindows();</code></pre>
|
||||
await client.getClients();</code></pre>
|
||||
</div>
|
||||
|
||||
<h3>Node.js Client</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>const WebSocket = require('ws');
|
||||
|
||||
const ws = new WebSocket('ws://localhost:8777');
|
||||
const ws = new WebSocket('ws://localhost:8777/ws');
|
||||
|
||||
ws.on('open', () => {
|
||||
console.log('Connected to DWN');
|
||||
|
||||
// List windows
|
||||
ws.send(JSON.stringify({ command: 'list_windows' }));
|
||||
// List clients
|
||||
ws.send(JSON.stringify({ command: 'get_clients' }));
|
||||
});
|
||||
|
||||
ws.on('message', (data) => {
|
||||
const response = JSON.parse(data);
|
||||
|
||||
if (response.windows) {
|
||||
response.windows.forEach(w => {
|
||||
console.log(`${w.title} - ${w.class}`);
|
||||
if (response.clients) {
|
||||
response.clients.forEach(c => {
|
||||
console.log(`${c.title} - ${c.class}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -311,18 +314,18 @@ ws.on('error', (err) => {
|
||||
<div class="code-block">
|
||||
<pre><code>#!/bin/bash
|
||||
|
||||
# List windows
|
||||
echo '{"command": "list_windows"}' | websocat ws://localhost:8777
|
||||
# List clients
|
||||
echo '{"command": "get_clients"}' | websocat ws://localhost:8777/ws
|
||||
|
||||
# Focus window by ID
|
||||
echo '{"command": "focus_window", "window": 12345678}' | websocat ws://localhost:8777
|
||||
# Focus client by ID
|
||||
echo '{"command": "focus_client", "window": 12345678}' | websocat ws://localhost:8777/ws
|
||||
|
||||
# Switch workspace
|
||||
echo '{"command": "switch_workspace", "workspace": 2}' | websocat ws://localhost:8777
|
||||
echo '{"command": "switch_workspace", "workspace": 2}' | websocat ws://localhost:8777/ws
|
||||
|
||||
# Take screenshot and save
|
||||
echo '{"command": "screenshot", "mode": "fullscreen"}' | \
|
||||
websocat ws://localhost:8777 | \
|
||||
websocat ws://localhost:8777/ws | \
|
||||
jq -r '.data' | \
|
||||
base64 -d > screenshot.png</code></pre>
|
||||
</div>
|
||||
@@ -332,10 +335,10 @@ echo '{"command": "screenshot", "mode": "fullscreen"}' | \
|
||||
<pre><code>#!/bin/bash
|
||||
|
||||
# One-liner command
|
||||
echo '{"command": "list_windows"}' | wscat -c ws://localhost:8777 -w 1
|
||||
echo '{"command": "get_clients"}' | wscat -c ws://localhost:8777/ws -w 1
|
||||
|
||||
# Interactive session
|
||||
wscat -c ws://localhost:8777
|
||||
wscat -c ws://localhost:8777/ws
|
||||
# Then type commands manually</code></pre>
|
||||
</div>
|
||||
|
||||
@@ -344,17 +347,65 @@ wscat -c ws://localhost:8777
|
||||
<pre><code>#!/bin/bash
|
||||
|
||||
dwn_command() {
|
||||
echo "$1" | websocat -n1 ws://localhost:8777
|
||||
echo "$1" | websocat -n1 ws://localhost:8777/ws
|
||||
}
|
||||
|
||||
# Get focused window
|
||||
dwn_command '{"command": "get_focused"}' | jq '.window.title'
|
||||
# Get focused client
|
||||
dwn_command '{"command": "get_focused_client"}' | jq '.client.title'
|
||||
|
||||
# Type text
|
||||
dwn_command '{"command": "type_text", "text": "Hello!"}'
|
||||
dwn_command '{"command": "key_type", "text": "Hello!"}'
|
||||
|
||||
# Launch application
|
||||
dwn_command '{"command": "spawn", "program": "firefox"}'</code></pre>
|
||||
dwn_command '{"command": "run_command", "exec": "firefox"}'</code></pre>
|
||||
</div>
|
||||
|
||||
<h3>Fade Effect Control</h3>
|
||||
<p>Control DWN's fade animation effects via API:</p>
|
||||
<div class="code-block">
|
||||
<pre><code>import json
|
||||
import websocket
|
||||
|
||||
ws = websocket.create_connection("ws://localhost:8777/ws")
|
||||
|
||||
# Get current fade settings
|
||||
ws.send(json.dumps({"command": "get_fade_settings"}))
|
||||
response = json.loads(ws.recv())
|
||||
print(f"Speed: {response['fade_speed']}, Intensity: {response['fade_intensity']}")
|
||||
|
||||
# Set fade animation speed (0.1 - 3.0)
|
||||
ws.send(json.dumps({"command": "set_fade_speed", "speed": 1.5}))
|
||||
|
||||
# Set fade glow intensity (0.0 - 1.0)
|
||||
ws.send(json.dumps({"command": "set_fade_intensity", "intensity": 0.8}))
|
||||
|
||||
# Subscribe to fade change events
|
||||
ws.send(json.dumps({
|
||||
"command": "subscribe",
|
||||
"events": ["fade_speed_changed", "fade_intensity_changed"]
|
||||
}))
|
||||
|
||||
# Listen for events
|
||||
while True:
|
||||
event = json.loads(ws.recv())
|
||||
if event.get("type") == "event":
|
||||
print(f"Event: {event['event']}")
|
||||
print(f"Data: {event['data']}")</code></pre>
|
||||
</div>
|
||||
|
||||
<p>Using the provided fade control demo script:</p>
|
||||
<div class="code-block">
|
||||
<pre><code># Get current fade settings
|
||||
python3 examples/fade_control_demo.py
|
||||
|
||||
# Set fade speed
|
||||
python3 examples/fade_control_demo.py --speed 1.5
|
||||
|
||||
# Set fade intensity
|
||||
python3 examples/fade_control_demo.py --intensity 0.8
|
||||
|
||||
# Listen for fade events
|
||||
python3 examples/fade_control_demo.py --listen --duration 30</code></pre>
|
||||
</div>
|
||||
|
||||
<h2 id="events">Event Subscription</h2>
|
||||
@@ -452,7 +503,7 @@ activity_logger()</code></pre>
|
||||
|
||||
<h3>JavaScript Event Listener</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>const ws = new WebSocket('ws://localhost:8777');
|
||||
<pre><code>const ws = new WebSocket('ws://localhost:8777/ws');
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('Connected to DWN');
|
||||
@@ -491,7 +542,7 @@ ws.onclose = () => {
|
||||
};</code></pre>
|
||||
</div>
|
||||
|
||||
<h3>Reactive Window Arrangement</h3>
|
||||
<h3>Reactive Client Arrangement</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>from dwn_api_client import DWNClient
|
||||
|
||||
@@ -508,7 +559,7 @@ def auto_arrange():
|
||||
|
||||
client.subscribe(events=["window_created"])
|
||||
|
||||
print("Auto-arranging windows...")
|
||||
print("Auto-arranging clients...")
|
||||
|
||||
try:
|
||||
while True:
|
||||
@@ -541,7 +592,7 @@ auto_arrange()</code></pre>
|
||||
|
||||
<h2 id="automation">Automation Recipes</h2>
|
||||
|
||||
<h3>Auto-Arrange Windows by Class</h3>
|
||||
<h3>Auto-Arrange Clients by Class</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>from dwn_api_client import DWNClient
|
||||
|
||||
@@ -556,17 +607,17 @@ def auto_arrange():
|
||||
client = DWNClient()
|
||||
client.connect()
|
||||
|
||||
windows = client.list_windows()
|
||||
for w in windows:
|
||||
win_class = w["class"].lower()
|
||||
clients = client.get_clients()
|
||||
for c in clients:
|
||||
wm_class = c["class"].lower()
|
||||
for pattern, rules in LAYOUT_RULES.items():
|
||||
if pattern in win_class:
|
||||
if w["workspace"] != rules["workspace"]:
|
||||
client.move_window_to_workspace(
|
||||
w["id"], rules["workspace"]
|
||||
if pattern in wm_class:
|
||||
if c["workspace"] != rules["workspace"]:
|
||||
client.move_client_to_workspace(
|
||||
c["window"], rules["workspace"]
|
||||
)
|
||||
if w["floating"] != rules["floating"]:
|
||||
client.set_floating(w["id"], rules["floating"])
|
||||
if c["floating"] != rules["floating"]:
|
||||
client.float_client(c["window"], rules["floating"])
|
||||
break
|
||||
|
||||
client.disconnect()
|
||||
@@ -609,14 +660,14 @@ def screenshot_monitor(interval=60, output_dir="screenshots"):
|
||||
screenshot_monitor(interval=300) # Every 5 minutes</code></pre>
|
||||
</div>
|
||||
|
||||
<h3>Window Focus Logger</h3>
|
||||
<h3>Client Focus Logger</h3>
|
||||
<div class="code-block">
|
||||
<pre><code>import time
|
||||
from datetime import datetime
|
||||
from dwn_api_client import DWNClient
|
||||
|
||||
def focus_logger(log_file="focus_log.txt"):
|
||||
"""Log window focus changes"""
|
||||
"""Log client focus changes"""
|
||||
client = DWNClient()
|
||||
client.connect()
|
||||
|
||||
@@ -625,16 +676,16 @@ def focus_logger(log_file="focus_log.txt"):
|
||||
try:
|
||||
with open(log_file, "a") as f:
|
||||
while True:
|
||||
windows = client.list_windows()
|
||||
focused = next((w for w in windows if w["focused"]), None)
|
||||
clients = client.get_clients()
|
||||
focused = next((c for c in clients if c["focused"]), None)
|
||||
|
||||
if focused and focused["id"] != last_focused:
|
||||
if focused and focused["window"] != last_focused:
|
||||
timestamp = datetime.now().isoformat()
|
||||
entry = f"{timestamp} | {focused['title']} ({focused['class']})\n"
|
||||
f.write(entry)
|
||||
f.flush()
|
||||
print(entry.strip())
|
||||
last_focused = focused["id"]
|
||||
last_focused = focused["window"]
|
||||
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
@@ -682,24 +733,24 @@ client.disconnect()</code></pre>
|
||||
<div class="code-block">
|
||||
<pre><code>from dwn_api_client import DWNClient
|
||||
|
||||
def read_active_window():
|
||||
"""Extract and print text from active window"""
|
||||
def read_active_client():
|
||||
"""Extract and print text from active client"""
|
||||
client = DWNClient()
|
||||
client.connect()
|
||||
|
||||
# Capture active window
|
||||
# Capture active client
|
||||
screenshot = client.screenshot("active")
|
||||
|
||||
# Extract text
|
||||
ocr_result = client.ocr(screenshot["data"])
|
||||
|
||||
print(f"Text from active window (confidence: {ocr_result['confidence']:.0%}):")
|
||||
print(f"Text from active client (confidence: {ocr_result['confidence']:.0%}):")
|
||||
print("-" * 40)
|
||||
print(ocr_result["text"])
|
||||
|
||||
client.disconnect()
|
||||
|
||||
read_active_window()</code></pre>
|
||||
read_active_client()</code></pre>
|
||||
</div>
|
||||
|
||||
<h3 id="browser-ocr">Browser Automation with OCR</h3>
|
||||
|
||||
Reference in New Issue
Block a user