build: add libxtst, libpng, tesseract, and leptonica dependencies to Makefile for screenshot and ocr support

This commit is contained in:
2026-01-24 15:17:47 +00:00
parent 51c3d4b919
commit fb28931535
42 changed files with 9207 additions and 79 deletions
+338
View File
@@ -0,0 +1,338 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Integration - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link active">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>AI Integration</h1>
<p class="lead">Setup and usage of AI-powered features</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#overview">Overview</a></li>
<li><a href="#setup">Setup</a></li>
<li><a href="#command-palette">AI Command Palette</a></li>
<li><a href="#context-analysis">Context Analysis</a></li>
<li><a href="#exa-search">Exa Semantic Search</a></li>
<li><a href="#configuration">Configuration</a></li>
</ul>
</div>
<h2 id="overview">Overview</h2>
<p>DWN includes optional AI integration for intelligent command execution and semantic web search. These features require API keys from external services.</p>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-title">AI Command Palette</div>
<div class="feature-desc">Natural language commands via OpenRouter API. Ask AI to launch apps, answer questions, or perform tasks.</div>
</div>
<div class="feature-card">
<div class="feature-title">Context Analysis</div>
<div class="feature-desc">AI analyzes your current workspace to understand what task you're working on.</div>
</div>
<div class="feature-card">
<div class="feature-title">Exa Search</div>
<div class="feature-desc">Semantic web search that understands meaning, not just keywords. Find relevant docs and tutorials.</div>
</div>
</div>
<h2 id="setup">Setup</h2>
<h3>OpenRouter API Key</h3>
<p>Required for AI Command Palette and Context Analysis.</p>
<ol>
<li>Visit <a href="https://openrouter.ai/keys" target="_blank">https://openrouter.ai/keys</a></li>
<li>Create an account and generate an API key</li>
<li>Set the key via environment variable or config file</li>
</ol>
<div class="code-block">
<pre><code># Option 1: Environment variable (add to ~/.bashrc or ~/.zshrc)
export OPENROUTER_API_KEY=sk-or-v1-your-key-here
# Option 2: Config file (~/.config/dwn/config)
[ai]
openrouter_api_key = sk-or-v1-your-key-here</code></pre>
</div>
<h3>Exa API Key</h3>
<p>Required for Exa Semantic Search.</p>
<ol>
<li>Visit <a href="https://dashboard.exa.ai/api-keys" target="_blank">https://dashboard.exa.ai/api-keys</a></li>
<li>Create an account and generate an API key</li>
<li>Set the key via environment variable or config file</li>
</ol>
<div class="code-block">
<pre><code># Option 1: Environment variable
export EXA_API_KEY=your-exa-key-here
# Option 2: Config file (~/.config/dwn/config)
[ai]
exa_api_key = your-exa-key-here</code></pre>
</div>
<div class="alert alert-info">
<strong>Note:</strong> AI features are optional. DWN functions fully without them.
</div>
<h2 id="command-palette">AI Command Palette</h2>
<p>Press <code>Super+Shift+A</code> to open the AI command palette.</p>
<h3>Usage</h3>
<ol>
<li>Press <code>Super+Shift+A</code></li>
<li>Type a natural language command</li>
<li>Press Enter</li>
<li>AI interprets and executes your request</li>
</ol>
<h3>Example Commands</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Command</th>
<th>Result</th>
</tr>
</thead>
<tbody>
<tr>
<td>"open firefox"</td>
<td>Launches Firefox browser</td>
</tr>
<tr>
<td>"launch terminal"</td>
<td>Opens configured terminal</td>
</tr>
<tr>
<td>"run file manager"</td>
<td>Opens configured file manager</td>
</tr>
<tr>
<td>"what time is it"</td>
<td>Shows current time</td>
</tr>
<tr>
<td>"how much memory is free"</td>
<td>Shows system memory info</td>
</tr>
</tbody>
</table>
</div>
<h3>Supported Actions</h3>
<ul>
<li><strong>Application launching</strong> - "open", "launch", "run", "start"</li>
<li><strong>System queries</strong> - Time, date, system information</li>
<li><strong>General questions</strong> - AI will provide helpful responses</li>
</ul>
<h2 id="context-analysis">Context Analysis</h2>
<p>Press <code>Super+A</code> to see AI analysis of your current workspace.</p>
<h3>What It Shows</h3>
<ul>
<li>Detected task type (coding, browsing, communication, etc.)</li>
<li>Currently focused window</li>
<li>AI-generated suggestions based on context</li>
</ul>
<h3>Example Output</h3>
<div class="code-block">
<pre><code>Current Task: Software Development
Focused: Visual Studio Code - project.py
Windows: VS Code, Terminal, Firefox (Stack Overflow)
Suggestions:
- Run tests with Ctrl+Shift+T
- Toggle terminal with Ctrl+`
- Search documentation with Super+Shift+E</code></pre>
</div>
<h2 id="exa-search">Exa Semantic Search</h2>
<p>Press <code>Super+Shift+E</code> to open semantic web search.</p>
<h3>How It Works</h3>
<p>Exa uses neural search to find content based on meaning rather than keyword matching. This produces more relevant results for technical queries.</p>
<h3>Usage</h3>
<ol>
<li>Press <code>Super+Shift+E</code></li>
<li>Type a natural language query</li>
<li>Press Enter</li>
<li>Results appear in a selection menu</li>
<li>Select a result to open in browser</li>
</ol>
<h3>Example Queries</h3>
<ul>
<li>"how to configure nginx reverse proxy"</li>
<li>"python async await tutorial"</li>
<li>"X11 window manager development guide"</li>
<li>"systemd service file examples"</li>
</ul>
<h3>Best Practices</h3>
<ul>
<li>Use natural language, not keywords</li>
<li>Be specific about what you're looking for</li>
<li>Include context (language, framework, etc.)</li>
</ul>
<h2 id="configuration">Configuration</h2>
<p>AI settings in <code>~/.config/dwn/config</code>:</p>
<div class="code-block">
<pre><code>[ai]
# AI model for OpenRouter (see https://openrouter.ai/models)
model = google/gemini-2.0-flash-exp:free
# API keys (can also use environment variables)
openrouter_api_key = sk-or-v1-your-key
exa_api_key = your-exa-key</code></pre>
</div>
<h3>Available Models</h3>
<p>OpenRouter provides access to many AI models. Some options:</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Model</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>google/gemini-2.0-flash-exp:free</code></td>
<td>Fast, free tier available (default)</td>
</tr>
<tr>
<td><code>anthropic/claude-3-haiku</code></td>
<td>Fast and efficient</td>
</tr>
<tr>
<td><code>openai/gpt-4o-mini</code></td>
<td>Good balance of speed and quality</td>
</tr>
<tr>
<td><code>x-ai/grok-code-fast-1</code></td>
<td>Optimized for code tasks</td>
</tr>
</tbody>
</table>
</div>
<p>See <a href="https://openrouter.ai/models" target="_blank">OpenRouter Models</a> for the full list.</p>
<h2>Keyboard Shortcuts</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+A</code></td>
<td>Show AI context analysis</td>
</tr>
<tr>
<td><code>Super+Shift+A</code></td>
<td>Open AI command palette</td>
</tr>
<tr>
<td><code>Super+Shift+E</code></td>
<td>Open Exa semantic search</td>
</tr>
</tbody>
</table>
</div>
<h2>Troubleshooting</h2>
<h3>AI features not working</h3>
<ol>
<li>Verify API key is set correctly</li>
<li>Check network connectivity</li>
<li>Look for errors in <code>~/.local/share/dwn/dwn.log</code></li>
</ol>
<h3>Slow responses</h3>
<ul>
<li>Try a faster model (e.g., <code>google/gemini-2.0-flash-exp:free</code>)</li>
<li>Check network latency to API endpoints</li>
</ul>
<h3>API rate limits</h3>
<p>Free tiers have usage limits. Consider upgrading for heavy use or use a paid model.</p>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+828
View File
@@ -0,0 +1,828 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Examples - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link active">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>API Examples</h1>
<p class="lead">Code examples for common automation tasks</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<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="#events">Event Subscription</a></li>
<li><a href="#automation">Automation Recipes</a></li>
<li><a href="#browser-ocr">Browser Automation with OCR</a></li>
</ul>
</div>
<h2 id="python">Python Examples</h2>
<h3>Basic Connection</h3>
<div class="code-block">
<pre><code>import json
import websocket
ws = websocket.create_connection("ws://localhost:8777")
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']})")
ws.close()</code></pre>
</div>
<h3>Using the Client Library</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
client = DWNClient()
client.connect()
# Get all windows
windows = client.list_windows()
print(f"Found {len(windows)} windows")
# Focus window by title
for w in windows:
if "Firefox" in w["title"]:
client.focus_window(w["id"])
break
# Switch to workspace 3
client.switch_workspace(2)
# Type some text
client.type_text("Hello from Python!")
client.disconnect()</code></pre>
</div>
<h3>Screenshot and OCR</h3>
<div class="code-block">
<pre><code>import base64
from dwn_api_client import DWNClient
client = DWNClient()
client.connect()
# Take fullscreen screenshot
result = client.screenshot("fullscreen")
# Save to file
png_data = base64.b64decode(result["data"])
with open("screenshot.png", "wb") as f:
f.write(png_data)
print(f"Saved {result['width']}x{result['height']} screenshot")
# Extract text with OCR
ocr_result = client.ocr(result["data"])
print(f"Extracted text (confidence: {ocr_result['confidence']:.0%}):")
print(ocr_result["text"])
client.disconnect()</code></pre>
</div>
<h3>Window 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()
# 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
if vscode:
client.move_window(vscode["id"], 0, 32)
client.resize_window(vscode["id"], 960, 1048)
if terminal:
client.move_window(terminal["id"], 960, 32)
client.resize_window(terminal["id"], 960, 1048)
client = DWNClient()
client.connect()
arrange_coding_setup(client)
client.disconnect()</code></pre>
</div>
<h3>Async Client</h3>
<div class="code-block">
<pre><code>import asyncio
import json
import websockets
async def main():
async with websockets.connect("ws://localhost:8777") as ws:
# Send command
await ws.send(json.dumps({"command": "list_windows"}))
# Receive response
response = json.loads(await ws.recv())
for window in response["windows"]:
print(f"Window: {window['title']}")
asyncio.run(main())</code></pre>
</div>
<h2 id="javascript">JavaScript Examples</h2>
<h3>Browser WebSocket</h3>
<div class="code-block">
<pre><code>class DWNClient {
constructor(url = 'ws://localhost:8777') {
this.url = url;
this.ws = null;
this.pending = new Map();
this.requestId = 0;
}
connect() {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => resolve();
this.ws.onerror = (e) => reject(e);
this.ws.onmessage = (e) => this.handleMessage(e);
});
}
handleMessage(event) {
const response = JSON.parse(event.data);
// Handle response
console.log('Received:', response);
}
send(command, params = {}) {
const request = { command, ...params };
this.ws.send(JSON.stringify(request));
}
async listWindows() {
this.send('list_windows');
}
async focusWindow(windowId) {
this.send('focus_window', { window: windowId });
}
async typeText(text) {
this.send('type_text', { text });
}
async screenshot(mode = 'fullscreen') {
this.send('screenshot', { mode });
}
}
// Usage
const client = new DWNClient();
await client.connect();
await client.listWindows();</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');
ws.on('open', () => {
console.log('Connected to DWN');
// List windows
ws.send(JSON.stringify({ command: 'list_windows' }));
});
ws.on('message', (data) => {
const response = JSON.parse(data);
if (response.windows) {
response.windows.forEach(w => {
console.log(`${w.title} - ${w.class}`);
});
}
ws.close();
});
ws.on('error', (err) => {
console.error('Error:', err.message);
});</code></pre>
</div>
<h3>Screenshot to Canvas</h3>
<div class="code-block">
<pre><code>async function captureToCanvas(client, canvasId) {
return new Promise((resolve) => {
client.ws.onmessage = (event) => {
const response = JSON.parse(event.data);
if (response.data) {
const img = new Image();
img.onload = () => {
const canvas = document.getElementById(canvasId);
const ctx = canvas.getContext('2d');
canvas.width = response.width;
canvas.height = response.height;
ctx.drawImage(img, 0, 0);
resolve();
};
img.src = 'data:image/png;base64,' + response.data;
}
};
client.send('screenshot', { mode: 'fullscreen' });
});
}</code></pre>
</div>
<h2 id="bash">Bash Examples</h2>
<h3>Using websocat</h3>
<div class="code-block">
<pre><code>#!/bin/bash
# List windows
echo '{"command": "list_windows"}' | websocat ws://localhost:8777
# Focus window by ID
echo '{"command": "focus_window", "window": 12345678}' | websocat ws://localhost:8777
# Switch workspace
echo '{"command": "switch_workspace", "workspace": 2}' | websocat ws://localhost:8777
# Take screenshot and save
echo '{"command": "screenshot", "mode": "fullscreen"}' | \
websocat ws://localhost:8777 | \
jq -r '.data' | \
base64 -d > screenshot.png</code></pre>
</div>
<h3>Using wscat</h3>
<div class="code-block">
<pre><code>#!/bin/bash
# One-liner command
echo '{"command": "list_windows"}' | wscat -c ws://localhost:8777 -w 1
# Interactive session
wscat -c ws://localhost:8777
# Then type commands manually</code></pre>
</div>
<h3>Using curl with websocat</h3>
<div class="code-block">
<pre><code>#!/bin/bash
dwn_command() {
echo "$1" | websocat -n1 ws://localhost:8777
}
# Get focused window
dwn_command '{"command": "get_focused"}' | jq '.window.title'
# Type text
dwn_command '{"command": "type_text", "text": "Hello!"}'
# Launch application
dwn_command '{"command": "spawn", "program": "firefox"}'</code></pre>
</div>
<h2 id="events">Event Subscription</h2>
<h3>Using dwn_automation_demo.py</h3>
<p>The included automation demo script provides ready-to-use event monitoring:</p>
<div class="code-block">
<pre><code># List available events
python3 examples/dwn_automation_demo.py events --list
# Monitor all events (Ctrl+C to stop)
python3 examples/dwn_automation_demo.py events --monitor
# Monitor specific events
python3 examples/dwn_automation_demo.py events -e window_focused -e workspace_switched
# Run event demo (10 seconds)
python3 examples/dwn_automation_demo.py demo events
# Full event stream demo with summary
python3 examples/dwn_automation_demo.py demo events-all</code></pre>
</div>
<h3>Python Event Listener</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
def on_event(event):
event_name = event.get("event", "unknown")
data = event.get("data", {})
if event_name == "window_focused":
print(f"Focus: {data.get('title')}")
elif event_name == "workspace_switched":
print(f"Workspace: {data.get('new_workspace') + 1}")
elif event_name == "shortcut_triggered":
print(f"Shortcut: {data.get('description')}")
return True # Continue listening
client = DWNClient()
client.connect()
# Subscribe and listen
client.listen_events(on_event, events=[
"window_focused",
"workspace_switched",
"shortcut_triggered"
])</code></pre>
</div>
<h3>Window Activity Logger</h3>
<div class="code-block">
<pre><code>import json
from datetime import datetime
from dwn_api_client import DWNClient
def activity_logger():
client = DWNClient()
client.connect()
client.subscribe(events=[
"window_focused",
"window_created",
"window_destroyed"
])
print("Logging window activity (Ctrl+C to stop)...")
try:
with open("activity.log", "a") as f:
while True:
event = client.receive_event(timeout=1.0)
if event and event.get("type") == "event":
timestamp = datetime.now().isoformat()
ev_name = event.get("event")
data = event.get("data", {})
log_entry = {
"time": timestamp,
"event": ev_name,
"data": data
}
f.write(json.dumps(log_entry) + "\\n")
f.flush()
print(f"[{timestamp}] {ev_name}")
except KeyboardInterrupt:
print("\\nStopped logging")
finally:
client.disconnect()
activity_logger()</code></pre>
</div>
<h3>JavaScript Event Listener</h3>
<div class="code-block">
<pre><code>const ws = new WebSocket('ws://localhost:8777');
ws.onopen = () => {
console.log('Connected to DWN');
// Subscribe to events
ws.send(JSON.stringify({
command: 'subscribe',
events: ['window_focused', 'workspace_switched']
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
if (msg.type === 'event') {
console.log(`Event: ${msg.event}`, msg.data);
// Handle specific events
switch (msg.event) {
case 'window_focused':
document.title = msg.data.title || 'DWN';
break;
case 'workspace_switched':
updateWorkspaceIndicator(msg.data.new_workspace);
break;
}
}
};
ws.onerror = (err) => {
console.error('WebSocket error:', err);
};
ws.onclose = () => {
console.log('Disconnected from DWN');
};</code></pre>
</div>
<h3>Reactive Window Arrangement</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
RULES = {
"code": {"floating": False, "workspace": 0},
"firefox": {"floating": False, "workspace": 1},
"slack": {"floating": True, "workspace": 2},
"telegram": {"floating": True, "workspace": 2},
}
def auto_arrange():
client = DWNClient()
client.connect()
client.subscribe(events=["window_created"])
print("Auto-arranging windows...")
try:
while True:
event = client.receive_event(timeout=1.0)
if event and event.get("event") == "window_created":
data = event.get("data", {})
window_id = data.get("window")
wm_class = data.get("class", "").lower()
for pattern, rules in RULES.items():
if pattern in wm_class:
print(f"Applying rules to {wm_class}")
if "workspace" in rules:
client.move_client_to_workspace(
window_id, rules["workspace"]
)
if "floating" in rules:
client.float_client(
window_id, rules["floating"]
)
break
except KeyboardInterrupt:
pass
finally:
client.disconnect()
auto_arrange()</code></pre>
</div>
<h2 id="automation">Automation Recipes</h2>
<h3>Auto-Arrange Windows by Class</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
LAYOUT_RULES = {
"code": {"workspace": 0, "floating": False},
"firefox": {"workspace": 1, "floating": False},
"telegram": {"workspace": 2, "floating": True},
"slack": {"workspace": 2, "floating": True},
}
def auto_arrange():
client = DWNClient()
client.connect()
windows = client.list_windows()
for w in windows:
win_class = w["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 w["floating"] != rules["floating"]:
client.set_floating(w["id"], rules["floating"])
break
client.disconnect()
auto_arrange()</code></pre>
</div>
<h3>Screenshot Monitor</h3>
<div class="code-block">
<pre><code>import time
import base64
from datetime import datetime
from dwn_api_client import DWNClient
def screenshot_monitor(interval=60, output_dir="screenshots"):
"""Take periodic screenshots"""
import os
os.makedirs(output_dir, exist_ok=True)
client = DWNClient()
client.connect()
try:
while True:
result = client.screenshot("fullscreen")
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{output_dir}/screen_{timestamp}.png"
with open(filename, "wb") as f:
f.write(base64.b64decode(result["data"]))
print(f"Saved: {filename}")
time.sleep(interval)
except KeyboardInterrupt:
print("Stopped")
finally:
client.disconnect()
screenshot_monitor(interval=300) # Every 5 minutes</code></pre>
</div>
<h3>Window 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"""
client = DWNClient()
client.connect()
last_focused = None
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)
if focused and focused["id"] != 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"]
time.sleep(1)
except KeyboardInterrupt:
print("Stopped")
finally:
client.disconnect()
focus_logger()</code></pre>
</div>
<h3>Keyboard Macro</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
import time
def run_macro(client, actions, delay=0.1):
"""Execute a sequence of actions"""
for action in actions:
if action["type"] == "key":
client.key_press(action["key"], action.get("modifiers", []))
elif action["type"] == "type":
client.type_text(action["text"])
elif action["type"] == "click":
client.mouse_click(action.get("button", 1),
action.get("x"), action.get("y"))
elif action["type"] == "wait":
time.sleep(action["seconds"])
time.sleep(delay)
# Example: Open terminal and run command
macro = [
{"type": "key", "key": "t", "modifiers": ["ctrl", "alt"]},
{"type": "wait", "seconds": 1},
{"type": "type", "text": "ls -la"},
{"type": "key", "key": "Return"},
]
client = DWNClient()
client.connect()
run_macro(client, macro)
client.disconnect()</code></pre>
</div>
<h3>OCR Screen Reader</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
def read_active_window():
"""Extract and print text from active window"""
client = DWNClient()
client.connect()
# Capture active window
screenshot = client.screenshot("active")
# Extract text
ocr_result = client.ocr(screenshot["data"])
print(f"Text from active window (confidence: {ocr_result['confidence']:.0%}):")
print("-" * 40)
print(ocr_result["text"])
client.disconnect()
read_active_window()</code></pre>
</div>
<h3 id="browser-ocr">Browser Automation with OCR</h3>
<p>Complete example that opens a browser, performs a Google search, scrolls through results, and extracts text using OCR. See <code>examples/browser_ocr_demo.py</code> for the full script.</p>
<div class="code-block">
<pre><code>#!/usr/bin/env python3
import asyncio
import json
import sys
import base64
from typing import List
class DWNAutomation:
def __init__(self, host: str = "localhost", port: int = 8777):
self.uri = f"ws://{host}:{port}/ws"
self.ws = None
async def connect(self):
import websockets
self.ws = await websockets.connect(self.uri)
async def disconnect(self):
if self.ws:
await self.ws.close()
async def send_command(self, command: dict) -> dict:
await self.ws.send(json.dumps(command))
return json.loads(await self.ws.recv())
async def run_command(self, exec_cmd: str) -> dict:
return await self.send_command({"command": "run_command", "exec": exec_cmd})
async def get_focused_client(self) -> dict:
return await self.send_command({"command": "get_focused_client"})
async def focus_client(self, window_id: int) -> dict:
return await self.send_command({"command": "focus_client", "window": window_id})
async def key_tap(self, keysym: str, modifiers: List[str] = None) -> dict:
cmd = {"command": "key_tap", "keysym": keysym}
if modifiers:
cmd["modifiers"] = modifiers
return await self.send_command(cmd)
async def key_type(self, text: str) -> dict:
return await self.send_command({"command": "key_type", "text": text})
async def mouse_scroll(self, direction: str, amount: int = 1) -> dict:
return await self.send_command({
"command": "mouse_scroll", "direction": direction, "amount": amount
})
async def screenshot(self, mode: str = "active") -> dict:
return await self.send_command({"command": "screenshot", "mode": mode})
async def ocr(self, image_base64: str) -> dict:
return await self.send_command({"command": "ocr", "image": image_base64})
async def main():
automation = DWNAutomation()
await automation.connect()
# Open default browser with Google
await automation.run_command("xdg-open https://www.google.nl")
await asyncio.sleep(5.0)
# Get focused browser window
result = await automation.get_focused_client()
window_id = int(result.get("client", {}).get("window", 0))
# Search for something
await automation.key_type("ponies")
await automation.key_tap("Return")
await asyncio.sleep(4.0)
# Scroll and collect OCR text from multiple pages
all_text = []
for i in range(4):
# Take screenshot and run OCR
screenshot = await automation.screenshot("active")
image_data = screenshot.get("data", "")
# Save screenshot
with open(f"screenshot_{i+1}.png", "wb") as f:
f.write(base64.b64decode(image_data))
# Extract text
ocr_result = await automation.ocr(image_data)
text = ocr_result.get("text", "").strip()
if text:
all_text.append(f"--- Page {i+1} ---\n{text}")
# Scroll down for next page
if i < 3:
await automation.mouse_scroll("down", 5)
await asyncio.sleep(1.5)
# Print combined results
print("EXTRACTED TEXT:")
print("\n\n".join(all_text))
await automation.disconnect()
asyncio.run(main())</code></pre>
</div>
<p>Run the included demo script:</p>
<div class="code-block">
<pre><code># Install dependency
pip install websockets
# Run the demo
python3 examples/browser_ocr_demo.py</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+313
View File
@@ -0,0 +1,313 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Overview - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link active">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>API Overview</h1>
<p class="lead">Introduction to DWN's WebSocket API</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#introduction">Introduction</a></li>
<li><a href="#enabling">Enabling the API</a></li>
<li><a href="#connecting">Connecting</a></li>
<li><a href="#protocol">Protocol</a></li>
<li><a href="#quick-start">Quick Start</a></li>
<li><a href="#clients">Client Libraries</a></li>
</ul>
</div>
<h2 id="introduction">Introduction</h2>
<p>DWN provides a WebSocket API for full programmatic control of the window manager. This enables:</p>
<ul>
<li><strong>Test Automation</strong> - Automated UI testing and scripting</li>
<li><strong>Custom Hotkeys</strong> - Build custom keyboard shortcuts</li>
<li><strong>Remote Control</strong> - Control DWN from other machines or devices</li>
<li><strong>Integration</strong> - Connect DWN to other applications</li>
<li><strong>Accessibility</strong> - Build alternative input methods</li>
</ul>
<h2 id="enabling">Enabling the API</h2>
<p>The API is disabled by default. Enable it in <code>~/.config/dwn/config</code>:</p>
<div class="code-block">
<pre><code>[api]
enabled = true
port = 8777</code></pre>
</div>
<p>Restart DWN for changes to take effect.</p>
<div class="alert alert-info">
<strong>Security Note:</strong> The API listens on localhost only by default. Be cautious when exposing to network.
</div>
<h2 id="connecting">Connecting</h2>
<p>Connect via WebSocket to <code>ws://localhost:8777</code> (or your configured port).</p>
<h3>Testing with wscat</h3>
<div class="code-block">
<pre><code># Install wscat
npm install -g wscat
# Connect to DWN
wscat -c ws://localhost:8777
# Send a command
> {"command": "list_windows"}
< {"status": "ok", "windows": [...]}</code></pre>
</div>
<h3>Testing with websocat</h3>
<div class="code-block">
<pre><code># Install websocat
cargo install websocat
# Connect and send command
echo '{"command": "list_windows"}' | websocat ws://localhost:8777</code></pre>
</div>
<h2 id="protocol">Protocol</h2>
<h3>Request Format</h3>
<p>All requests are JSON objects with a <code>command</code> field:</p>
<div class="code-block">
<pre><code>{
"command": "command_name",
"param1": "value1",
"param2": "value2"
}</code></pre>
</div>
<h3>Response Format</h3>
<p>Responses include a <code>status</code> field:</p>
<div class="code-block">
<pre><code>// Success
{
"status": "ok",
"data": {...}
}
// Error
{
"status": "error",
"message": "Error description"
}</code></pre>
</div>
<h3>Command Categories</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Category</th>
<th>Commands</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows</td>
<td>list_windows, focus_window, close_window, move_window, resize_window</td>
</tr>
<tr>
<td>Workspaces</td>
<td>list_workspaces, switch_workspace, move_to_workspace</td>
</tr>
<tr>
<td>Layout</td>
<td>get_layout, set_layout, set_master_ratio</td>
</tr>
<tr>
<td>Keyboard</td>
<td>key_press, key_release, type_text</td>
</tr>
<tr>
<td>Mouse</td>
<td>mouse_move, mouse_click, mouse_drag</td>
</tr>
<tr>
<td>Screenshot</td>
<td>screenshot</td>
</tr>
<tr>
<td>OCR</td>
<td>ocr</td>
</tr>
<tr>
<td>System</td>
<td>get_monitors, spawn, notify</td>
</tr>
</tbody>
</table>
</div>
<h2 id="quick-start">Quick Start</h2>
<h3>List Windows</h3>
<div class="code-block">
<pre><code>// Request
{"command": "list_windows"}
// Response
{
"status": "ok",
"windows": [
{
"id": 12345678,
"title": "Firefox",
"class": "firefox",
"workspace": 0,
"x": 0, "y": 32,
"width": 960, "height": 540,
"focused": true,
"floating": false
}
]
}</code></pre>
</div>
<h3>Focus a Window</h3>
<div class="code-block">
<pre><code>{"command": "focus_window", "window": 12345678}</code></pre>
</div>
<h3>Switch Workspace</h3>
<div class="code-block">
<pre><code>{"command": "switch_workspace", "workspace": 2}</code></pre>
</div>
<h3>Type Text</h3>
<div class="code-block">
<pre><code>{"command": "type_text", "text": "Hello, World!"}</code></pre>
</div>
<h3>Take Screenshot</h3>
<div class="code-block">
<pre><code>// Request
{"command": "screenshot", "mode": "fullscreen"}
// Response
{
"status": "ok",
"format": "png",
"encoding": "base64",
"width": 1920,
"height": 1080,
"data": "iVBORw0KGgo..."
}</code></pre>
</div>
<h2 id="clients">Client Libraries</h2>
<h3>Python Client</h3>
<p>A Python client is included in the <code>examples/</code> directory:</p>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
# Connect
client = DWNClient()
client.connect()
# List windows
windows = client.list_windows()
# Focus a window
client.focus_window(windows[0]['id'])
# Take screenshot
result = client.screenshot('fullscreen')
with open('screenshot.png', 'wb') as f:
f.write(base64.b64decode(result['data']))
# Disconnect
client.disconnect()</code></pre>
</div>
<h3>JavaScript (Browser)</h3>
<div class="code-block">
<pre><code>const ws = new WebSocket('ws://localhost:8777');
ws.onopen = () => {
ws.send(JSON.stringify({command: 'list_windows'}));
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
console.log(response);
};</code></pre>
</div>
<h3>Web Remote</h3>
<p>A web-based remote control interface is included at <code>examples/web_remote.html</code>. Open it in a browser to control DWN graphically.</p>
<h2>Next Steps</h2>
<ul>
<li><a href="api-reference.html">API Reference</a> - Complete command documentation</li>
<li><a href="api-examples.html">API Examples</a> - Code examples for common tasks</li>
</ul>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+858
View File
@@ -0,0 +1,858 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>API Reference - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link active">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>API Reference</h1>
<p class="lead">Complete WebSocket API command reference</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#windows">Window Commands</a></li>
<li><a href="#workspaces">Workspace Commands</a></li>
<li><a href="#layout">Layout Commands</a></li>
<li><a href="#keyboard">Keyboard Commands</a></li>
<li><a href="#mouse">Mouse Commands</a></li>
<li><a href="#screenshot">Screenshot Commands</a></li>
<li><a href="#ocr">OCR Commands</a></li>
<li><a href="#events">Event Subscription</a></li>
<li><a href="#system">System Commands</a></li>
</ul>
</div>
<h2 id="windows">Window Commands</h2>
<h3>list_windows</h3>
<p>Get list of all managed windows.</p>
<div class="code-block">
<pre><code>// Request
{"command": "list_windows"}
// Response
{
"status": "ok",
"windows": [
{
"id": 12345678,
"title": "Window Title",
"class": "window-class",
"workspace": 0,
"x": 0,
"y": 32,
"width": 960,
"height": 540,
"focused": true,
"floating": false,
"fullscreen": false,
"maximized": false,
"minimized": false
}
]
}</code></pre>
</div>
<h3>focus_window</h3>
<p>Focus a window by ID.</p>
<div class="code-block">
<pre><code>// Request
{"command": "focus_window", "window": 12345678}
// Response
{"status": "ok"}</code></pre>
</div>
<h3>close_window</h3>
<p>Close a window by ID.</p>
<div class="code-block">
<pre><code>// Request
{"command": "close_window", "window": 12345678}
// Response
{"status": "ok"}</code></pre>
</div>
<h3>move_window</h3>
<p>Move a window to specified position.</p>
<div class="code-block">
<pre><code>// Request
{
"command": "move_window",
"window": 12345678,
"x": 100,
"y": 100
}
// Response
{"status": "ok"}</code></pre>
</div>
<h3>resize_window</h3>
<p>Resize a window to specified dimensions.</p>
<div class="code-block">
<pre><code>// Request
{
"command": "resize_window",
"window": 12345678,
"width": 800,
"height": 600
}
// Response
{"status": "ok"}</code></pre>
</div>
<h3>minimize_window</h3>
<p>Minimize a window.</p>
<div class="code-block">
<pre><code>{"command": "minimize_window", "window": 12345678}</code></pre>
</div>
<h3>maximize_window</h3>
<p>Maximize a window.</p>
<div class="code-block">
<pre><code>{"command": "maximize_window", "window": 12345678}</code></pre>
</div>
<h3>restore_window</h3>
<p>Restore a minimized or maximized window.</p>
<div class="code-block">
<pre><code>{"command": "restore_window", "window": 12345678}</code></pre>
</div>
<h3>set_fullscreen</h3>
<p>Set window fullscreen state.</p>
<div class="code-block">
<pre><code>{"command": "set_fullscreen", "window": 12345678, "fullscreen": true}</code></pre>
</div>
<h3>set_floating</h3>
<p>Set window floating state.</p>
<div class="code-block">
<pre><code>{"command": "set_floating", "window": 12345678, "floating": true}</code></pre>
</div>
<h3>raise_window</h3>
<p>Raise window to top of stack.</p>
<div class="code-block">
<pre><code>{"command": "raise_window", "window": 12345678}</code></pre>
</div>
<h3>lower_window</h3>
<p>Lower window to bottom of stack.</p>
<div class="code-block">
<pre><code>{"command": "lower_window", "window": 12345678}</code></pre>
</div>
<h2 id="workspaces">Workspace Commands</h2>
<h3>list_workspaces</h3>
<p>Get list of all workspaces.</p>
<div class="code-block">
<pre><code>// Request
{"command": "list_workspaces"}
// Response
{
"status": "ok",
"workspaces": [
{
"index": 0,
"name": "1",
"layout": "tiling",
"window_count": 3,
"current": true
}
],
"current": 0
}</code></pre>
</div>
<h3>switch_workspace</h3>
<p>Switch to specified workspace (0-8).</p>
<div class="code-block">
<pre><code>{"command": "switch_workspace", "workspace": 2}</code></pre>
</div>
<h3>move_to_workspace</h3>
<p>Move focused window to specified workspace.</p>
<div class="code-block">
<pre><code>{"command": "move_to_workspace", "workspace": 3}</code></pre>
</div>
<h3>move_window_to_workspace</h3>
<p>Move specific window to workspace.</p>
<div class="code-block">
<pre><code>{
"command": "move_window_to_workspace",
"window": 12345678,
"workspace": 3
}</code></pre>
</div>
<h2 id="layout">Layout Commands</h2>
<h3>get_layout</h3>
<p>Get current workspace layout.</p>
<div class="code-block">
<pre><code>// Request
{"command": "get_layout"}
// Response
{
"status": "ok",
"layout": "tiling",
"master_ratio": 0.55,
"master_count": 1
}</code></pre>
</div>
<h3>set_layout</h3>
<p>Set workspace layout mode.</p>
<div class="code-block">
<pre><code>// Options: "tiling", "floating", "monocle"
{"command": "set_layout", "layout": "floating"}</code></pre>
</div>
<h3>cycle_layout</h3>
<p>Cycle to next layout mode.</p>
<div class="code-block">
<pre><code>{"command": "cycle_layout"}</code></pre>
</div>
<h3>set_master_ratio</h3>
<p>Set master area ratio (0.1 to 0.9).</p>
<div class="code-block">
<pre><code>{"command": "set_master_ratio", "ratio": 0.6}</code></pre>
</div>
<h3>adjust_master_ratio</h3>
<p>Adjust master ratio by delta.</p>
<div class="code-block">
<pre><code>{"command": "adjust_master_ratio", "delta": 0.05}</code></pre>
</div>
<h3>set_master_count</h3>
<p>Set number of master windows (1 to 10).</p>
<div class="code-block">
<pre><code>{"command": "set_master_count", "count": 2}</code></pre>
</div>
<h3>adjust_master_count</h3>
<p>Adjust master count by delta.</p>
<div class="code-block">
<pre><code>{"command": "adjust_master_count", "delta": 1}</code></pre>
</div>
<h2 id="keyboard">Keyboard Commands</h2>
<h3>key_press</h3>
<p>Simulate key press. Supports modifiers.</p>
<div class="code-block">
<pre><code>// Simple key
{"command": "key_press", "key": "a"}
// With modifiers
{"command": "key_press", "key": "c", "modifiers": ["ctrl"]}
// Special keys
{"command": "key_press", "key": "Return"}
{"command": "key_press", "key": "Tab"}
{"command": "key_press", "key": "Escape"}
{"command": "key_press", "key": "F1"}</code></pre>
</div>
<p>Available modifiers: <code>ctrl</code>, <code>shift</code>, <code>alt</code>, <code>super</code></p>
<h3>key_release</h3>
<p>Simulate key release.</p>
<div class="code-block">
<pre><code>{"command": "key_release", "key": "a"}</code></pre>
</div>
<h3>type_text</h3>
<p>Type a string of text.</p>
<div class="code-block">
<pre><code>{"command": "type_text", "text": "Hello, World!"}</code></pre>
</div>
<h3>key_combo</h3>
<p>Press a key combination.</p>
<div class="code-block">
<pre><code>// Ctrl+Alt+T
{
"command": "key_combo",
"keys": ["ctrl", "alt", "t"]
}
// Ctrl+Shift+S
{
"command": "key_combo",
"keys": ["ctrl", "shift", "s"]
}</code></pre>
</div>
<h2 id="mouse">Mouse Commands</h2>
<h3>mouse_move</h3>
<p>Move mouse to absolute position.</p>
<div class="code-block">
<pre><code>{"command": "mouse_move", "x": 500, "y": 300}</code></pre>
</div>
<h3>mouse_move_relative</h3>
<p>Move mouse relative to current position.</p>
<div class="code-block">
<pre><code>{"command": "mouse_move_relative", "dx": 10, "dy": -5}</code></pre>
</div>
<h3>mouse_click</h3>
<p>Click mouse button.</p>
<div class="code-block">
<pre><code>// Left click at current position
{"command": "mouse_click", "button": 1}
// Right click at position
{"command": "mouse_click", "button": 3, "x": 100, "y": 200}
// Double click
{"command": "mouse_click", "button": 1, "count": 2}</code></pre>
</div>
<p>Buttons: 1=left, 2=middle, 3=right, 4=scroll up, 5=scroll down</p>
<h3>mouse_down</h3>
<p>Press mouse button without releasing.</p>
<div class="code-block">
<pre><code>{"command": "mouse_down", "button": 1}</code></pre>
</div>
<h3>mouse_up</h3>
<p>Release mouse button.</p>
<div class="code-block">
<pre><code>{"command": "mouse_up", "button": 1}</code></pre>
</div>
<h3>mouse_drag</h3>
<p>Drag from one position to another.</p>
<div class="code-block">
<pre><code>{
"command": "mouse_drag",
"from_x": 100,
"from_y": 100,
"to_x": 500,
"to_y": 300,
"button": 1
}</code></pre>
</div>
<h3>mouse_scroll</h3>
<p>Scroll mouse wheel.</p>
<div class="code-block">
<pre><code>// Scroll up
{"command": "mouse_scroll", "direction": "up", "amount": 3}
// Scroll down
{"command": "mouse_scroll", "direction": "down", "amount": 3}</code></pre>
</div>
<h2 id="screenshot">Screenshot Commands</h2>
<h3>screenshot</h3>
<p>Capture screenshot. Returns base64-encoded PNG.</p>
<h4>Async Mode (Default)</h4>
<p>By default, screenshot operations run asynchronously to avoid blocking the API. Set <code>"async": false</code> for synchronous behavior.</p>
<div class="code-block">
<pre><code>// Async (default) - returns when capture completes
{"command": "screenshot", "mode": "fullscreen"}
// Synchronous - blocks until complete
{"command": "screenshot", "mode": "fullscreen", "async": false}</code></pre>
</div>
<h4>Fullscreen Mode</h4>
<div class="code-block">
<pre><code>// Request
{"command": "screenshot", "mode": "fullscreen"}
// Response
{
"status": "ok",
"format": "png",
"encoding": "base64",
"width": 1920,
"height": 1080,
"data": "iVBORw0KGgoAAAANSUhEUg..."
}</code></pre>
</div>
<h4>Active Window Mode</h4>
<div class="code-block">
<pre><code>{"command": "screenshot", "mode": "active"}</code></pre>
</div>
<h4>Specific Window Mode</h4>
<div class="code-block">
<pre><code>{"command": "screenshot", "mode": "window", "window": 12345678}</code></pre>
</div>
<h4>Area Mode</h4>
<div class="code-block">
<pre><code>{
"command": "screenshot",
"mode": "area",
"x": 100,
"y": 100,
"width": 400,
"height": 300
}</code></pre>
</div>
<h2 id="ocr">OCR Commands</h2>
<h3>ocr</h3>
<p>Extract text from image using OCR.</p>
<h4>Async Mode (Default)</h4>
<p>OCR operations run asynchronously by default since text extraction can take 1-10 seconds. Set <code>"async": false</code> for synchronous behavior.</p>
<div class="code-block">
<pre><code>// Async (default)
{"command": "ocr", "image": "...", "language": "eng"}
// Synchronous
{"command": "ocr", "image": "...", "language": "eng", "async": false}</code></pre>
</div>
<h4>Request/Response</h4>
<div class="code-block">
<pre><code>// Request
{
"command": "ocr",
"image": "iVBORw0KGgoAAAANSUhEUg...", // base64 PNG
"language": "eng" // optional, default "eng"
}
// Response
{
"status": "ok",
"text": "Extracted text from image...",
"confidence": 0.95
}</code></pre>
</div>
<p>Common language codes: <code>eng</code> (English), <code>deu</code> (German), <code>fra</code> (French), <code>spa</code> (Spanish)</p>
<h2 id="events">Event Subscription</h2>
<p>DWN supports real-time event streaming over WebSocket. Subscribe to events to receive notifications about window actions, workspace changes, input events, and more.</p>
<h3>list_events</h3>
<p>Get list of all available event types.</p>
<div class="code-block">
<pre><code>// Request
{"command": "list_events"}
// Response
{
"status": "ok",
"events": [
"window_created",
"window_destroyed",
"window_focused",
"window_unfocused",
"window_moved",
"window_resized",
"window_minimized",
"window_restored",
"window_maximized",
"window_unmaximized",
"window_fullscreen",
"window_unfullscreen",
"window_floating",
"window_unfloating",
"window_title_changed",
"window_raised",
"window_lowered",
"workspace_switched",
"workspace_window_added",
"workspace_window_removed",
"workspace_layout_changed",
"workspace_master_ratio_changed",
"workspace_master_count_changed",
"workspace_arranged",
"layout_changed",
"key_pressed",
"key_released",
"shortcut_triggered",
"mouse_moved",
"mouse_button_pressed",
"mouse_button_released",
"drag_started",
"drag_ended",
"show_desktop_toggled",
"notification_shown",
"notification_closed"
]
}</code></pre>
</div>
<h3>subscribe</h3>
<p>Subscribe to specific events or all events.</p>
<div class="code-block">
<pre><code>// Subscribe to specific events
{"command": "subscribe", "events": ["window_focused", "window_created"]}
// Subscribe to all events
{"command": "subscribe", "all": true}
// Response
{
"status": "ok",
"subscribed": ["window_focused", "window_created"]
}</code></pre>
</div>
<h3>unsubscribe</h3>
<p>Unsubscribe from specific events or all events.</p>
<div class="code-block">
<pre><code>// Unsubscribe from specific events
{"command": "unsubscribe", "events": ["window_focused"]}
// Unsubscribe from all events
{"command": "unsubscribe", "all": true}
// Response
{"status": "ok"}</code></pre>
</div>
<h3>Event Message Format</h3>
<p>When subscribed, events are pushed to the client as JSON messages:</p>
<div class="code-block">
<pre><code>// Window focus event
{
"type": "event",
"event": "window_focused",
"data": {
"window": 12345678,
"title": "Firefox",
"previous_window": 87654321
}
}
// Window moved event
{
"type": "event",
"event": "window_moved",
"data": {
"window": 12345678,
"old_x": 0,
"old_y": 32,
"new_x": 100,
"new_y": 100
}
}
// Workspace switched event
{
"type": "event",
"event": "workspace_switched",
"data": {
"old_workspace": 0,
"new_workspace": 2
}
}
// Keyboard shortcut triggered
{
"type": "event",
"event": "shortcut_triggered",
"data": {
"name": "Return",
"description": "Open terminal"
}
}
// Notification shown
{
"type": "event",
"event": "notification_shown",
"data": {
"id": 1,
"summary": "New Message",
"body": "You have a new message"
}
}</code></pre>
</div>
<h3>Event Types Reference</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Event</th>
<th>Description</th>
<th>Data Fields</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>window_created</code></td>
<td>New window managed</td>
<td>window, title, class, workspace</td>
</tr>
<tr>
<td><code>window_destroyed</code></td>
<td>Window closed</td>
<td>window, title, workspace</td>
</tr>
<tr>
<td><code>window_focused</code></td>
<td>Window received focus</td>
<td>window, title, previous_window</td>
</tr>
<tr>
<td><code>window_unfocused</code></td>
<td>Window lost focus</td>
<td>window, title</td>
</tr>
<tr>
<td><code>window_moved</code></td>
<td>Window position changed</td>
<td>window, old_x, old_y, new_x, new_y</td>
</tr>
<tr>
<td><code>window_resized</code></td>
<td>Window size changed</td>
<td>window, old_width, old_height, new_width, new_height</td>
</tr>
<tr>
<td><code>window_minimized</code></td>
<td>Window minimized</td>
<td>window</td>
</tr>
<tr>
<td><code>window_restored</code></td>
<td>Window restored from minimized</td>
<td>window</td>
</tr>
<tr>
<td><code>window_maximized</code></td>
<td>Window maximized/unmaximized</td>
<td>window, maximized</td>
</tr>
<tr>
<td><code>window_fullscreen</code></td>
<td>Fullscreen toggled</td>
<td>window, fullscreen</td>
</tr>
<tr>
<td><code>window_floating</code></td>
<td>Floating mode toggled</td>
<td>window, floating</td>
</tr>
<tr>
<td><code>window_title_changed</code></td>
<td>Window title updated</td>
<td>window, old_title, new_title</td>
</tr>
<tr>
<td><code>workspace_switched</code></td>
<td>Active workspace changed</td>
<td>old_workspace, new_workspace</td>
</tr>
<tr>
<td><code>workspace_layout_changed</code></td>
<td>Workspace layout changed</td>
<td>workspace, old_layout, new_layout</td>
</tr>
<tr>
<td><code>key_pressed</code></td>
<td>Key pressed</td>
<td>keycode, keysym, modifiers</td>
</tr>
<tr>
<td><code>shortcut_triggered</code></td>
<td>Keyboard shortcut executed</td>
<td>name, description</td>
</tr>
<tr>
<td><code>mouse_button_pressed</code></td>
<td>Mouse button pressed</td>
<td>button, x, y</td>
</tr>
<tr>
<td><code>drag_started</code></td>
<td>Window drag/resize started</td>
<td>window, is_resize</td>
</tr>
<tr>
<td><code>drag_ended</code></td>
<td>Window drag/resize ended</td>
<td>window, is_resize</td>
</tr>
<tr>
<td><code>show_desktop_toggled</code></td>
<td>Show desktop mode toggled</td>
<td>shown</td>
</tr>
<tr>
<td><code>notification_shown</code></td>
<td>Notification displayed</td>
<td>id, summary, body</td>
</tr>
<tr>
<td><code>notification_closed</code></td>
<td>Notification closed</td>
<td>id</td>
</tr>
</tbody>
</table>
</div>
<h2 id="system">System Commands</h2>
<h3>get_monitors</h3>
<p>Get monitor information.</p>
<div class="code-block">
<pre><code>// Response
{
"status": "ok",
"monitors": [
{
"index": 0,
"x": 0,
"y": 0,
"width": 1920,
"height": 1080,
"primary": true
}
]
}</code></pre>
</div>
<h3>spawn</h3>
<p>Spawn a process.</p>
<div class="code-block">
<pre><code>{"command": "spawn", "program": "firefox"}</code></pre>
</div>
<h3>notify</h3>
<p>Show a notification.</p>
<div class="code-block">
<pre><code>{
"command": "notify",
"summary": "Title",
"body": "Notification body text",
"timeout": 5000
}</code></pre>
</div>
<h3>get_focused</h3>
<p>Get currently focused window.</p>
<div class="code-block">
<pre><code>// Response
{
"status": "ok",
"window": {
"id": 12345678,
"title": "...",
...
}
}</code></pre>
</div>
<h3>show_desktop</h3>
<p>Toggle show desktop mode.</p>
<div class="code-block">
<pre><code>{"command": "show_desktop"}</code></pre>
</div>
<h3>quit</h3>
<p>Quit DWN.</p>
<div class="code-block">
<pre><code>{"command": "quit"}</code></pre>
</div>
<h2>Error Responses</h2>
<p>All commands may return errors:</p>
<div class="code-block">
<pre><code>{
"status": "error",
"message": "Window not found"
}
{
"status": "error",
"message": "Invalid parameter: workspace must be 0-8"
}
{
"status": "error",
"message": "Unknown command: invalid_command"
}</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+510
View File
@@ -0,0 +1,510 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Architecture - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link active">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Architecture</h1>
<p class="lead">Technical overview of DWN's design and implementation</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#overview">Overview</a></li>
<li><a href="#modules">Module Structure</a></li>
<li><a href="#event-loop">Event Loop</a></li>
<li><a href="#data-structures">Core Data Structures</a></li>
<li><a href="#protocols">X11 Protocols</a></li>
<li><a href="#design-patterns">Design Patterns</a></li>
</ul>
</div>
<h2 id="overview">Overview</h2>
<p>DWN is written in ANSI C for X11/Xorg. It uses a modular architecture with a global state singleton and event-driven design.</p>
<div class="code-block">
<pre><code>┌─────────────────────────────────────────────────────────┐
│ DWN Window Manager │
├─────────────────────────────────────────────────────────┤
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ main │ │ keys │ │ panel │ │ api │ │
│ │ loop │ │ handler │ │ render │ │ server │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │ │
│ ┌────┴────────────┴────────────┴────────────┴────┐ │
│ │ DWNState (Global Singleton) │ │
│ └────┬────────────┬────────────┬────────────┬────┘ │
│ │ │ │ │ │
│ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ ┌────┴────┐ │
│ │ client │ │workspace│ │ layout │ │ atoms │ │
│ │ mgmt │ │ mgmt │ │ engine │ │ (EWMH) │ │
│ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────────────────────┤
│ X11 / Xlib │
└─────────────────────────────────────────────────────────┘</code></pre>
</div>
<h2 id="modules">Module Structure</h2>
<p>Each module has a header in <code>include/</code> and implementation in <code>src/</code>.</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Module</th>
<th>Responsibility</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>main.c</code></td>
<td>X11 initialization, event loop, signal handling</td>
</tr>
<tr>
<td><code>client.c</code></td>
<td>Window management, focus, frame creation</td>
</tr>
<tr>
<td><code>workspace.c</code></td>
<td>9 virtual desktops, per-workspace state</td>
</tr>
<tr>
<td><code>layout.c</code></td>
<td>Tiling, floating, monocle algorithms</td>
</tr>
<tr>
<td><code>decorations.c</code></td>
<td>Window title bars and borders</td>
</tr>
<tr>
<td><code>panel.c</code></td>
<td>Top/bottom panels, taskbar, workspace indicators</td>
</tr>
<tr>
<td><code>systray.c</code></td>
<td>XEmbed system tray, WiFi/audio/battery widgets</td>
</tr>
<tr>
<td><code>notifications.c</code></td>
<td>D-Bus notification daemon</td>
</tr>
<tr>
<td><code>atoms.c</code></td>
<td>X11 EWMH/ICCCM atom management</td>
</tr>
<tr>
<td><code>keys.c</code></td>
<td>Keyboard shortcut capture and callbacks</td>
</tr>
<tr>
<td><code>config.c</code></td>
<td>INI-style config loading</td>
</tr>
<tr>
<td><code>api.c</code></td>
<td>WebSocket JSON API server</td>
</tr>
<tr>
<td><code>screenshot.c</code></td>
<td>X11 capture + PNG encoding</td>
</tr>
<tr>
<td><code>ocr.c</code></td>
<td>Tesseract OCR integration</td>
</tr>
<tr>
<td><code>ai.c</code></td>
<td>OpenRouter API, Exa search</td>
</tr>
<tr>
<td><code>util.c</code></td>
<td>Logging, memory, string utilities</td>
</tr>
</tbody>
</table>
</div>
<h2 id="event-loop">Event Loop</h2>
<p>The main event loop uses <code>select()</code> for multiplexed I/O across X11, D-Bus, and timers.</p>
<div class="code-block">
<pre><code>void dwn_run(void) {
int x11_fd = ConnectionNumber(dwn->display);
int dbus_fd = /* from dbus_connection */;
while (dwn->running) {
// 1. Process all pending X11 events
while (XPending(dwn->display)) {
XEvent ev;
XNextEvent(dwn->display, &ev);
dwn_handle_event(&ev);
}
// 2. Process D-Bus messages
notifications_process_messages();
// 3. Process async AI/Exa requests
ai_process_pending();
// 4. Check notification timeouts
notifications_update();
// 5. Handle delayed focus (focus-follow mode)
handle_pending_focus();
// 6. Periodic updates (animation, clock)
if (now - last_update >= 16) { // 60fps
news_update();
panel_render_all();
}
// 7. Wait for events with timeout
fd_set fds;
FD_SET(x11_fd, &fds);
FD_SET(dbus_fd, &fds);
struct timeval tv = {0, 16000}; // 16ms
select(max_fd + 1, &fds, NULL, NULL, &tv);
}
}</code></pre>
</div>
<h3>X11 Event Dispatch</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Event</th>
<th>Handler</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>MapRequest</code></td>
<td>New window → client_manage()</td>
</tr>
<tr>
<td><code>UnmapNotify</code></td>
<td>Window hidden → possibly client_unmanage()</td>
</tr>
<tr>
<td><code>DestroyNotify</code></td>
<td>Window destroyed → client_unmanage()</td>
</tr>
<tr>
<td><code>ConfigureRequest</code></td>
<td>Window resize request</td>
</tr>
<tr>
<td><code>PropertyNotify</code></td>
<td>Property changed → update title</td>
</tr>
<tr>
<td><code>Expose</code></td>
<td>Repaint needed → render</td>
</tr>
<tr>
<td><code>ButtonPress</code></td>
<td>Mouse click → focus, drag, panel click</td>
</tr>
<tr>
<td><code>MotionNotify</code></td>
<td>Mouse move → window drag/resize</td>
</tr>
<tr>
<td><code>KeyPress</code></td>
<td>Key pressed → shortcut callback</td>
</tr>
<tr>
<td><code>ClientMessage</code></td>
<td>EWMH requests, systray dock</td>
</tr>
</tbody>
</table>
</div>
<h2 id="data-structures">Core Data Structures</h2>
<h3>DWNState</h3>
<p>Global singleton containing all window manager state.</p>
<div class="code-block">
<pre><code>typedef struct {
Display *display; // X11 connection
int screen; // Default screen
Window root; // Root window
int screen_width, screen_height;
Monitor monitors[MAX_MONITORS];
int monitor_count;
Workspace workspaces[MAX_WORKSPACES]; // 9 workspaces
int current_workspace;
Client *client_list; // Doubly-linked list head
int client_count;
Panel *top_panel;
Panel *bottom_panel;
Config *config;
bool running;
bool ai_enabled;
// Drag state
Client *drag_client;
int drag_start_x, drag_start_y;
bool resizing;
// Alt-Tab state
bool is_alt_tabbing;
Client *alt_tab_client;
} DWNState;</code></pre>
</div>
<h3>Client</h3>
<p>Represents a managed window with decoration frame.</p>
<div class="code-block">
<pre><code>struct Client {
Window window; // Application window
Window frame; // Decoration frame (parent)
int x, y, width, height; // Current geometry
int old_x, old_y; // Saved for restore
int old_width, old_height;
uint32_t flags; // CLIENT_FLOATING, etc.
unsigned int workspace; // Workspace index (0-8)
char title[256];
char class[64];
SnapConstraint snap; // Snap state
Client *next, *prev; // Global list
Client *mru_next, *mru_prev; // MRU stack
};</code></pre>
</div>
<h3>Workspace</h3>
<p>Per-workspace layout and window state.</p>
<div class="code-block">
<pre><code>struct Workspace {
Client *clients; // Workspace client list
Client *focused; // Currently focused
Client *mru_head, *mru_tail; // MRU stack
LayoutType layout; // tiling/floating/monocle
float master_ratio; // 0.1 to 0.9
int master_count; // 1 to 10
char name[32];
};</code></pre>
</div>
<h3>Client Flags</h3>
<div class="code-block">
<pre><code>#define CLIENT_NORMAL 0
#define CLIENT_FLOATING (1 << 0)
#define CLIENT_FULLSCREEN (1 << 1)
#define CLIENT_URGENT (1 << 2)
#define CLIENT_MINIMIZED (1 << 3)
#define CLIENT_STICKY (1 << 4)
#define CLIENT_MAXIMIZED (1 << 5)
// Usage
if (c->flags & CLIENT_FLOATING) { ... }
c->flags |= CLIENT_FLOATING; // Set
c->flags &= ~CLIENT_FLOATING; // Clear</code></pre>
</div>
<h2 id="protocols">X11 Protocols</h2>
<h3>EWMH (Extended Window Manager Hints)</h3>
<p>Standard hints for modern window manager features.</p>
<ul>
<li><code>_NET_SUPPORTED</code> - List of supported atoms</li>
<li><code>_NET_CLIENT_LIST</code> - List of managed windows</li>
<li><code>_NET_CURRENT_DESKTOP</code> - Current workspace</li>
<li><code>_NET_ACTIVE_WINDOW</code> - Focused window</li>
<li><code>_NET_WM_STATE</code> - Window state (fullscreen, etc.)</li>
<li><code>_NET_WM_WINDOW_TYPE</code> - Window type (dialog, etc.)</li>
</ul>
<h3>ICCCM (Inter-Client Communication)</h3>
<p>Core X11 window manager protocol.</p>
<ul>
<li><code>WM_PROTOCOLS</code> - Supported protocols (WM_DELETE_WINDOW)</li>
<li><code>WM_NAME</code> - Window title</li>
<li><code>WM_CLASS</code> - Application class</li>
<li><code>WM_HINTS</code> - Input model, icons</li>
<li><code>WM_NORMAL_HINTS</code> - Size constraints</li>
</ul>
<h3>XEmbed (System Tray)</h3>
<p>Protocol for embedding application icons in system tray.</p>
<ul>
<li>Acquire <code>_NET_SYSTEM_TRAY_S0</code> selection</li>
<li>Handle <code>SYSTEM_TRAY_REQUEST_DOCK</code> messages</li>
<li>Send <code>XEMBED_EMBEDDED_NOTIFY</code> to docked icons</li>
</ul>
<h2 id="design-patterns">Design Patterns</h2>
<h3>Opaque Pointers</h3>
<p>Hide implementation details. Header exposes typedef, source defines struct.</p>
<div class="code-block">
<pre><code>// header.h
typedef struct module_t* Module;
Module module_create(void);
void module_destroy(Module m);
// source.c
struct module_t {
int private_field;
};</code></pre>
</div>
<h3>Return Code Error Handling</h3>
<p>Functions return status codes, pass results via output parameters.</p>
<div class="code-block">
<pre><code>typedef enum {
STATUS_OK = 0,
STATUS_ERROR_INVALID_ARG,
STATUS_ERROR_NO_MEMORY
} Status;
Status do_work(int input, int *output);</code></pre>
</div>
<h3>Goto Cleanup</h3>
<p>Centralized resource cleanup for functions with multiple allocations.</p>
<div class="code-block">
<pre><code>int process(void) {
char *buf = NULL;
int status = -1;
buf = malloc(1024);
if (!buf) goto cleanup;
// ... work ...
status = 0;
cleanup:
free(buf);
return status;
}</code></pre>
</div>
<h3>Module Prefix Convention</h3>
<p>All public functions prefixed with module name.</p>
<div class="code-block">
<pre><code>// client.h
void client_focus(Client *c);
void client_move(Client *c, int x, int y);
void client_resize(Client *c, int w, int h);
// workspace.h
void workspace_switch(int index);
void workspace_arrange(int index);</code></pre>
</div>
<h2>Key Constants</h2>
<div class="table-container">
<table>
<thead>
<tr>
<th>Constant</th>
<th>Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>MAX_CLIENTS</code></td>
<td>256</td>
<td>Maximum managed windows</td>
</tr>
<tr>
<td><code>MAX_WORKSPACES</code></td>
<td>9</td>
<td>Virtual desktops</td>
</tr>
<tr>
<td><code>MAX_MONITORS</code></td>
<td>8</td>
<td>Multi-monitor support</td>
</tr>
<tr>
<td><code>MAX_NOTIFICATIONS</code></td>
<td>32</td>
<td>Visible notifications</td>
</tr>
<tr>
<td><code>MAX_KEYBINDINGS</code></td>
<td>64</td>
<td>Keyboard shortcuts</td>
</tr>
<tr>
<td><code>MAX_TRAY_ICONS</code></td>
<td>32</td>
<td>System tray icons</td>
</tr>
</tbody>
</table>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+438
View File
@@ -0,0 +1,438 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Building from Source - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link active">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Building from Source</h1>
<p class="lead">Compile DWN for development or custom builds</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#requirements">Requirements</a></li>
<li><a href="#dependencies">Dependencies</a></li>
<li><a href="#building">Building</a></li>
<li><a href="#targets">Make Targets</a></li>
<li><a href="#development">Development</a></li>
<li><a href="#troubleshooting">Troubleshooting</a></li>
</ul>
</div>
<h2 id="requirements">Requirements</h2>
<ul>
<li>Linux with X11 (Xorg)</li>
<li>GCC compiler (or Clang)</li>
<li>GNU Make</li>
<li>pkg-config</li>
</ul>
<h2 id="dependencies">Dependencies</h2>
<h3>Required Libraries</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Library</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td>libX11</td>
<td>Core X11 protocol</td>
</tr>
<tr>
<td>libXext</td>
<td>X11 extensions</td>
</tr>
<tr>
<td>libXinerama</td>
<td>Multi-monitor support</td>
</tr>
<tr>
<td>libXrandr</td>
<td>Display configuration</td>
</tr>
<tr>
<td>libXft</td>
<td>Font rendering</td>
</tr>
<tr>
<td>libXtst</td>
<td>Input simulation (API)</td>
</tr>
<tr>
<td>fontconfig</td>
<td>Font discovery</td>
</tr>
<tr>
<td>libdbus-1</td>
<td>Notifications</td>
</tr>
<tr>
<td>libcurl</td>
<td>HTTP for AI features</td>
</tr>
<tr>
<td>libpng</td>
<td>Screenshot encoding</td>
</tr>
<tr>
<td>libtesseract</td>
<td>OCR text extraction</td>
</tr>
<tr>
<td>libleptonica</td>
<td>Image processing for OCR</td>
</tr>
</tbody>
</table>
</div>
<h3>Ubuntu / Debian</h3>
<div class="code-block">
<pre><code>sudo apt update && sudo apt install -y \
build-essential \
pkg-config \
libx11-dev \
libxext-dev \
libxinerama-dev \
libxrandr-dev \
libxft-dev \
libxtst-dev \
libfontconfig1-dev \
libdbus-1-dev \
libcurl4-openssl-dev \
libpng-dev \
libtesseract-dev \
libleptonica-dev \
tesseract-ocr \
tesseract-ocr-eng</code></pre>
</div>
<h3>Fedora / RHEL</h3>
<div class="code-block">
<pre><code>sudo dnf install -y \
gcc make \
pkg-config \
libX11-devel \
libXext-devel \
libXinerama-devel \
libXrandr-devel \
libXtst-devel \
dbus-devel \
libcurl-devel \
libpng-devel \
tesseract-devel \
leptonica-devel \
tesseract-langpack-eng</code></pre>
</div>
<h3>Arch Linux</h3>
<div class="code-block">
<pre><code>sudo pacman -S --needed \
base-devel \
pkg-config \
libx11 \
libxext \
libxinerama \
libxrandr \
libxtst \
dbus \
curl \
libpng \
tesseract \
tesseract-data-eng \
leptonica</code></pre>
</div>
<h3>Automatic Installation</h3>
<p>The Makefile can auto-detect your package manager:</p>
<div class="code-block">
<pre><code>make deps</code></pre>
</div>
<h2 id="building">Building</h2>
<h3>Clone Repository</h3>
<div class="code-block">
<pre><code>git clone https://github.com/retoor/dwn.git
cd dwn</code></pre>
</div>
<h3>Build Release</h3>
<div class="code-block">
<pre><code>make</code></pre>
</div>
<p>Output: <code>bin/dwn</code></p>
<h3>Build Debug</h3>
<div class="code-block">
<pre><code>make debug</code></pre>
</div>
<p>Includes debug symbols (<code>-g</code>) and <code>-DDEBUG</code> define.</p>
<h3>Build with Sanitizers</h3>
<div class="code-block">
<pre><code>make sanitize</code></pre>
</div>
<p>Enables AddressSanitizer and UndefinedBehaviorSanitizer for debugging memory issues.</p>
<h3>Install System-wide</h3>
<div class="code-block">
<pre><code>sudo make install</code></pre>
</div>
<p>Installs to:</p>
<ul>
<li><code>/usr/local/bin/dwn</code> - Binary</li>
<li><code>/usr/local/share/xsessions/dwn.desktop</code> - Session file</li>
<li><code>/etc/dwn/config.example</code> - Example config</li>
</ul>
<h3>Custom Prefix</h3>
<div class="code-block">
<pre><code>make PREFIX=/opt/dwn install</code></pre>
</div>
<h2 id="targets">Make Targets</h2>
<div class="table-container">
<table>
<thead>
<tr>
<th>Target</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>make</code></td>
<td>Build release version (optimized)</td>
</tr>
<tr>
<td><code>make debug</code></td>
<td>Build with debug symbols</td>
</tr>
<tr>
<td><code>make sanitize</code></td>
<td>Build with sanitizers</td>
</tr>
<tr>
<td><code>make clean</code></td>
<td>Remove build artifacts</td>
</tr>
<tr>
<td><code>make install</code></td>
<td>Install to PREFIX</td>
</tr>
<tr>
<td><code>make uninstall</code></td>
<td>Remove installation</td>
</tr>
<tr>
<td><code>make run</code></td>
<td>Test in Xephyr</td>
</tr>
<tr>
<td><code>make deps</code></td>
<td>Install dependencies</td>
</tr>
<tr>
<td><code>make format</code></td>
<td>Run clang-format</td>
</tr>
<tr>
<td><code>make check</code></td>
<td>Run cppcheck static analysis</td>
</tr>
</tbody>
</table>
</div>
<h2 id="development">Development</h2>
<h3>Testing in Xephyr</h3>
<p>Test without affecting your current session:</p>
<div class="code-block">
<pre><code># Automatic (recommended)
make run
# Manual
Xephyr :1 -screen 1920x1080 &amp;
DISPLAY=:1 ./bin/dwn</code></pre>
</div>
<h3>Code Formatting</h3>
<div class="code-block">
<pre><code>make format</code></pre>
</div>
<p>Uses clang-format with project style.</p>
<h3>Static Analysis</h3>
<div class="code-block">
<pre><code>make check</code></pre>
</div>
<p>Runs cppcheck for common issues.</p>
<h3>Debug Logging</h3>
<p>Debug builds write to <code>~/.local/share/dwn/dwn.log</code>:</p>
<div class="code-block">
<pre><code>tail -f ~/.local/share/dwn/dwn.log</code></pre>
</div>
<h3>Directory Structure</h3>
<div class="code-block">
<pre><code>dwn/
├── include/ # Header files
│ ├── dwn.h # Main state struct
│ ├── client.h # Window management
│ ├── workspace.h # Virtual desktops
│ ├── layout.h # Layout algorithms
│ ├── panel.h # Panels
│ ├── api.h # WebSocket API
│ ├── screenshot.h
│ ├── ocr.h
│ └── ...
├── src/ # Implementation
│ ├── main.c # Entry point, event loop
│ ├── client.c
│ ├── workspace.c
│ ├── layout.c
│ ├── panel.c
│ ├── api.c
│ ├── screenshot.c
│ ├── ocr.c
│ └── ...
├── build/ # Object files
├── bin/ # Output binary
├── examples/ # Client examples
├── manual/ # Documentation
├── Makefile
└── CLAUDE.md # Development guide</code></pre>
</div>
<h3>Adding a New Module</h3>
<ol>
<li>Create <code>include/newmodule.h</code> with public API</li>
<li>Create <code>src/newmodule.c</code> with implementation</li>
<li>Add <code>newmodule_init()</code> call to <code>main.c:dwn_init()</code></li>
<li>Add <code>newmodule_cleanup()</code> call to <code>main.c:dwn_cleanup()</code></li>
<li>Makefile automatically picks up new .c files</li>
</ol>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3>Missing pkg-config</h3>
<div class="code-block">
<pre><code># Check if library is found
pkg-config --cflags --libs x11
# If not found, install dev package
sudo apt install libx11-dev # Debian/Ubuntu</code></pre>
</div>
<h3>Tesseract not found</h3>
<div class="code-block">
<pre><code># Check installation
pkg-config --cflags --libs tesseract
# Install if missing
sudo apt install libtesseract-dev tesseract-ocr tesseract-ocr-eng</code></pre>
</div>
<h3>Linker errors</h3>
<p>Ensure all -dev packages are installed. The Makefile uses pkg-config to find libraries.</p>
<h3>Runtime errors</h3>
<ol>
<li>Check log file: <code>~/.local/share/dwn/dwn.log</code></li>
<li>Build with debug: <code>make clean && make debug</code></li>
<li>Build with sanitizers: <code>make clean && make sanitize</code></li>
<li>Run in GDB: <code>DISPLAY=:1 gdb ./bin/dwn</code></li>
</ol>
<h3>X11 errors</h3>
<p>Run with synchronous X11 for detailed errors:</p>
<div class="code-block">
<pre><code>DISPLAY=:1 ./bin/dwn --sync</code></pre>
</div>
<h3>Xephyr not starting</h3>
<div class="code-block">
<pre><code># Check if display :1 is in use
ls /tmp/.X11-unix/
# Use different display
Xephyr :2 -screen 1920x1080 &amp;
DISPLAY=:2 ./bin/dwn</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+389
View File
@@ -0,0 +1,389 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Configuration - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Configuration</h1>
<p class="lead">Customize DWN to your preferences</p>
</div>
<h2>Configuration File</h2>
<p>DWN uses an INI-style configuration file located at:</p>
<div class="code-block">
<pre><code>~/.config/dwn/config</code></pre>
</div>
<p>Create it from the example:</p>
<div class="code-block">
<pre><code>mkdir -p ~/.config/dwn
cp /etc/dwn/config.example ~/.config/dwn/config</code></pre>
</div>
<h2>Configuration Sections</h2>
<h3>[general] - Core Settings</h3>
<div class="code-block">
<pre><code>[general]
terminal = xfce4-terminal
launcher = dmenu_run
file_manager = thunar
focus_mode = click
focus_follow_delay = 100
decorations = true</code></pre>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>terminal</code></td>
<td>xfce4-terminal</td>
<td>Terminal emulator command</td>
</tr>
<tr>
<td><code>launcher</code></td>
<td>dmenu_run</td>
<td>Application launcher command</td>
</tr>
<tr>
<td><code>file_manager</code></td>
<td>thunar</td>
<td>File manager command</td>
</tr>
<tr>
<td><code>focus_mode</code></td>
<td>click</td>
<td><code>click</code> or <code>follow</code> (sloppy focus)</td>
</tr>
<tr>
<td><code>focus_follow_delay</code></td>
<td>100</td>
<td>Delay in ms for focus-follow mode (0-1000)</td>
</tr>
<tr>
<td><code>decorations</code></td>
<td>true</td>
<td>Show window decorations (title bar)</td>
</tr>
</tbody>
</table>
</div>
<h3>[appearance] - Visual Settings</h3>
<div class="code-block">
<pre><code>[appearance]
border_width = 0
title_height = 28
panel_height = 32
gap = 0
font = fixed</code></pre>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Option</th>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>border_width</code></td>
<td>0-50</td>
<td>Window border width in pixels</td>
</tr>
<tr>
<td><code>title_height</code></td>
<td>0-100</td>
<td>Title bar height in pixels</td>
</tr>
<tr>
<td><code>panel_height</code></td>
<td>0-100</td>
<td>Panel height in pixels</td>
</tr>
<tr>
<td><code>gap</code></td>
<td>0-100</td>
<td>Gap between windows in pixels</td>
</tr>
<tr>
<td><code>font</code></td>
<td>-</td>
<td>X11 font name</td>
</tr>
</tbody>
</table>
</div>
<h3>[layout] - Layout Behavior</h3>
<div class="code-block">
<pre><code>[layout]
default = tiling
master_ratio = 0.55
master_count = 1</code></pre>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Option</th>
<th>Values</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>default</code></td>
<td>tiling, floating, monocle</td>
<td>Default layout for new workspaces</td>
</tr>
<tr>
<td><code>master_ratio</code></td>
<td>0.1-0.9</td>
<td>Master area ratio in tiling layout</td>
</tr>
<tr>
<td><code>master_count</code></td>
<td>1-10</td>
<td>Number of windows in master area</td>
</tr>
</tbody>
</table>
</div>
<h3>[panels] - Panel Visibility</h3>
<div class="code-block">
<pre><code>[panels]
top = true
bottom = true</code></pre>
</div>
<h3>[colors] - Color Scheme</h3>
<p>Colors are specified in hex format <code>#RRGGBB</code>.</p>
<div class="code-block">
<pre><code>[colors]
panel_bg = #1a1a2e
panel_fg = #e0e0e0
workspace_active = #4a90d9
workspace_inactive = #3a3a4e
workspace_urgent = #d94a4a
title_focused_bg = #2d3a4a
title_focused_fg = #ffffff
title_unfocused_bg = #1a1a1a
title_unfocused_fg = #808080
border_focused = #4a90d9
border_unfocused = #333333
notification_bg = #2a2a3e
notification_fg = #ffffff</code></pre>
</div>
<h3>[api] - WebSocket API</h3>
<div class="code-block">
<pre><code>[api]
enabled = true
port = 8777</code></pre>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>enabled</code></td>
<td>false</td>
<td>Enable WebSocket API server</td>
</tr>
<tr>
<td><code>port</code></td>
<td>8777</td>
<td>API server port number</td>
</tr>
</tbody>
</table>
</div>
<h3>[ai] - AI Integration</h3>
<div class="code-block">
<pre><code>[ai]
model = google/gemini-2.0-flash-exp:free
openrouter_api_key = sk-or-v1-your-key
exa_api_key = your-exa-key</code></pre>
</div>
<p>API keys can also be set via environment variables:</p>
<ul>
<li><code>OPENROUTER_API_KEY</code></li>
<li><code>EXA_API_KEY</code></li>
</ul>
<h3>[autostart] - XDG Autostart</h3>
<div class="code-block">
<pre><code>[autostart]
enabled = true
xdg_autostart = true
path = ~/.config/dwn/autostart.d</code></pre>
</div>
<p>Directories scanned when <code>xdg_autostart = true</code>:</p>
<ul>
<li><code>/etc/xdg/autostart/*.desktop</code> - System defaults</li>
<li><code>~/.config/autostart/*.desktop</code> - User autostart</li>
<li><code>~/.config/dwn/autostart.d/*</code> - DWN-specific scripts</li>
</ul>
<h3>[demo] - Demo Mode Timing</h3>
<div class="code-block">
<pre><code>[demo]
step_delay = 4000
ai_timeout = 15000
window_timeout = 5000</code></pre>
</div>
<h2>Environment Variables</h2>
<div class="table-container">
<table>
<thead>
<tr>
<th>Variable</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>OPENROUTER_API_KEY</code></td>
<td>API key for AI command palette</td>
</tr>
<tr>
<td><code>EXA_API_KEY</code></td>
<td>API key for Exa semantic search</td>
</tr>
<tr>
<td><code>DISPLAY</code></td>
<td>X server to connect to</td>
</tr>
</tbody>
</table>
</div>
<h2>Example Configuration</h2>
<div class="code-block">
<pre><code>[general]
terminal = alacritty
launcher = rofi -show drun
file_manager = nautilus
focus_mode = click
decorations = true
[appearance]
border_width = 2
title_height = 24
panel_height = 28
gap = 5
font = monospace
[layout]
default = tiling
master_ratio = 0.55
master_count = 1
[panels]
top = true
bottom = false
[colors]
panel_bg = #282c34
panel_fg = #abb2bf
workspace_active = #61afef
workspace_inactive = #3e4451
border_focused = #61afef
border_unfocused = #3e4451
[api]
enabled = true
port = 8777
[autostart]
enabled = true
xdg_autostart = true</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+611
View File
@@ -0,0 +1,611 @@
/* retoor <retoor@molodetz.nl> */
/* DWN Documentation Styles */
:root {
--bg-primary: #1a1a2e;
--bg-secondary: #16213e;
--bg-tertiary: #0f3460;
--bg-code: #0d1117;
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--text-muted: #666;
--accent: #4a90d9;
--accent-hover: #5da0e9;
--border-color: #2d3748;
--success: #48bb78;
--warning: #ed8936;
--error: #f56565;
--sidebar-width: 280px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
min-height: 100vh;
}
a {
color: var(--accent);
text-decoration: none;
transition: color 0.2s;
}
a:hover {
color: var(--accent-hover);
}
.layout {
display: flex;
min-height: 100vh;
}
.sidebar {
width: var(--sidebar-width);
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
position: fixed;
top: 0;
left: 0;
height: 100vh;
overflow-y: auto;
z-index: 100;
}
.sidebar-header {
padding: 20px;
border-bottom: 1px solid var(--border-color);
}
.sidebar-header h1 {
font-size: 1.5rem;
color: var(--accent);
margin-bottom: 5px;
}
.sidebar-header .version {
font-size: 0.85rem;
color: var(--text-muted);
}
.sidebar-nav {
padding: 15px 0;
}
.nav-section {
margin-bottom: 10px;
}
.nav-section-title {
padding: 8px 20px;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
font-weight: 600;
}
.nav-link {
display: block;
padding: 8px 20px 8px 30px;
color: var(--text-secondary);
font-size: 0.9rem;
transition: all 0.2s;
border-left: 3px solid transparent;
}
.nav-link:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.nav-link.active {
background: var(--bg-tertiary);
color: var(--accent);
border-left-color: var(--accent);
}
.main-content {
flex: 1;
margin-left: var(--sidebar-width);
min-width: 0;
}
.content {
max-width: 900px;
margin: 0 auto;
padding: 40px;
}
.page-header {
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.page-header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
color: var(--text-primary);
}
.page-header .lead {
font-size: 1.2rem;
color: var(--text-secondary);
}
h2 {
font-size: 1.75rem;
margin: 40px 0 20px;
padding-bottom: 10px;
border-bottom: 1px solid var(--border-color);
color: var(--text-primary);
}
h3 {
font-size: 1.35rem;
margin: 30px 0 15px;
color: var(--text-primary);
}
h4 {
font-size: 1.1rem;
margin: 20px 0 10px;
color: var(--text-secondary);
}
p {
margin-bottom: 15px;
}
ul, ol {
margin: 0 0 20px 25px;
}
li {
margin-bottom: 8px;
}
code {
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
background: var(--bg-code);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
color: var(--accent);
}
pre {
background: var(--bg-code);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
overflow-x: auto;
margin: 20px 0;
}
pre code {
background: none;
padding: 0;
color: var(--text-primary);
font-size: 0.85rem;
line-height: 1.5;
}
.table-container {
overflow-x: auto;
margin: 20px 0;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-secondary);
font-weight: 600;
color: var(--text-primary);
}
tr:hover {
background: var(--bg-secondary);
}
.card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 25px;
margin: 20px 0;
}
.card-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 10px;
color: var(--text-primary);
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin: 30px 0;
}
.feature-card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 25px;
transition: transform 0.2s, border-color 0.2s;
}
.feature-card:hover {
transform: translateY(-2px);
border-color: var(--accent);
}
.feature-icon {
font-size: 2rem;
margin-bottom: 15px;
}
.feature-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 10px;
}
.feature-desc {
font-size: 0.9rem;
color: var(--text-secondary);
}
.shortcut-table code {
background: var(--bg-tertiary);
padding: 4px 8px;
border-radius: 4px;
font-weight: 500;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.badge-get { background: var(--success); color: #000; }
.badge-post { background: var(--accent); color: #fff; }
.badge-required { background: var(--error); color: #fff; }
.badge-optional { background: var(--text-muted); color: #fff; }
.method-block {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
margin: 25px 0;
overflow: hidden;
}
.method-header {
display: flex;
align-items: center;
gap: 15px;
padding: 15px 20px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
}
.method-name {
font-family: 'JetBrains Mono', monospace;
font-size: 1rem;
font-weight: 600;
}
.method-body {
padding: 20px;
}
.param-table {
margin: 15px 0;
}
.param-table th {
background: var(--bg-primary);
}
.example-tabs {
display: flex;
gap: 5px;
margin-bottom: -1px;
}
.example-tab {
padding: 8px 16px;
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-bottom: none;
border-radius: 6px 6px 0 0;
cursor: pointer;
font-size: 0.85rem;
color: var(--text-secondary);
transition: all 0.2s;
}
.example-tab:hover,
.example-tab.active {
background: var(--bg-code);
color: var(--text-primary);
}
.example-content {
display: none;
}
.example-content.active {
display: block;
}
.alert {
padding: 15px 20px;
border-radius: 8px;
margin: 20px 0;
border-left: 4px solid;
}
.alert-info {
background: rgba(74, 144, 217, 0.1);
border-color: var(--accent);
}
.alert-warning {
background: rgba(237, 137, 54, 0.1);
border-color: var(--warning);
}
.alert-success {
background: rgba(72, 187, 120, 0.1);
border-color: var(--success);
}
.toc {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.toc-title {
font-weight: 600;
margin-bottom: 10px;
color: var(--text-primary);
}
.toc-list {
list-style: none;
margin: 0;
padding: 0;
}
.toc-list li {
margin: 5px 0;
}
.toc-list a {
color: var(--text-secondary);
font-size: 0.9rem;
}
.copy-btn {
position: absolute;
top: 10px;
right: 10px;
padding: 5px 10px;
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-secondary);
cursor: pointer;
font-size: 0.75rem;
transition: all 0.2s;
}
.copy-btn:hover {
background: var(--accent);
color: #fff;
}
.code-block {
position: relative;
}
.search-box {
padding: 15px 20px;
border-bottom: 1px solid var(--border-color);
}
.search-input {
width: 100%;
padding: 10px 15px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 0.9rem;
}
.search-input:focus {
outline: none;
border-color: var(--accent);
}
.search-input::placeholder {
color: var(--text-muted);
}
footer {
margin-top: 60px;
padding: 30px 0;
border-top: 1px solid var(--border-color);
text-align: center;
color: var(--text-muted);
font-size: 0.85rem;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s;
}
.sidebar.open {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
.content {
padding: 20px;
}
.mobile-menu-btn {
display: block;
position: fixed;
top: 15px;
left: 15px;
z-index: 200;
padding: 10px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
cursor: pointer;
}
}
@media (min-width: 769px) {
.mobile-menu-btn {
display: none;
}
}
.hero {
text-align: center;
padding: 60px 0;
}
.hero h1 {
font-size: 3rem;
margin-bottom: 20px;
}
.hero .tagline {
font-size: 1.3rem;
color: var(--text-secondary);
margin-bottom: 30px;
}
.btn {
display: inline-block;
padding: 12px 24px;
background: var(--accent);
color: #fff;
border-radius: 6px;
font-weight: 600;
transition: background 0.2s;
}
.btn:hover {
background: var(--accent-hover);
color: #fff;
}
.btn-outline {
background: transparent;
border: 2px solid var(--accent);
color: var(--accent);
}
.btn-outline:hover {
background: var(--accent);
color: #fff;
}
.btn-group {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.quick-start {
background: var(--bg-secondary);
border-radius: 8px;
padding: 30px;
margin: 40px 0;
}
.quick-start h3 {
margin-top: 0;
}
.step {
display: flex;
align-items: flex-start;
gap: 15px;
margin: 20px 0;
}
.step-num {
width: 30px;
height: 30px;
background: var(--accent);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
flex-shrink: 0;
}
.step-content {
flex: 1;
}
.anchor {
color: var(--text-muted);
margin-left: 8px;
opacity: 0;
transition: opacity 0.2s;
}
h2:hover .anchor,
h3:hover .anchor {
opacity: 1;
}
+298
View File
@@ -0,0 +1,298 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Features - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link active">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Features</h1>
<p class="lead">Comprehensive overview of DWN capabilities</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#window-management">Window Management</a></li>
<li><a href="#workspaces">Virtual Workspaces</a></li>
<li><a href="#layouts">Layout System</a></li>
<li><a href="#panels">Panels & System Tray</a></li>
<li><a href="#notifications">Notifications</a></li>
<li><a href="#screenshot-ocr">Screenshot & OCR</a></li>
<li><a href="#ai">AI Integration</a></li>
<li><a href="#api">WebSocket API</a></li>
</ul>
</div>
<h2 id="window-management">Window Management</h2>
<p>DWN provides comprehensive window management with both manual and automatic control.</p>
<h3>Window States</h3>
<ul>
<li><strong>Normal</strong> - Standard window state, subject to layout rules</li>
<li><strong>Floating</strong> - Exempt from tiling, freely movable and resizable</li>
<li><strong>Maximized</strong> - Fills usable area with decorations</li>
<li><strong>Fullscreen</strong> - Fills entire screen, no decorations or panels</li>
<li><strong>Minimized</strong> - Hidden from view, accessible via taskbar</li>
</ul>
<h3>Window Snapping</h3>
<p>Snap windows to screen edges with <code>Super+Arrow</code> keys. Snapping is composable:</p>
<ul>
<li><code>Super+Left</code> - Left half (press twice for full width)</li>
<li><code>Super+Right</code> - Right half</li>
<li><code>Super+Up</code> - Top half</li>
<li><code>Super+Down</code> - Bottom half</li>
<li>Combine for quarter-screen: <code>Super+Left</code> then <code>Super+Up</code></li>
</ul>
<h3>Focus Modes</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Mode</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>click</code></td>
<td>Focus window on mouse click (default)</td>
</tr>
<tr>
<td><code>follow</code></td>
<td>Focus follows mouse pointer with configurable delay</td>
</tr>
</tbody>
</table>
</div>
<h3>Alt-Tab Window Cycling</h3>
<p>DWN maintains a Most Recently Used (MRU) stack per workspace. <code>Alt+Tab</code> cycles through windows in order of recent use, not visual order.</p>
<h2 id="workspaces">Virtual Workspaces</h2>
<p>DWN provides 9 virtual workspaces for organizing your windows.</p>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-title">Independent Layouts</div>
<div class="feature-desc">Each workspace maintains its own layout mode, master ratio, and master count.</div>
</div>
<div class="feature-card">
<div class="feature-title">Quick Switching</div>
<div class="feature-desc">Switch with F1-F9, or Ctrl+Alt+Left/Right for sequential navigation.</div>
</div>
<div class="feature-card">
<div class="feature-title">Window Movement</div>
<div class="feature-desc">Move windows between workspaces with Shift+F1-F9.</div>
</div>
<div class="feature-card">
<div class="feature-title">Workspace Indicator</div>
<div class="feature-desc">Panel shows which workspaces have windows and current workspace.</div>
</div>
</div>
<h2 id="layouts">Layout System</h2>
<p>Three layout modes available per workspace:</p>
<h3>Tiling Layout</h3>
<p>Master-stack tiling with configurable ratios. The first window(s) occupy the master area, others stack on the side.</p>
<ul>
<li><code>Super+H/L</code> - Adjust master area size</li>
<li><code>Super+I/D</code> - Adjust master window count</li>
<li>Default master ratio: 55%</li>
</ul>
<h3>Floating Layout</h3>
<p>Traditional floating window management. Drag title bars to move, drag edges to resize.</p>
<h3>Monocle Layout</h3>
<p>All windows fullscreen and stacked. Use Alt-Tab to switch between them. Ideal for focused work.</p>
<p>See <a href="layouts.html">Layouts</a> for detailed documentation.</p>
<h2 id="panels">Panels & System Tray</h2>
<h3>Top Panel</h3>
<p>Contains workspace indicators, taskbar, layout indicator, and system tray.</p>
<h3>Bottom Panel</h3>
<p>Contains clock and news ticker (when configured).</p>
<h3>System Tray</h3>
<p>Full XEmbed protocol support for external application icons plus built-in widgets:</p>
<ul>
<li><strong>Battery</strong> - Percentage display with charging indicator</li>
<li><strong>Volume</strong> - Click for slider, scroll to adjust, right-click to mute</li>
<li><strong>WiFi</strong> - SSID display, click for network list</li>
<li><strong>External Icons</strong> - nm-applet, blueman, Telegram, etc.</li>
</ul>
<h2 id="notifications">Notifications</h2>
<p>DWN includes a built-in D-Bus notification daemon implementing the freedesktop.org specification.</p>
<ul>
<li>Automatic service registration on startup</li>
<li>Configurable colors and timeout</li>
<li>Stacking notification display</li>
<li>Urgency level support (low, normal, critical)</li>
</ul>
<p>No external notification daemon required.</p>
<h2 id="screenshot-ocr">Screenshot & OCR</h2>
<p>Built-in screenshot capture and OCR text extraction, accessible via the WebSocket API.</p>
<h3>Screenshot Modes</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Mode</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>fullscreen</code></td>
<td>Capture entire screen</td>
</tr>
<tr>
<td><code>window</code></td>
<td>Capture specific window by ID</td>
</tr>
<tr>
<td><code>active</code></td>
<td>Capture currently focused window</td>
</tr>
<tr>
<td><code>area</code></td>
<td>Capture arbitrary rectangle</td>
</tr>
</tbody>
</table>
</div>
<h3>OCR Text Extraction</h3>
<p>Extract text from screenshots using Tesseract OCR engine:</p>
<ul>
<li>Multi-language support (English by default)</li>
<li>Confidence score reporting</li>
<li>Useful for automation and accessibility</li>
</ul>
<p>See <a href="api-reference.html">API Reference</a> for usage details.</p>
<h2 id="ai">AI Integration</h2>
<p>Optional AI features powered by OpenRouter API:</p>
<h3>AI Command Palette</h3>
<p>Natural language command input (<code>Super+Shift+A</code>). Ask the AI to:</p>
<ul>
<li>Launch applications ("open firefox")</li>
<li>Answer questions</li>
<li>Get system information</li>
</ul>
<h3>AI Context Analysis</h3>
<p>Press <code>Super+A</code> to see AI analysis of your current task based on open windows.</p>
<h3>Exa Semantic Search</h3>
<p>Web search with semantic understanding (<code>Super+Shift+E</code>). Find documentation, tutorials, and resources.</p>
<p>See <a href="ai-features.html">AI Integration</a> for setup instructions.</p>
<h2 id="api">WebSocket API</h2>
<p>Full programmatic control via JSON WebSocket API on configurable port (default 8777).</p>
<h3>Capabilities</h3>
<ul>
<li>Window management (list, focus, move, resize, close)</li>
<li>Workspace control (switch, move windows)</li>
<li>Layout management (mode, master ratio)</li>
<li>Keyboard simulation (key presses, typing)</li>
<li>Mouse simulation (move, click, drag)</li>
<li>Screenshot capture and OCR</li>
<li>System queries (windows, workspaces, monitors)</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Test automation</li>
<li>Custom hotkey scripts</li>
<li>Remote desktop control</li>
<li>Accessibility tools</li>
<li>Integration with other applications</li>
</ul>
<p>See <a href="api-overview.html">API Overview</a> for getting started.</p>
<h2>Protocol Compliance</h2>
<p>DWN implements standard X11 protocols for compatibility:</p>
<ul>
<li><strong>EWMH</strong> - Extended Window Manager Hints</li>
<li><strong>ICCCM</strong> - Inter-Client Communication Conventions</li>
<li><strong>XEmbed</strong> - System tray embedding protocol</li>
<li><strong>Xinerama</strong> - Multi-monitor support</li>
</ul>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+168
View File
@@ -0,0 +1,168 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DWN - Desktop Window Manager Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link active">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="hero">
<h1>DWN</h1>
<p class="tagline">A modern X11 window manager with AI integration and WebSocket API</p>
<div class="btn-group">
<a href="installation.html" class="btn">Get Started</a>
<a href="api-overview.html" class="btn btn-outline">API Docs</a>
</div>
</div>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">🪟</div>
<div class="feature-title">Multiple Layouts</div>
<div class="feature-desc">Tiling, floating, and monocle layouts with per-workspace configuration. Master-stack tiling with adjustable ratios.</div>
</div>
<div class="feature-card">
<div class="feature-icon">🖥️</div>
<div class="feature-title">9 Workspaces</div>
<div class="feature-desc">Virtual desktops with independent layout settings. Quick switching with F1-F9 keys.</div>
</div>
<div class="feature-card">
<div class="feature-icon">🤖</div>
<div class="feature-title">AI Integration</div>
<div class="feature-desc">OpenRouter API for intelligent command palette and Exa semantic search integration.</div>
</div>
<div class="feature-card">
<div class="feature-icon">🔌</div>
<div class="feature-title">WebSocket API</div>
<div class="feature-desc">Full programmatic control via JSON WebSocket API. Automate everything.</div>
</div>
<div class="feature-card">
<div class="feature-icon">📸</div>
<div class="feature-title">Screenshot & OCR</div>
<div class="feature-desc">Capture screenshots and extract text with Tesseract OCR, all via API.</div>
</div>
<div class="feature-card">
<div class="feature-icon">🔔</div>
<div class="feature-title">Notifications</div>
<div class="feature-desc">Built-in D-Bus notification daemon with configurable appearance.</div>
</div>
</div>
<div class="quick-start">
<h3>Quick Start</h3>
<div class="step">
<div class="step-num">1</div>
<div class="step-content">
<strong>Install dependencies</strong>
<pre><code>make deps</code></pre>
</div>
</div>
<div class="step">
<div class="step-num">2</div>
<div class="step-content">
<strong>Build DWN</strong>
<pre><code>make</code></pre>
</div>
</div>
<div class="step">
<div class="step-num">3</div>
<div class="step-content">
<strong>Test in Xephyr</strong>
<pre><code>make run</code></pre>
</div>
</div>
<div class="step">
<div class="step-num">4</div>
<div class="step-content">
<strong>Install system-wide</strong>
<pre><code>sudo make install</code></pre>
</div>
</div>
</div>
<h2>Key Features</h2>
<h3>Window Management</h3>
<ul>
<li>Three layout modes: tiling, floating, and monocle</li>
<li>Window snapping with Super+Arrow keys</li>
<li>Alt-Tab window cycling with MRU (Most Recently Used) stack</li>
<li>EWMH/ICCCM protocol compliance</li>
<li>Multi-monitor support via Xinerama</li>
</ul>
<h3>Automation API</h3>
<ul>
<li>WebSocket JSON API on configurable port</li>
<li>Keyboard and mouse simulation</li>
<li>Window management and layout control</li>
<li>Screenshot capture and OCR text extraction</li>
<li>Python client library included</li>
</ul>
<h3>System Integration</h3>
<ul>
<li>XDG autostart support</li>
<li>System tray with XEmbed protocol</li>
<li>D-Bus notification daemon</li>
<li>Configurable panels (top and bottom)</li>
</ul>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+240
View File
@@ -0,0 +1,240 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Installation - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Installation</h1>
<p class="lead">Install DWN on your Linux system</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#requirements">Requirements</a></li>
<li><a href="#dependencies">Dependencies</a></li>
<li><a href="#building">Building</a></li>
<li><a href="#installing">Installing</a></li>
<li><a href="#testing">Testing</a></li>
</ul>
</div>
<h2 id="requirements">Requirements</h2>
<ul>
<li>Linux with X11 (Xorg)</li>
<li>GCC compiler</li>
<li>Make build system</li>
<li>pkg-config</li>
</ul>
<h2 id="dependencies">Dependencies</h2>
<h3>Ubuntu / Debian</h3>
<div class="code-block">
<pre><code>sudo apt update && sudo apt install -y \
build-essential \
pkg-config \
libx11-dev \
libxext-dev \
libxinerama-dev \
libxrandr-dev \
libxft-dev \
libxtst-dev \
libfontconfig1-dev \
libdbus-1-dev \
libcurl4-openssl-dev \
libpng-dev \
libtesseract-dev \
libleptonica-dev \
tesseract-ocr \
tesseract-ocr-eng \
xserver-xephyr \
dmenu</code></pre>
</div>
<h3>Fedora / RHEL</h3>
<div class="code-block">
<pre><code>sudo dnf install -y \
gcc make \
pkg-config \
libX11-devel \
libXext-devel \
libXinerama-devel \
libXrandr-devel \
libXtst-devel \
dbus-devel \
libcurl-devel \
libpng-devel \
tesseract-devel \
leptonica-devel \
tesseract-langpack-eng \
xorg-x11-server-Xephyr \
dmenu</code></pre>
</div>
<h3>Arch Linux</h3>
<div class="code-block">
<pre><code>sudo pacman -S --needed \
base-devel \
pkg-config \
libx11 \
libxext \
libxinerama \
libxrandr \
libxtst \
dbus \
curl \
libpng \
tesseract \
tesseract-data-eng \
leptonica \
xorg-server-xephyr \
dmenu</code></pre>
</div>
<h3>Automatic Installation</h3>
<p>The Makefile can automatically detect your package manager and install dependencies:</p>
<div class="code-block">
<pre><code>make deps</code></pre>
</div>
<h2 id="building">Building</h2>
<h3>Clone the Repository</h3>
<div class="code-block">
<pre><code>git clone https://github.com/retoor/dwn.git
cd dwn</code></pre>
</div>
<h3>Build Release Version</h3>
<div class="code-block">
<pre><code>make</code></pre>
</div>
<h3>Build Debug Version</h3>
<div class="code-block">
<pre><code>make debug</code></pre>
</div>
<h3>Build with Sanitizers</h3>
<div class="code-block">
<pre><code>make sanitize</code></pre>
</div>
<h2 id="installing">Installing</h2>
<h3>System-wide Installation</h3>
<div class="code-block">
<pre><code>sudo make install</code></pre>
</div>
<p>This installs:</p>
<ul>
<li><code>/usr/local/bin/dwn</code> - The window manager binary</li>
<li><code>/usr/local/share/xsessions/dwn.desktop</code> - Session file for display managers</li>
<li><code>/etc/dwn/config.example</code> - Example configuration file</li>
</ul>
<h3>User Configuration</h3>
<p>Copy the example configuration to your home directory:</p>
<div class="code-block">
<pre><code>mkdir -p ~/.config/dwn
cp /etc/dwn/config.example ~/.config/dwn/config</code></pre>
</div>
<h3>Uninstalling</h3>
<div class="code-block">
<pre><code>sudo make uninstall</code></pre>
</div>
<h2 id="testing">Testing</h2>
<h3>Test in Xephyr (Recommended)</h3>
<p>Test DWN in a nested X server without affecting your current session:</p>
<div class="code-block">
<pre><code>make run</code></pre>
</div>
<p>This starts DWN in a 1280x720 Xephyr window on display :1.</p>
<h3>Manual Testing</h3>
<div class="code-block">
<pre><code># Start Xephyr
Xephyr :1 -screen 1920x1080 &
# Run DWN on the new display
DISPLAY=:1 ./bin/dwn</code></pre>
</div>
<h3>Using DWN as Your Window Manager</h3>
<ol>
<li>Log out of your current session</li>
<li>At the login screen, select "DWN" from the session menu</li>
<li>Log in</li>
</ol>
<div class="alert alert-info">
<strong>Tip:</strong> Keep a terminal open or know the shortcut <code>Ctrl+Alt+T</code> to open one, in case you need to recover from any issues.
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+119
View File
@@ -0,0 +1,119 @@
/* retoor <retoor@molodetz.nl> */
/* DWN Documentation Scripts */
document.addEventListener('DOMContentLoaded', function() {
highlightCurrentPage();
setupCopyButtons();
setupMobileMenu();
setupSearch();
setupTabs();
});
function highlightCurrentPage() {
const currentPath = window.location.pathname.split('/').pop() || 'index.html';
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const href = link.getAttribute('href');
if (href === currentPath || (currentPath === '' && href === 'index.html')) {
link.classList.add('active');
}
});
}
function setupCopyButtons() {
const codeBlocks = document.querySelectorAll('pre code');
codeBlocks.forEach(block => {
const wrapper = block.closest('.code-block') || block.parentElement;
wrapper.style.position = 'relative';
const btn = document.createElement('button');
btn.className = 'copy-btn';
btn.textContent = 'Copy';
btn.addEventListener('click', function() {
const text = block.textContent;
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'Copied!';
setTimeout(() => {
btn.textContent = 'Copy';
}, 2000);
});
});
wrapper.appendChild(btn);
});
}
function setupMobileMenu() {
const menuBtn = document.querySelector('.mobile-menu-btn');
const sidebar = document.querySelector('.sidebar');
if (menuBtn && sidebar) {
menuBtn.addEventListener('click', function() {
sidebar.classList.toggle('open');
});
document.addEventListener('click', function(e) {
if (!sidebar.contains(e.target) && !menuBtn.contains(e.target)) {
sidebar.classList.remove('open');
}
});
}
}
function setupSearch() {
const searchInput = document.querySelector('.search-input');
if (!searchInput) return;
searchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const text = link.textContent.toLowerCase();
const section = link.closest('.nav-section');
if (query === '' || text.includes(query)) {
link.style.display = 'block';
} else {
link.style.display = 'none';
}
});
});
}
function setupTabs() {
const tabGroups = document.querySelectorAll('.example-tabs');
tabGroups.forEach(group => {
const tabs = group.querySelectorAll('.example-tab');
const container = group.nextElementSibling;
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const target = this.dataset.tab;
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
if (container) {
const contents = container.querySelectorAll('.example-content');
contents.forEach(c => {
c.classList.remove('active');
if (c.dataset.tab === target) {
c.classList.add('active');
}
});
}
});
});
});
}
function scrollToSection(id) {
const element = document.getElementById(id);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}
+298
View File
@@ -0,0 +1,298 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Layouts - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link active">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Layouts</h1>
<p class="lead">Understanding DWN's window layout system</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#overview">Overview</a></li>
<li><a href="#tiling">Tiling Layout</a></li>
<li><a href="#floating">Floating Layout</a></li>
<li><a href="#monocle">Monocle Layout</a></li>
<li><a href="#per-workspace">Per-Workspace Settings</a></li>
<li><a href="#shortcuts">Layout Shortcuts</a></li>
</ul>
</div>
<h2 id="overview">Overview</h2>
<p>DWN provides three layout modes that determine how windows are arranged on screen. Each workspace maintains its own layout settings independently.</p>
<p>Press <code>Super+Space</code> to cycle through layouts: Tiling → Floating → Monocle → Tiling</p>
<div class="alert alert-info">
<strong>Tip:</strong> The current layout is shown in the panel with a symbol: <code>[]=</code> for tiling, <code>&gt;&lt;&gt;</code> for floating, <code>[M]</code> for monocle.
</div>
<h2 id="tiling">Tiling Layout</h2>
<p>The default layout. Windows automatically arrange themselves in a master-stack pattern.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+------------------+----------+
| | Window |
| Master | 2 |
| Window +----------+
| 1 | Window |
| | 3 |
+------------------+----------+</code></pre>
</div>
<ul>
<li>The <strong>master area</strong> (left) contains the primary window(s)</li>
<li>The <strong>stack area</strong> (right) contains secondary windows</li>
<li>Windows are automatically sized to fill the screen</li>
<li>No manual positioning required</li>
</ul>
<h3>Master Ratio</h3>
<p>Controls the width of the master area relative to the screen.</p>
<ul>
<li><code>Super+H</code> - Decrease master ratio (shrink master area)</li>
<li><code>Super+L</code> - Increase master ratio (expand master area)</li>
<li>Range: 10% to 90%</li>
<li>Default: 55%</li>
</ul>
<h3>Master Count</h3>
<p>Controls how many windows occupy the master area.</p>
<ul>
<li><code>Super+I</code> - Increase master count</li>
<li><code>Super+D</code> - Decrease master count</li>
<li>Range: 1 to 10</li>
<li>Default: 1</li>
</ul>
<p>With master count of 2:</p>
<div class="code-block">
<pre><code>+--------+---------+----------+
| Master | Master | Stack |
| 1 | 2 | 3 |
| | +----------+
| | | Stack |
| | | 4 |
+--------+---------+----------+</code></pre>
</div>
<h3>Floating Windows in Tiling</h3>
<p>Press <code>Super+F9</code> to toggle floating mode for a window. Floating windows:</p>
<ul>
<li>Are exempt from automatic tiling</li>
<li>Can be freely moved and resized</li>
<li>Float above tiled windows</li>
<li>Useful for dialogs, small utilities, reference windows</li>
</ul>
<h2 id="floating">Floating Layout</h2>
<p>Traditional overlapping window management like most desktop environments.</p>
<h3>Characteristics</h3>
<ul>
<li>Windows maintain their own position and size</li>
<li>No automatic arrangement</li>
<li>Drag title bars to move windows</li>
<li>Drag window edges to resize</li>
<li>Click to raise windows</li>
</ul>
<h3>Window Controls</h3>
<p>In floating mode, windows have full manual control:</p>
<ul>
<li><strong>Move</strong>: Drag the title bar</li>
<li><strong>Resize</strong>: Drag the window border</li>
<li><strong>Maximize</strong>: <code>Alt+F10</code> or click maximize button</li>
<li><strong>Minimize</strong>: <code>Alt+F9</code> or click minimize button</li>
</ul>
<h2 id="monocle">Monocle Layout</h2>
<p>Each window takes the full screen. Only one window visible at a time.</p>
<h3>Use Cases</h3>
<ul>
<li>Focused work on a single application</li>
<li>Small screens or high window count</li>
<li>Reading or writing documents</li>
<li>Video or media playback</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><code>Alt+Tab</code> - Switch to next window</li>
<li><code>Alt+Shift+Tab</code> - Switch to previous window</li>
<li>Taskbar click - Switch to specific window</li>
</ul>
<div class="alert alert-info">
<strong>Note:</strong> Windows maintain decorations in monocle mode unless fullscreen (<code>Alt+F11</code>).
</div>
<h2 id="per-workspace">Per-Workspace Settings</h2>
<p>Each workspace maintains independent layout settings:</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Setting</th>
<th>Scope</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Layout Mode</td>
<td>Per-workspace</td>
<td>Tiling, floating, or monocle</td>
</tr>
<tr>
<td>Master Ratio</td>
<td>Per-workspace</td>
<td>Width of master area</td>
</tr>
<tr>
<td>Master Count</td>
<td>Per-workspace</td>
<td>Windows in master area</td>
</tr>
<tr>
<td>Window Floating State</td>
<td>Per-window</td>
<td>Individual floating toggle</td>
</tr>
</tbody>
</table>
</div>
<p>Example workflow:</p>
<ul>
<li>Workspace 1: Tiling layout for coding (editor + terminal)</li>
<li>Workspace 2: Floating layout for design work</li>
<li>Workspace 3: Monocle layout for focused writing</li>
<li>Workspace 4: Tiling with master count 2 for comparison</li>
</ul>
<h2 id="shortcuts">Layout Shortcuts</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+Space</code></td>
<td>Cycle layout mode</td>
</tr>
<tr>
<td><code>Super+H</code></td>
<td>Shrink master area</td>
</tr>
<tr>
<td><code>Super+L</code></td>
<td>Expand master area</td>
</tr>
<tr>
<td><code>Super+I</code></td>
<td>Increase master count</td>
</tr>
<tr>
<td><code>Super+D</code></td>
<td>Decrease master count</td>
</tr>
<tr>
<td><code>Super+F9</code></td>
<td>Toggle window floating</td>
</tr>
<tr>
<td><code>Alt+F10</code></td>
<td>Toggle maximize</td>
</tr>
<tr>
<td><code>Alt+F11</code></td>
<td>Toggle fullscreen</td>
</tr>
</tbody>
</table>
</div>
<h2>Configuration</h2>
<p>Set default layout behavior in <code>~/.config/dwn/config</code>:</p>
<div class="code-block">
<pre><code>[layout]
default = tiling # Default layout for new workspaces
master_ratio = 0.55 # Default master area ratio (0.1-0.9)
master_count = 1 # Default master window count (1-10)
[appearance]
gap = 5 # Gap between tiled windows (0-100)</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+241
View File
@@ -0,0 +1,241 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Quick Start - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Quick Start</h1>
<p class="lead">Get up and running with DWN in 5 minutes</p>
</div>
<h2>Essential Shortcuts</h2>
<p>These are the most important shortcuts to know:</p>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Ctrl+Alt+T</code></td>
<td>Open terminal</td>
</tr>
<tr>
<td><code>Super</code> or <code>Alt+F2</code></td>
<td>Open application launcher (dmenu)</td>
</tr>
<tr>
<td><code>Alt+F4</code></td>
<td>Close focused window</td>
</tr>
<tr>
<td><code>Alt+Tab</code></td>
<td>Cycle through windows</td>
</tr>
<tr>
<td><code>F1</code> - <code>F9</code></td>
<td>Switch to workspace 1-9</td>
</tr>
<tr>
<td><code>Super+Space</code></td>
<td>Cycle layout mode</td>
</tr>
<tr>
<td><code>Super+Backspace</code></td>
<td>Quit DWN</td>
</tr>
</tbody>
</table>
</div>
<h2>Your First Session</h2>
<h3>1. Launch Applications</h3>
<p>Press <code>Super</code> (Windows key) to open the application launcher. Type the name of the application and press Enter.</p>
<h3>2. Navigate Workspaces</h3>
<p>DWN provides 9 virtual workspaces. Use <code>F1</code> through <code>F9</code> to switch between them. The panel at the top shows which workspaces have windows.</p>
<h3>3. Manage Windows</h3>
<p>By default, DWN uses a tiling layout where windows automatically arrange themselves. The first window takes the "master" area (left side), and subsequent windows stack on the right.</p>
<h3>4. Move Windows Between Workspaces</h3>
<p>Press <code>Shift+F1</code> through <code>Shift+F9</code> to move the focused window to a different workspace.</p>
<h2>Layout Modes</h2>
<p>Press <code>Super+Space</code> to cycle through layout modes:</p>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-title">Tiling</div>
<div class="feature-desc">Master-stack layout. First window on the left, others stacked on the right. Use <code>Super+H/L</code> to resize the master area.</div>
</div>
<div class="feature-card">
<div class="feature-title">Floating</div>
<div class="feature-desc">Traditional floating windows. Drag title bars to move, drag edges to resize.</div>
</div>
<div class="feature-card">
<div class="feature-title">Monocle</div>
<div class="feature-desc">All windows fullscreen, stacked. Use Alt+Tab to switch between them.</div>
</div>
</div>
<h2>Window Snapping</h2>
<p>Use <code>Super+Arrow</code> keys to snap windows:</p>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+Left</code></td>
<td>Snap to left half (press twice for full width)</td>
</tr>
<tr>
<td><code>Super+Right</code></td>
<td>Snap to right half</td>
</tr>
<tr>
<td><code>Super+Up</code></td>
<td>Snap to top half</td>
</tr>
<tr>
<td><code>Super+Down</code></td>
<td>Snap to bottom half</td>
</tr>
</tbody>
</table>
</div>
<p>Snapping is composable: <code>Super+Left</code> then <code>Super+Up</code> snaps to the top-left quarter.</p>
<h2>Window States</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Alt+F9</code></td>
<td>Minimize window</td>
</tr>
<tr>
<td><code>Alt+F10</code></td>
<td>Maximize window</td>
</tr>
<tr>
<td><code>Alt+F11</code></td>
<td>Fullscreen (no decorations)</td>
</tr>
<tr>
<td><code>Super+F9</code></td>
<td>Toggle floating mode</td>
</tr>
</tbody>
</table>
</div>
<h2>Configuration</h2>
<p>DWN reads configuration from <code>~/.config/dwn/config</code>. Create one from the example:</p>
<div class="code-block">
<pre><code>mkdir -p ~/.config/dwn
cp /etc/dwn/config.example ~/.config/dwn/config</code></pre>
</div>
<p>Edit the file to customize terminal, launcher, colors, and more. See the <a href="configuration.html">Configuration</a> page for details.</p>
<h2>Enable the API</h2>
<p>To enable the WebSocket API for automation, add this to your config:</p>
<div class="code-block">
<pre><code>[api]
enabled = true
port = 8777</code></pre>
</div>
<p>Then you can control DWN programmatically. See <a href="api-overview.html">API Overview</a>.</p>
<h2>Next Steps</h2>
<ul>
<li><a href="shortcuts.html">Full keyboard shortcuts reference</a></li>
<li><a href="configuration.html">Configuration options</a></li>
<li><a href="ai-features.html">AI integration setup</a></li>
<li><a href="api-overview.html">API documentation</a></li>
</ul>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+326
View File
@@ -0,0 +1,326 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Keyboard Shortcuts - DWN Documentation</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<button class="mobile-menu-btn">Menu</button>
<div class="layout">
<aside class="sidebar">
<div class="sidebar-header">
<h1>DWN</h1>
<span class="version">v1.0.0</span>
</div>
<div class="search-box">
<input type="text" class="search-input" placeholder="Search docs...">
</div>
<nav class="sidebar-nav">
<div class="nav-section">
<div class="nav-section-title">Getting Started</div>
<a href="index.html" class="nav-link">Introduction</a>
<a href="installation.html" class="nav-link">Installation</a>
<a href="quickstart.html" class="nav-link">Quick Start</a>
</div>
<div class="nav-section">
<div class="nav-section-title">User Guide</div>
<a href="features.html" class="nav-link">Features</a>
<a href="shortcuts.html" class="nav-link">Keyboard Shortcuts</a>
<a href="configuration.html" class="nav-link">Configuration</a>
<a href="layouts.html" class="nav-link">Layouts</a>
<a href="ai-features.html" class="nav-link">AI Integration</a>
</div>
<div class="nav-section">
<div class="nav-section-title">API Reference</div>
<a href="api-overview.html" class="nav-link">API Overview</a>
<a href="api-reference.html" class="nav-link">API Reference</a>
<a href="api-examples.html" class="nav-link">API Examples</a>
</div>
<div class="nav-section">
<div class="nav-section-title">Advanced</div>
<a href="architecture.html" class="nav-link">Architecture</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Keyboard Shortcuts</h1>
<p class="lead">Complete reference of all keyboard shortcuts</p>
</div>
<div class="toc">
<div class="toc-title">On this page</div>
<ul class="toc-list">
<li><a href="#launchers">Application Launchers</a></li>
<li><a href="#windows">Window Management</a></li>
<li><a href="#workspaces">Workspace Navigation</a></li>
<li><a href="#layouts">Layout Control</a></li>
<li><a href="#snapping">Window Snapping</a></li>
<li><a href="#ai">AI Features</a></li>
<li><a href="#system">Help & System</a></li>
</ul>
</div>
<h2 id="launchers">Application Launchers</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Ctrl+Alt+T</code></td>
<td>Open terminal (configurable)</td>
</tr>
<tr>
<td><code>Super</code> / <code>Alt+F2</code></td>
<td>Open application launcher (dmenu/rofi)</td>
</tr>
<tr>
<td><code>Super+E</code></td>
<td>Open file manager (configurable)</td>
</tr>
<tr>
<td><code>Super+B</code></td>
<td>Open web browser</td>
</tr>
<tr>
<td><code>Print</code></td>
<td>Take screenshot (xfce4-screenshooter)</td>
</tr>
</tbody>
</table>
</div>
<h2 id="windows">Window Management</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Alt+F4</code></td>
<td>Close focused window</td>
</tr>
<tr>
<td><code>Alt+Tab</code></td>
<td>Cycle to next window (MRU order)</td>
</tr>
<tr>
<td><code>Alt+Shift+Tab</code></td>
<td>Cycle to previous window</td>
</tr>
<tr>
<td><code>Alt+F9</code></td>
<td>Toggle minimize/restore</td>
</tr>
<tr>
<td><code>Alt+F10</code></td>
<td>Toggle maximize</td>
</tr>
<tr>
<td><code>Alt+F11</code></td>
<td>Toggle fullscreen (no decorations)</td>
</tr>
<tr>
<td><code>Super+F9</code></td>
<td>Toggle floating mode for focused window</td>
</tr>
</tbody>
</table>
</div>
<h2 id="workspaces">Workspace Navigation</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>F1</code> - <code>F9</code></td>
<td>Switch to workspace 1-9</td>
</tr>
<tr>
<td><code>Shift+F1</code> - <code>Shift+F9</code></td>
<td>Move focused window to workspace 1-9</td>
</tr>
<tr>
<td><code>Ctrl+Alt+Right</code></td>
<td>Switch to next workspace</td>
</tr>
<tr>
<td><code>Ctrl+Alt+Left</code></td>
<td>Switch to previous workspace</td>
</tr>
</tbody>
</table>
</div>
<h2 id="layouts">Layout Control</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+Space</code></td>
<td>Cycle layout mode (tiling → floating → monocle)</td>
</tr>
<tr>
<td><code>Super+H</code></td>
<td>Shrink master area</td>
</tr>
<tr>
<td><code>Super+L</code></td>
<td>Expand master area</td>
</tr>
<tr>
<td><code>Super+I</code></td>
<td>Increase master window count</td>
</tr>
<tr>
<td><code>Super+D</code></td>
<td>Decrease master window count</td>
</tr>
</tbody>
</table>
</div>
<h2 id="snapping">Window Snapping</h2>
<p>Window snapping is composable. Press the same key twice to expand to full width/height in that direction.</p>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+Left</code></td>
<td>Snap left 50% (press twice for full width)</td>
</tr>
<tr>
<td><code>Super+Right</code></td>
<td>Snap right 50% (press twice for full width)</td>
</tr>
<tr>
<td><code>Super+Up</code></td>
<td>Snap top 50% (press twice for full height)</td>
</tr>
<tr>
<td><code>Super+Down</code></td>
<td>Snap bottom 50% (press twice for full height)</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-info">
<strong>Tip:</strong> Combine horizontal and vertical snaps for quarter-screen windows. For example, <code>Super+Left</code> then <code>Super+Up</code> snaps to top-left quarter.
</div>
<h2 id="ai">AI Features</h2>
<p>These shortcuts require API keys to be configured. See <a href="ai-features.html">AI Integration</a>.</p>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+A</code></td>
<td>Show AI context analysis</td>
</tr>
<tr>
<td><code>Super+Shift+A</code></td>
<td>Open AI command palette</td>
</tr>
<tr>
<td><code>Super+Shift+E</code></td>
<td>Open Exa semantic web search</td>
</tr>
</tbody>
</table>
</div>
<h2 id="system">Help & System</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+S</code></td>
<td>Show all keyboard shortcuts</td>
</tr>
<tr>
<td><code>Super+T</code></td>
<td>Start/continue interactive tutorial</td>
</tr>
<tr>
<td><code>Super+Shift+D</code></td>
<td>Start/stop demo mode</td>
</tr>
<tr>
<td><code>Super+D</code></td>
<td>Toggle show desktop</td>
</tr>
<tr>
<td><code>Super+Return</code></td>
<td>Open current news article in browser</td>
</tr>
<tr>
<td><code>Super+Backspace</code></td>
<td>Quit DWN</td>
</tr>
</tbody>
</table>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>