build: add -ldl linker flag, recursive source discovery, and API test targets to Makefile

This commit is contained in:
2026-02-07 12:04:52 +00:00
parent 8e86b2d106
commit cd9e55dc64
181 changed files with 20646 additions and 7198 deletions
+132 -81
View File
@@ -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>