Compare commits

..
3 Commits
Author SHA1 Message Date
retoor a6b9fa3469 docs: add comprehensive readme with installation, features, and usage guide 2025-12-28 04:34:24 +01:00
retoor 9182d9931e feat: add news ticker with sentiment analysis and error codes
docs: update site links to retoor repository and documentation
chore: add author lines to header files
build: rebuild binary and object files
2025-12-28 04:30:10 +01:00
retoor 7a4f7a82ad chore: update c, css, d files 2025-12-28 03:14:31 +01:00
212 changed files with 7782 additions and 56048 deletions
+10 -95
View File
@@ -3,9 +3,8 @@
# Compiler settings
CC = gcc
CFLAGS = -Wall -Wextra -Wpedantic -Wshadow -O2 -I./include
CFLAGS += -fstack-protector-strong -D_FORTIFY_SOURCE=2
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread -lXtst -lXi -ldl
CFLAGS = -Wall -Wextra -O2 -I./include
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread
# Directories
SRC_DIR = src
@@ -13,9 +12,9 @@ INC_DIR = include
BUILD_DIR = build
BIN_DIR = bin
# Find all source files automatically (including subdirectories)
SRCS = $(shell find $(SRC_DIR) -name '*.c' -type f)
OBJS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SRCS))
# Find all source files automatically
SRCS = $(wildcard $(SRC_DIR)/*.c)
OBJS = $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
DEPS = $(OBJS:.o=.d)
# Output binary
@@ -28,8 +27,8 @@ DATADIR = $(PREFIX)/share
SYSCONFDIR = /etc
# Get pkg-config flags (with error handling)
PKG_CFLAGS := $(shell pkg-config --cflags x11 xext xinerama xrandr xft fontconfig dbus-1 xtst xi libpng tesseract lept 2>/dev/null)
PKG_LIBS := $(shell pkg-config --libs x11 xext xinerama xrandr xft fontconfig dbus-1 xtst xi libpng tesseract lept 2>/dev/null)
PKG_CFLAGS := $(shell pkg-config --cflags x11 xext xinerama xrandr xft fontconfig dbus-1 2>/dev/null)
PKG_LIBS := $(shell pkg-config --libs x11 xext xinerama xrandr xft fontconfig dbus-1 2>/dev/null)
# Use pkg-config if available
ifneq ($(PKG_LIBS),)
@@ -43,7 +42,7 @@ endif
# MAIN TARGETS
# =============================================================================
.PHONY: all help clean install uninstall debug sanitize run test deps check-deps
.PHONY: all help clean install uninstall debug run test deps check-deps
# Default target - show help if first time, otherwise build
all: check-deps $(TARGET)
@@ -70,7 +69,6 @@ help:
@echo "ALL COMMANDS:"
@echo " make - Build DWN (release version)"
@echo " make debug - Build with debug symbols"
@echo " make sanitize - Build with address/UB sanitizers"
@echo " make run - Test in Xephyr window (safe!)"
@echo " make install - Install to $(BINDIR)"
@echo " make uninstall- Remove from system"
@@ -78,11 +76,6 @@ help:
@echo " make deps - Install dependencies (needs sudo)"
@echo " make help - Show this help"
@echo ""
@echo "API TESTING:"
@echo " make test - Run API integration tests"
@echo " make test-quick - Run quick tests (skip OCR)"
@echo " make test-coverage- Run tests with coverage report"
@echo ""
@echo "AFTER INSTALL:"
@echo " 1. Log out of your current session"
@echo " 2. At login screen, select 'DWN' as your session"
@@ -98,13 +91,6 @@ debug: CFLAGS += -g -DDEBUG
debug: clean $(TARGET)
@echo "Debug build complete!"
# Build with address and undefined behavior sanitizers
sanitize: CFLAGS += -g -fsanitize=address,undefined -fno-omit-frame-pointer
sanitize: LDFLAGS += -fsanitize=address,undefined
sanitize: clean $(TARGET)
@echo "Sanitizer build complete!"
@echo "Run with: ASAN_OPTIONS=detect_leaks=1 ./$(TARGET)"
# Link all object files into final binary
$(TARGET): $(OBJS) | $(BIN_DIR)
@echo "Linking..."
@@ -113,7 +99,6 @@ $(TARGET): $(OBJS) | $(BIN_DIR)
# Compile each source file
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c | $(BUILD_DIR)
@echo "Compiling $<..."
@mkdir -p $(dir $@)
@$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
# Create build directory
@@ -134,7 +119,7 @@ $(BIN_DIR):
install: $(TARGET)
@echo "Installing DWN..."
@install -Dm755 $(TARGET) $(DESTDIR)$(BINDIR)/dwn
@install -Dm644 examples/dwn.desktop $(DESTDIR)$(DATADIR)/xsessions/dwn.desktop
@install -Dm644 scripts/dwn.desktop $(DESTDIR)$(DATADIR)/xsessions/dwn.desktop
@mkdir -p $(DESTDIR)$(SYSCONFDIR)/dwn
@install -Dm644 config/config.example $(DESTDIR)$(SYSCONFDIR)/dwn/config.example
@echo ""
@@ -210,15 +195,9 @@ deps:
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; \
echo ""; \
@@ -232,13 +211,8 @@ deps:
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; \
echo ""; \
@@ -252,13 +226,8 @@ deps:
libxext \
libxinerama \
libxrandr \
libxtst \
dbus \
curl \
libpng \
tesseract \
tesseract-data-eng \
leptonica \
xorg-server-xephyr \
dmenu; \
echo ""; \
@@ -269,7 +238,7 @@ deps:
echo "Please install these packages manually:"; \
echo " - GCC and Make"; \
echo " - pkg-config"; \
echo " - X11, Xext, Xinerama, Xrandr, Xtst development libraries"; \
echo " - X11, Xext, Xinerama, Xrandr development libraries"; \
echo " - D-Bus development library"; \
echo " - libcurl development library"; \
echo " - Xephyr (for testing)"; \
@@ -277,60 +246,6 @@ deps:
exit 1; \
fi
# =============================================================================
# API INTEGRATION TESTS
# =============================================================================
TEST_PORT ?= 18777
TEST_DISPLAY ?= :99
.PHONY: test test-quick test-coverage test-isolated
test:
@echo "Running API integration tests..."
@echo "Note: DWN must be running with API enabled (port 8777)"
@cd tests && python -m pytest -v
test-quick:
@echo "Running quick API tests (skipping OCR)..."
@cd tests && python -m pytest -v -x --ignore=test_ocr_commands.py
test-coverage:
@echo "Running API tests with coverage..."
@cd tests && python -m pytest --cov=. --cov-report=term-missing
test-isolated: $(TARGET)
@echo "Starting isolated DWN test instance on port $(TEST_PORT)..."
@-kill $$(cat /tmp/dwn_test_dwn.pid 2>/dev/null) 2>/dev/null; true
@-kill $$(cat /tmp/dwn_test_xephyr.pid 2>/dev/null) 2>/dev/null; true
@rm -f /tmp/dwn_test_xephyr.pid /tmp/dwn_test_dwn.pid /tmp/dwn_test.log
@sleep 1
@Xephyr $(TEST_DISPLAY) -screen 1280x720 2>/dev/null & echo $$! > /tmp/dwn_test_xephyr.pid
@sleep 2
@DISPLAY=$(TEST_DISPLAY) $(TARGET) -p $(TEST_PORT) > /tmp/dwn_test.log 2>&1 & echo $$! > /tmp/dwn_test_dwn.pid
@sleep 3
@for i in 1 2 3 4 5; do \
if curl -s http://localhost:$(TEST_PORT)/api/status >/dev/null 2>&1; then \
echo "DWN API ready on port $(TEST_PORT)"; \
break; \
fi; \
echo "Waiting for DWN API... ($$i/5)"; \
sleep 1; \
done
@if ! curl -s http://localhost:$(TEST_PORT)/api/status >/dev/null 2>&1; then \
echo "ERROR: DWN API not responding. Check /tmp/dwn_test.log"; \
cat /tmp/dwn_test.log 2>/dev/null | tail -50; \
kill $$(cat /tmp/dwn_test_dwn.pid 2>/dev/null) 2>/dev/null || true; \
kill $$(cat /tmp/dwn_test_xephyr.pid 2>/dev/null) 2>/dev/null || true; \
exit 1; \
fi
@DWN_TEST_PORT=$(TEST_PORT) python -m pytest tests/ -v; \
EXIT_CODE=$$?; \
kill $$(cat /tmp/dwn_test_dwn.pid 2>/dev/null) 2>/dev/null || true; \
kill $$(cat /tmp/dwn_test_xephyr.pid 2>/dev/null) 2>/dev/null || true; \
rm -f /tmp/dwn_test_xephyr.pid /tmp/dwn_test_dwn.pid; \
exit $$EXIT_CODE
# =============================================================================
# CODE QUALITY (for developers)
# =============================================================================
+308 -542
View File
@@ -1,626 +1,392 @@
# DWN - Desktop Window Manager
retoor <retoor@molodetz.nl>
Author: retoor <retoor@molodetz.nl>
A production-ready X11 window manager written in ANSI C with EWMH/ICCCM protocol compliance, tiling-first workflow, integrated desktop components, and optional AI integration via OpenRouter API.
A lightweight, AI-enhanced window manager for Linux with tiling, floating, and fullscreen layouts.
## Design Philosophy
DWN prioritizes a seamless, distraction-free desktop experience:
- **Zero borders and gaps**: Windows tile edge-to-edge without visual separation
- **Minimal chrome**: 28px title bars provide window controls without excess decoration
- **Tiling-first**: Master-stack layout as the default, with floating and monocle modes available
- **Keyboard-driven**: Comprehensive shortcuts for all operations
- **Modular architecture**: Single-responsibility modules with strict encapsulation
- **ANSI C**: Maximum portability and minimal dependencies
## Features
### Window Management
**Layout Modes**
- **Tiling**: Master-stack layout with configurable master ratio (0.1-0.9) and master window count
- **Floating**: Traditional overlapping windows with free positioning
- **Monocle**: Single maximized window per workspace
**Composable Window Snapping**
- Super+Arrow keys snap windows to half-screen positions
- Double-press extends to full width/height
- Combinations create quarter-screen positions (e.g., Super+Left then Super+Up = top-left quarter)
**Directional Resizing**
- Resize windows from any edge or corner
- Respects layout bounds and snap constraints
**Window States**
- Fullscreen (Alt+F11): Covers entire screen including panels
- Maximized (Alt+F10): Fills usable area with title bar
- Minimized (Alt+F9): Hidden from view
- Floating (Super+F9): Exempt from tiling layout
- Sticky: Visible on all workspaces
- Urgent: Demands attention with taskbar highlight
### Workspace System
- 9 independent virtual workspaces (F1-F9)
- Per-workspace layout persistence
- MRU (Most Recently Used) focus stack per workspace
- Alt-Tab cycles through MRU order
- Shift+F1-F9 moves focused window to target workspace
- Ctrl+Alt+Left/Right navigates adjacent workspaces
### Panels
**Top Panel**
- Workspace indicators with active/inactive/urgent states
- Taskbar with window buttons
- System tray with XEmbed protocol support
- Battery indicator (multi-battery support)
- Volume control with slider
- Top memory process display
- Top CPU process display
- Key press counter
- Mouse distance traveled
- Clock
**Bottom Panel**
- News ticker with scrolling headlines
- Configurable visibility
### System Tray
**XEmbed Protocol**
- Acquires `_NET_SYSTEM_TRAY_S0` selection
- Docks external application icons (nm-applet, blueman, Telegram, etc.)
- Forwards click events to icon windows
- Automatic cleanup on application close
**Built-in Widgets**
- Battery: Percentage display, charging indicator, multi-battery support
- Volume: Click for slider, scroll to adjust, right-click to mute
- Fade effects: Click "S:X.X" or "I:XX%" to adjust animation speed and glow intensity
- Process monitors: Rotating display of top CPU/memory consumers
### Ambient Glow Effects
Visual feedback system with phase-offset animations:
- Panel background subtle color cycling
- Workspace indicator glow
- Taskbar button highlighting
- Clock and statistics display
- Window title text glow on focus
- Configurable animation speed
### Activity Tracking
- **Key press counter**: Total keypresses displayed in panel
- **Mouse distance**: Cumulative mouse movement in pixels
- **XInput2 integration**: Raw event monitoring for accurate tracking
### Focus Transitions
- Animated color transitions on window focus changes
- Taskbar button color interpolation
- Title bar glow animations
- Bold font rendering for Alt-Tab selection
### Notification System
D-Bus implementation of `org.freedesktop.Notifications`:
- Receives notifications from all applications
- Stacked display with timeout management
- Urgency levels: low, normal, critical
- Click to dismiss
### News Ticker
- Fetches headlines from configured sources
- Smooth pixel-based scrolling animation
- Super+Return opens current article in browser
- Automatic refresh interval
- Sentiment indicators (positive/negative/neutral)
### AI Integration
**Command Palette (Super+Shift+A)**
- Natural language command execution
- OpenRouter API with configurable model selection
- Context-aware responses based on current workspace
**Context Analysis (Super+A)**
- Task type detection (coding, browsing, communication)
- Window and application analysis
- Intelligent suggestions
**Exa Semantic Search (Super+Shift+E)**
- Meaning-based web search
- Results displayed in dmenu/rofi
- Select to open in browser
### Screenshot and OCR
**Screenshot API**
- Fullscreen capture
- Active window capture
- Area selection capture
- Async capture with callbacks
- Base64 encoding for API transmission
- PNG output
**OCR API**
- Tesseract-based text extraction
- Multi-language support
- Confidence scoring
- Async processing
### WebSocket API
Programmatic control on port 8777:
| Command | Description |
|---------|-------------|
| `get_status` | Window manager state |
| `get_workspaces` | Workspace information |
| `get_clients` | All managed windows |
| `switch_workspace` | Change active workspace |
| `focus_client` | Focus specific window |
| `run_command` | Execute shell command |
| `screenshot` | Capture screen |
| `ocr` | Extract text from image |
| `get_fade_settings` | Get fade effect settings |
| `set_fade_speed` | Set animation speed (0.1-3.0) |
| `set_fade_intensity` | Set glow intensity (0.0-1.0) |
**Event Subscription**
Subscribe to real-time events including fade changes:
## Quick Start (Copy & Paste These Commands)
```bash
# Subscribe to fade events
python3 examples/dwn_api_client.py subscribe fade_speed_changed fade_intensity_changed
# 1. Install dependencies (enter your password when asked)
make deps
# Listen for all events
python3 examples/dwn_api_client.py listen
# 2. Build DWN
make
# 3. Test it (opens in a safe window - won't affect your desktop)
make run
```
**Fade Control Example**
That's it! Press `Super+Backspace` to exit the test window.
```bash
# Get current fade settings
python3 examples/dwn_api_client.py fade-settings
---
# Set fade speed (faster animation)
python3 examples/dwn_api_client.py fade-speed 1.5
## What is DWN?
# Set fade intensity (dimmer glow)
python3 examples/dwn_api_client.py fade-intensity 0.5
DWN is a **window manager** - the program that controls how windows look and behave on your screen. It replaces your current desktop environment (like GNOME, KDE, or XFCE) with a faster, keyboard-driven alternative.
# Run interactive demo
python3 examples/fade_control_demo.py
```
**Features:**
- 3 window layouts: Tiling (windows don't overlap), Floating (like normal), Monocle (fullscreen)
- 9 virtual workspaces to organize your windows
- Built-in panels with clock, taskbar, and system tray
- WiFi network selector with dropdown menu (like XFCE)
- Audio volume indicator with scroll-to-adjust
- Desktop notifications
- Optional AI assistant (can launch apps by command!)
- Very fast and lightweight
### Automation
**XDG Autostart**
- Scans `/etc/xdg/autostart/` and `~/.config/autostart/`
- Parses `.desktop` files
- Custom autostart directory support
**Service Manager**
- Background process management
- Automatic restart on failure
### Interactive Features
**Tutorial (Super+T)**
- Step-by-step keyboard shortcut training
- Waits for correct input
- Progressive difficulty
**Demo Mode (Super+Shift+D)**
- Automated feature showcase
- Configurable timing
- Covers all major features
**Shortcuts Help (Super+S)**
- Quick reference overlay
- All keyboard bindings displayed
---
## Installation
### Dependencies
### Step 1: Install Dependencies
Required libraries (via pkg-config):
- X11, Xext, Xinerama, Xrandr, Xft, Xi
- fontconfig
- libdbus-1
- libcurl
- libpng
- tesseract (optional, for OCR)
### Build
Run this command (works on Ubuntu, Debian, Fedora, and Arch):
```bash
make deps # Auto-install dependencies (apt/dnf/pacman)
make # Build with -O2 optimization
make install # Install to /usr/local/bin (PREFIX configurable)
make deps
```
### Testing
This installs everything DWN needs. You'll need to enter your password.
**If `make deps` doesn't work**, install manually:
- Ubuntu/Debian: `sudo apt install build-essential pkg-config libx11-dev libxext-dev libxinerama-dev libxrandr-dev libdbus-1-dev libcurl4-openssl-dev xserver-xephyr dmenu network-manager alsa-utils`
- Fedora: `sudo dnf install gcc make pkg-config libX11-devel libXext-devel libXinerama-devel libXrandr-devel dbus-devel libcurl-devel xorg-x11-server-Xephyr dmenu NetworkManager alsa-utils`
- Arch: `sudo pacman -S base-devel pkg-config libx11 libxext libxinerama libxrandr dbus curl xorg-server-xephyr dmenu networkmanager alsa-utils`
### Step 2: Build
```bash
make run # Launch in nested Xephyr window
make
```
### Build Targets
You should see "Build successful!" at the end.
| Target | Description |
|--------|-------------|
| `make` | Release build with optimization |
| `make debug` | Debug build with -g -DDEBUG |
| `make clean` | Remove build artifacts |
| `make format` | Run clang-format |
| `make check` | Run cppcheck static analysis |
### Step 3: Test (Recommended!)
## Configuration
Before installing, test DWN in a safe window:
Configuration file: `~/.config/dwn/config` (INI format)
### General
```ini
[general]
terminal = xfce4-terminal
launcher = dmenu_run
file_manager = thunar
focus_mode = click # click or follow
focus_follow_delay = 100 # 0-1000ms
decorations = true
```
### Appearance
```ini
[appearance]
border_width = 0 # 0-50px
title_height = 28 # 0-100px
panel_height = 32 # 0-100px
gap = 0 # 0-100px
font = fixed
```
### Layout
```ini
[layout]
default = tiling # tiling, floating, monocle
master_ratio = 0.55 # 0.1-0.9
master_count = 1 # 1-10
```
### Panels
```ini
[panels]
top = true
bottom = true
```
### Colors
```ini
[colors]
panel_bg = #080808
panel_fg = #00ff00
workspace_active = #ff00ff
workspace_inactive = #222222
workspace_urgent = #ff0000
title_focused_bg = #00ffff
title_focused_fg = #000000
title_unfocused_bg = #111111
title_unfocused_fg = #444444
border_focused = #00ff00
border_unfocused = #000000
notification_bg = #111111
notification_fg = #00ffff
```
### AI
```ini
[ai]
model = google/gemini-2.0-flash-exp:free
openrouter_api_key = sk-or-v1-your-key
exa_api_key = your-exa-key
```
Environment variables:
```bash
export OPENROUTER_API_KEY=sk-or-v1-your-key
export EXA_API_KEY=your-exa-key
make run
```
API keys:
- OpenRouter: https://openrouter.ai/keys
- Exa: https://dashboard.exa.ai/api-keys
This opens DWN inside a window on your current desktop. You can try it out without changing anything. Press `Super+Backspace` to close it.
### Autostart
### Step 4: Install
```ini
[autostart]
enabled = true
xdg_autostart = true
path = ~/.config/dwn/autostart.d
```bash
sudo make install
```
### API
### Step 5: Use DWN
```ini
[api]
enabled = true
port = 8777
```
1. Log out of your current session
2. At the login screen, click the **gear icon** (or session selector) near your username
3. Select **"DWN"** from the list
4. Log in!
### Demo
---
```ini
[demo]
step_delay = 4000 # 1000-30000ms
ai_timeout = 15000 # 5000-60000ms
window_timeout = 5000 # 1000-30000ms
```
## Basic Controls
## Keyboard Shortcuts
### Essential Shortcuts (Memorize These!)
### Application Launchers
| Keys | What it does |
|------|--------------|
| `Ctrl + Alt + T` | Open a terminal |
| `Super` or `Alt + F2` | Open app launcher |
| `Super + E` | Open file manager |
| `Super + B` | Open web browser |
| `Alt + Tab` | Switch between windows |
| `Alt + F4` | Close current window |
| `Super + Backspace` | **Exit DWN** (important!) |
| `Super + S` | **Show all shortcuts** |
| `Super + T` | **Start interactive tutorial** |
| Shortcut | Action |
|----------|--------|
| `Ctrl+Alt+T` | Terminal |
| `Super` / `Alt+F2` | Application launcher |
| `Super+E` | File manager |
| `Super+B` | Web browser |
| `Print` | Screenshot |
### Workspaces
| Keys | What it does |
|------|--------------|
| `Ctrl + Alt + Right` | Next workspace |
| `Ctrl + Alt + Left` | Previous workspace |
| `F1` to `F9` | Switch to workspace 1-9 |
| `Shift + F1` to `Shift + F9` | Move window to workspace 1-9 |
### Window Management
| Shortcut | Action |
|----------|--------|
| `Alt+F4` | Close window |
| `Alt+Tab` | Next window (MRU order) |
| `Alt+Shift+Tab` | Previous window |
| `Alt+F9` | Toggle minimize |
| `Alt+F10` | Toggle maximize |
| `Alt+F11` | Toggle fullscreen |
| `Super+F9` | Toggle floating |
| Keys | What it does |
|------|--------------|
| `Super + F9` | Toggle floating mode |
| `Alt + F10` | Toggle maximize |
| `Alt + F11` | Toggle fullscreen |
| `Alt + Tab` | Cycle windows forward |
| `Alt + Shift + Tab` | Cycle windows backward |
### Workspace Navigation
### Layout Control (Super key shortcuts)
| Shortcut | Action |
|----------|--------|
| `F1` - `F9` | Switch to workspace 1-9 |
| `Shift+F1` - `Shift+F9` | Move window to workspace 1-9 |
| `Ctrl+Alt+Right` | Next workspace |
| `Ctrl+Alt+Left` | Previous workspace |
| Keys | What it does |
|------|--------------|
| `Super + Space` | Change layout mode (tiling → floating → monocle) |
| `Super + H` | Shrink main area |
| `Super + L` | Expand main area |
| `Super + I` | Add window to main area |
| `Super + D` | Remove window from main area |
### Layout Control
### AI Features (Optional)
| Shortcut | Action |
|----------|--------|
| `Super+Space` | Cycle layout mode |
| `Super+H` | Shrink master area |
| `Super+L` | Expand master area |
| `Super+I` | Increase master count |
| `Super+D` | Decrease master count / Show desktop |
| Keys | What it does |
|------|--------------|
| `Super + A` | Show AI context info |
| `Super + Shift + A` | Ask AI to do something (e.g., "open firefox") |
| `Super + Shift + E` | Exa semantic web search |
### Window Snapping
### News Ticker
| Shortcut | Action |
|----------|--------|
| `Super+Left` | Snap left (50% or 100%) |
| `Super+Right` | Snap right (50% or 100%) |
| `Super+Up` | Snap top (50% or 100%) |
| `Super+Down` | Snap bottom (50% or 100%) |
The bottom panel displays a scrolling news ticker. Navigate articles with these shortcuts:
### AI Features
| Keys | What it does |
|------|--------------|
| `Super + Down` | Next news article |
| `Super + Up` | Previous news article |
| `Super + Return` | Open current article in browser |
| Shortcut | Action |
|----------|--------|
| `Super+A` | Context analysis |
| `Super+Shift+A` | Command palette |
| `Super+Shift+E` | Exa semantic search |
### Other Shortcuts
### News and Help
| Keys | What it does |
|------|--------------|
| `Print` | Take screenshot (xfce4-screenshooter) |
| Shortcut | Action |
|----------|--------|
| `Super+Return` | Open current article |
| `Super+S` | Shortcuts help |
| `Super+T` | Interactive tutorial |
| `Super+Shift+D` | Toggle demo mode |
| `Super+Backspace` | Quit DWN |
---
## Architecture
## System Tray
### Module Structure
The top panel includes a system tray on the right side with battery, audio, and WiFi indicators.
| Module | Responsibility |
|--------|----------------|
| main.c | X11 initialization, event loop, signal handling |
| client.c | Window management, focus, frame creation |
| workspace.c | Virtual desktop management, MRU stacks |
| layout.c | Tiling, floating, monocle algorithms |
| decorations.c | Title bars, borders, glow animations |
| panel.c | Top/bottom panel rendering, widgets |
| systray.c | XEmbed system tray, battery/volume widgets |
| notifications.c | D-Bus notification daemon |
| atoms.c | EWMH/ICCCM atom management |
| keys.c | Keyboard shortcut handling |
| config.c | INI configuration parser |
| ai.c | OpenRouter API, Exa search integration |
| news.c | News ticker with scrolling animation |
| screenshot.c | Screen capture API |
| ocr.c | Tesseract text extraction |
| autostart.c | XDG autostart implementation |
| services.c | Background service management |
| demo.c | Automated feature demonstration |
| api.c | WebSocket server |
| util.c | Logging, memory, string utilities, glow effects |
### Battery Indicator
### New Abstraction Layer (v2.0)
Shows battery percentage on laptops with color coding:
- **Green**: > 50%
- **Yellow**: 20-50%
- **Red**: < 20%
- **Blue**: Charging
DWN now includes a modern abstraction layer for future extensibility:
### WiFi Indicator
**Core Abstractions** (`include/core/`)
- `wm_types.h` - Abstract handles, geometry, colors, events
- `wm_string.h/c` - Safe dynamic strings with automatic memory management
- `wm_list.h/c` - Dynamic array container with sorting and iteration
- `wm_hashmap.h/c` - Hash table with automatic resizing
- `wm_client.h/c` - Abstract client type with bidirectional legacy sync
Located in the top-right corner of the panel, showing your current connection status.
**Backend Interface** (`include/backends/`)
- `backend_interface.h` - Backend-agnostic vtable (80+ operations)
- `x11/x11_backend.h/c` - X11 implementation with event translation
- Designed for future Wayland and headless backends
| Action | What it does |
|--------|--------------|
| **Click** on WiFi icon | Open dropdown with available networks |
| **Click** on a network | Connect to that network |
| **Click** on connected network | Disconnect |
**Plugin System** (`include/plugins/`)
- `layout_plugin.h` - Layout plugin API with state management
- `widget_plugin.h` - Widget plugin API for panel components
- Built-in layouts: tiling, floating, monocle, grid
- Support for dynamic plugin loading
The dropdown shows:
- Available WiFi networks with signal strength
- `●` marker next to the currently connected network
- Networks are sorted and updated automatically
The abstraction layer maintains **100% API compatibility** with existing code while enabling:
- Backend portability (X11 → Wayland migration path)
- Dynamic plugin loading for layouts and widgets
- Type-safe handles replacing void* casts
- Memory-safe string and container operations
**Note:** Requires NetworkManager (`nmcli`) to be installed.
### Design Patterns
### Audio Indicator
**Encapsulation**
- Opaque pointer types hide internal structures
- Header exposes only public API
- Implementation details remain private
Shows current volume level next to the WiFi indicator.
**Error Handling**
- Status code return values
- Output parameters for results
- Enum-based error codes
| Action | What it does |
|--------|--------------|
| **Left-click** | Toggle mute/unmute |
| **Scroll up** | Increase volume by 5% |
| **Scroll down** | Decrease volume by 5% |
**Resource Management**
- Goto cleanup pattern for multi-resource functions
- Every allocation has corresponding free
- XGrabServer/XUngrabServer for atomic X11 operations
**Note:** Requires ALSA utilities (`amixer`) to be installed.
**Event Architecture**
- Select-based multiplexed I/O
- 60fps animation loop with 16ms timeout
- Async request queues for AI/screenshot/OCR
---
**Naming Conventions**
- Module prefix for functions: `client_focus()`, `workspace_switch()`
- Snake_case for functions and variables
- CamelCase for types and structs
## Configuration
### Design Patterns
DWN can be customized by editing a config file.
**Encapsulation**
- Opaque pointer types hide internal structures
- Header exposes only public API
- Implementation details remain private
### Create Your Config
**Error Handling**
- Status code return values
- Output parameters for results
- Enum-based error codes
**Resource Management**
- Goto cleanup pattern for multi-resource functions
- Every allocation has corresponding free
- XGrabServer/XUngrabServer for atomic X11 operations
**Event Architecture**
- Select-based multiplexed I/O
- 60fps animation loop with 16ms timeout
- Async request queues for AI/screenshot/OCR
**Naming Conventions**
- Module prefix for functions: `client_focus()`, `workspace_switch()`
- Snake_case for functions and variables
- CamelCase for types and structs
### Key Constants
| Constant | Value |
|----------|-------|
| MAX_CLIENTS | 256 |
| MAX_WORKSPACES | 9 |
| MAX_MONITORS | 8 |
| MAX_NOTIFICATIONS | 32 |
| MAX_KEYBINDINGS | 64 |
## WebSocket API Examples
### Python
```python
import websocket
import json
ws = websocket.create_connection("ws://localhost:8777/ws")
ws.send(json.dumps({"command": "get_clients"}))
clients = json.loads(ws.recv())
ws.send(json.dumps({"command": "switch_workspace", "workspace": 3}))
ws.send(json.dumps({"command": "focus_client", "window": 12345678}))
ws.close()
```bash
mkdir -p ~/.config/dwn
cp /etc/dwn/config.example ~/.config/dwn/config
```
### Response Format
### Edit Your Config
```json
{
"status": "ok",
"data": {
"clients": [
{
"window": 12345678,
"title": "Firefox",
"class": "firefox",
"workspace": 1,
"x": 0, "y": 32,
"width": 960, "height": 540,
"focused": true
}
]
}
}
Open `~/.config/dwn/config` in any text editor. Here are common changes:
**Change terminal:**
```ini
[general]
terminal = gnome-terminal
```
Options: `xfce4-terminal`, `gnome-terminal`, `konsole`, `alacritty`, `xterm`
**Change app launcher:**
```ini
[general]
launcher = rofi -show run
```
## Project Structure
**Change layout:**
```ini
[layout]
default = floating
```
Options: `tiling`, `floating`, `monocle`
**Change window gaps:**
```ini
[appearance]
gap = 10
border_width = 2
```
dwn/
├── src/ # Implementation files
│ ├── core/ # Abstract core types (string, list, hashmap, client)
│ ├── backends/x11/ # X11 backend implementation
│ └── plugins/ # Plugin system (layout, widget managers)
│ └── layouts/ # Built-in layout plugins
├── include/ # Header files
│ ├── core/ # Core abstraction headers
│ ├── backends/ # Backend interface headers
│ └── plugins/ # Plugin API headers
├── manual/ # Documentation website (HTML)
├── config/ # Configuration templates
├── scripts/ # Utility scripts
├── examples/ # API usage examples
└── build/ # Build artifacts
---
## AI Features (Optional)
DWN has an AI assistant that can run commands for you. For example, you can press `Super+Shift+A`, type "open chrome", and it will launch Chrome.
### AI Command Setup (OpenRouter)
1. Go to [openrouter.ai](https://openrouter.ai) and create a free account
2. Get your API key
3. Add it to your shell config:
```bash
echo 'export OPENROUTER_API_KEY="your-key-here"' >> ~/.bashrc
source ~/.bashrc
```
4. Make sure dmenu is installed: `sudo apt install dmenu`
### Exa Semantic Search Setup
1. Go to [dashboard.exa.ai](https://dashboard.exa.ai/api-keys) and create an account
2. Get your API key
3. Add it to your shell config:
```bash
echo 'export EXA_API_KEY="your-key-here"' >> ~/.bashrc
source ~/.bashrc
```
### Usage
- Press `Super + A` to see AI context info about your current task
- Press `Super + Shift + A` to ask the AI to do something:
- "open firefox" - launches Firefox
- "run terminal" - opens a terminal
- "what time is it" - answers your question
- Press `Super + Shift + E` to search the web semantically:
- Type a question like "how to configure nginx"
- Select a result to open in browser
---
## Troubleshooting
### "make: command not found"
Install build tools:
```bash
sudo apt install build-essential
```
### "X11 libraries not found" or build errors
Install dependencies:
```bash
make deps
```
### Can't find DWN at login screen
Make sure you ran `sudo make install`, then:
```bash
ls /usr/share/xsessions/dwn.desktop
```
If missing:
```bash
sudo cp scripts/dwn.desktop /usr/share/xsessions/
```
### Terminal shortcut doesn't work
Install the default terminal:
```bash
sudo apt install xfce4-terminal
```
Or change terminal in config (see Configuration section).
### Screen is blank after login
DWN started but there's nothing to show. Press `Ctrl+Alt+T` to open a terminal.
### How do I get back to my normal desktop?
1. Press `Super+Backspace` to exit DWN
2. At login screen, select your previous desktop (GNOME, KDE, etc.)
### AI says "dmenu not found"
```bash
sudo apt install dmenu
```
### WiFi indicator not working
Make sure NetworkManager is installed and running:
```bash
sudo apt install network-manager
sudo systemctl enable --now NetworkManager
```
### Audio indicator not working
Make sure ALSA utilities are installed:
```bash
sudo apt install alsa-utils
```
---
## Uninstall
```bash
sudo make uninstall
```
Then select a different desktop at the login screen.
---
## All Make Commands
| Command | What it does |
|---------|--------------|
| `make help` | Show all commands |
| `make deps` | Install dependencies |
| `make` | Build DWN |
| `make debug` | Build with debug symbols |
| `make run` | Test in a window |
| `make install` | Install system-wide |
| `make uninstall` | Remove from system |
| `make clean` | Delete build files |
| `make format` | Format source code |
| `make check` | Run static analysis |
---
## License
MIT License - see LICENSE file.
MIT License - do whatever you want with it.
## Credits
- Inspired by [dwm](https://dwm.suckless.org/) and [XFCE](https://xfce.org/)
- AI powered by [OpenRouter](https://openrouter.ai/)
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
-7
View File
@@ -1,7 +0,0 @@
build/autostart.o: src/autostart.c include/autostart.h include/dwn.h \
include/config.h include/dwn.h include/util.h
include/autostart.h:
include/dwn.h:
include/config.h:
include/dwn.h:
include/util.h:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -7
View File
@@ -18,8 +18,7 @@ build/client.o: src/client.c include/client.h include/dwn.h \
/usr/include/dbus-1.0/dbus/dbus-server.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/layout.h \
include/panel.h include/api.h include/rules.h include/marks.h
/usr/include/dbus-1.0/dbus/dbus-threads.h
include/client.h:
include/dwn.h:
include/atoms.h:
@@ -46,8 +45,3 @@ include/notifications.h:
/usr/include/dbus-1.0/dbus/dbus-signature.h:
/usr/include/dbus-1.0/dbus/dbus-syntax.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/layout.h:
include/panel.h:
include/api.h:
include/rules.h:
include/marks.h:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -3
View File
@@ -1,9 +1,7 @@
build/decorations.o: src/decorations.c include/decorations.h \
include/dwn.h include/client.h include/config.h include/util.h \
include/workspace.h
include/dwn.h include/client.h include/config.h include/util.h
include/decorations.h:
include/dwn.h:
include/client.h:
include/config.h:
include/util.h:
include/workspace.h:
Binary file not shown.
-52
View File
@@ -1,52 +0,0 @@
build/demo.o: src/demo.c include/demo.h include/dwn.h \
include/notifications.h include/dwn.h /usr/include/dbus-1.0/dbus/dbus.h \
/usr/lib/x86_64-linux-gnu/dbus-1.0/include/dbus/dbus-arch-deps.h \
/usr/include/dbus-1.0/dbus/dbus-macros.h \
/usr/include/dbus-1.0/dbus/dbus-address.h \
/usr/include/dbus-1.0/dbus/dbus-types.h \
/usr/include/dbus-1.0/dbus/dbus-errors.h \
/usr/include/dbus-1.0/dbus/dbus-protocol.h \
/usr/include/dbus-1.0/dbus/dbus-bus.h \
/usr/include/dbus-1.0/dbus/dbus-connection.h \
/usr/include/dbus-1.0/dbus/dbus-memory.h \
/usr/include/dbus-1.0/dbus/dbus-message.h \
/usr/include/dbus-1.0/dbus/dbus-shared.h \
/usr/include/dbus-1.0/dbus/dbus-misc.h \
/usr/include/dbus-1.0/dbus/dbus-pending-call.h \
/usr/include/dbus-1.0/dbus/dbus-server.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/workspace.h \
include/client.h include/layout.h include/keys.h include/ai.h \
include/news.h include/util.h include/panel.h include/config.h
include/demo.h:
include/dwn.h:
include/notifications.h:
include/dwn.h:
/usr/include/dbus-1.0/dbus/dbus.h:
/usr/lib/x86_64-linux-gnu/dbus-1.0/include/dbus/dbus-arch-deps.h:
/usr/include/dbus-1.0/dbus/dbus-macros.h:
/usr/include/dbus-1.0/dbus/dbus-address.h:
/usr/include/dbus-1.0/dbus/dbus-types.h:
/usr/include/dbus-1.0/dbus/dbus-errors.h:
/usr/include/dbus-1.0/dbus/dbus-protocol.h:
/usr/include/dbus-1.0/dbus/dbus-bus.h:
/usr/include/dbus-1.0/dbus/dbus-connection.h:
/usr/include/dbus-1.0/dbus/dbus-memory.h:
/usr/include/dbus-1.0/dbus/dbus-message.h:
/usr/include/dbus-1.0/dbus/dbus-shared.h:
/usr/include/dbus-1.0/dbus/dbus-misc.h:
/usr/include/dbus-1.0/dbus/dbus-pending-call.h:
/usr/include/dbus-1.0/dbus/dbus-server.h:
/usr/include/dbus-1.0/dbus/dbus-signature.h:
/usr/include/dbus-1.0/dbus/dbus-syntax.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/workspace.h:
include/client.h:
include/layout.h:
include/keys.h:
include/ai.h:
include/news.h:
include/util.h:
include/panel.h:
include/config.h:
BIN
View File
Binary file not shown.
+1 -8
View File
@@ -18,8 +18,7 @@ build/keys.o: src/keys.c include/keys.h include/dwn.h include/client.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/news.h \
include/applauncher.h include/decorations.h include/demo.h \
include/layout.h include/api.h include/marks.h include/panel.h
include/applauncher.h
include/keys.h:
include/dwn.h:
include/client.h:
@@ -48,9 +47,3 @@ include/notifications.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/news.h:
include/applauncher.h:
include/decorations.h:
include/demo.h:
include/layout.h:
include/api.h:
include/marks.h:
include/panel.h:
BIN
View File
Binary file not shown.
+1 -40
View File
@@ -1,47 +1,8 @@
build/layout.o: src/layout.c include/layout.h include/dwn.h \
include/client.h include/workspace.h include/config.h include/util.h \
include/panel.h include/notifications.h \
/usr/include/dbus-1.0/dbus/dbus.h \
/usr/lib/x86_64-linux-gnu/dbus-1.0/include/dbus/dbus-arch-deps.h \
/usr/include/dbus-1.0/dbus/dbus-macros.h \
/usr/include/dbus-1.0/dbus/dbus-address.h \
/usr/include/dbus-1.0/dbus/dbus-types.h \
/usr/include/dbus-1.0/dbus/dbus-errors.h \
/usr/include/dbus-1.0/dbus/dbus-protocol.h \
/usr/include/dbus-1.0/dbus/dbus-bus.h \
/usr/include/dbus-1.0/dbus/dbus-connection.h \
/usr/include/dbus-1.0/dbus/dbus-memory.h \
/usr/include/dbus-1.0/dbus/dbus-message.h \
/usr/include/dbus-1.0/dbus/dbus-shared.h \
/usr/include/dbus-1.0/dbus/dbus-misc.h \
/usr/include/dbus-1.0/dbus/dbus-pending-call.h \
/usr/include/dbus-1.0/dbus/dbus-server.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h
include/client.h include/workspace.h include/config.h include/util.h
include/layout.h:
include/dwn.h:
include/client.h:
include/workspace.h:
include/config.h:
include/util.h:
include/panel.h:
include/notifications.h:
/usr/include/dbus-1.0/dbus/dbus.h:
/usr/lib/x86_64-linux-gnu/dbus-1.0/include/dbus/dbus-arch-deps.h:
/usr/include/dbus-1.0/dbus/dbus-macros.h:
/usr/include/dbus-1.0/dbus/dbus-address.h:
/usr/include/dbus-1.0/dbus/dbus-types.h:
/usr/include/dbus-1.0/dbus/dbus-errors.h:
/usr/include/dbus-1.0/dbus/dbus-protocol.h:
/usr/include/dbus-1.0/dbus/dbus-bus.h:
/usr/include/dbus-1.0/dbus/dbus-connection.h:
/usr/include/dbus-1.0/dbus/dbus-memory.h:
/usr/include/dbus-1.0/dbus/dbus-message.h:
/usr/include/dbus-1.0/dbus/dbus-shared.h:
/usr/include/dbus-1.0/dbus/dbus-misc.h:
/usr/include/dbus-1.0/dbus/dbus-pending-call.h:
/usr/include/dbus-1.0/dbus/dbus-server.h:
/usr/include/dbus-1.0/dbus/dbus-signature.h:
/usr/include/dbus-1.0/dbus/dbus-syntax.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
BIN
View File
Binary file not shown.
+1 -22
View File
@@ -19,12 +19,7 @@ build/main.o: src/main.c include/dwn.h include/config.h include/dwn.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/systray.h \
include/slider.h include/news.h include/applauncher.h include/ai.h \
include/autostart.h include/services.h include/api.h include/demo.h \
include/screenshot.h include/ocr.h include/util.h include/slider.h \
include/rules.h include/marks.h include/core/wm_client.h \
include/core/wm_types.h include/core/wm_string.h include/core/wm_types.h \
include/plugins/layout_plugin.h include/plugins/builtin_layouts.h
include/news.h include/applauncher.h include/ai.h include/util.h
include/dwn.h:
include/config.h:
include/dwn.h:
@@ -55,23 +50,7 @@ include/notifications.h:
/usr/include/dbus-1.0/dbus/dbus-syntax.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/systray.h:
include/slider.h:
include/news.h:
include/applauncher.h:
include/ai.h:
include/autostart.h:
include/services.h:
include/api.h:
include/demo.h:
include/screenshot.h:
include/ocr.h:
include/util.h:
include/slider.h:
include/rules.h:
include/marks.h:
include/core/wm_client.h:
include/core/wm_types.h:
include/core/wm_string.h:
include/core/wm_types.h:
include/plugins/layout_plugin.h:
include/plugins/builtin_layouts.h:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -2
View File
@@ -17,7 +17,7 @@ build/notifications.o: src/notifications.c include/notifications.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/config.h \
include/util.h include/api.h
include/util.h
include/notifications.h:
include/dwn.h:
/usr/include/dbus-1.0/dbus/dbus.h:
@@ -40,4 +40,3 @@ include/dwn.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/config.h:
include/util.h:
include/api.h:
Binary file not shown.
+1 -3
View File
@@ -1,7 +1,6 @@
build/panel.o: src/panel.c include/panel.h include/dwn.h \
include/workspace.h include/layout.h include/client.h include/config.h \
include/util.h include/atoms.h include/systray.h include/slider.h \
include/news.h
include/util.h include/atoms.h include/systray.h include/news.h
include/panel.h:
include/dwn.h:
include/workspace.h:
@@ -11,5 +10,4 @@ include/config.h:
include/util.h:
include/atoms.h:
include/systray.h:
include/slider.h:
include/news.h:
BIN
View File
Binary file not shown.
+3 -6
View File
@@ -1,6 +1,6 @@
build/systray.o: src/systray.c include/systray.h include/dwn.h \
include/slider.h include/panel.h include/config.h include/util.h \
include/notifications.h /usr/include/dbus-1.0/dbus/dbus.h \
include/panel.h include/config.h include/util.h include/notifications.h \
/usr/include/dbus-1.0/dbus/dbus.h \
/usr/lib/x86_64-linux-gnu/dbus-1.0/include/dbus/dbus-arch-deps.h \
/usr/include/dbus-1.0/dbus/dbus-macros.h \
/usr/include/dbus-1.0/dbus/dbus-address.h \
@@ -17,10 +17,9 @@ build/systray.o: src/systray.c include/systray.h include/dwn.h \
/usr/include/dbus-1.0/dbus/dbus-server.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/atoms.h include/api.h
/usr/include/dbus-1.0/dbus/dbus-threads.h
include/systray.h:
include/dwn.h:
include/slider.h:
include/panel.h:
include/config.h:
include/util.h:
@@ -43,5 +42,3 @@ include/notifications.h:
/usr/include/dbus-1.0/dbus/dbus-signature.h:
/usr/include/dbus-1.0/dbus/dbus-syntax.h:
/usr/include/dbus-1.0/dbus/dbus-threads.h:
include/atoms.h:
include/api.h:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -4
View File
@@ -1,6 +1,6 @@
build/workspace.o: src/workspace.c include/workspace.h include/dwn.h \
include/client.h include/layout.h include/atoms.h include/util.h \
include/config.h include/panel.h include/api.h include/decorations.h
include/config.h
include/workspace.h:
include/dwn.h:
include/client.h:
@@ -8,6 +8,3 @@ include/layout.h:
include/atoms.h:
include/util.h:
include/config.h:
include/panel.h:
include/api.h:
include/decorations.h:
BIN
View File
Binary file not shown.
+8 -21
View File
@@ -18,17 +18,17 @@ focus_mode = click
decorations = true
[appearance]
# Border width in pixels (default: 0)
border_width = 0
# Border width in pixels (default: 2)
border_width = 2
# Title bar height in pixels (default: 28)
title_height = 28
# Title bar height in pixels (default: 24)
title_height = 24
# Panel height in pixels (default: 32)
panel_height = 32
# Panel height in pixels (default: 28)
panel_height = 28
# Gap between windows in pixels (default: 0)
gap = 0
# Gap between windows in pixels (default: 4)
gap = 4
# Font for titles and panels (default: fixed)
# Use xlsfonts to list available fonts
@@ -91,16 +91,3 @@ model = google/gemini-2.0-flash-exp:free
# Can also be set via EXA_API_KEY environment variable
# Sign up and get your key at: https://dashboard.exa.ai/api-keys
# exa_api_key = your-exa-key-here
[autostart]
# Enable XDG autostart support (default: true)
# Automatically starts applications from XDG autostart directories
enabled = true
# Scan XDG .desktop files (default: true)
# Directories: /etc/xdg/autostart and ~/.config/autostart
xdg_autostart = true
# Additional directory for symlinks and scripts (default: ~/.config/dwn/autostart.d)
# Create symlinks to binaries you want to start: ln -s /usr/bin/telegram-desktop ~/.config/dwn/autostart.d/
path = ~/.config/dwn/autostart.d
-188
View File
@@ -1,188 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>DWN Remote Control</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body { background-color: #1a1a1a; color: #e0e0e0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; }
.card { background-color: #2d2d2d; border: 1px solid #404040; margin-bottom: 20px; }
.btn-ws { width: 50px; height: 50px; margin: 5px; font-weight: bold; }
.client-item { border-bottom: 1px solid #404040; padding: 10px; cursor: pointer; transition: background 0.2s; }
.client-item:hover { background-color: #3d3d3d; }
.focused { border-left: 4px solid #0d6efd; background-color: #343a40; }
.badge-ws { font-size: 0.8rem; }
</style>
</head>
<body>
<div class="container py-5">
<h1 class="mb-4 text-primary">DWN Remote</h1>
<div class="row">
<div class="col-md-4">
<div class="card p-3">
<h3>Workspaces</h3>
<div id="workspace-grid" class="d-flex flex-wrap">
</div>
</div>
<div class="card p-3">
<h3>Quick Launch</h3>
<div class="input-group mb-3">
<input type="text" id="cmd-input" class="form-control bg-dark text-white border-secondary" placeholder="Terminal command...">
<button class="btn btn-outline-primary" onclick="launchCmd()">Run</button>
</div>
</div>
<div class="card p-3">
<h3>Screenshot</h3>
<div class="d-flex flex-wrap gap-2 mb-3">
<button class="btn btn-outline-info" onclick="takeScreenshot('fullscreen')">Fullscreen</button>
<button class="btn btn-outline-info" onclick="takeScreenshot('active')">Active Window</button>
</div>
<div id="screenshot-container" style="display:none;">
<img id="screenshot-img" class="img-fluid rounded" style="max-height: 300px;">
<div class="mt-2">
<small id="screenshot-info" class="text-muted"></small>
<button class="btn btn-sm btn-outline-success ms-2" onclick="downloadScreenshot()">Download</button>
<button class="btn btn-sm btn-outline-warning ms-2" onclick="runOCR()">OCR</button>
</div>
</div>
<div id="ocr-result" class="mt-3" style="display:none;">
<h6>OCR Result <small class="text-muted" id="ocr-confidence"></small></h6>
<pre class="bg-dark p-2 rounded" style="max-height:150px;overflow:auto;"><code id="ocr-text"></code></pre>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card p-3">
<h3>Active Windows</h3>
<div id="client-list">
</div>
</div>
</div>
</div>
</div>
<script>
let ws;
let lastScreenshotData = null;
const port = 8777;
const uri = `ws://${window.location.hostname || 'localhost'}:${port}/ws`;
function connect() {
ws = new WebSocket(uri);
ws.onopen = () => {
console.log("Connected to DWN API");
refresh();
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.format === 'png' && data.encoding === 'base64') {
handleScreenshotResponse(data);
} else if (data.text !== undefined && data.confidence !== undefined) {
handleOCRResponse(data);
} else if (Array.isArray(data)) {
if (data.length > 0 && 'title' in data[0]) renderClients(data);
else if (data.length > 0 && 'layout' in data[0]) renderWorkspaces(data);
}
};
ws.onclose = () => setTimeout(connect, 2000);
}
function refresh() {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({command: "get_workspaces"}));
ws.send(JSON.stringify({command: "get_clients"}));
}
}
function renderWorkspaces(workspaces) {
const container = document.getElementById('workspace-grid');
container.innerHTML = '';
workspaces.forEach(ws_info => {
const btn = document.createElement('button');
btn.className = `btn btn-ws ${ws_info.client_count > 0 ? 'btn-primary' : 'btn-outline-secondary'}`;
btn.innerText = ws_info.id;
btn.onclick = () => {
ws.send(JSON.stringify({command: "switch_workspace", workspace: ws_info.id}));
setTimeout(refresh, 100);
};
container.appendChild(btn);
});
}
function renderClients(clients) {
const container = document.getElementById('client-list');
container.innerHTML = '';
clients.forEach(c => {
const div = document.createElement('div');
div.className = `client-item ${c.focused ? 'focused' : ''} d-flex justify-content-between align-items-center`;
div.innerHTML = `
<div>
<strong>${c.title}</strong><br>
<small class="text-muted">${c.class}</small>
</div>
<span class="badge bg-secondary badge-ws">WS ${c.workspace}</span>
`;
div.onclick = () => {
ws.send(JSON.stringify({command: "focus_client", window: c.window}));
setTimeout(refresh, 100);
};
container.appendChild(div);
});
}
function launchCmd() {
const input = document.getElementById('cmd-input');
if (input.value) {
ws.send(JSON.stringify({command: "run_command", exec: input.value}));
input.value = '';
}
}
function takeScreenshot(mode) {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({command: "screenshot", mode: mode}));
}
}
function handleScreenshotResponse(data) {
if (data.status === 'ok' && data.data) {
lastScreenshotData = data.data;
const img = document.getElementById('screenshot-img');
img.src = 'data:image/png;base64,' + data.data;
document.getElementById('screenshot-container').style.display = 'block';
document.getElementById('screenshot-info').textContent = `${data.width}x${data.height}`;
document.getElementById('ocr-result').style.display = 'none';
}
}
function downloadScreenshot() {
if (!lastScreenshotData) return;
const link = document.createElement('a');
link.href = 'data:image/png;base64,' + lastScreenshotData;
link.download = 'screenshot.png';
link.click();
}
function runOCR() {
if (!lastScreenshotData || !ws || ws.readyState !== WebSocket.OPEN) return;
ws.send(JSON.stringify({command: "ocr", image: lastScreenshotData, language: "eng"}));
}
function handleOCRResponse(data) {
if (data.status === 'ok') {
document.getElementById('ocr-result').style.display = 'block';
document.getElementById('ocr-confidence').textContent = `(${(data.confidence * 100).toFixed(1)}% confidence)`;
document.getElementById('ocr-text').textContent = data.text || '(No text detected)';
}
}
connect();
setInterval(refresh, 3000);
</script>
</body>
</html>
+13 -11
View File
@@ -10,11 +10,7 @@
#include "dwn.h"
#include <stdbool.h>
/* Forward declarations for libcurl types */
struct curl_slist;
struct Curl_easy;
typedef struct Curl_easy DWN_CURL;
/* AI request states */
typedef enum {
AI_STATE_IDLE,
AI_STATE_PENDING,
@@ -22,6 +18,7 @@ typedef enum {
AI_STATE_ERROR
} AIState;
/* AI request structure */
typedef struct AIRequest {
char *prompt;
char *response;
@@ -29,10 +26,9 @@ typedef struct AIRequest {
void (*callback)(struct AIRequest *req);
void *user_data;
struct AIRequest *next;
struct curl_slist *headers; /* Stored for proper cleanup */
struct Curl_easy *curl_handle; /* Stored for cancellation */
} AIRequest;
/* AI context for window analysis */
typedef struct {
char focused_window[256];
char focused_class[64];
@@ -40,32 +36,40 @@ typedef struct {
int window_count;
} AIContext;
/* Initialization */
bool ai_init(void);
void ai_cleanup(void);
bool ai_is_available(void);
/* API calls */
AIRequest *ai_send_request(const char *prompt, void (*callback)(AIRequest *));
void ai_cancel_request(AIRequest *req);
void ai_process_pending(void);
/* Context analysis */
void ai_update_context(void);
const char *ai_analyze_task(void);
const char *ai_suggest_window(void);
const char *ai_suggest_app(void);
/* Command palette */
void ai_show_command_palette(void);
void ai_execute_command(const char *command);
/* Smart features */
void ai_auto_organize_workspace(void);
void ai_suggest_layout(void);
void ai_analyze_workflow(void);
/* Notification intelligence */
bool ai_should_show_notification(const char *app, const char *summary);
int ai_notification_priority(const char *app, const char *summary);
/* Performance monitoring */
void ai_monitor_performance(void);
const char *ai_performance_suggestion(void);
/* Exa semantic search */
typedef struct {
char title[256];
char url[512];
@@ -77,12 +81,10 @@ typedef struct ExaRequest {
char *query;
ExaSearchResult results[10];
int result_count;
int state;
int state; /* Uses AIState enum */
void (*callback)(struct ExaRequest *req);
void *user_data;
struct ExaRequest *next;
struct curl_slist *headers; /* Stored for proper cleanup */
struct Curl_easy *curl_handle; /* Stored for cancellation */
} ExaRequest;
bool exa_is_available(void);
@@ -90,4 +92,4 @@ ExaRequest *exa_search(const char *query, void (*callback)(ExaRequest *));
void exa_process_pending(void);
void exa_show_app_launcher(void);
#endif
#endif /* DWN_AI_H */
-141
View File
@@ -1,141 +0,0 @@
/*
* retoor <retoor@molodetz.nl>
* DWN - Desktop Window Manager
* WebSocket API with event subscription system
*/
#ifndef DWN_API_H
#define DWN_API_H
#include <stdbool.h>
#include <X11/Xlib.h>
typedef enum {
EVENT_WINDOW_CREATED,
EVENT_WINDOW_DESTROYED,
EVENT_WINDOW_FOCUSED,
EVENT_WINDOW_UNFOCUSED,
EVENT_WINDOW_MOVED,
EVENT_WINDOW_RESIZED,
EVENT_WINDOW_MINIMIZED,
EVENT_WINDOW_RESTORED,
EVENT_WINDOW_MAXIMIZED,
EVENT_WINDOW_UNMAXIMIZED,
EVENT_WINDOW_FULLSCREEN,
EVENT_WINDOW_UNFULLSCREEN,
EVENT_WINDOW_FLOATING,
EVENT_WINDOW_UNFLOATING,
EVENT_WINDOW_TITLE_CHANGED,
EVENT_WINDOW_RAISED,
EVENT_WINDOW_LOWERED,
EVENT_WORKSPACE_SWITCHED,
EVENT_WORKSPACE_WINDOW_ADDED,
EVENT_WORKSPACE_WINDOW_REMOVED,
EVENT_WORKSPACE_LAYOUT_CHANGED,
EVENT_WORKSPACE_MASTER_RATIO_CHANGED,
EVENT_WORKSPACE_MASTER_COUNT_CHANGED,
EVENT_WORKSPACE_ARRANGED,
EVENT_LAYOUT_CHANGED,
EVENT_KEY_PRESSED,
EVENT_KEY_RELEASED,
EVENT_SHORTCUT_TRIGGERED,
EVENT_MOUSE_MOVED,
EVENT_MOUSE_BUTTON_PRESSED,
EVENT_MOUSE_BUTTON_RELEASED,
EVENT_DRAG_STARTED,
EVENT_DRAG_ENDED,
EVENT_SHOW_DESKTOP_TOGGLED,
EVENT_NOTIFICATION_SHOWN,
EVENT_NOTIFICATION_CLOSED,
EVENT_FADE_SPEED_CHANGED,
EVENT_FADE_INTENSITY_CHANGED,
EVENT_WINDOW_SNAPPED,
EVENT_AUDIO_VOLUME_CHANGED,
EVENT_AUDIO_MUTE_TOGGLED,
EVENT_PANEL_VISIBILITY_CHANGED,
EVENT_CONFIG_RELOADED,
EVENT_AI_RESPONSE_RECEIVED,
EVENT_EXA_SEARCH_COMPLETED,
EVENT_NEWS_ARTICLE_CHANGED,
EVENT_DEMO_STARTED,
EVENT_DEMO_STOPPED,
EVENT_DEMO_PHASE_CHANGED,
EVENT_TUTORIAL_STARTED,
EVENT_TUTORIAL_STOPPED,
EVENT_COUNT
} ApiEventType;
void api_init(void);
void api_process(void);
void api_cleanup(void);
void api_handle_json_command(const char *json_str);
const char *api_event_name(ApiEventType type);
void api_emit_window_created(Window window, const char *title, const char *class_name, int workspace);
void api_emit_window_destroyed(Window window, const char *title, int workspace);
void api_emit_window_focused(Window window, const char *title, Window prev_window);
void api_emit_window_unfocused(Window window, const char *title);
void api_emit_window_moved(Window window, int old_x, int old_y, int new_x, int new_y);
void api_emit_window_resized(Window window, int old_w, int old_h, int new_w, int new_h);
void api_emit_window_minimized(Window window);
void api_emit_window_restored(Window window);
void api_emit_window_maximized(Window window, bool maximized);
void api_emit_window_fullscreen(Window window, bool fullscreen);
void api_emit_window_floating(Window window, bool floating);
void api_emit_window_title_changed(Window window, const char *old_title, const char *new_title);
void api_emit_window_raised(Window window);
void api_emit_window_lowered(Window window);
void api_emit_workspace_switched(int old_workspace, int new_workspace);
void api_emit_workspace_window_added(int workspace, Window window);
void api_emit_workspace_window_removed(int workspace, Window window);
void api_emit_workspace_layout_changed(int workspace, int old_layout, int new_layout);
void api_emit_workspace_master_ratio_changed(int workspace, float old_ratio, float new_ratio);
void api_emit_workspace_master_count_changed(int workspace, int old_count, int new_count);
void api_emit_workspace_arranged(int workspace, int layout);
void api_emit_key_pressed(unsigned int keycode, unsigned int keysym, unsigned int modifiers);
void api_emit_key_released(unsigned int keycode, unsigned int keysym, unsigned int modifiers);
void api_emit_shortcut_triggered(const char *name, const char *description);
void api_emit_mouse_moved(int x, int y);
void api_emit_mouse_button_pressed(int button, int x, int y);
void api_emit_mouse_button_released(int button, int x, int y);
void api_emit_drag_started(Window window, bool is_resize);
void api_emit_drag_ended(Window window, bool is_resize);
void api_emit_show_desktop_toggled(bool shown);
void api_emit_notification_shown(unsigned int id, const char *summary, const char *body);
void api_emit_notification_closed(unsigned int id);
void api_emit_fade_speed_changed(float old_speed, float new_speed);
void api_emit_fade_intensity_changed(float old_intensity, float new_intensity);
void api_emit_window_snapped(Window window, int horizontal, int vertical);
void api_emit_audio_volume_changed(int old_volume, int new_volume);
void api_emit_audio_mute_toggled(bool muted);
void api_emit_panel_visibility_changed(const char *panel, bool visible);
void api_emit_config_reloaded(void);
void api_emit_ai_response_received(const char *prompt, const char *response, bool success);
void api_emit_exa_search_completed(const char *query, int result_count, bool success);
void api_emit_news_article_changed(int old_index, int new_index, const char *title);
void api_emit_demo_started(void);
void api_emit_demo_stopped(void);
void api_emit_demo_phase_changed(int phase, const char *phase_name);
void api_emit_tutorial_started(void);
void api_emit_tutorial_stopped(void);
#endif
+16 -8
View File
@@ -9,33 +9,41 @@
#include <stdbool.h>
/* Maximum applications to track */
#define MAX_APPS 512
#define MAX_RECENT_APPS 10
/* Application entry from .desktop file */
typedef struct {
char name[128];
char exec[512];
char icon[128];
char desktop_id[256];
bool terminal;
bool hidden;
char name[128]; /* Display name */
char exec[512]; /* Command to execute */
char icon[128]; /* Icon name (unused for now) */
char desktop_id[256]; /* Desktop file basename for tracking */
bool terminal; /* Run in terminal */
bool hidden; /* Should be hidden */
} AppEntry;
/* Application launcher state */
typedef struct {
AppEntry apps[MAX_APPS];
int app_count;
char recent[MAX_RECENT_APPS][256];
char recent[MAX_RECENT_APPS][256]; /* Desktop IDs of recent apps */
int recent_count;
} AppLauncherState;
/* Initialize the app launcher (scans .desktop files) */
void applauncher_init(void);
/* Cleanup resources */
void applauncher_cleanup(void);
/* Rescan .desktop files */
void applauncher_refresh(void);
/* Show the application launcher (dmenu-based) */
void applauncher_show(void);
/* Launch an application by desktop_id */
void applauncher_launch(const char *desktop_id);
#endif
#endif /* DWN_APPLAUNCHER_H */
+17 -6
View File
@@ -10,7 +10,9 @@
#include <X11/Xlib.h>
#include <stdbool.h>
/* EWMH (Extended Window Manager Hints) atoms */
typedef struct {
/* Root window properties */
Atom NET_SUPPORTED;
Atom NET_SUPPORTING_WM_CHECK;
Atom NET_CLIENT_LIST;
@@ -23,6 +25,7 @@ typedef struct {
Atom NET_ACTIVE_WINDOW;
Atom NET_WORKAREA;
/* Client window properties */
Atom NET_WM_NAME;
Atom NET_WM_VISIBLE_NAME;
Atom NET_WM_DESKTOP;
@@ -33,6 +36,7 @@ typedef struct {
Atom NET_WM_STRUT_PARTIAL;
Atom NET_WM_PID;
/* Window types */
Atom NET_WM_WINDOW_TYPE_DESKTOP;
Atom NET_WM_WINDOW_TYPE_DOCK;
Atom NET_WM_WINDOW_TYPE_TOOLBAR;
@@ -43,6 +47,7 @@ typedef struct {
Atom NET_WM_WINDOW_TYPE_NORMAL;
Atom NET_WM_WINDOW_TYPE_NOTIFICATION;
/* Window states */
Atom NET_WM_STATE_MODAL;
Atom NET_WM_STATE_STICKY;
Atom NET_WM_STATE_MAXIMIZED_VERT;
@@ -57,6 +62,7 @@ typedef struct {
Atom NET_WM_STATE_DEMANDS_ATTENTION;
Atom NET_WM_STATE_FOCUSED;
/* Actions */
Atom NET_WM_ACTION_MOVE;
Atom NET_WM_ACTION_RESIZE;
Atom NET_WM_ACTION_MINIMIZE;
@@ -68,21 +74,22 @@ typedef struct {
Atom NET_WM_ACTION_CHANGE_DESKTOP;
Atom NET_WM_ACTION_CLOSE;
/* Client messages */
Atom NET_CLOSE_WINDOW;
Atom NET_MOVERESIZE_WINDOW;
Atom NET_WM_MOVERESIZE;
Atom NET_REQUEST_FRAME_EXTENTS;
Atom NET_FRAME_EXTENTS;
/* System tray */
Atom NET_SYSTEM_TRAY_OPCODE;
Atom NET_SYSTEM_TRAY_S0;
Atom NET_SYSTEM_TRAY_ORIENTATION;
Atom NET_SYSTEM_TRAY_VISUAL;
Atom MANAGER;
Atom XEMBED;
Atom XEMBED_INFO;
} EWMHAtoms;
/* ICCCM (Inter-Client Communication Conventions Manual) atoms */
typedef struct {
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
@@ -96,6 +103,7 @@ typedef struct {
Atom WM_WINDOW_ROLE;
} ICCCMAtoms;
/* Other useful atoms */
typedef struct {
Atom UTF8_STRING;
Atom COMPOUND_TEXT;
@@ -105,13 +113,15 @@ typedef struct {
Atom DWN_RESTART;
} MiscAtoms;
/* Global atom containers */
extern EWMHAtoms ewmh;
extern ICCCMAtoms icccm;
extern MiscAtoms misc_atoms;
/* Initialization */
void atoms_init(Display *display);
void atoms_cleanup(void);
/* EWMH root window setup */
void atoms_setup_ewmh(void);
void atoms_update_client_list(void);
void atoms_update_desktop_names(void);
@@ -119,6 +129,7 @@ void atoms_set_current_desktop(int desktop);
void atoms_set_active_window(Window window);
void atoms_set_number_of_desktops(int count);
/* Window property helpers */
bool atoms_get_window_type(Window window, Atom *type);
bool atoms_get_window_state(Window window, Atom **states, int *count);
bool atoms_set_window_state(Window window, Atom *states, int count);
@@ -127,12 +138,12 @@ bool atoms_set_window_desktop(Window window, int desktop);
char *atoms_get_window_name(Window window);
bool atoms_get_wm_class(Window window, char *class_name, char *instance_name, size_t len);
/* Protocol helpers */
bool atoms_window_supports_protocol(Window window, Atom protocol);
void atoms_send_protocol(Window window, Atom protocol, Time timestamp);
/* Client message sending */
void atoms_send_client_message(Window window, Atom message_type,
long data0, long data1, long data2, long data3, long data4);
bool atoms_update_wm_state(Window window, Atom state, bool add);
#endif
#endif /* DWN_ATOMS_H */
-14
View File
@@ -1,14 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* XDG Autostart support
*/
#ifndef DWN_AUTOSTART_H
#define DWN_AUTOSTART_H
void autostart_init(void);
void autostart_run(void);
void autostart_cleanup(void);
#endif
-311
View File
@@ -1,311 +0,0 @@
/*
* DWN - Desktop Window Manager
* Backend Abstraction Interface
*/
#ifndef BACKEND_INTERFACE_H
#define BACKEND_INTERFACE_H
#include "core/wm_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Backend Capabilities
*============================================================================*/
typedef enum {
WM_BACKEND_CAP_WINDOWS = (1 << 0),
WM_BACKEND_CAP_COMPOSITING = (1 << 1),
WM_BACKEND_CAP_TRANSPARENCY = (1 << 2),
WM_BACKEND_CAP_ANIMATIONS = (1 << 3),
WM_BACKEND_CAP_MULTI_MONITOR = (1 << 4),
WM_BACKEND_CAP_INPUT_EVENTS = (1 << 5),
WM_BACKEND_CAP_CLIPBOARD = (1 << 6),
WM_BACKEND_CAP_DRAG_DROP = (1 << 7),
WM_BACKEND_CAP_TOUCH = (1 << 8),
WM_BACKEND_CAP_GESTURES = (1 << 9)
} WmBackendCapabilities;
/*==============================================================================
* Backend Information
*============================================================================*/
typedef struct {
const char *name;
const char *version;
const char *description;
uint32_t capabilities;
bool supports_multiple_instances;
bool requires_compositor;
} WmBackendInfo;
/*==============================================================================
* Backend Event Types
*============================================================================*/
typedef enum {
WM_BACKEND_EVENT_NONE,
WM_BACKEND_EVENT_EXPOSE,
WM_BACKEND_EVENT_CONFIGURE,
WM_BACKEND_EVENT_MAP,
WM_BACKEND_EVENT_UNMAP,
WM_BACKEND_EVENT_DESTROY,
WM_BACKEND_EVENT_FOCUS_IN,
WM_BACKEND_EVENT_FOCUS_OUT,
WM_BACKEND_EVENT_KEY_PRESS,
WM_BACKEND_EVENT_KEY_RELEASE,
WM_BACKEND_EVENT_BUTTON_PRESS,
WM_BACKEND_EVENT_BUTTON_RELEASE,
WM_BACKEND_EVENT_MOTION,
WM_BACKEND_EVENT_ENTER,
WM_BACKEND_EVENT_LEAVE,
WM_BACKEND_EVENT_PROPERTY,
WM_BACKEND_EVENT_CLIENT_MESSAGE,
WM_BACKEND_EVENT_SELECTION,
} WmBackendEventType;
typedef struct {
WmBackendEventType type;
WmWindowHandle window;
WmTime timestamp;
union {
struct { int x, y, width, height; } configure;
struct { int x, y; } point;
struct { unsigned int keycode; unsigned int state; } key;
struct { unsigned int button; int x, y; unsigned int state; } button;
struct { const char *name; const void *data; size_t size; } property;
} data;
} WmBackendEvent;
/*==============================================================================
* Backend Interface Definition
*============================================================================*/
typedef struct BackendInterface {
/* Identification */
const char *name;
WmBackendInfo (*get_info)(void);
/* Lifecycle */
bool (*init)(void *config);
void (*shutdown)(void);
bool (*is_initialized)(void);
/* Connection */
bool (*connect)(void);
void (*disconnect)(void);
bool (*is_connected)(void);
int (*get_file_descriptor)(void);
void (*flush)(void);
void (*sync)(void);
/* Screen/Display */
void (*get_screen_dimensions)(int *width, int *height);
int (*get_screen_count)(void);
void (*get_screen_geometry)(int screen, WmRect *geometry);
/* Monitor Management */
int (*get_monitor_count)(void);
void (*get_monitor_geometry)(int monitor, WmRect *geometry);
bool (*get_monitor_primary)(int monitor);
const char* (*get_monitor_name)(int monitor);
/* Work Area */
void (*get_work_area)(int monitor, WmRect *area);
void (*set_work_area)(int monitor, const WmRect *area);
/* Window Management - Lifecycle */
WmWindowHandle (*window_create)(const WmRect *geometry, uint32_t flags);
void (*window_destroy)(WmWindowHandle window);
WmWindowHandle (*window_create_frame)(WmWindowHandle parent, const WmRect *geometry);
/* Window Management - Geometry */
void (*window_get_geometry)(WmWindowHandle window, WmRect *geometry);
void (*window_set_geometry)(WmWindowHandle window, const WmRect *geometry);
void (*window_move)(WmWindowHandle window, int x, int y);
void (*window_resize)(WmWindowHandle window, int width, int height);
void (*window_move_resize)(WmWindowHandle window, const WmRect *geometry);
/* Window Management - Visibility */
void (*window_show)(WmWindowHandle window);
void (*window_hide)(WmWindowHandle window);
bool (*window_is_visible)(WmWindowHandle window);
void (*window_map)(WmWindowHandle window);
void (*window_unmap)(WmWindowHandle window);
bool (*window_is_mapped)(WmWindowHandle window);
/* Window Management - Stacking */
void (*window_raise)(WmWindowHandle window);
void (*window_lower)(WmWindowHandle window);
void (*window_raise_above)(WmWindowHandle window, WmWindowHandle above);
void (*window_lower_below)(WmWindowHandle window, WmWindowHandle below);
void (*window_set_stack_position)(WmWindowHandle window, int position);
int (*window_get_stack_position)(WmWindowHandle window);
/* Window Management - Reparenting */
void (*window_reparent)(WmWindowHandle window, WmWindowHandle parent, int x, int y);
WmWindowHandle (*window_get_parent)(WmWindowHandle window);
/* Window Management - Focus */
void (*window_focus)(WmWindowHandle window);
void (*window_unfocus)(WmWindowHandle window);
bool (*window_is_focused)(WmWindowHandle window);
WmWindowHandle (*window_get_focused)(void);
/* Window Management - Decoration */
void (*window_set_decorated)(WmWindowHandle window, bool decorated);
bool (*window_is_decorated)(WmWindowHandle window);
void (*window_set_border_width)(WmWindowHandle window, int width);
int (*window_get_border_width)(WmWindowHandle window);
void (*window_set_border_color)(WmWindowHandle window, WmColor color);
/* Window Management - Properties */
void (*window_set_title)(WmWindowHandle window, const char *title);
char* (*window_get_title)(WmWindowHandle window);
void (*window_set_class)(WmWindowHandle window, const char *class, const char *instance);
void (*window_get_class)(WmWindowHandle window, char **class, char **instance);
void (*window_set_icon_name)(WmWindowHandle window, const char *name);
/* Window Management - Protocols */
bool (*window_supports_protocol)(WmWindowHandle window, const char *protocol);
void (*window_send_protocol)(WmWindowHandle window, const char *protocol, WmTime timestamp);
void (*window_close)(WmWindowHandle window);
void (*window_kill)(WmWindowHandle window);
/* Window Management - State */
void (*window_set_fullscreen)(WmWindowHandle window, bool fullscreen);
bool (*window_is_fullscreen)(WmWindowHandle window);
void (*window_set_maximized)(WmWindowHandle window, bool maximized);
bool (*window_is_maximized)(WmWindowHandle window);
void (*window_set_minimized)(WmWindowHandle window, bool minimized);
bool (*window_is_minimized)(WmWindowHandle window);
void (*window_set_modal)(WmWindowHandle window, bool modal);
void (*window_set_sticky)(WmWindowHandle window, bool sticky);
void (*window_set_shaded)(WmWindowHandle window, bool shaded);
void (*window_set_skip_taskbar)(WmWindowHandle window, bool skip);
void (*window_set_skip_pager)(WmWindowHandle window, bool skip);
void (*window_set_urgent)(WmWindowHandle window, bool urgent);
bool (*window_is_urgent)(WmWindowHandle window);
/* Window Management - Type */
void (*window_set_type)(WmWindowHandle window, WmWindowType type);
WmWindowType (*window_get_type)(WmWindowHandle window);
/* Window Management - Selection */
void (*window_set_selection_owner)(WmWindowHandle window, const char *selection, WmTime time);
WmWindowHandle (*window_get_selection_owner)(const char *selection);
void (*window_convert_selection)(WmWindowHandle requestor, const char *selection,
const char *target, WmTime time);
/* Window Management - Client List */
WmContainer* (*window_get_stacking_list)(void);
WmContainer* (*window_get_client_list)(void);
/* Input - Events */
bool (*poll_event)(WmBackendEvent *event);
void (*wait_event)(WmBackendEvent *event);
bool (*check_mask_event)(uint32_t mask, WmBackendEvent *event);
void (*put_back_event)(const WmBackendEvent *event);
/* Input - Key Grabbing */
void (*grab_key)(int keycode, uint32_t modifiers, WmWindowHandle window,
bool owner_events, uint32_t pointer_mode, uint32_t keyboard_mode);
void (*ungrab_key)(int keycode, uint32_t modifiers, WmWindowHandle window);
void (*grab_keyboard)(WmWindowHandle window, bool owner_events,
uint32_t pointer_mode, uint32_t keyboard_mode, WmTime time);
void (*ungrab_keyboard)(WmTime time);
/* Input - Button Grabbing */
void (*grab_button)(int button, uint32_t modifiers, WmWindowHandle window,
bool owner_events, uint32_t event_mask,
uint32_t pointer_mode, uint32_t keyboard_mode,
WmWindowHandle confine_to, uint32_t cursor);
void (*ungrab_button)(int button, uint32_t modifiers, WmWindowHandle window);
void (*grab_pointer)(WmWindowHandle window, bool owner_events, uint32_t event_mask,
uint32_t pointer_mode, uint32_t keyboard_mode,
WmWindowHandle confine_to, uint32_t cursor, WmTime time);
void (*ungrab_pointer)(WmTime time);
/* Input - Pointer */
void (*query_pointer)(WmWindowHandle window, int *root_x, int *root_y,
int *win_x, int *win_y, uint32_t *mask);
void (*warp_pointer)(WmWindowHandle dest_w, int dest_x, int dest_y);
void (*set_cursor)(WmWindowHandle window, uint32_t cursor);
/* Rendering - Basic */
void (*fill_rectangle)(WmWindowHandle window, const WmRect *rect, WmColor color);
void (*draw_rectangle)(WmWindowHandle window, const WmRect *rect, WmColor color, int line_width);
void (*draw_line)(WmWindowHandle window, int x1, int y1, int x2, int y2,
WmColor color, int line_width);
void (*clear_window)(WmWindowHandle window);
void (*copy_area)(WmWindowHandle src, WmWindowHandle dst,
int src_x, int src_y, int width, int height,
int dst_x, int dst_y);
/* Rendering - Text (if supported) */
void* (*font_load)(const char *name, int size);
void (*font_destroy)(void *font);
int (*font_text_width)(void *font, const char *text, int len);
void (*draw_text)(WmWindowHandle window, void *font, int x, int y,
const char *text, int len, WmColor color);
/* Rendering - Images (if supported) */
void* (*image_load)(const uint8_t *data, size_t size);
void (*image_destroy)(void *image);
void (*image_get_size)(void *image, int *width, int *height);
void (*draw_image)(WmWindowHandle window, void *image, int x, int y,
int width, int height);
/* Rendering - Sync */
void (*begin_paint)(WmWindowHandle window);
void (*end_paint)(WmWindowHandle window);
/* Atoms/Properties */
uint32_t (*atom_get)(const char *name);
const char* (*atom_get_name)(uint32_t atom);
void (*property_set)(WmWindowHandle window, uint32_t property, uint32_t type,
int format, const void *data, int num_items);
int (*property_get)(WmWindowHandle window, uint32_t property, uint32_t type,
void **data, int *num_items);
void (*property_delete)(WmWindowHandle window, uint32_t property);
/* Session Management */
void (*set_selection_owner)(WmWindowHandle owner, const char *selection);
void (*send_client_message)(WmWindowHandle window, const char *message_type,
const void *data, int format);
/* Error Handling */
int (*get_last_error)(void);
void (*set_error_handler)(int (*handler)(void *display, void *event));
void (*set_io_error_handler)(int (*handler)(void *display));
} BackendInterface;
/*==============================================================================
* Backend Registration
*============================================================================*/
typedef const BackendInterface* (*BackendEntryFunc)(void);
void wm_backend_register(const char *name, BackendEntryFunc entry);
const BackendInterface* wm_backend_get(const char *name);
WmContainer* wm_backend_get_available(void);
/*==============================================================================
* Backend Helpers
*============================================================================*/
static inline bool wm_backend_has_capability(const BackendInterface *backend,
WmBackendCapabilities cap) {
WmBackendInfo info = backend->get_info();
return (info.capabilities & cap) != 0;
}
#ifdef __cplusplus
}
#endif
#endif /* BACKEND_INTERFACE_H */
-35
View File
@@ -1,35 +0,0 @@
/*
* DWN - Desktop Window Manager
* X11 Backend Header
*/
#ifndef X11_BACKEND_H
#define X11_BACKEND_H
#include "backends/backend_interface.h"
#include <X11/Xlib.h>
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* X11 Backend Access
*============================================================================*/
const BackendInterface* x11_backend_get_interface(void);
Display* x11_backend_get_display(void);
Window x11_backend_get_root(void);
int x11_backend_get_screen(void);
static inline bool x11_backend_is_available(void) {
const BackendInterface *iface = x11_backend_get_interface();
return iface && iface->init(NULL);
}
#ifdef __cplusplus
}
#endif
#endif /* X11_BACKEND_H */
+13 -10
View File
@@ -10,69 +10,72 @@
#include "dwn.h"
#include <stdbool.h>
/* Client creation and destruction */
Client *client_create(Window window);
void client_destroy(Client *client);
/* Client management */
Client *client_manage(Window window);
void client_unmanage(Client *client);
Client *client_find_by_window(Window window);
Client *client_find_by_frame(Window frame);
void client_focus(Client *client, bool update_mru);
/* Client state */
void client_focus(Client *client);
void client_unfocus(Client *client);
void client_raise(Client *client);
void client_lower(Client *client);
void client_minimize(Client *client);
void client_restore(Client *client);
/* Client geometry */
void client_move(Client *client, int x, int y);
void client_resize(Client *client, int width, int height);
void client_move_resize(Client *client, int x, int y, int width, int height);
void client_configure(Client *client);
void client_apply_size_hints(Client *client, int *width, int *height);
/* Client properties */
void client_update_title(Client *client);
void client_update_class(Client *client);
void client_set_fullscreen(Client *client, bool fullscreen);
void client_toggle_fullscreen(Client *client);
void client_set_maximize(Client *client, bool maximized);
void client_toggle_maximize(Client *client);
void client_set_floating(Client *client, bool floating);
void client_toggle_floating(Client *client);
/* Window type checking */
bool client_is_floating(Client *client);
bool client_is_fullscreen(Client *client);
bool client_is_maximized(Client *client);
bool client_is_minimized(Client *client);
bool client_is_dialog(Window window);
bool client_is_dock(Window window);
bool client_is_desktop(Window window);
/* Frame management */
void client_create_frame(Client *client);
void client_destroy_frame(Client *client);
void client_reparent_to_frame(Client *client);
void client_reparent_from_frame(Client *client);
/* Visibility */
void client_show(Client *client);
void client_hide(Client *client);
bool client_is_visible(Client *client);
/* Close handling */
void client_close(Client *client);
void client_kill(Client *client);
/* List operations */
void client_add_to_list(Client *client);
void client_remove_from_list(Client *client);
int client_count(void);
int client_count_on_workspace(int workspace);
/* Iteration */
Client *client_get_next(Client *client);
Client *client_get_prev(Client *client);
Client *client_get_first(void);
Client *client_get_last(void);
void client_start_focus_animation(Client *client, bool gaining_focus);
unsigned long client_get_animated_title_color(Client *client);
unsigned long client_get_glow_text_color(Client *client);
void client_update_animations(void);
#endif
#endif /* DWN_CLIENT_H */
+12 -37
View File
@@ -10,6 +10,7 @@
#include "dwn.h"
#include <stdbool.h>
/* Color configuration */
typedef struct {
unsigned long panel_bg;
unsigned long panel_fg;
@@ -26,14 +27,16 @@ typedef struct {
unsigned long notification_fg;
} ColorScheme;
/* Configuration structure */
struct Config {
/* General */
char terminal[128];
char launcher[128];
char file_manager[128];
FocusMode focus_mode;
int focus_follow_delay_ms;
bool show_decorations;
/* Appearance */
int border_width;
int title_height;
int panel_height;
@@ -41,59 +44,34 @@ struct Config {
char font_name[128];
ColorScheme colors;
/* Layout */
float default_master_ratio;
int default_master_count;
LayoutType default_layout;
/* Panels */
bool top_panel_enabled;
bool bottom_panel_enabled;
/* AI */
char openrouter_api_key[256];
char exa_api_key[256];
char ai_model[64];
bool ai_enabled;
/* Paths */
char config_path[512];
char log_path[512];
bool autostart_enabled;
bool autostart_xdg;
char autostart_path[512];
char services_path[512];
bool api_enabled;
int api_port;
int demo_step_delay_ms;
int demo_ai_timeout_ms;
int demo_window_timeout_ms;
/* Fade effects */
float fade_speed; /* Animation speed multiplier (0.1 - 3.0) */
float fade_intensity; /* Glow intensity (0.0 - 1.0) */
char color_panel_bg[16];
char color_panel_fg[16];
char color_workspace_active[16];
char color_workspace_inactive[16];
char color_workspace_urgent[16];
char color_title_focused_bg[16];
char color_title_focused_fg[16];
char color_title_unfocused_bg[16];
char color_title_unfocused_fg[16];
char color_border_focused[16];
char color_border_unfocused[16];
char color_notification_bg[16];
char color_notification_fg[16];
};
/* Configuration functions */
Config *config_create(void);
void config_destroy(Config *cfg);
bool config_load(Config *cfg, const char *path);
bool config_reload(Config *cfg);
void config_set_defaults(Config *cfg);
void config_validate(Config *cfg);
/* Getters for commonly used values */
const char *config_get_terminal(void);
const char *config_get_launcher(void);
int config_get_border_width(void);
@@ -101,13 +79,10 @@ int config_get_title_height(void);
int config_get_panel_height(void);
int config_get_gap(void);
const ColorScheme *config_get_colors(void);
float config_get_fade_speed(void);
float config_get_fade_intensity(void);
void config_set_fade_speed(float speed);
void config_set_fade_intensity(float intensity);
/* INI parsing helpers */
typedef void (*ConfigCallback)(const char *section, const char *key,
const char *value, void *user_data);
bool config_parse_ini(const char *path, ConfigCallback callback, void *user_data);
#endif
#endif /* DWN_CONFIG_H */
-523
View File
@@ -1,523 +0,0 @@
/*
* DWN - Desktop Window Manager
* Abstract Client Interface
*/
#ifndef WM_CLIENT_H
#define WM_CLIENT_H
#include "core/wm_types.h"
#include "core/wm_string.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Forward declarations */
typedef struct Client Client; /* Legacy client structure */
/*==============================================================================
* Abstract Client Type
*============================================================================*/
typedef struct AbstractClient AbstractClient;
/*==============================================================================
* Client State Flags
*============================================================================*/
typedef enum {
WM_CLIENT_STATE_NORMAL = 0,
WM_CLIENT_STATE_FLOATING = (1 << 0),
WM_CLIENT_STATE_FULLSCREEN = (1 << 1),
WM_CLIENT_STATE_MAXIMIZED = (1 << 2),
WM_CLIENT_STATE_MINIMIZED = (1 << 3),
WM_CLIENT_STATE_URGENT = (1 << 4),
WM_CLIENT_STATE_STICKY = (1 << 5),
WM_CLIENT_STATE_HIDDEN = (1 << 6),
WM_CLIENT_STATE_FOCUS = (1 << 7),
WM_CLIENT_STATE_MAPPED = (1 << 8),
WM_CLIENT_STATE_MANAGED = (1 << 9),
WM_CLIENT_STATE_DESTROYING = (1 << 10)
} WmClientState;
/*==============================================================================
* Client Window Type
*============================================================================*/
typedef enum {
WM_CLIENT_TYPE_UNKNOWN = 0,
WM_CLIENT_TYPE_NORMAL,
WM_CLIENT_TYPE_DIALOG,
WM_CLIENT_TYPE_DOCK,
WM_CLIENT_TYPE_DESKTOP,
WM_CLIENT_TYPE_TOOLBAR,
WM_CLIENT_TYPE_MENU,
WM_CLIENT_TYPE_UTILITY,
WM_CLIENT_TYPE_SPLASH,
WM_CLIENT_TYPE_NOTIFICATION
} WmClientType;
/*==============================================================================
* Client Lifecycle
*============================================================================*/
/**
* Create an abstract client from a native window handle.
* This takes ownership of the native window.
*/
AbstractClient* wm_client_create(WmWindowHandle window, WmClientType type);
/**
* Destroy a client and release all associated resources.
*/
void wm_client_destroy(AbstractClient *client);
/**
* Check if a client is valid (not NULL and not being destroyed).
*/
bool wm_client_is_valid(const AbstractClient *client);
/*==============================================================================
* Native Access (for compatibility during migration)
*============================================================================*/
/**
* Get the native window handle.
* Note: This should be avoided in backend-agnostic code.
*/
WmWindowHandle wm_client_get_window(const AbstractClient *client);
/**
* Get the native frame window handle (if reparented).
*/
WmWindowHandle wm_client_get_frame(const AbstractClient *client);
/**
* Get the legacy Client structure.
* Note: This is for gradual migration only.
*/
Client* wm_client_get_legacy(const AbstractClient *client);
/**
* Create an abstract client wrapper around an existing legacy Client.
* Note: The abstract client does NOT own the legacy client.
*/
AbstractClient* wm_client_from_legacy(Client *client);
/*==============================================================================
* Client Identification
*============================================================================*/
/**
* Get the unique client ID.
*/
WmClientId wm_client_get_id(const AbstractClient *client);
/**
* Get the client window type.
*/
WmClientType wm_client_get_type(const AbstractClient *client);
/**
* Set the client window type.
*/
void wm_client_set_type(AbstractClient *client, WmClientType type);
/*==============================================================================
* Client State
*============================================================================*/
/**
* Get the current state flags.
*/
WmClientState wm_client_get_state(const AbstractClient *client);
/**
* Set state flags.
*/
void wm_client_set_state(AbstractClient *client, WmClientState state);
/**
* Add state flags.
*/
void wm_client_add_state(AbstractClient *client, WmClientState state);
/**
* Remove state flags.
*/
void wm_client_remove_state(AbstractClient *client, WmClientState state);
/**
* Toggle state flags.
*/
void wm_client_toggle_state(AbstractClient *client, WmClientState state);
/**
* Check if a specific state is set.
*/
bool wm_client_has_state(const AbstractClient *client, WmClientState state);
/* Convenience state checks */
static inline bool wm_client_is_floating(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_FLOATING);
}
static inline bool wm_client_is_fullscreen(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_FULLSCREEN);
}
static inline bool wm_client_is_maximized(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_MAXIMIZED);
}
static inline bool wm_client_is_minimized(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_MINIMIZED);
}
static inline bool wm_client_is_urgent(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_URGENT);
}
static inline bool wm_client_is_sticky(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_STICKY);
}
static inline bool wm_client_is_focused(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_FOCUS);
}
static inline bool wm_client_is_mapped(const AbstractClient *c) {
return c && wm_client_has_state(c, WM_CLIENT_STATE_MAPPED);
}
/*==============================================================================
* Client Geometry
*============================================================================*/
/**
* Get the client geometry (position and size).
*/
WmRect wm_client_get_geometry(const AbstractClient *client);
/**
* Set the client geometry.
*/
void wm_client_set_geometry(AbstractClient *client, const WmRect *geometry);
/**
* Get the client position.
*/
WmPoint wm_client_get_position(const AbstractClient *client);
/**
* Set the client position.
*/
void wm_client_set_position(AbstractClient *client, int x, int y);
/**
* Get the client size.
*/
WmSize wm_client_get_size(const AbstractClient *client);
/**
* Set the client size.
*/
void wm_client_set_size(AbstractClient *client, int width, int height);
/**
* Get the border width.
*/
int wm_client_get_border_width(const AbstractClient *client);
/**
* Set the border width.
*/
void wm_client_set_border_width(AbstractClient *client, int width);
/**
* Store previous geometry (for restore after maximize/fullscreen).
*/
void wm_client_save_geometry(AbstractClient *client);
/**
* Restore previous geometry.
*/
void wm_client_restore_geometry(AbstractClient *client);
/*==============================================================================
* Client Workspace
*============================================================================*/
/**
* Get the client's workspace ID.
*/
WmWorkspaceId wm_client_get_workspace(const AbstractClient *client);
/**
* Set the client's workspace ID.
*/
void wm_client_set_workspace(AbstractClient *client, WmWorkspaceId workspace);
/**
* Check if the client is on a specific workspace.
*/
bool wm_client_is_on_workspace(const AbstractClient *client, WmWorkspaceId workspace);
/**
* Check if the client is visible on the current workspace.
*/
bool wm_client_is_visible_on_current(const AbstractClient *client);
/*==============================================================================
* Client Properties
*============================================================================*/
/**
* Get the client title.
* Returns a reference to the internal string (do not free).
*/
const char* wm_client_get_title(const AbstractClient *client);
/**
* Set the client title.
*/
void wm_client_set_title(AbstractClient *client, const char *title);
/**
* Get the client class (application class).
*/
const char* wm_client_get_class(const AbstractClient *client);
/**
* Set the client class.
*/
void wm_client_set_class(AbstractClient *client, const char *class_name);
/**
* Get the client instance name.
*/
const char* wm_client_get_instance(const AbstractClient *client);
/**
* Set the client instance name.
*/
void wm_client_set_instance(AbstractClient *client, const char *instance);
/**
* Get the client's taskbar color.
*/
WmColor wm_client_get_color(const AbstractClient *client);
/**
* Set the client's taskbar color.
*/
void wm_client_set_color(AbstractClient *client, WmColor color);
/*==============================================================================
* Client Actions
*============================================================================*/
/**
* Focus the client.
*/
void wm_client_focus(AbstractClient *client);
/**
* Unfocus the client.
*/
void wm_client_unfocus(AbstractClient *client);
/**
* Raise the client to the top of the stack.
*/
void wm_client_raise(AbstractClient *client);
/**
* Lower the client to the bottom of the stack.
*/
void wm_client_lower(AbstractClient *client);
/**
* Show/map the client window.
*/
void wm_client_show(AbstractClient *client);
/**
* Hide/unmap the client window.
*/
void wm_client_hide(AbstractClient *client);
/**
* Minimize the client.
*/
void wm_client_minimize(AbstractClient *client);
/**
* Restore the client from minimized state.
*/
void wm_client_restore(AbstractClient *client);
/**
* Close the client (politely request close).
*/
void wm_client_close(AbstractClient *client);
/**
* Kill the client (forcefully terminate).
*/
void wm_client_kill(AbstractClient *client);
/*==============================================================================
* Client Management
*============================================================================*/
/**
* Reparent the client into a frame window.
*/
void wm_client_reparent(AbstractClient *client, WmWindowHandle frame);
/**
* Unreparent the client from its frame.
*/
void wm_client_unreparent(AbstractClient *client);
/**
* Configure the client (apply size hints, constraints).
*/
void wm_client_configure(AbstractClient *client);
/**
* Apply size hints to constrain width/height.
*/
void wm_client_apply_size_hints(AbstractClient *client, int *width, int *height);
/**
* Move and resize a client.
*/
void wm_client_move_resize(AbstractClient *client, int x, int y, int width, int height);
/*==============================================================================
* Client List Management
*============================================================================*/
/**
* Get the next client in the global list.
*/
AbstractClient* wm_client_get_next(const AbstractClient *client);
/**
* Get the previous client in the global list.
*/
AbstractClient* wm_client_get_prev(const AbstractClient *client);
/**
* Get the first client in the global list.
*/
AbstractClient* wm_client_get_first(void);
/**
* Get the last client in the global list.
*/
AbstractClient* wm_client_get_last(void);
/**
* Get the next client in MRU (Most Recently Used) order.
*/
AbstractClient* wm_client_get_next_mru(const AbstractClient *client);
/**
* Get the previous client in MRU order.
*/
AbstractClient* wm_client_get_prev_mru(const AbstractClient *client);
/*==============================================================================
* Client Lookup
*============================================================================*/
/**
* Find a client by its window handle.
*/
AbstractClient* wm_client_find_by_window(WmWindowHandle window);
/**
* Find a client by its frame handle.
*/
AbstractClient* wm_client_find_by_frame(WmWindowHandle frame);
/**
* Find a client by its ID.
*/
AbstractClient* wm_client_find_by_id(WmClientId id);
/*==============================================================================
* Client Iteration
*============================================================================*/
typedef bool (*WmClientForeachFunc)(AbstractClient *client, void *user_data);
/**
* Iterate over all clients.
*/
void wm_client_foreach(WmClientForeachFunc func, void *user_data);
/**
* Iterate over clients on a specific workspace.
*/
void wm_client_foreach_on_workspace(WmWorkspaceId workspace,
WmClientForeachFunc func, void *user_data);
/**
* Count total clients.
*/
int wm_client_count(void);
/**
* Count clients on a specific workspace.
*/
int wm_client_count_on_workspace(WmWorkspaceId workspace);
/*==============================================================================
* Client Manager
*============================================================================*/
typedef struct WmClientManager WmClientManager;
/**
* Get the global client manager.
*/
WmClientManager* wm_client_manager_get(void);
/**
* Initialize the client manager.
*/
bool wm_client_manager_init(void);
/**
* Shutdown the client manager.
*/
void wm_client_manager_shutdown(void);
/**
* Register a client with the manager.
*/
void wm_client_manager_add(WmClientManager *mgr, AbstractClient *client);
/**
* Unregister a client from the manager.
*/
void wm_client_manager_remove(WmClientManager *mgr, AbstractClient *client);
/**
* Move a client to the front of the MRU list.
*/
void wm_client_manager_touch(WmClientManager *mgr, AbstractClient *client);
/**
* Get the focused client.
*/
AbstractClient* wm_client_manager_get_focused(const WmClientManager *mgr);
/**
* Set the focused client.
*/
void wm_client_manager_set_focused(WmClientManager *mgr, AbstractClient *client);
#ifdef __cplusplus
}
#endif
#endif /* WM_CLIENT_H */
-220
View File
@@ -1,220 +0,0 @@
/*
* DWN - Desktop Window Manager
* Generic Container Interface
*/
#ifndef WM_CONTAINER_H
#define WM_CONTAINER_H
#include "wm_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Container Lifecycle
*============================================================================*/
WmContainer* wm_container_create(WmContainerType type, WmDestroyFunc destroy_fn);
void wm_container_destroy(WmContainer *container);
void wm_container_clear(WmContainer *container);
WmContainer* wm_container_clone(const WmContainer *container, WmCloneFunc clone_fn);
/*==============================================================================
* Container Properties
*============================================================================*/
WmContainerType wm_container_get_type(const WmContainer *container);
size_t wm_container_size(const WmContainer *container);
bool wm_container_is_empty(const WmContainer *container);
/*==============================================================================
* List Operations
*============================================================================*/
/* These work for LIST and QUEUE */
void wm_list_append(WmContainer *list, void *data);
void wm_list_prepend(WmContainer *list, void *data);
void wm_list_insert(WmContainer *list, size_t index, void *data);
bool wm_list_remove(WmContainer *list, void *data);
void* wm_list_remove_at(WmContainer *list, size_t index);
void* wm_list_get(const WmContainer *list, size_t index);
void* wm_list_get_first(const WmContainer *list);
void* wm_list_get_last(const WmContainer *list);
size_t wm_list_index_of(const WmContainer *list, void *data);
bool wm_list_contains(const WmContainer *list, void *data);
void* wm_list_find(const WmContainer *list, WmCompareFunc cmp, const void *key);
void* wm_list_find_custom(const WmContainer *list,
bool (*predicate)(const void *item, const void *user_data),
const void *user_data);
void wm_list_sort(WmContainer *list, WmCompareFunc cmp);
void wm_list_reverse(WmContainer *list);
/*==============================================================================
* Array Operations
*============================================================================*/
void wm_array_append(WmContainer *array, void *data);
void wm_array_insert(WmContainer *array, size_t index, void *data);
void wm_array_set(WmContainer *array, size_t index, void *data);
void* wm_array_get(const WmContainer *array, size_t index);
void* wm_array_remove_at(WmContainer *array, size_t index);
void wm_array_resize(WmContainer *array, size_t new_size);
void wm_array_reserve(WmContainer *array, size_t capacity);
size_t wm_array_capacity(const WmContainer *array);
void wm_array_compact(WmContainer *array);
void wm_array_sort(WmContainer *array, WmCompareFunc cmp);
/*==============================================================================
* HashMap Operations
*============================================================================*/
/* Key types supported */
typedef enum {
WM_HASH_KEY_STRING,
WM_HASH_KEY_INT,
WM_HASH_KEY_POINTER
} WmHashKeyType;
WmContainer* wm_hashmap_create(WmHashKeyType key_type, WmDestroyFunc destroy_fn);
void wm_hashmap_insert(WmContainer *map, const void *key, void *value);
void* wm_hashmap_get(const WmContainer *map, const void *key);
bool wm_hashmap_contains(const WmContainer *map, const void *key);
bool wm_hashmap_remove(WmContainer *map, const void *key);
void* wm_hashmap_lookup(const WmContainer *map, WmCompareFunc cmp, const void *key);
WmContainer* wm_hashmap_get_keys(const WmContainer *map);
WmContainer* wm_hashmap_get_values(const WmContainer *map);
void wm_hashmap_rehash(WmContainer *map, size_t new_capacity);
float wm_hashmap_load_factor(const WmContainer *map);
/* Convenience functions for string keys */
void wm_hashmap_insert_string(WmContainer *map, const char *key, void *value);
void* wm_hashmap_get_string(const WmContainer *map, const char *key);
bool wm_hashmap_contains_string(const WmContainer *map, const char *key);
bool wm_hashmap_remove_string(WmContainer *map, const char *key);
/* Convenience functions for int keys */
void wm_hashmap_insert_int(WmContainer *map, int key, void *value);
void* wm_hashmap_get_int(const WmContainer *map, int key);
bool wm_hashmap_contains_int(const WmContainer *map, int key);
bool wm_hashmap_remove_int(WmContainer *map, int key);
/*==============================================================================
* Stack Operations
*============================================================================*/
void wm_stack_push(WmContainer *stack, void *data);
void* wm_stack_pop(WmContainer *stack);
void* wm_stack_peek(const WmContainer *stack);
/*==============================================================================
* Queue Operations
*============================================================================*/
void wm_queue_enqueue(WmContainer *queue, void *data);
void* wm_queue_dequeue(WmContainer *queue);
void* wm_queue_peek(const WmContainer *queue);
void* wm_queue_peek_tail(const WmContainer *queue);
/*==============================================================================
* Tree Operations (if type is TREE)
*============================================================================*/
typedef struct WmTreeNode WmTreeNode;
WmTreeNode* wm_tree_get_root(const WmContainer *tree);
WmTreeNode* wm_tree_node_get_left(const WmTreeNode *node);
WmTreeNode* wm_tree_node_get_right(const WmTreeNode *node);
WmTreeNode* wm_tree_node_get_parent(const WmTreeNode *node);
void* wm_tree_node_get_data(const WmTreeNode *node);
/*==============================================================================
* Iteration
*============================================================================*/
WmIterator* wm_container_iterator(const WmContainer *container);
WmIterator* wm_container_iterator_reverse(const WmContainer *container);
void wm_iterator_destroy(WmIterator *it);
bool wm_iterator_has_next(const WmIterator *it);
void* wm_iterator_next(WmIterator *it);
const void* wm_iterator_peek(const WmIterator *it);
/* HashMap iteration - returns key-value pairs */
typedef struct {
const void *key;
void *value;
} WmHashEntry;
WmIterator* wm_hashmap_iterator(const WmContainer *map);
WmHashEntry* wm_hashmap_iterator_next(WmIterator *it);
/*==============================================================================
* Functional Operations
*============================================================================*/
typedef void (*WmForeachFunc)(void *item, void *user_data);
typedef void* (*WmMapFunc)(const void *item, void *user_data);
typedef bool (*WmFilterFunc)(const void *item, void *user_data);
typedef void* (*WmReduceFunc)(void *accumulator, const void *item, void *user_data);
void wm_container_foreach(const WmContainer *container, WmForeachFunc func, void *user_data);
WmContainer* wm_container_map(const WmContainer *container, WmContainerType new_type,
WmMapFunc func, WmDestroyFunc destroy_fn, void *user_data);
WmContainer* wm_container_filter(const WmContainer *container,
WmFilterFunc predicate, void *user_data);
void* wm_container_reduce(const WmContainer *container,
WmReduceFunc func, void *initial, void *user_data);
bool wm_container_all_match(const WmContainer *container,
WmFilterFunc predicate, void *user_data);
bool wm_container_any_match(const WmContainer *container,
WmFilterFunc predicate, void *user_data);
/*==============================================================================
* Convenience Constructors
*============================================================================*/
static inline WmContainer* wm_list_create(WmDestroyFunc destroy_fn) {
return wm_container_create(WM_CONTAINER_LIST, destroy_fn);
}
static inline WmContainer* wm_array_create(WmDestroyFunc destroy_fn) {
return wm_container_create(WM_CONTAINER_ARRAY, destroy_fn);
}
static inline WmContainer* wm_hashmap_create_string(WmDestroyFunc destroy_fn) {
return wm_hashmap_create(WM_HASH_KEY_STRING, destroy_fn);
}
static inline WmContainer* wm_queue_create(WmDestroyFunc destroy_fn) {
return wm_container_create(WM_CONTAINER_QUEUE, destroy_fn);
}
static inline WmContainer* wm_stack_create(WmDestroyFunc destroy_fn) {
return wm_container_create(WM_CONTAINER_STACK, destroy_fn);
}
#ifdef __cplusplus
}
#endif
#endif /* WM_CONTAINER_H */
-265
View File
@@ -1,265 +0,0 @@
/*
* DWN - Desktop Window Manager
* Event Bus System
*/
#ifndef WM_EVENT_H
#define WM_EVENT_H
#include "wm_types.h"
#include "wm_client.h"
#include "wm_string.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Event Data Structures
*============================================================================*/
typedef struct {
WmEventType type;
WmTime timestamp;
WmId source_id;
} WmEventHeader;
/* Window Events */
typedef struct {
WmEventHeader header;
AbstractClient *client;
} WmEventWindowBase;
typedef struct {
WmEventHeader header;
AbstractClient *client;
} WmEventWindowCreated;
typedef struct {
WmEventHeader header;
AbstractClient *client;
WmString *old_title;
WmString *new_title;
} WmEventWindowTitleChanged;
typedef struct {
WmEventHeader header;
AbstractClient *client;
WmRect old_geometry;
WmRect new_geometry;
} WmEventWindowMoved;
typedef struct {
WmEventHeader header;
AbstractClient *client;
WmRect old_geometry;
WmRect new_geometry;
} WmEventWindowResized;
typedef struct {
WmEventHeader header;
AbstractClient *client;
WmClientFlags old_flags;
WmClientFlags new_flags;
} WmEventWindowStateChanged;
/* Workspace Events */
typedef struct {
WmEventHeader header;
WmWorkspaceId workspace;
} WmEventWorkspaceBase;
typedef struct {
WmEventHeader header;
WmWorkspaceId old_workspace;
WmWorkspaceId new_workspace;
} WmEventWorkspaceSwitched;
typedef struct {
WmEventHeader header;
WmWorkspaceId workspace;
WmLayoutType old_layout;
WmLayoutType new_layout;
} WmEventWorkspaceLayoutChanged;
typedef struct {
WmEventHeader header;
WmWorkspaceId workspace;
AbstractClient *client;
} WmEventClientWorkspaceChanged;
/* Input Events */
typedef struct {
WmEventHeader header;
unsigned int keycode;
unsigned int keysym;
unsigned int modifiers;
const char *key_name;
} WmEventKey;
typedef struct {
WmEventHeader header;
unsigned int button;
int x;
int y;
unsigned int modifiers;
} WmEventButton;
typedef struct {
WmEventHeader header;
int x;
int y;
int delta_x;
int delta_y;
unsigned int modifiers;
} WmEventMotion;
typedef struct {
WmEventHeader header;
int delta;
int x;
int y;
} WmEventScroll;
/* Monitor Events */
typedef struct {
WmEventHeader header;
int monitor_index;
WmRect geometry;
bool primary;
} WmEventMonitorAdded;
typedef struct {
WmEventHeader header;
int monitor_index;
} WmEventMonitorRemoved;
/* Configuration Events */
typedef struct {
WmEventHeader header;
WmString *config_path;
bool success;
WmString *error_message;
} WmEventConfigReloaded;
/* Command Events */
typedef struct {
WmEventHeader header;
WmCommandType command;
bool success;
WmString *error_message;
} WmEventCommandExecuted;
/* Custom Event */
typedef struct {
WmEventHeader header;
WmString *event_name;
void *custom_data;
WmDestroyFunc destroy_func;
} WmEventCustom;
/*==============================================================================
* Event Bus Lifecycle
*============================================================================*/
WmEventBus* wm_event_bus_create(void);
void wm_event_bus_destroy(WmEventBus *bus);
WmEventBus* wm_event_bus_get_default(void);
void wm_event_bus_set_default(WmEventBus *bus);
/*==============================================================================
* Event Subscription
*============================================================================*/
WmEventSubscription* wm_event_subscribe(WmEventBus *bus,
WmEventType type,
WmEventHandler handler,
void *user_data);
WmEventSubscription* wm_event_subscribe_pattern(WmEventBus *bus,
WmEventType type_min,
WmEventType type_max,
WmEventHandler handler,
void *user_data);
WmEventSubscription* wm_event_subscribe_all(WmEventBus *bus,
WmEventHandler handler,
void *user_data);
void wm_event_unsubscribe(WmEventBus *bus, WmEventSubscription *subscription);
void wm_event_unsubscribe_all(WmEventBus *bus, void *user_data);
/*==============================================================================
* Event Emission
*============================================================================*/
void wm_event_emit(WmEventBus *bus, WmEventType type, const void *event_data);
void wm_event_emit_async(WmEventBus *bus, WmEventType type, const void *event_data);
/* Convenience emitters */
void wm_event_emit_window_created(WmEventBus *bus, AbstractClient *client);
void wm_event_emit_window_destroyed(WmEventBus *bus, AbstractClient *client);
void wm_event_emit_window_focused(WmEventBus *bus, AbstractClient *client, AbstractClient *prev_focus);
void wm_event_emit_window_moved(WmEventBus *bus, AbstractClient *client, const WmRect *old_geom);
void wm_event_emit_window_resized(WmEventBus *bus, AbstractClient *client, const WmRect *old_geom);
void wm_event_emit_window_state_changed(WmEventBus *bus, AbstractClient *client,
WmClientFlags old_flags, WmClientFlags new_flags);
void wm_event_emit_workspace_switched(WmEventBus *bus, WmWorkspaceId old_ws, WmWorkspaceId new_ws);
void wm_event_emit_workspace_layout_changed(WmEventBus *bus, WmWorkspaceId ws,
WmLayoutType old_layout, WmLayoutType new_layout);
/*==============================================================================
* Event Processing
*============================================================================*/
void wm_event_process(WmEventBus *bus);
void wm_event_process_all(WmEventBus *bus);
bool wm_event_process_one(WmEventBus *bus);
void wm_event_dispatch_pending(WmEventBus *bus);
int wm_event_get_pending_count(const WmEventBus *bus);
/*==============================================================================
* Event Data Helpers
*============================================================================*/
WmEventHeader* wm_event_get_header(void *event_data);
WmTime wm_event_get_timestamp(const void *event_data);
const char* wm_event_type_to_string(WmEventType type);
WmEventType wm_event_type_from_string(const char *str);
/*==============================================================================
* Event Blocking/Filtering
*============================================================================*/
void wm_event_begin_block(WmEventBus *bus, WmEventType type);
void wm_event_end_block(WmEventBus *bus, WmEventType type);
bool wm_event_is_blocked(const WmEventBus *bus, WmEventType type);
void wm_event_suppress(WmEventBus *bus, WmEventType type);
void wm_event_unsuppress(WmEventBus *bus, WmEventType type);
bool wm_event_is_suppressed(const WmEventBus *bus, WmEventType type);
/*==============================================================================
* Custom Events
*============================================================================*/
void wm_event_emit_custom(WmEventBus *bus, const char *event_name, void *data, WmDestroyFunc destroy_fn);
WmEventSubscription* wm_event_subscribe_custom(WmEventBus *bus, const char *event_name,
WmEventHandler handler, void *user_data);
/*==============================================================================
* Event Statistics
*============================================================================*/
uint64_t wm_event_get_emit_count(const WmEventBus *bus, WmEventType type);
uint64_t wm_event_get_total_emit_count(const WmEventBus *bus);
void wm_event_reset_statistics(WmEventBus *bus);
#ifdef __cplusplus
}
#endif
#endif /* WM_EVENT_H */
-107
View File
@@ -1,107 +0,0 @@
/*
* DWN - Desktop Window Manager
* Abstract Hash Map Container
*/
#ifndef WM_HASHMAP_H
#define WM_HASHMAP_H
#include "core/wm_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Opaque Types
*============================================================================*/
typedef struct WmHashMap WmHashMap;
/*==============================================================================
* Function Types
*============================================================================*/
typedef uint32_t (*WmHashFunc)(const void *key);
typedef bool (*WmKeyEqualFunc)(const void *a, const void *b);
typedef void (*WmHashForeachFunc)(void *key, void *value, void *user_data);
/*==============================================================================
* Hash Functions
*============================================================================*/
static inline uint32_t wm_hash_ptr(const void *ptr) {
uintptr_t p = (uintptr_t)ptr;
return (uint32_t)(p ^ (p >> 32));
}
static inline uint32_t wm_hash_int(int key) {
return (uint32_t)key;
}
/*==============================================================================
* Lifecycle
*============================================================================*/
WmHashMap* wm_hashmap_new(void);
WmHashMap* wm_hashmap_new_full(WmHashFunc hash_func, WmKeyEqualFunc key_equal,
WmFreeFunc key_free, WmFreeFunc value_free);
WmHashMap* wm_hashmap_new_string_key(void);
void wm_hashmap_destroy(WmHashMap *map);
WmHashMap* wm_hashmap_clone(const WmHashMap *map);
/*==============================================================================
* Capacity
*============================================================================*/
size_t wm_hashmap_size(const WmHashMap *map);
bool wm_hashmap_is_empty(const WmHashMap *map);
void wm_hashmap_clear(WmHashMap *map);
/*==============================================================================
* Element Access
*============================================================================*/
void* wm_hashmap_get(const WmHashMap *map, const void *key);
void* wm_hashmap_get_or_default(const WmHashMap *map, const void *key, void *default_val);
bool wm_hashmap_contains(const WmHashMap *map, const void *key);
/*==============================================================================
* Modifiers
*============================================================================*/
bool wm_hashmap_insert(WmHashMap *map, void *key, void *value);
bool wm_hashmap_insert_no_replace(WmHashMap *map, void *key, void *value);
void* wm_hashmap_set(WmHashMap *map, void *key, void *value);
void* wm_hashmap_remove(WmHashMap *map, const void *key);
bool wm_hashmap_steal(WmHashMap *map, const void *key);
/*==============================================================================
* Iteration
*============================================================================*/
typedef struct {
void *map; /* Opaque pointer to hashmap */
size_t bucket;
void *entry; /* Opaque pointer to current entry */
void *next_entry; /* Opaque pointer to next entry */
} WmHashMapIter;
void wm_hashmap_iter_init(WmHashMapIter *iter, WmHashMap *map);
bool wm_hashmap_iter_next(WmHashMapIter *iter, void **key, void **value);
void wm_hashmap_foreach(const WmHashMap *map, WmHashForeachFunc func, void *user_data);
/*==============================================================================
* String Helpers
*============================================================================*/
bool wm_hashmap_insert_string(WmHashMap *map, const char *key, void *value);
void* wm_hashmap_get_string(const WmHashMap *map, const char *key);
bool wm_hashmap_contains_string(const WmHashMap *map, const char *key);
void* wm_hashmap_remove_string(WmHashMap *map, const char *key);
#ifdef __cplusplus
}
#endif
#endif /* WM_HASHMAP_H */
-123
View File
@@ -1,123 +0,0 @@
/*
* DWN - Desktop Window Manager
* Abstract List Container
*/
#ifndef WM_LIST_H
#define WM_LIST_H
#include "core/wm_types.h"
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Opaque Types
*============================================================================*/
typedef struct WmList WmList;
/*==============================================================================
* Function Types
*============================================================================*/
typedef void (*WmFreeFunc)(void *item);
typedef int (*WmCompareFunc)(const void *a, const void *b);
typedef bool (*WmForeachFunc)(void *item, size_t index, void *user_data);
typedef void* (*WmCloneFunc)(const void *item);
/*==============================================================================
* Lifecycle
*============================================================================*/
WmList* wm_list_new(void);
WmList* wm_list_new_sized(size_t initial_capacity);
void wm_list_destroy(WmList *list);
void wm_list_destroy_no_free(WmList *list);
WmList* wm_list_clone(const WmList *list);
void wm_list_set_free_func(WmList *list, WmFreeFunc func);
/*==============================================================================
* Capacity
*============================================================================*/
size_t wm_list_size(const WmList *list);
size_t wm_list_capacity(const WmList *list);
bool wm_list_is_empty(const WmList *list);
void wm_list_reserve(WmList *list, size_t capacity);
void wm_list_compact(WmList *list);
void wm_list_clear(WmList *list);
/*==============================================================================
* Modifiers
*============================================================================*/
void wm_list_append(WmList *list, void *item);
void wm_list_prepend(WmList *list, void *item);
void wm_list_insert(WmList *list, size_t index, void *item);
void wm_list_insert_sorted(WmList *list, void *item, WmCompareFunc compare);
void wm_list_remove(WmList *list, void *item);
void wm_list_remove_at(WmList *list, size_t index);
void* wm_list_take_at(WmList *list, size_t index);
bool wm_list_remove_one(WmList *list, void *item);
bool wm_list_remove_all(WmList *list, void *item);
void* wm_list_pop_back(WmList *list);
void* wm_list_pop_front(WmList *list);
/*==============================================================================
* Accessors
*============================================================================*/
void* wm_list_get(const WmList *list, size_t index);
void* wm_list_first(const WmList *list);
void* wm_list_last(const WmList *list);
void* wm_list_front(const WmList *list);
void* wm_list_back(const WmList *list);
void wm_list_set(WmList *list, size_t index, void *item);
/*==============================================================================
* Iteration
*============================================================================*/
void wm_list_foreach(const WmList *list, WmForeachFunc func, void *user_data);
void wm_list_foreach_reverse(const WmList *list, WmForeachFunc func, void *user_data);
/*==============================================================================
* Search
*============================================================================*/
ssize_t wm_list_index_of(const WmList *list, void *item);
bool wm_list_contains(const WmList *list, void *item);
void* wm_list_find(const WmList *list, WmCompareFunc compare, const void *key);
ssize_t wm_list_find_index(const WmList *list, WmCompareFunc compare, const void *key);
/*==============================================================================
* Sorting
*============================================================================*/
void wm_list_sort(WmList *list, WmCompareFunc compare);
/*==============================================================================
* Data Operations
*============================================================================*/
void** wm_list_data(WmList *list);
void* wm_list_steal(WmList *list, size_t index);
WmList* wm_list_slice(const WmList *list, size_t start, size_t end);
void wm_list_move(WmList *list, size_t from, size_t to);
void wm_list_swap(WmList *list, size_t i, size_t j);
/*==============================================================================
* Comparison
*============================================================================*/
bool wm_list_equals(const WmList *list1, const WmList *list2);
#ifdef __cplusplus
}
#endif
#endif /* WM_LIST_H */
-222
View File
@@ -1,222 +0,0 @@
/*
* DWN - Desktop Window Manager
* Abstract String Type
*/
#ifndef WM_STRING_H
#define WM_STRING_H
#include "wm_types.h"
#include <stdarg.h>
#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* String Lifecycle
*============================================================================*/
WmString* wm_string_new(const char *str);
WmString* wm_string_new_empty(void);
WmString* wm_string_new_sized(size_t capacity);
WmString* wm_string_new_printf(const char *fmt, ...);
WmString* wm_string_new_vprintf(const char *fmt, va_list args);
WmString* wm_string_new_n(const char *str, size_t n);
void wm_string_destroy(WmString *str);
WmString* wm_string_clone(const WmString *str);
/*==============================================================================
* String Operations
*============================================================================*/
void wm_string_append(WmString *str, const char *suffix);
void wm_string_append_char(WmString *str, char c);
void wm_string_append_n(WmString *str, const char *suffix, size_t n);
void wm_string_append_printf(WmString *str, const char *fmt, ...);
void wm_string_append_vprintf(WmString *str, const char *fmt, va_list args);
void wm_string_append_string(WmString *str, const WmString *suffix);
void wm_string_prepend(WmString *str, const char *prefix);
void wm_string_prepend_char(WmString *str, char c);
void wm_string_insert(WmString *str, size_t pos, const char *insert);
void wm_string_insert_char(WmString *str, size_t pos, char c);
void wm_string_insert_string(WmString *str, size_t pos, const WmString *insert);
void wm_string_erase(WmString *str, size_t pos, size_t len);
void wm_string_clear(WmString *str);
/*==============================================================================
* String Queries
*============================================================================*/
const char* wm_string_cstr(const WmString *str);
char* wm_string_detach(WmString *str);
size_t wm_string_length(const WmString *str);
size_t wm_string_capacity(const WmString *str);
size_t wm_string_bytes(const WmString *str);
bool wm_string_is_empty(const WmString *str);
bool wm_string_is_null(const WmString *str);
char wm_string_char_at(const WmString *str, size_t pos);
/*==============================================================================
* String Comparison
*============================================================================*/
bool wm_string_equals(const WmString *str1, const WmString *str2);
bool wm_string_equals_cstr(const WmString *str, const char *cstr);
int wm_string_compare(const WmString *str1, const WmString *str2);
int wm_string_compare_cstr(const WmString *str, const char *cstr);
int wm_string_case_compare(const WmString *str1, const WmString *str2);
int wm_string_case_compare_cstr(const WmString *str, const char *cstr);
/*==============================================================================
* String Searching
*============================================================================*/
bool wm_string_contains(const WmString *str, const char *needle);
bool wm_string_contains_char(const WmString *str, char c);
size_t wm_string_find(const WmString *str, const char *needle, size_t start);
size_t wm_string_find_char(const WmString *str, char c, size_t start);
size_t wm_string_find_last(const WmString *str, const char *needle);
size_t wm_string_find_last_char(const WmString *str, char c);
bool wm_string_starts_with(const WmString *str, const char *prefix);
bool wm_string_starts_with_char(const WmString *str, char c);
bool wm_string_ends_with(const WmString *str, const char *suffix);
bool wm_string_ends_with_char(const WmString *str, char c);
/*==============================================================================
* String Modification
*============================================================================*/
void wm_string_trim(WmString *str);
void wm_string_trim_left(WmString *str);
void wm_string_trim_right(WmString *str);
void wm_string_replace(WmString *str, const char *search, const char *replace);
void wm_string_replace_char(WmString *str, char search, char replace);
size_t wm_string_replace_all(WmString *str, const char *search, const char *replace);
void wm_string_to_lower(WmString *str);
void wm_string_to_upper(WmString *str);
void wm_string_reverse(WmString *str);
/*==============================================================================
* String Substrings
*============================================================================*/
WmString* wm_string_substring(const WmString *str, size_t start, size_t len);
WmString* wm_string_left(const WmString *str, size_t n);
WmString* wm_string_right(const WmString *str, size_t n);
/*==============================================================================
* String Splitting and Joining
*============================================================================*/
WmContainer* wm_string_split(const WmString *str, const char *delimiter);
WmContainer* wm_string_split_chars(const WmString *str, const char *delimiters);
WmContainer* wm_string_split_lines(const WmString *str);
WmString* wm_string_join(const WmContainer *strings, const char *separator);
WmString* wm_string_join_cstr(const char **strings, size_t count, const char *separator);
/*==============================================================================
* String Formatting
*============================================================================*/
void wm_string_printf(WmString *str, const char *fmt, ...);
void wm_string_vprintf(WmString *str, const char *fmt, va_list args);
/*==============================================================================
* String Validation
*============================================================================*/
bool wm_string_is_valid_utf8(const WmString *str);
bool wm_string_is_ascii(const WmString *str);
bool wm_string_is_numeric(const WmString *str);
bool wm_string_is_integer(const WmString *str);
bool wm_string_is_float(const WmString *str);
/*==============================================================================
* String Conversion
*============================================================================*/
int wm_string_to_int(const WmString *str, int default_val);
long wm_string_to_long(const WmString *str, long default_val);
float wm_string_to_float(const WmString *str, float default_val);
double wm_string_to_double(const WmString *str, double default_val);
bool wm_string_to_bool(const WmString *str, bool default_val);
WmString* wm_string_from_int(int val);
WmString* wm_string_from_long(long val);
WmString* wm_string_from_float(float val, int precision);
WmString* wm_string_from_double(double val, int precision);
WmString* wm_string_from_bool(bool val);
/*==============================================================================
* String Hash
*============================================================================*/
uint32_t wm_string_hash(const WmString *str);
uint32_t wm_string_hash_cstr(const char *str);
/*==============================================================================
* String Escaping
*============================================================================*/
WmString* wm_string_escape_json(const WmString *str);
WmString* wm_string_escape_xml(const WmString *str);
WmString* wm_string_escape_html(const WmString *str);
WmString* wm_string_escape_shell(const WmString *str);
WmString* wm_string_escape_regex(const WmString *str);
WmString* wm_string_unescape_json(const WmString *str);
WmString* wm_string_unescape_xml(const WmString *str);
WmString* wm_string_unescape_html(const WmString *str);
/*==============================================================================
* Static String Helpers
*============================================================================*/
static inline bool wm_cstr_is_empty(const char *str) {
return str == NULL || str[0] == '\0';
}
static inline size_t wm_cstr_length(const char *str) {
return str == NULL ? 0 : strlen(str);
}
static inline bool wm_cstr_equals(const char *a, const char *b) {
if (a == b) return true;
if (a == NULL || b == NULL) return false;
return strcmp(a, b) == 0;
}
static inline bool wm_cstr_case_equals(const char *a, const char *b) {
if (a == b) return true;
if (a == NULL || b == NULL) return false;
return strcasecmp(a, b) == 0;
}
static inline void wm_cstr_copy(char *dest, const char *src, size_t size) {
if (size == 0) return;
strncpy(dest, src, size - 1);
dest[size - 1] = '\0';
}
#ifdef __cplusplus
}
#endif
#endif /* WM_STRING_H */
-490
View File
@@ -1,490 +0,0 @@
/*
* DWN - Desktop Window Manager
* Core Abstract Types
* Extreme Abstraction Architecture
*/
#ifndef WM_TYPES_H
#define WM_TYPES_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Version and API Information
*============================================================================*/
#define WM_CORE_VERSION_MAJOR 2
#define WM_CORE_VERSION_MINOR 0
#define WM_CORE_VERSION_PATCH 0
#define WM_PLUGIN_API_VERSION 1
/*==============================================================================
* Opaque Type Declarations
*============================================================================*/
typedef struct WmCore WmCore;
typedef struct WmBackend WmBackend;
typedef struct WmPlugin WmPlugin;
typedef struct WmPluginManager WmPluginManager;
typedef struct WmEventBus WmEventBus;
typedef struct WmEventSubscription WmEventSubscription;
typedef struct WmContainer WmContainer;
typedef struct WmIterator WmIterator;
typedef struct WmString WmString;
typedef struct WmRenderer WmRenderer;
typedef struct WmSurface WmSurface;
typedef struct WmFont WmFont;
typedef struct WmImage WmImage;
typedef struct WmCommand WmCommand;
typedef struct WmCommandBuilder WmCommandBuilder;
typedef struct WmConfigSchema WmConfigSchema;
typedef struct WmConfigInstance WmConfigInstance;
typedef struct WmLayoutEngine WmLayoutEngine;
typedef struct WmPanelSystem WmPanelSystem;
typedef struct WmAnimation WmAnimation;
typedef struct WmTimer WmTimer;
typedef struct WmMutex WmMutex;
typedef struct WmCondition WmCondition;
typedef struct WmThread WmThread;
/*==============================================================================
* Basic Type Definitions
*============================================================================*/
typedef uint32_t WmColor;
typedef uint64_t WmTime;
typedef uint32_t WmHandle;
typedef uint32_t WmId;
typedef WmId WmClientId;
typedef int WmWorkspaceId;
typedef void* WmWindowHandle;
typedef void* WmNativeHandle;
/*==============================================================================
* Window Types
*============================================================================*/
typedef enum {
WM_WINDOW_TYPE_UNKNOWN = 0,
WM_WINDOW_TYPE_NORMAL,
WM_WINDOW_TYPE_DIALOG,
WM_WINDOW_TYPE_DOCK,
WM_WINDOW_TYPE_DESKTOP,
WM_WINDOW_TYPE_TOOLBAR,
WM_WINDOW_TYPE_MENU,
WM_WINDOW_TYPE_UTILITY,
WM_WINDOW_TYPE_SPLASH,
WM_WINDOW_TYPE_NOTIFICATION,
WM_WINDOW_TYPE_COMBO,
WM_WINDOW_TYPE_DND,
WM_WINDOW_TYPE_POPUP_MENU,
WM_WINDOW_TYPE_TOOLTIP
} WmWindowType;
/*==============================================================================
* Geometry Types
*============================================================================*/
typedef struct {
int x;
int y;
} WmPoint;
typedef struct {
int width;
int height;
} WmSize;
typedef struct {
int x;
int y;
int width;
int height;
} WmRect;
typedef struct {
WmPoint location;
WmSize size;
} WmGeometry;
typedef struct {
int top;
int bottom;
int left;
int right;
} WmInsets;
typedef struct {
float x;
float y;
} WmPointF;
typedef struct {
float width;
float height;
} WmSizeF;
typedef struct {
WmPointF location;
WmSizeF size;
} WmGeometryF;
/*==============================================================================
* Rectangle Operations (Inline)
*============================================================================*/
static inline WmRect wm_rect_make(int x, int y, int width, int height) {
WmRect r = { x, y, width, height };
return r;
}
static inline bool wm_rect_is_empty(const WmRect *r) {
return r == NULL || r->width <= 0 || r->height <= 0;
}
static inline bool wm_rect_contains_point(const WmRect *r, int x, int y) {
return r != NULL &&
x >= r->x && x < r->x + r->width &&
y >= r->y && y < r->y + r->height;
}
static inline bool wm_rect_intersects(const WmRect *a, const WmRect *b) {
if (a == NULL || b == NULL) return false;
return !(a->x + a->width <= b->x ||
b->x + b->width <= a->x ||
a->y + a->height <= b->y ||
b->y + b->height <= a->y);
}
static inline WmRect wm_rect_intersection(const WmRect *a, const WmRect *b) {
WmRect r = { 0, 0, 0, 0 };
if (a == NULL || b == NULL) return r;
int x1 = a->x > b->x ? a->x : b->x;
int y1 = a->y > b->y ? a->y : b->y;
int x2 = (a->x + a->width) < (b->x + b->width) ? (a->x + a->width) : (b->x + b->width);
int y2 = (a->y + a->height) < (b->y + b->height) ? (a->y + a->height) : (b->y + b->height);
if (x2 > x1 && y2 > y1) {
r.x = x1;
r.y = y1;
r.width = x2 - x1;
r.height = y2 - y1;
}
return r;
}
static inline WmRect wm_rect_union(const WmRect *a, const WmRect *b) {
WmRect r = { 0, 0, 0, 0 };
if (a == NULL && b == NULL) return r;
if (a == NULL) return *b;
if (b == NULL) return *a;
int x1 = a->x < b->x ? a->x : b->x;
int y1 = a->y < b->y ? a->y : b->y;
int x2 = (a->x + a->width) > (b->x + b->width) ? (a->x + a->width) : (b->x + b->width);
int y2 = (a->y + a->height) > (b->y + b->height) ? (a->y + a->height) : (b->y + b->height);
r.x = x1;
r.y = y1;
r.width = x2 - x1;
r.height = y2 - y1;
return r;
}
/*==============================================================================
* Color Operations (Inline)
*============================================================================*/
static inline WmColor wm_color_rgb(uint8_t r, uint8_t g, uint8_t b) {
return (0xFF << 24) | (r << 16) | (g << 8) | b;
}
static inline WmColor wm_color_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
return (a << 24) | (r << 16) | (g << 8) | b;
}
static inline uint8_t wm_color_get_red(WmColor c) {
return (c >> 16) & 0xFF;
}
static inline uint8_t wm_color_get_green(WmColor c) {
return (c >> 8) & 0xFF;
}
static inline uint8_t wm_color_get_blue(WmColor c) {
return c & 0xFF;
}
static inline uint8_t wm_color_get_alpha(WmColor c) {
return (c >> 24) & 0xFF;
}
/*==============================================================================
* Client State Flags
*============================================================================*/
typedef enum {
WM_CLIENT_FLAG_NONE = 0,
WM_CLIENT_FLAG_FLOATING = (1 << 0),
WM_CLIENT_FLAG_FULLSCREEN = (1 << 1),
WM_CLIENT_FLAG_URGENT = (1 << 2),
WM_CLIENT_FLAG_MINIMIZED = (1 << 3),
WM_CLIENT_FLAG_STICKY = (1 << 4),
WM_CLIENT_FLAG_MAXIMIZED = (1 << 5),
WM_CLIENT_FLAG_UNMANAGING = (1 << 6),
WM_CLIENT_FLAG_FOCUSED = (1 << 7),
WM_CLIENT_FLAG_MAPPED = (1 << 8),
WM_CLIENT_FLAG_DECORATED = (1 << 9),
WM_CLIENT_FLAG_DIALOG = (1 << 10),
WM_CLIENT_FLAG_DOCK = (1 << 11),
WM_CLIENT_FLAG_UTILITY = (1 << 12),
WM_CLIENT_FLAG_SPLASH = (1 << 13),
WM_CLIENT_FLAG_MENU = (1 << 14),
WM_CLIENT_FLAG_TOOLBAR = (1 << 15)
} WmClientFlags;
/*==============================================================================
* Layout Types
*============================================================================*/
typedef enum {
WM_LAYOUT_TYPE_TILING,
WM_LAYOUT_TYPE_FLOATING,
WM_LAYOUT_TYPE_MONOCLE,
WM_LAYOUT_TYPE_GRID,
WM_LAYOUT_TYPE_SPIRAL,
WM_LAYOUT_TYPE_DWINDLE,
WM_LAYOUT_TYPE_MAX
} WmLayoutType;
/*==============================================================================
* Panel Positions
*============================================================================*/
typedef enum {
WM_PANEL_POSITION_TOP,
WM_PANEL_POSITION_BOTTOM,
WM_PANEL_POSITION_LEFT,
WM_PANEL_POSITION_RIGHT
} WmPanelPosition;
/*==============================================================================
* Event Types
*============================================================================*/
typedef enum {
/* Window events */
WM_EVENT_NONE = 0,
WM_EVENT_WINDOW_CREATED,
WM_EVENT_WINDOW_DESTROYED,
WM_EVENT_WINDOW_MAPPED,
WM_EVENT_WINDOW_UNMAPPED,
WM_EVENT_WINDOW_CONFIGURED,
WM_EVENT_WINDOW_FOCUSED,
WM_EVENT_WINDOW_UNFOCUSED,
WM_EVENT_WINDOW_RAISED,
WM_EVENT_WINDOW_LOWERED,
WM_EVENT_WINDOW_MINIMIZED,
WM_EVENT_WINDOW_RESTORED,
WM_EVENT_WINDOW_MAXIMIZED,
WM_EVENT_WINDOW_UNMAXIMIZED,
WM_EVENT_WINDOW_FULLSCREENED,
WM_EVENT_WINDOW_UNFULLSCREENED,
WM_EVENT_WINDOW_FLOATING_CHANGED,
WM_EVENT_WINDOW_PROPERTY_CHANGED,
WM_EVENT_WINDOW_STATE_CHANGED,
WM_EVENT_WINDOW_TITLE_CHANGED,
WM_EVENT_WINDOW_CLASS_CHANGED,
WM_EVENT_WINDOW_ROLE_CHANGED,
WM_EVENT_WINDOW_URGENCY_CHANGED,
WM_EVENT_WINDOW_MOVED,
WM_EVENT_WINDOW_RESIZED,
/* Workspace events */
WM_EVENT_WORKSPACE_CREATED,
WM_EVENT_WORKSPACE_DESTROYED,
WM_EVENT_WORKSPACE_SWITCHED,
WM_EVENT_WORKSPACE_RENAMED,
WM_EVENT_WORKSPACE_LAYOUT_CHANGED,
WM_EVENT_WORKSPACE_MASTER_RATIO_CHANGED,
WM_EVENT_WORKSPACE_MASTER_COUNT_CHANGED,
/* Client/workspace relationship */
WM_EVENT_CLIENT_ADDED_TO_WORKSPACE,
WM_EVENT_CLIENT_REMOVED_FROM_WORKSPACE,
/* Input events */
WM_EVENT_KEY_PRESSED,
WM_EVENT_KEY_RELEASED,
WM_EVENT_BUTTON_PRESSED,
WM_EVENT_BUTTON_RELEASED,
WM_EVENT_MOTION,
WM_EVENT_ENTER,
WM_EVENT_LEAVE,
WM_EVENT_SCROLL,
/* System events */
WM_EVENT_MONITOR_ADDED,
WM_EVENT_MONITOR_REMOVED,
WM_EVENT_MONITOR_CONFIGURED,
WM_EVENT_SCREEN_RESIZED,
/* Configuration events */
WM_EVENT_CONFIG_RELOADED,
WM_EVENT_CONFIG_CHANGED,
/* Lifecycle events */
WM_EVENT_WM_STARTED,
WM_EVENT_WM_SHUTDOWN,
WM_EVENT_WM_READY,
/* Plugin events */
WM_EVENT_PLUGIN_LOADED,
WM_EVENT_PLUGIN_UNLOADED,
WM_EVENT_PLUGIN_ERROR,
/* Custom events for plugins */
WM_EVENT_CUSTOM = 1000
} WmEventType;
/*==============================================================================
* Container Types
*============================================================================*/
typedef enum {
WM_CONTAINER_LIST,
WM_CONTAINER_ARRAY,
WM_CONTAINER_HASHMAP,
WM_CONTAINER_QUEUE,
WM_CONTAINER_STACK,
WM_CONTAINER_TREE
} WmContainerType;
/*==============================================================================
* Command Types
*============================================================================*/
typedef enum {
WM_COMMAND_NONE = 0,
/* Window commands */
WM_COMMAND_WINDOW_FOCUS,
WM_COMMAND_WINDOW_MOVE,
WM_COMMAND_WINDOW_RESIZE,
WM_COMMAND_WINDOW_MOVE_RESIZE,
WM_COMMAND_WINDOW_CLOSE,
WM_COMMAND_WINDOW_KILL,
WM_COMMAND_WINDOW_MINIMIZE,
WM_COMMAND_WINDOW_RESTORE,
WM_COMMAND_WINDOW_MAXIMIZE,
WM_COMMAND_WINDOW_UNMAXIMIZE,
WM_COMMAND_WINDOW_FULLSCREEN,
WM_COMMAND_WINDOW_UNFULLSCREEN,
WM_COMMAND_WINDOW_FLOAT,
WM_COMMAND_WINDOW_UNFLOAT,
WM_COMMAND_WINDOW_RAISE,
WM_COMMAND_WINDOW_LOWER,
WM_COMMAND_WINDOW_SET_WORKSPACE,
/* Workspace commands */
WM_COMMAND_WORKSPACE_SWITCH,
WM_COMMAND_WORKSPACE_CREATE,
WM_COMMAND_WORKSPACE_DESTROY,
WM_COMMAND_WORKSPACE_RENAME,
WM_COMMAND_WORKSPACE_SET_LAYOUT,
WM_COMMAND_WORKSPACE_ADJUST_MASTER_RATIO,
WM_COMMAND_WORKSPACE_ADJUST_MASTER_COUNT,
WM_COMMAND_WORKSPACE_SWAP,
/* Client manipulation */
WM_COMMAND_CLIENT_SWAP,
WM_COMMAND_CLIENT_SWAP_MASTER,
WM_COMMAND_CLIENT_CYCLE,
WM_COMMAND_CLIENT_CYCLE_REVERSE,
/* System commands */
WM_COMMAND_RELOAD_CONFIG,
WM_COMMAND_QUIT,
WM_COMMAND_RESTART,
/* Plugin commands */
WM_COMMAND_PLUGIN_LOAD,
WM_COMMAND_PLUGIN_UNLOAD,
WM_COMMAND_PLUGIN_ENABLE,
WM_COMMAND_PLUGIN_DISABLE
} WmCommandType;
/*==============================================================================
* Result/Error Types
*============================================================================*/
typedef enum {
WM_RESULT_OK = 0,
WM_RESULT_ERROR_GENERIC = -1,
WM_RESULT_ERROR_INVALID_ARG = -2,
WM_RESULT_ERROR_NO_MEMORY = -3,
WM_RESULT_ERROR_NOT_FOUND = -4,
WM_RESULT_ERROR_EXISTS = -5,
WM_RESULT_ERROR_PERMISSION = -6,
WM_RESULT_ERROR_NOT_SUPPORTED = -7,
WM_RESULT_ERROR_NOT_IMPLEMENTED = -8,
WM_RESULT_ERROR_BACKEND = -9,
WM_RESULT_ERROR_PLUGIN = -10,
WM_RESULT_ERROR_TIMEOUT = -11,
WM_RESULT_ERROR_CANCELED = -12
} WmResult;
/*==============================================================================
* Callback Types
*============================================================================*/
typedef void (*WmDestroyFunc)(void *data);
typedef int (*WmCompareFunc)(const void *a, const void *b);
typedef uint32_t (*WmHashFunc)(const void *key);
typedef void* (*WmCloneFunc)(const void *data);
typedef void (*WmEventHandler)(WmEventType type, const void *event_data, void *user_data);
typedef void (*WmTimerCallback)(WmTimer *timer, void *user_data);
typedef void* (*WmThreadFunc)(void *arg);
/*==============================================================================
* Memory Management Types
*============================================================================*/
typedef void* (*WmAllocFunc)(size_t size);
typedef void* (*WmReallocFunc)(void *ptr, size_t old_size, size_t new_size);
typedef void (*WmFreeFunc)(void *ptr);
typedef struct {
WmAllocFunc alloc;
WmReallocFunc realloc;
WmFreeFunc free;
} WmAllocator;
/*==============================================================================
* Logging Types
*============================================================================*/
typedef enum {
WM_LOG_LEVEL_DEBUG,
WM_LOG_LEVEL_INFO,
WM_LOG_LEVEL_WARN,
WM_LOG_LEVEL_ERROR,
WM_LOG_LEVEL_FATAL
} WmLogLevel;
typedef void (*WmLogHandler)(WmLogLevel level, const char *file, int line,
const char *func, const char *message);
#ifdef __cplusplus
}
#endif
#endif /* WM_TYPES_H */
-161
View File
@@ -1,161 +0,0 @@
/*
* DWN - Desktop Window Manager
* Abstract Workspace Interface
*/
#ifndef WM_WORKSPACE_H
#define WM_WORKSPACE_H
#include "wm_types.h"
#include "wm_client.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Workspace Configuration
*============================================================================*/
#define WM_MAX_WORKSPACES 32
typedef struct {
WmLayoutType layout_type;
float master_ratio;
int master_count;
char name[64];
bool persistent;
WmString *custom_data;
} WmWorkspaceConfig;
/*==============================================================================
* Workspace Lifecycle
*============================================================================*/
bool wm_workspace_exists(WmCore *core, WmWorkspaceId id);
WmWorkspaceId wm_workspace_create(WmCore *core, const char *name);
void wm_workspace_destroy(WmCore *core, WmWorkspaceId id);
void wm_workspace_init_all(WmCore *core);
void wm_workspace_cleanup_all(WmCore *core);
/*==============================================================================
* Workspace Properties
*============================================================================*/
const char* wm_workspace_get_name(WmCore *core, WmWorkspaceId id);
void wm_workspace_set_name(WmCore *core, WmWorkspaceId id, const char *name);
bool wm_workspace_is_empty(WmCore *core, WmWorkspaceId id);
size_t wm_workspace_get_client_count(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* Current Workspace
*============================================================================*/
WmWorkspaceId wm_workspace_get_current(WmCore *core);
void wm_workspace_set_current(WmCore *core, WmWorkspaceId id);
void wm_workspace_switch(WmCore *core, WmWorkspaceId id);
void wm_workspace_switch_next(WmCore *core);
void wm_workspace_switch_prev(WmCore *core);
/*==============================================================================
* Client Management
*============================================================================*/
void wm_workspace_add_client(WmCore *core, WmWorkspaceId id, AbstractClient *client);
void wm_workspace_remove_client(WmCore *core, WmWorkspaceId id, AbstractClient *client);
void wm_workspace_move_client(WmCore *core, AbstractClient *client, WmWorkspaceId to_workspace);
WmContainer* wm_workspace_get_clients(WmCore *core, WmWorkspaceId id);
AbstractClient* wm_workspace_get_focused_client(WmCore *core, WmWorkspaceId id);
void wm_workspace_set_focused_client(WmCore *core, WmWorkspaceId id, AbstractClient *client);
AbstractClient* wm_workspace_get_first_client(WmCore *core, WmWorkspaceId id);
AbstractClient* wm_workspace_get_last_client(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* Layout Management
*============================================================================*/
WmLayoutType wm_workspace_get_layout(WmCore *core, WmWorkspaceId id);
void wm_workspace_set_layout(WmCore *core, WmWorkspaceId id, WmLayoutType layout);
void wm_workspace_cycle_layout(WmCore *core, WmWorkspaceId id);
void wm_workspace_cycle_layout_reverse(WmCore *core, WmWorkspaceId id);
float wm_workspace_get_master_ratio(WmCore *core, WmWorkspaceId id);
void wm_workspace_set_master_ratio(WmCore *core, WmWorkspaceId id, float ratio);
void wm_workspace_adjust_master_ratio(WmCore *core, WmWorkspaceId id, float delta);
int wm_workspace_get_master_count(WmCore *core, WmWorkspaceId id);
void wm_workspace_set_master_count(WmCore *core, WmWorkspaceId id, int count);
void wm_workspace_adjust_master_count(WmCore *core, WmWorkspaceId id, int delta);
/*==============================================================================
* Workspace Arrangement
*============================================================================*/
void wm_workspace_arrange(WmCore *core, WmWorkspaceId id);
void wm_workspace_arrange_all(WmCore *core);
void wm_workspace_arrange_current(WmCore *core);
WmRect wm_workspace_get_arrange_area(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* Workspace Visibility
*============================================================================*/
void wm_workspace_show(WmCore *core, WmWorkspaceId id);
void wm_workspace_hide(WmCore *core, WmWorkspaceId id);
bool wm_workspace_is_visible(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* Focus Management
*============================================================================*/
void wm_workspace_focus_next(WmCore *core, WmWorkspaceId id);
void wm_workspace_focus_prev(WmCore *core, WmWorkspaceId id);
void wm_workspace_focus_master(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* MRU (Most Recently Used) Stack
*============================================================================*/
void wm_workspace_mru_push(WmCore *core, WmWorkspaceId id, AbstractClient *client);
void wm_workspace_mru_remove(WmCore *core, WmWorkspaceId id, AbstractClient *client);
AbstractClient* wm_workspace_mru_get_next(WmCore *core, WmWorkspaceId id, AbstractClient *current);
AbstractClient* wm_workspace_mru_get_prev(WmCore *core, WmWorkspaceId id, AbstractClient *current);
/*==============================================================================
* Alt-Tab Navigation
*============================================================================*/
void wm_workspace_alt_tab_start(WmCore *core, WmWorkspaceId id);
void wm_workspace_alt_tab_next(WmCore *core, WmWorkspaceId id);
void wm_workspace_alt_tab_prev(WmCore *core, WmWorkspaceId id);
void wm_workspace_alt_tab_end(WmCore *core, WmWorkspaceId id);
bool wm_workspace_alt_tab_is_active(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* Workspace Iteration
*============================================================================*/
WmWorkspaceId wm_workspace_get_first(WmCore *core);
WmWorkspaceId wm_workspace_get_last(WmCore *core);
WmWorkspaceId wm_workspace_get_next(WmCore *core, WmWorkspaceId id);
WmWorkspaceId wm_workspace_get_prev(WmCore *core, WmWorkspaceId id);
/*==============================================================================
* Configuration
*============================================================================*/
void wm_workspace_save_config(WmCore *core, WmWorkspaceId id, const WmWorkspaceConfig *config);
bool wm_workspace_load_config(WmCore *core, WmWorkspaceId id, WmWorkspaceConfig *config);
#ifdef __cplusplus
}
#endif
#endif /* WM_WORKSPACE_H */
+8 -1
View File
@@ -10,6 +10,7 @@
#include "dwn.h"
#include <stdbool.h>
/* Button types */
typedef enum {
BUTTON_CLOSE,
BUTTON_MAXIMIZE,
@@ -17,26 +18,32 @@ typedef enum {
BUTTON_COUNT
} ButtonType;
/* Button areas for hit testing */
typedef struct {
int x, y;
int width, height;
} ButtonArea;
/* Decoration initialization */
void decorations_init(void);
void decorations_cleanup(void);
/* Rendering */
void decorations_render(Client *client, bool focused);
void decorations_render_title_bar(Client *client, bool focused);
void decorations_render_buttons(Client *client, bool focused);
void decorations_render_border(Client *client, bool focused);
/* Hit testing */
ButtonType decorations_hit_test_button(Client *client, int x, int y);
bool decorations_hit_test_title_bar(Client *client, int x, int y);
bool decorations_hit_test_resize_area(Client *client, int x, int y, int *direction);
/* Button actions */
void decorations_button_press(Client *client, ButtonType button);
/* Text rendering */
void decorations_draw_text(Window window, GC gc, int x, int y,
const char *text, unsigned long color);
#endif
#endif /* DWN_DECORATIONS_H */
-44
View File
@@ -1,44 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Demo mode - automated feature showcase
*/
#ifndef DWN_DEMO_H
#define DWN_DEMO_H
#include <stdbool.h>
typedef enum {
DEMO_IDLE,
DEMO_INTRO,
DEMO_WINDOW_MGMT,
DEMO_WORKSPACES,
DEMO_LAYOUTS,
DEMO_SNAPPING,
DEMO_PANELS,
DEMO_AI,
DEMO_NEWS,
DEMO_SHORTCUTS,
DEMO_COMPLETE
} DemoPhase;
typedef enum {
DEMO_WAIT_NONE,
DEMO_WAIT_TIME,
DEMO_WAIT_WINDOW_SPAWN,
DEMO_WAIT_AI_RESPONSE,
DEMO_WAIT_EXA_RESPONSE
} DemoWaitCondition;
void demo_init(void);
void demo_cleanup(void);
void demo_start(void);
void demo_stop(void);
void demo_update(void);
bool demo_is_active(void);
DemoPhase demo_get_phase(void);
int demo_get_step(void);
const char *demo_get_phase_name(DemoPhase phase);
#endif
+48 -127
View File
@@ -16,25 +16,24 @@
#include <stdbool.h>
#include <stdint.h>
/* Version */
#define DWN_VERSION "1.0.0"
#define DWN_NAME "DWN"
/* Limits */
#define MAX_CLIENTS 256
#define MAX_WORKSPACES 9
#define MAX_MONITORS 8
#define MAX_NOTIFICATIONS 32
#define MAX_KEYBINDINGS 64
#define RESIZE_LEFT 1
#define RESIZE_RIGHT 2
#define RESIZE_TOP 4
#define RESIZE_BOTTOM 8
#define DEFAULT_BORDER_WIDTH 0
#define DEFAULT_TITLE_HEIGHT 28
#define DEFAULT_PANEL_HEIGHT 32
#define DEFAULT_GAP 0
/* Default dimensions */
#define DEFAULT_BORDER_WIDTH 2
#define DEFAULT_TITLE_HEIGHT 24
#define DEFAULT_PANEL_HEIGHT 28
#define DEFAULT_GAP 4
/* Common error/status codes */
typedef enum {
DWN_OK = 0,
DWN_ERROR = -1,
@@ -45,96 +44,55 @@ typedef enum {
DWN_ERROR_IO = -6
} DwnStatus;
/* Layout types */
typedef enum {
LAYOUT_TILING,
LAYOUT_FLOATING,
LAYOUT_MONOCLE,
LAYOUT_CENTERED_MASTER,
LAYOUT_COLUMNS,
LAYOUT_FIBONACCI,
LAYOUT_COUNT
} LayoutType;
/* Focus modes */
typedef enum {
FOCUS_CLICK,
FOCUS_FOLLOW
} FocusMode;
/* Client state flags */
typedef enum {
CLIENT_NORMAL = 0,
CLIENT_FLOATING = (1 << 0),
CLIENT_FULLSCREEN = (1 << 1),
CLIENT_URGENT = (1 << 2),
CLIENT_MINIMIZED = (1 << 3),
CLIENT_STICKY = (1 << 4),
CLIENT_MAXIMIZED = (1 << 5),
CLIENT_UNMANAGING = (1 << 6) /* Being destroyed, skip processing */
CLIENT_STICKY = (1 << 4)
} ClientFlags;
typedef enum {
SNAP_H_NONE = 0,
SNAP_H_LEFT,
SNAP_H_RIGHT,
SNAP_H_FULL
} SnapHorizontal;
typedef enum {
SNAP_V_NONE = 0,
SNAP_V_TOP,
SNAP_V_BOTTOM,
SNAP_V_FULL
} SnapVertical;
typedef struct {
SnapHorizontal horizontal;
SnapVertical vertical;
long timestamp;
} SnapConstraint;
typedef struct {
long start_time;
float progress;
unsigned long from_color;
unsigned long to_color;
bool active;
} ColorAnimation;
typedef struct {
float phase;
float speed;
unsigned long base_color;
bool active;
} TextGlowAnimation;
/* Forward declarations */
typedef struct Client Client;
typedef struct Workspace Workspace;
typedef struct Monitor Monitor;
typedef struct Panel Panel;
typedef struct Config Config;
/* Client structure - represents a managed window */
struct Client {
Window window;
Window frame;
int x, y;
int width, height;
int old_x, old_y;
Window window; /* Application window */
Window frame; /* Frame window (decoration) */
int x, y; /* Position */
int width, height; /* Size */
int old_x, old_y; /* Previous position (for floating restore) */
int old_width, old_height;
int border_width;
uint32_t flags;
unsigned int workspace;
char title[256];
char class[64];
SnapConstraint snap;
bool floating_before_maximize;
unsigned long taskbar_color;
ColorAnimation title_anim;
TextGlowAnimation text_glow;
Client *next;
uint32_t flags; /* ClientFlags bitmask */
unsigned int workspace; /* Current workspace (0-8) */
char title[256]; /* Window title */
char class[64]; /* Window class */
Client *next; /* Linked list */
Client *prev;
Client *mru_next;
Client *mru_prev;
};
/* Monitor structure - represents a physical display */
struct Monitor {
int x, y;
int width, height;
@@ -142,17 +100,17 @@ struct Monitor {
bool primary;
};
/* Workspace structure */
struct Workspace {
Client *clients;
Client *focused;
Client *mru_head;
Client *mru_tail;
LayoutType layout;
float master_ratio;
int master_count;
char name[32];
Client *clients; /* Head of client list */
Client *focused; /* Currently focused client */
LayoutType layout; /* Current layout */
float master_ratio; /* Ratio for master area in tiling */
int master_count; /* Number of windows in master area */
char name[32]; /* Workspace name */
};
/* Panel widget types */
typedef enum {
WIDGET_WORKSPACES,
WIDGET_TASKBAR,
@@ -162,6 +120,7 @@ typedef enum {
WIDGET_SEPARATOR
} WidgetType;
/* Global state - singleton pattern */
typedef struct {
Display *display;
int screen;
@@ -169,91 +128,53 @@ typedef struct {
int screen_width;
int screen_height;
/* Monitors */
Monitor monitors[MAX_MONITORS];
int monitor_count;
/* Workspaces */
Workspace workspaces[MAX_WORKSPACES];
int current_workspace;
Client *client_list;
/* Clients */
Client *client_list; /* All clients */
int client_count;
/* Panels */
Panel *top_panel;
Panel *bottom_panel;
/* Configuration */
Config *config;
/* State */
bool running;
bool ai_enabled;
/* Graphics contexts */
GC gc;
XFontStruct *font;
XftFont *xft_font;
XftFont *xft_font_bold;
XftFont *xft_font; /* Xft font for UTF-8 rendering */
Colormap colormap;
/* Drag state */
Client *drag_client;
int drag_start_x, drag_start_y;
int drag_orig_x, drag_orig_y;
int drag_orig_w, drag_orig_h;
bool resizing;
int drag_direction;
Client *pending_focus_client;
long pending_focus_time;
bool is_alt_tabbing;
Client *alt_tab_client;
bool desktop_shown;
Window desktop_minimized[MAX_CLIENTS];
int desktop_minimized_count;
float ambient_phase;
float ambient_speed;
long typing_activity_time;
unsigned long key_press_count;
long key_delete_flicker_time;
int xi2_opcode;
double mouse_distance_pixels;
int last_mouse_x;
int last_mouse_y;
bool mouse_tracking_initialized;
Cursor cursor_default;
Cursor cursor_resize_top_left;
Cursor cursor_resize_top_right;
Cursor cursor_resize_bottom_left;
Cursor cursor_resize_bottom_right;
Cursor cursor_resize_left;
Cursor cursor_resize_right;
Cursor cursor_resize_top;
Cursor cursor_resize_bottom;
Cursor cursor_move;
} DWNState;
#define PHASE_OFFSET_PANEL_BG 0.0f
#define PHASE_OFFSET_WORKSPACES 0.8f
#define PHASE_OFFSET_TASKBAR 1.6f
#define PHASE_OFFSET_CLOCK 2.4f
#define PHASE_OFFSET_STATS 3.2f
#define PHASE_OFFSET_TOP_MEM 4.0f
#define PHASE_OFFSET_TOP_CPU 4.4f
#define PHASE_OFFSET_DECORATIONS 4.8f
#define PHASE_OFFSET_SYSTRAY 5.6f
#define PHASE_OFFSET_NEWS 1.2f
#define PHASE_OFFSET_KEY_COUNTER 5.2f
#define PHASE_OFFSET_MOUSE_DIST 5.8f
#define PHASE_OFFSET_DISK_SPACE 6.2f
/* Global state accessor */
extern DWNState *dwn;
/* Core functions */
int dwn_init(void);
void dwn_cleanup(void);
void dwn_run(void);
void dwn_quit(void);
/* Event handlers */
void dwn_handle_event(XEvent *ev);
#endif
#endif /* DWN_H */
+10 -22
View File
@@ -11,13 +11,16 @@
#include <stdbool.h>
#include <X11/keysym.h>
/* Modifier masks */
#define MOD_ALT Mod1Mask
#define MOD_CTRL ControlMask
#define MOD_SHIFT ShiftMask
#define MOD_SUPER Mod4Mask
/* Key binding callback type */
typedef void (*KeyCallback)(void);
/* Key binding structure */
typedef struct {
unsigned int modifiers;
KeySym keysym;
@@ -25,21 +28,26 @@ typedef struct {
const char *description;
} KeyBinding;
/* Initialization */
void keys_init(void);
void keys_cleanup(void);
void keys_grab_all(void);
void keys_ungrab_all(void);
/* Key binding registration */
void keys_bind(unsigned int modifiers, KeySym keysym,
KeyCallback callback, const char *description);
void keys_unbind(unsigned int modifiers, KeySym keysym);
void keys_clear_all(void);
/* Key event handling */
void keys_handle_press(XKeyEvent *ev);
void keys_handle_release(XKeyEvent *ev);
/* Default key bindings */
void keys_setup_defaults(void);
/* Key binding callbacks */
void key_spawn_terminal(void);
void key_spawn_launcher(void);
void key_spawn_file_manager(void);
@@ -50,7 +58,6 @@ void key_cycle_layout(void);
void key_toggle_floating(void);
void key_toggle_fullscreen(void);
void key_toggle_maximize(void);
void key_toggle_minimize(void);
void key_focus_next(void);
void key_focus_prev(void);
void key_workspace_next(void);
@@ -81,31 +88,12 @@ void key_toggle_ai(void);
void key_ai_command(void);
void key_show_shortcuts(void);
void key_start_tutorial(void);
void key_snap_left(void);
void key_snap_right(void);
void key_snap_up(void);
void key_snap_down(void);
void key_start_demo(void);
void key_show_desktop(void);
void key_move_to_workspace_prev(void);
void key_move_to_workspace_next(void);
void key_kill_all_clients(void);
void key_move_window_left(void);
void key_move_window_right(void);
void key_move_window_up(void);
void key_move_window_down(void);
void key_resize_window_left(void);
void key_resize_window_right(void);
void key_resize_window_up(void);
void key_resize_window_down(void);
void key_set_mark(void);
void key_goto_mark(void);
void key_expose_all(void);
/* Tutorial system */
void tutorial_start(void);
void tutorial_stop(void);
void tutorial_next_step(void);
void tutorial_check_key(unsigned int modifiers, KeySym keysym);
bool tutorial_is_active(void);
#endif
#endif /* DWN_KEYS_H */
+4 -7
View File
@@ -9,21 +9,18 @@
#include "dwn.h"
/* Layout arrangement */
void layout_arrange(int workspace);
void layout_arrange_tiling(int workspace);
void layout_arrange_floating(int workspace);
void layout_arrange_monocle(int workspace);
void layout_arrange_centered_master(int workspace);
void layout_arrange_columns(int workspace);
void layout_arrange_fibonacci(int workspace);
/* Layout helpers */
int layout_get_usable_area(int *x, int *y, int *width, int *height);
int layout_count_tiled_clients(int workspace);
void layout_apply_snap_constraint(Client *c, int area_x, int area_y,
int area_w, int area_h, int gap);
/* Layout names */
const char *layout_get_name(LayoutType layout);
const char *layout_get_symbol(LayoutType layout);
#endif
#endif /* DWN_LAYOUT_H */
-33
View File
@@ -1,33 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Window marks for quick navigation
*/
#ifndef DWN_MARKS_H
#define DWN_MARKS_H
#include "dwn.h"
#include <stdbool.h>
#define MAX_MARKS 26
void marks_init(void);
void marks_cleanup(void);
void marks_set(char mark, Client *client);
Client *marks_get(char mark);
void marks_clear(char mark);
void marks_clear_all(void);
void marks_remove_client(Client *client);
bool marks_is_waiting_for_mark(void);
bool marks_is_waiting_for_goto(void);
void marks_start_set_mode(void);
void marks_start_goto_mode(void);
void marks_cancel_mode(void);
bool marks_handle_key(char key);
char marks_get_mark_for_client(Client *client);
#endif
-3013
View File
File diff suppressed because it is too large Load Diff
+24 -14
View File
@@ -10,15 +10,18 @@
#include "dwn.h"
#include <stdbool.h>
/* Maximum articles to cache */
#define MAX_NEWS_ARTICLES 50
#define NEWS_API_URL "https://news.app.molodetz.nl/api"
/* Sentiment classification */
typedef enum {
SENTIMENT_NEUTRAL = 0,
SENTIMENT_POSITIVE,
SENTIMENT_NEGATIVE
} NewsSentiment;
/* News article */
typedef struct {
char title[256];
char content[1024];
@@ -28,39 +31,46 @@ typedef struct {
float sentiment_score;
} NewsArticle;
/* News ticker state */
typedef struct {
NewsArticle articles[MAX_NEWS_ARTICLES];
int article_count;
int current_article;
double scroll_offset;
bool fetching;
bool has_error;
long last_fetch;
long last_scroll_update;
bool interactive_mode;
int display_widths[MAX_NEWS_ARTICLES];
int total_width;
int render_x;
int render_width;
bool widths_dirty;
int current_article; /* Currently displayed article index */
double scroll_offset; /* Sub-pixel offset for smooth scrolling */
bool fetching; /* Currently fetching from API */
bool has_error; /* Last fetch failed */
long last_fetch; /* Timestamp of last fetch */
long last_scroll_update; /* Timestamp of last scroll update */
bool interactive_mode; /* User is navigating with up/down */
int display_widths[MAX_NEWS_ARTICLES]; /* Cached text widths */
int total_width; /* Total scrollable width */
int render_x; /* X position where news starts rendering */
int render_width; /* Width of news render area */
bool widths_dirty; /* Need to recalculate widths */
} NewsState;
/* Global state */
extern NewsState news_state;
/* Initialization */
void news_init(void);
void news_cleanup(void);
/* Fetching */
void news_fetch_async(void);
void news_update(void);
void news_update(void); /* Called from main loop */
/* Navigation */
void news_next_article(void);
void news_prev_article(void);
void news_open_current(void);
/* Rendering */
void news_render(Panel *panel, int x, int max_width, int *used_width);
void news_handle_click(int x, int y);
/* Thread-safe access */
void news_lock(void);
void news_unlock(void);
#endif
#endif /* DWN_NEWS_H */
+18 -9
View File
@@ -11,36 +11,42 @@
#include <stdbool.h>
#include <dbus/dbus.h>
/* Notification urgency levels */
typedef enum {
NOTIFY_URGENCY_LOW,
NOTIFY_URGENCY_NORMAL,
NOTIFY_URGENCY_CRITICAL
} NotifyUrgency;
/* Notification structure */
typedef struct Notification {
uint32_t id;
char app_name[64];
char summary[512];
char *body;
size_t body_len;
char summary[512]; /* Larger summary for AI responses */
char *body; /* Dynamically allocated - unlimited size */
size_t body_len; /* Length of body text */
char icon[256];
int timeout;
int timeout; /* -1 = default, 0 = never, >0 = milliseconds */
NotifyUrgency urgency;
long expire_time;
Window window;
int width;
int height;
long expire_time; /* Timestamp when notification should disappear */
Window window; /* X11 window for rendering */
int width; /* Dynamic width based on content */
int height; /* Dynamic height based on content */
struct Notification *next;
} Notification;
/* D-Bus connection */
extern DBusConnection *dbus_conn;
/* Initialization */
bool notifications_init(void);
void notifications_cleanup(void);
/* D-Bus handling */
void notifications_process_messages(void);
bool notifications_register_service(void);
/* Notification management */
uint32_t notification_show(const char *app_name, const char *summary,
const char *body, const char *icon, int timeout);
void notification_close(uint32_t id);
@@ -48,18 +54,21 @@ void notification_close_all(void);
Notification *notification_find(uint32_t id);
Notification *notification_find_by_window(Window window);
/* Rendering */
void notification_render(Notification *notif);
void notifications_render_all(void);
void notifications_update(void);
void notifications_position(void);
void notifications_raise_all(void);
/* D-Bus method handlers */
DBusHandlerResult notifications_handle_message(DBusConnection *conn,
DBusMessage *msg,
void *user_data);
/* Server info */
void notifications_get_server_info(const char **name, const char **vendor,
const char **version, const char **spec_version);
void notifications_get_capabilities(const char ***caps, int *count);
#endif
#endif /* DWN_NOTIFICATIONS_H */
-70
View File
@@ -1,70 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* OCR text extraction API
*/
#ifndef DWN_OCR_H
#define DWN_OCR_H
#include <stdbool.h>
#include <stddef.h>
#include <pthread.h>
typedef enum {
OCR_OK = 0,
OCR_ERROR_INVALID_ARG,
OCR_ERROR_INIT,
OCR_ERROR_IMAGE,
OCR_ERROR_NO_MEMORY,
OCR_ERROR_NOT_AVAILABLE
} OcrStatus;
typedef enum {
OCR_STATE_IDLE,
OCR_STATE_PENDING,
OCR_STATE_COMPLETED,
OCR_STATE_ERROR
} OcrState;
typedef struct {
char *text;
size_t length;
float confidence;
} OcrResult;
typedef struct OcrRequest {
unsigned char *png_data;
size_t png_size;
char language[16];
OcrResult result;
OcrStatus status;
OcrState state;
void (*callback)(struct OcrRequest *req);
void *user_data;
pthread_t thread;
struct OcrRequest *next;
} OcrRequest;
bool ocr_init(void);
void ocr_cleanup(void);
bool ocr_is_available(void);
OcrStatus ocr_extract_from_png(const unsigned char *png_data, size_t size,
const char *language, OcrResult *result);
OcrRequest *ocr_extract_async(const unsigned char *png_data, size_t size,
const char *language,
void (*callback)(OcrRequest *));
void ocr_process_pending(void);
void ocr_cancel(OcrRequest *req);
void ocr_result_free(OcrResult *result);
const char *ocr_status_string(OcrStatus status);
#endif
+11 -11
View File
@@ -10,35 +10,31 @@
#include "dwn.h"
#include <stdbool.h>
/* Panel position */
typedef enum {
PANEL_TOP,
PANEL_BOTTOM
} PanelPosition;
/* Panel structure */
struct Panel {
Window window;
PanelPosition position;
int x, y;
int width, height;
bool visible;
Pixmap buffer;
bool dirty;
Pixmap buffer; /* Double buffering */
};
/* Panel initialization */
Panel *panel_create(PanelPosition position);
void panel_destroy(Panel *panel);
void panels_init(void);
void panels_cleanup(void);
/* Panel rendering */
void panel_render(Panel *panel);
void panel_render_all(void);
void panel_invalidate(void);
void panel_invalidate_taskbar(void);
void panel_invalidate_news(void);
void panel_set_news_region(int x, int width);
void panel_render_news_only(void);
bool panel_needs_render(void);
bool panel_news_needs_render(void);
void panel_render_workspaces(Panel *panel, int x, int *width);
void panel_render_taskbar(Panel *panel, int x, int *width);
void panel_render_clock(Panel *panel, int x, int *width);
@@ -46,21 +42,25 @@ void panel_render_systray(Panel *panel, int x, int *width);
void panel_render_layout_indicator(Panel *panel, int x, int *width);
void panel_render_ai_status(Panel *panel, int x, int *width);
/* Panel interaction */
void panel_handle_click(Panel *panel, int x, int y, int button);
int panel_hit_test_workspace(Panel *panel, int x, int y);
Client *panel_hit_test_taskbar(Panel *panel, int x, int y);
/* Panel visibility */
void panel_show(Panel *panel);
void panel_hide(Panel *panel);
void panel_toggle(Panel *panel);
void panel_raise_all(void);
/* Clock updates */
void panel_update_clock(void);
/* System stats updates */
void panel_update_system_stats(void);
/* System tray */
void panel_init_systray(void);
void panel_add_systray_icon(Window icon);
void panel_remove_systray_icon(Window icon);
#endif
#endif /* DWN_PANEL_H */
-57
View File
@@ -1,57 +0,0 @@
/*
* DWN - Desktop Window Manager
* Built-in Layout Registration
*/
#ifndef BUILTIN_LAYOUTS_H
#define BUILTIN_LAYOUTS_H
#include "plugins/layout_plugin.h"
#include "dwn.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Initialize and register all built-in layouts.
* Called during DWN startup.
*/
bool wm_layouts_builtin_init(void);
/**
* Get a layout plugin by legacy LayoutType.
*/
LayoutPlugin* wm_layout_get_by_type(LayoutManager *mgr, LayoutType type);
/**
* Bridge function to arrange a workspace using the plugin system.
*/
bool wm_layout_arrange_workspace(LayoutManager *mgr, int workspace, LayoutType type);
/**
* Get symbol for a layout type.
*/
const char* wm_layout_get_symbol_for_type(LayoutType type);
/**
* Get name for a layout type.
*/
const char* wm_layout_get_name_for_type(LayoutType type);
/**
* Get LayoutType from name.
*/
LayoutType wm_layout_type_from_name(const char *name);
/* External layout interface getters */
const LayoutPluginInterface* wm_layout_tiling_get_interface(void);
const LayoutPluginInterface* wm_layout_floating_get_interface(void);
const LayoutPluginInterface* wm_layout_monocle_get_interface(void);
const LayoutPluginInterface* wm_layout_grid_get_interface(void);
#ifdef __cplusplus
}
#endif
#endif /* BUILTIN_LAYOUTS_H */
-365
View File
@@ -1,365 +0,0 @@
/*
* DWN - Desktop Window Manager
* Layout Plugin Interface
*/
#ifndef LAYOUT_PLUGIN_H
#define LAYOUT_PLUGIN_H
#include "core/wm_types.h"
#include "core/wm_client.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Layout Plugin API Version
*============================================================================*/
#define WM_LAYOUT_PLUGIN_API_VERSION 1
/*==============================================================================
* Forward Declarations
*============================================================================*/
typedef struct LayoutPlugin LayoutPlugin;
typedef struct LayoutContext LayoutContext;
/*==============================================================================
* Layout Geometry
*============================================================================*/
/**
* Layout geometry for a single client.
*/
typedef struct {
int x, y;
int width, height;
bool floating; /* If true, layout doesn't manage this client */
bool fullscreen; /* If true, client should be fullscreen */
bool hidden; /* If true, client should be hidden */
} WmLayoutGeometry;
/**
* Work area for layout calculations.
*/
typedef struct {
int x, y;
int width, height;
int padding_top;
int padding_bottom;
int padding_left;
int padding_right;
int gap;
} WmLayoutWorkArea;
/*==============================================================================
* Layout Configuration
*============================================================================*/
/**
* Layout configuration parameter.
*/
typedef struct {
const char *name;
const char *description;
union {
int int_value;
float float_value;
bool bool_value;
const char *string_value;
} default_value;
enum {
WM_LAYOUT_PARAM_INT,
WM_LAYOUT_PARAM_FLOAT,
WM_LAYOUT_PARAM_BOOL,
WM_LAYOUT_PARAM_STRING,
WM_LAYOUT_PARAM_CHOICE
} type;
/* For PARAM_CHOICE */
const char **choices;
int num_choices;
} WmLayoutParameter;
/**
* Layout configuration instance.
*/
typedef struct {
WmLayoutParameter *parameters;
int num_parameters;
void *user_data; /* Plugin can store parsed config here */
} WmLayoutConfig;
/*==============================================================================
* Layout State
*============================================================================*/
/**
* Opaque layout state per workspace.
* Layout plugins can store workspace-specific state here.
*/
typedef struct WmLayoutState WmLayoutState;
/*==============================================================================
* Layout Context
*============================================================================*/
/**
* Context passed to layout operations.
*/
struct LayoutContext {
/* Workspace info */
WmWorkspaceId workspace;
WmLayoutWorkArea work_area;
/* Client counts */
int client_count;
int master_count;
int stack_count;
/* Layout parameters */
float master_ratio;
int master_count_target;
int gap;
/* Configuration */
WmLayoutConfig *config;
/* Layout state (managed by plugin) */
WmLayoutState *state;
/* User data (for plugin use) */
void *user_data;
};
/*==============================================================================
* Layout Plugin Interface
*============================================================================*/
/**
* Layout plugin interface vtable.
* All layouts must implement these functions.
*/
typedef struct {
/* Version check */
int api_version;
/* Metadata */
const char *name;
const char *description;
const char *author;
const char *version;
/* Configuration */
WmLayoutParameter *parameters;
int num_parameters;
/* Lifecycle */
bool (*init)(LayoutPlugin *plugin);
void (*shutdown)(LayoutPlugin *plugin);
/* State management */
WmLayoutState* (*state_create)(LayoutPlugin *plugin, WmWorkspaceId workspace);
void (*state_destroy)(LayoutPlugin *plugin, WmLayoutState *state);
void (*state_reset)(LayoutPlugin *plugin, WmLayoutState *state);
/* Configuration */
bool (*config_parse)(LayoutPlugin *plugin, WmLayoutConfig *config,
const char *key, const char *value);
void (*config_apply)(LayoutPlugin *plugin, WmLayoutConfig *config);
/* Layout calculation */
bool (*arrange)(LayoutPlugin *plugin, LayoutContext *ctx,
AbstractClient **clients, int num_clients,
WmLayoutGeometry *geometries);
/* Client operations */
void (*client_added)(LayoutPlugin *plugin, LayoutContext *ctx,
AbstractClient *client);
void (*client_removed)(LayoutPlugin *plugin, LayoutContext *ctx,
AbstractClient *client);
void (*client_floating_changed)(LayoutPlugin *plugin, LayoutContext *ctx,
AbstractClient *client, bool floating);
/* Layout manipulation */
void (*inc_master)(LayoutPlugin *plugin, LayoutContext *ctx, int delta);
void (*set_master_ratio)(LayoutPlugin *plugin, LayoutContext *ctx, float ratio);
void (*set_master_count)(LayoutPlugin *plugin, LayoutContext *ctx, int count);
void (*swap_master)(LayoutPlugin *plugin, LayoutContext *ctx);
void (*rotate)(LayoutPlugin *plugin, LayoutContext *ctx, bool clockwise);
/* Mouse resizing support */
void (*resize_master)(LayoutPlugin *plugin, LayoutContext *ctx,
int x, int y, int *new_master_count, float *new_ratio);
} LayoutPluginInterface;
/*==============================================================================
* Layout Plugin Structure
*============================================================================*/
/**
* Layout plugin instance.
*/
struct LayoutPlugin {
/* Interface vtable */
const LayoutPluginInterface *interface;
/* Plugin handle (for dynamic loading) */
void *handle;
/* Plugin path */
char *path;
/* Enabled state */
bool enabled;
/* Global configuration */
WmLayoutConfig config;
/* Per-workspace states */
WmLayoutState **workspace_states;
int num_workspaces;
/* Plugin-private data */
void *user_data;
};
/*==============================================================================
* Layout Manager
*============================================================================*/
/**
* Layout manager handles all loaded layouts.
*/
typedef struct LayoutManager LayoutManager;
/**
* Get the global layout manager.
*/
LayoutManager* wm_layout_manager_get(void);
/**
* Initialize the layout manager.
*/
bool wm_layout_manager_init(void);
/**
* Shutdown the layout manager.
*/
void wm_layout_manager_shutdown(void);
/**
* Register a layout plugin.
*/
bool wm_layout_manager_register(LayoutManager *mgr, LayoutPlugin *plugin);
/**
* Unregister a layout plugin.
*/
void wm_layout_manager_unregister(LayoutManager *mgr, LayoutPlugin *plugin);
/**
* Load a layout plugin from a file.
*/
LayoutPlugin* wm_layout_manager_load(LayoutManager *mgr, const char *path);
/**
* Unload a layout plugin.
*/
void wm_layout_manager_unload(LayoutManager *mgr, LayoutPlugin *plugin);
/**
* Get a loaded layout by name.
*/
LayoutPlugin* wm_layout_manager_get_layout(LayoutManager *mgr, const char *name);
/**
* Get the default layout.
*/
LayoutPlugin* wm_layout_manager_get_default(LayoutManager *mgr);
/**
* Set the default layout.
*/
void wm_layout_manager_set_default(LayoutManager *mgr, LayoutPlugin *plugin);
/**
* Get list of available layouts.
*/
const char** wm_layout_manager_list(LayoutManager *mgr, int *count);
/*==============================================================================
* Layout Operations
*============================================================================*/
/**
* Apply a layout to a workspace.
*/
bool wm_layout_arrange(LayoutPlugin *plugin, WmWorkspaceId workspace);
/**
* Apply layout to specific clients.
*/
bool wm_layout_arrange_clients(LayoutPlugin *plugin, LayoutContext *ctx,
AbstractClient **clients, int num_clients);
/**
* Create a layout context for a workspace.
*/
bool wm_layout_context_init(LayoutContext *ctx, WmWorkspaceId workspace);
/**
* Clean up a layout context.
*/
void wm_layout_context_cleanup(LayoutContext *ctx);
/*==============================================================================
* Built-in Layouts
*============================================================================*/
/* Tiling layout */
extern const LayoutPluginInterface wm_layout_tiling;
/* Floating layout */
extern const LayoutPluginInterface wm_layout_floating;
/* Monocle (maximized) layout */
extern const LayoutPluginInterface wm_layout_monocle;
/* Grid layout */
extern const LayoutPluginInterface wm_layout_grid;
/* Spiral layout */
extern const LayoutPluginInterface wm_layout_spiral;
/* Dwindle layout */
extern const LayoutPluginInterface wm_layout_dwindle;
/*==============================================================================
* Plugin Entry Point
*============================================================================*/
/**
* Layout plugins must export this function.
*/
typedef const LayoutPluginInterface* (*LayoutPluginEntryFunc)(void);
#define WM_LAYOUT_PLUGIN_ENTRY "wm_layout_plugin_entry"
/*==============================================================================
* Helper Macros
*============================================================================*/
#define WM_LAYOUT_REGISTER(name, iface) \
__attribute__((unused)) static const LayoutPluginInterface* wm_layout_plugin_entry(void) { return &(iface); }
/* Typedef for built-in layout getters */
typedef const LayoutPluginInterface* (*WmLayoutBuiltinFunc)(void);
#ifdef __cplusplus
}
#endif
#endif /* LAYOUT_PLUGIN_H */
-435
View File
@@ -1,435 +0,0 @@
/*
* DWN - Desktop Window Manager
* Widget Plugin Interface
* Panel components (taskbar, clock, workspaces, etc.)
*/
#ifndef WIDGET_PLUGIN_H
#define WIDGET_PLUGIN_H
#include "core/wm_types.h"
#include "core/wm_client.h"
#ifdef __cplusplus
extern "C" {
#endif
/*==============================================================================
* Widget Plugin API Version
*============================================================================*/
#define WM_WIDGET_PLUGIN_API_VERSION 1
/*==============================================================================
* Forward Declarations
*============================================================================*/
typedef struct WidgetPlugin WidgetPlugin;
typedef struct WidgetContext WidgetContext;
typedef struct WidgetInstance WidgetInstance;
/*==============================================================================
* Widget Position
*============================================================================*/
typedef enum {
WM_WIDGET_POSITION_LEFT,
WM_WIDGET_POSITION_CENTER,
WM_WIDGET_POSITION_RIGHT
} WmWidgetPosition;
/*==============================================================================
* Widget Size Constraints
*============================================================================*/
typedef struct {
int min_width;
int min_height;
int max_width;
int max_height;
bool expand_horizontal; /* Take available horizontal space */
bool expand_vertical; /* Take available vertical space */
} WmWidgetConstraints;
/*==============================================================================
* Widget Geometry
*============================================================================*/
typedef struct {
int x, y;
int width, height;
} WmWidgetGeometry;
/*==============================================================================
* Widget State
*============================================================================*/
typedef enum {
WM_WIDGET_STATE_NORMAL = 0,
WM_WIDGET_STATE_HOVERED = (1 << 0),
WM_WIDGET_STATE_PRESSED = (1 << 1),
WM_WIDGET_STATE_ACTIVE = (1 << 2),
WM_WIDGET_STATE_DISABLED = (1 << 3),
WM_WIDGET_STATE_HIDDEN = (1 << 4),
WM_WIDGET_STATE_URGENT = (1 << 5)
} WmWidgetState;
/*==============================================================================
* Widget Context
*============================================================================*/
struct WidgetContext {
/* Position in panel */
WmWidgetPosition position;
/* Geometry */
WmWidgetGeometry geometry;
WmWidgetConstraints constraints;
/* Panel info */
WmRect panel_geometry;
bool is_top_panel;
/* Rendering context */
void *render_context; /* Backend-specific (X11 GC, cairo, etc.) */
WmColor bg_color;
WmColor fg_color;
/* Font */
void *font; /* Opaque font handle */
int font_size;
/* User data */
void *user_data;
};
/*==============================================================================
* Widget Event Types
*============================================================================*/
typedef enum {
WM_WIDGET_EVENT_NONE,
WM_WIDGET_EVENT_MOUSE_ENTER,
WM_WIDGET_EVENT_MOUSE_LEAVE,
WM_WIDGET_EVENT_MOUSE_MOVE,
WM_WIDGET_EVENT_BUTTON_PRESS,
WM_WIDGET_EVENT_BUTTON_RELEASE,
WM_WIDGET_EVENT_SCROLL,
WM_WIDGET_EVENT_KEY_PRESS,
WM_WIDGET_EVENT_FOCUS_IN,
WM_WIDGET_EVENT_FOCUS_OUT,
WM_WIDGET_EVENT_UPDATE, /* Request to redraw */
WM_WIDGET_EVENT_CONFIGURE, /* Size/position changed */
WM_WIDGET_EVENT_DATA_CHANGED /* External data changed */
} WmWidgetEventType;
typedef struct {
WmWidgetEventType type;
WmWidgetGeometry widget_geometry;
union {
struct { int x, y; } point;
struct { int button; int x, y; } button;
struct { int delta; bool horizontal; } scroll;
struct { unsigned int keycode; unsigned int state; } key;
} data;
} WmWidgetEvent;
/*==============================================================================
* Widget Configuration
*============================================================================*/
typedef struct {
const char *name;
const char *value;
} WmWidgetConfigOption;
typedef struct {
WmWidgetConfigOption *options;
int num_options;
} WmWidgetConfig;
/*==============================================================================
* Widget Information
*============================================================================*/
typedef struct {
const char *id; /* Unique identifier */
const char *name; /* Display name */
const char *description; /* Description */
const char *author; /* Author */
const char *version; /* Version */
/* Default constraints */
WmWidgetConstraints default_constraints;
/* Default position */
WmWidgetPosition default_position;
/* Configuration schema */
const char **config_options; /* List of accepted config option names */
int num_config_options;
} WmWidgetInfo;
/*==============================================================================
* Widget Plugin Interface
*============================================================================*/
typedef struct {
/* Version check */
int api_version;
/* Metadata */
WmWidgetInfo info;
/* Lifecycle */
bool (*init)(WidgetPlugin *plugin);
void (*shutdown)(WidgetPlugin *plugin);
/* Instance management */
WidgetInstance* (*instance_create)(WidgetPlugin *plugin,
const WmWidgetConfig *config);
void (*instance_destroy)(WidgetPlugin *plugin, WidgetInstance *instance);
void (*instance_configure)(WidgetPlugin *plugin, WidgetInstance *instance,
const WmWidgetConfig *config);
/* Rendering */
void (*render)(WidgetPlugin *plugin, WidgetInstance *instance,
WidgetContext *ctx);
/* Event handling */
bool (*handle_event)(WidgetPlugin *plugin, WidgetInstance *instance,
const WmWidgetEvent *event);
/* Update notification */
void (*update)(WidgetPlugin *plugin, WidgetInstance *instance);
/* Size calculation */
void (*get_preferred_size)(WidgetPlugin *plugin, WidgetInstance *instance,
int *width, int *height);
} WidgetPluginInterface;
/*==============================================================================
* Widget Instance
*============================================================================*/
struct WidgetInstance {
/* Plugin that created this instance */
WidgetPlugin *plugin;
/* Instance state */
WmWidgetState state;
WmWidgetGeometry geometry;
WmWidgetConstraints constraints;
/* Configuration */
WmWidgetConfig config;
/* Context */
WidgetContext *context;
/* Plugin-private data */
void *user_data;
/* Linked list for panel */
WidgetInstance *next;
WidgetInstance *prev;
};
/*==============================================================================
* Widget Plugin Structure
*============================================================================*/
struct WidgetPlugin {
/* Interface vtable */
const WidgetPluginInterface *interface;
/* Plugin handle (for dynamic loading) */
void *handle;
/* Plugin path */
char *path;
/* Enabled state */
bool enabled;
/* Instances */
WidgetInstance *instances;
int instance_count;
/* Plugin-private data */
void *user_data;
};
/*==============================================================================
* Widget Manager
*============================================================================*/
typedef struct WidgetManager WidgetManager;
/**
* Get the global widget manager.
*/
WidgetManager* wm_widget_manager_get(void);
/**
* Initialize the widget manager.
*/
bool wm_widget_manager_init(void);
/**
* Shutdown the widget manager.
*/
void wm_widget_manager_shutdown(void);
/**
* Register a widget plugin.
*/
bool wm_widget_manager_register(WidgetManager *mgr, WidgetPlugin *plugin);
/**
* Unregister a widget plugin.
*/
void wm_widget_manager_unregister(WidgetManager *mgr, WidgetPlugin *plugin);
/**
* Load a widget plugin from a file.
*/
WidgetPlugin* wm_widget_manager_load(WidgetManager *mgr, const char *path);
/**
* Unload a widget plugin.
*/
void wm_widget_manager_unload(WidgetManager *mgr, WidgetPlugin *plugin);
/**
* Get a loaded widget by ID.
*/
WidgetPlugin* wm_widget_manager_get_widget(WidgetManager *mgr, const char *id);
/**
* Get list of available widgets.
*/
const char** wm_widget_manager_list(WidgetManager *mgr, int *count);
/*==============================================================================
* Widget Instance Operations
*============================================================================*/
/**
* Create a widget instance.
*/
WidgetInstance* wm_widget_create_instance(WidgetPlugin *plugin,
const WmWidgetConfig *config);
/**
* Destroy a widget instance.
*/
void wm_widget_destroy_instance(WidgetInstance *instance);
/**
* Render a widget instance.
*/
void wm_widget_render(WidgetInstance *instance, WidgetContext *ctx);
/**
* Send an event to a widget instance.
*/
bool wm_widget_send_event(WidgetInstance *instance, const WmWidgetEvent *event);
/**
* Update a widget instance (request redraw).
*/
void wm_widget_update(WidgetInstance *instance);
/**
* Get preferred size for a widget instance.
*/
void wm_widget_get_preferred_size(WidgetInstance *instance,
int *width, int *height);
/*==============================================================================
* Widget Panel Integration
*============================================================================*/
typedef struct WidgetPanel WidgetPanel;
/**
* Create a widget panel (container for widget instances).
*/
WidgetPanel* wm_widget_panel_create(void);
/**
* Destroy a widget panel.
*/
void wm_widget_panel_destroy(WidgetPanel *panel);
/**
* Add a widget instance to a panel.
*/
void wm_widget_panel_add(WidgetPanel *panel, WidgetInstance *instance,
WmWidgetPosition position);
/**
* Remove a widget instance from a panel.
*/
void wm_widget_panel_remove(WidgetPanel *panel, WidgetInstance *instance);
/**
* Arrange widgets in a panel.
*/
void wm_widget_panel_arrange(WidgetPanel *panel, const WmRect *panel_geometry);
/**
* Render all widgets in a panel.
*/
void wm_widget_panel_render(WidgetPanel *panel);
/**
* Handle mouse event in a panel.
*/
bool wm_widget_panel_handle_event(WidgetPanel *panel,
const WmWidgetEvent *event);
/*==============================================================================
* Built-in Widgets
*============================================================================*/
/* Workspace switcher widget */
extern const WidgetPluginInterface wm_widget_workspaces;
/* Taskbar widget */
extern const WidgetPluginInterface wm_widget_taskbar;
/* Clock widget */
extern const WidgetPluginInterface wm_widget_clock;
/* System tray widget */
extern const WidgetPluginInterface wm_widget_systray;
/* CPU/Memory monitor widget */
extern const WidgetPluginInterface wm_widget_system_stats;
/* News ticker widget */
extern const WidgetPluginInterface wm_widget_news;
/*==============================================================================
* Plugin Entry Point
*============================================================================*/
typedef const WidgetPluginInterface* (*WidgetPluginEntryFunc)(void);
#define WM_WIDGET_PLUGIN_ENTRY "wm_widget_plugin_entry"
/*==============================================================================
* Helper Macros
*============================================================================*/
#define WM_WIDGET_REGISTER(name, iface) \
static const WidgetPluginInterface* wm_widget_plugin_entry(void) { return &(iface); }
#ifdef __cplusplus
}
#endif
#endif /* WIDGET_PLUGIN_H */
-61
View File
@@ -1,61 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Window rules engine for auto-applying settings
*/
#ifndef DWN_RULES_H
#define DWN_RULES_H
#include "dwn.h"
#include <stdbool.h>
#define MAX_RULES 64
#define RULE_PATTERN_SIZE 128
typedef enum {
RULE_MATCH_CLASS,
RULE_MATCH_TITLE,
RULE_MATCH_CLASS_AND_TITLE
} RuleMatchType;
typedef struct {
RuleMatchType match_type;
char class_pattern[RULE_PATTERN_SIZE];
char title_pattern[RULE_PATTERN_SIZE];
bool has_workspace;
int workspace;
bool has_floating;
bool floating;
bool has_sticky;
bool sticky;
bool has_fullscreen;
bool fullscreen;
bool has_size;
int width;
int height;
bool has_position;
int x;
int y;
bool has_opacity;
float opacity;
} WindowRule;
void rules_init(void);
void rules_cleanup(void);
bool rules_load(const char *path);
void rules_add(const WindowRule *rule);
void rules_clear(void);
int rules_count(void);
bool rules_apply_to_client(Client *client);
bool rules_match_pattern(const char *str, const char *pattern);
#endif
-84
View File
@@ -1,84 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Screenshot capture API
*/
#ifndef DWN_SCREENSHOT_H
#define DWN_SCREENSHOT_H
#include <stdbool.h>
#include <stddef.h>
#include <pthread.h>
#include <X11/Xlib.h>
typedef enum {
SCREENSHOT_OK = 0,
SCREENSHOT_ERROR_INVALID_ARG,
SCREENSHOT_ERROR_X11,
SCREENSHOT_ERROR_PNG,
SCREENSHOT_ERROR_NO_MEMORY,
SCREENSHOT_ERROR_WINDOW_NOT_FOUND
} ScreenshotStatus;
typedef enum {
SCREENSHOT_STATE_IDLE,
SCREENSHOT_STATE_PENDING,
SCREENSHOT_STATE_COMPLETED,
SCREENSHOT_STATE_ERROR
} ScreenshotState;
typedef enum {
SCREENSHOT_MODE_FULLSCREEN,
SCREENSHOT_MODE_WINDOW,
SCREENSHOT_MODE_ACTIVE,
SCREENSHOT_MODE_AREA
} ScreenshotMode;
typedef struct {
unsigned char *data;
size_t size;
int width;
int height;
} ScreenshotResult;
typedef struct ScreenshotRequest {
ScreenshotMode mode;
Window window;
int x, y, width, height;
ScreenshotResult result;
ScreenshotStatus status;
ScreenshotState state;
void (*callback)(struct ScreenshotRequest *req);
void *user_data;
pthread_t thread;
struct ScreenshotRequest *next;
} ScreenshotRequest;
bool screenshot_init(void);
void screenshot_cleanup(void);
ScreenshotStatus screenshot_capture_fullscreen(ScreenshotResult *result);
ScreenshotStatus screenshot_capture_window(Window window, ScreenshotResult *result);
ScreenshotStatus screenshot_capture_active(ScreenshotResult *result);
ScreenshotStatus screenshot_capture_area(int x, int y, int width, int height, ScreenshotResult *result);
ScreenshotRequest *screenshot_capture_async(ScreenshotMode mode,
void (*callback)(ScreenshotRequest *));
ScreenshotRequest *screenshot_capture_window_async(Window window,
void (*callback)(ScreenshotRequest *));
ScreenshotRequest *screenshot_capture_area_async(int x, int y, int w, int h,
void (*callback)(ScreenshotRequest *));
void screenshot_process_pending(void);
void screenshot_cancel(ScreenshotRequest *req);
char *screenshot_to_base64(const ScreenshotResult *result, size_t *out_len);
void screenshot_result_free(ScreenshotResult *result);
const char *screenshot_status_string(ScreenshotStatus status);
#endif
-13
View File
@@ -1,13 +0,0 @@
/*
* DWN - Desktop Window Manager
* Service management
*/
#ifndef DWN_SERVICES_H
#define DWN_SERVICES_H
void services_init(void);
void services_run(void);
void services_cleanup(void);
#endif
-72
View File
@@ -1,72 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Generic slider widget for panel controls
*/
#ifndef DWN_SLIDER_H
#define DWN_SLIDER_H
#include "dwn.h"
#include <stdbool.h>
#define SLIDER_WIDTH 30
#define SLIDER_HEIGHT 120
#define SLIDER_PADDING 8
#define SLIDER_KNOB_HEIGHT 8
/* Forward declaration */
typedef struct GenericSlider GenericSlider;
/* Callback type for value changes */
typedef void (*SliderValueChangedCallback)(GenericSlider *slider, int value);
/* Generic slider structure */
struct GenericSlider {
Window window;
int x, y;
int width, height;
bool visible;
bool dragging;
/* Value (always 0-100 internally) */
int value;
/* Display */
const char *label_prefix;
bool show_percentage;
/* Callback */
SliderValueChangedCallback on_value_changed;
void *user_data;
};
/* Lifecycle */
GenericSlider *slider_create(int x, int y,
const char *label_prefix,
bool show_percentage,
SliderValueChangedCallback callback,
void *user_data);
void slider_destroy(GenericSlider *slider);
/* Visibility */
void slider_show(GenericSlider *slider, int x, int y);
void slider_hide(GenericSlider *slider);
bool slider_is_visible(const GenericSlider *slider);
/* Rendering */
void slider_render(GenericSlider *slider);
/* Event handling */
void slider_handle_click(GenericSlider *slider, int x, int y);
void slider_handle_motion(GenericSlider *slider, int x, int y);
void slider_handle_release(GenericSlider *slider);
/* Value */
void slider_set_value(GenericSlider *slider, int value);
int slider_get_value(const GenericSlider *slider);
/* Hit testing */
bool slider_hit_test(const GenericSlider *slider, int x, int y);
#endif
+56 -83
View File
@@ -1,82 +1,52 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* System tray widgets (WiFi, Audio, etc.) and XEmbed protocol
* System tray widgets (WiFi, Audio, etc.)
*/
#ifndef DWN_SYSTRAY_H
#define DWN_SYSTRAY_H
#include "dwn.h"
#include "slider.h"
#include <stdbool.h>
#define MAX_TRAY_ICONS 32
#define MAX_BATTERIES 4
#define TRAY_ICON_SIZE 22
#define TRAY_ICON_SPACING 4
#define SYSTEM_TRAY_REQUEST_DOCK 0
#define SYSTEM_TRAY_BEGIN_MESSAGE 1
#define SYSTEM_TRAY_CANCEL_MESSAGE 2
#define XEMBED_EMBEDDED_NOTIFY 0
#define XEMBED_WINDOW_ACTIVATE 1
#define XEMBED_WINDOW_DEACTIVATE 2
#define XEMBED_REQUEST_FOCUS 3
#define XEMBED_FOCUS_IN 4
#define XEMBED_FOCUS_OUT 5
#define XEMBED_MAPPED (1 << 0)
#define XEMBED_PROTOCOL_VERSION 0
/* Maximum number of WiFi networks to show */
#define MAX_WIFI_NETWORKS 20
/* WiFi network info */
typedef struct {
Window window;
int x;
int width;
int height;
bool mapped;
} TrayIcon;
extern TrayIcon tray_icons[MAX_TRAY_ICONS];
extern int tray_icon_count;
extern Window tray_selection_owner;
char ssid[64];
int signal; /* Signal strength 0-100 */
char security[32]; /* Security type (WPA2, etc.) */
bool connected;
} WifiNetwork;
/* WiFi state */
typedef struct {
bool enabled;
bool connected;
char current_ssid[64];
int signal_strength;
WifiNetwork networks[MAX_WIFI_NETWORKS];
int network_count;
long last_scan; /* Timestamp of last scan */
} WifiState;
/* Audio state */
typedef struct {
int volume;
int volume; /* 0-100 */
bool muted;
} AudioState;
/* Battery state */
typedef struct {
bool present;
bool charging;
int percentage;
int time_remaining;
bool present; /* Battery exists */
bool charging; /* Currently charging */
int percentage; /* 0-100 */
int time_remaining; /* Minutes remaining */
} BatteryState;
typedef struct {
char name[32];
bool present;
bool charging;
int percentage;
unsigned long energy_now;
unsigned long energy_full;
} SingleBattery;
typedef struct {
int count;
SingleBattery batteries[MAX_BATTERIES];
bool any_charging;
int combined_percentage;
} MultiBatteryState;
/* Volume slider popup */
typedef struct {
Window window;
int x, y;
@@ -85,36 +55,46 @@ typedef struct {
bool dragging;
} VolumeSlider;
/* Generic slider wrapper for fade controls */
/* Dropdown menu */
typedef struct {
GenericSlider *slider;
int icon_x; /* Position for popup */
} FadeControl;
Window window;
int x, y;
int width, height;
int item_count;
int hovered_item;
bool visible;
void (*on_select)(int index);
} DropdownMenu;
/* System tray state */
extern WifiState wifi_state;
extern AudioState audio_state;
extern BatteryState battery_state;
extern MultiBatteryState multi_battery_state;
extern DropdownMenu *wifi_menu;
extern VolumeSlider *volume_slider;
/* Fade control externs */
extern FadeControl fade_speed_control;
extern FadeControl fade_intensity_control;
/* Initialization */
void systray_init(void);
void systray_cleanup(void);
/* WiFi functions */
void wifi_update_state(void);
void wifi_scan_networks(void);
void wifi_connect(const char *ssid);
void wifi_disconnect(void);
const char *wifi_get_icon(void);
/* Audio functions */
void audio_update_state(void);
void audio_set_volume(int volume);
void audio_toggle_mute(void);
const char *audio_get_icon(void);
/* Battery functions */
void battery_update_state(void);
const char *battery_get_icon(void);
/* Volume slider functions */
VolumeSlider *volume_slider_create(int x, int y);
void volume_slider_destroy(VolumeSlider *slider);
void volume_slider_show(VolumeSlider *slider);
@@ -124,39 +104,32 @@ void volume_slider_handle_click(VolumeSlider *slider, int x, int y);
void volume_slider_handle_motion(VolumeSlider *slider, int x, int y);
void volume_slider_handle_release(VolumeSlider *slider);
/* Fade control functions */
void fade_controls_init(void);
void fade_controls_cleanup(void);
void fade_controls_render(Panel *panel, int x);
int fade_controls_get_width(void);
void fade_controls_handle_click(int control_id, int x, int y, int button);
int fade_controls_hit_test(int x);
void fade_controls_hide_all(void);
/* Dropdown menu functions */
DropdownMenu *dropdown_create(int x, int y, int width);
void dropdown_destroy(DropdownMenu *menu);
void dropdown_show(DropdownMenu *menu);
void dropdown_hide(DropdownMenu *menu);
void dropdown_add_item(DropdownMenu *menu, const char *label);
void dropdown_render(DropdownMenu *menu);
int dropdown_hit_test(DropdownMenu *menu, int x, int y);
void dropdown_handle_click(DropdownMenu *menu, int x, int y);
void dropdown_handle_motion(DropdownMenu *menu, int x, int y);
/* Panel rendering for systray */
void systray_render(Panel *panel, int x, int *width);
int systray_get_width(void);
void systray_handle_click(int x, int y, int button);
int systray_hit_test(int x);
int systray_hit_test(int x); /* Returns: 0=wifi, 1=audio, -1=none */
/* Periodic update */
void systray_update(void);
/* Thread-safe state access */
void systray_lock(void);
void systray_unlock(void);
/* Thread-safe state snapshots (copies state under lock) */
BatteryState systray_get_battery_snapshot(void);
MultiBatteryState systray_get_multi_battery_snapshot(void);
AudioState systray_get_audio_snapshot(void);
void xembed_init(void);
void xembed_cleanup(void);
bool xembed_dock_icon(Window icon_win);
void xembed_remove_icon(Window icon_win);
TrayIcon *xembed_find_icon(Window icon_win);
void xembed_send_message(Window icon, long message, long detail, long data1, long data2);
int xembed_get_icons_width(void);
void xembed_render_icons(Panel *panel, int x);
void xembed_handle_click(int x, int y, int button);
int xembed_hit_test(int x);
void xembed_update_icon_state(Window icon_win);
#endif
#endif /* DWN_SYSTRAY_H */
-263
View File
@@ -1,263 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Specialized Worker Threads - High-Level API
*
* Pre-built worker types for common async operations:
* - I/O Worker: File operations, networking
* - Compute Worker: CPU-intensive tasks
* - System Worker: /proc monitoring, system stats
* - Deferred Worker: Delayed/scheduled execution
*/
#ifndef DWN_THREAD_WORKERS_H
#define DWN_THREAD_WORKERS_H
#include "threading.h"
/* ============================================================================
* I/O Worker - Async File and Network Operations
* ============================================================================ */
typedef void (*IoCallback)(void *result, size_t size, int error, void *user_data);
typedef void (*IoProgressCallback)(size_t processed, size_t total, void *user_data);
/**
* Initialize I/O worker subsystem
*/
bool io_worker_init(void);
/**
* Shutdown I/O worker subsystem
*/
void io_worker_shutdown(void);
/**
* Read file asynchronously
* @param path File path
* @param callback Called with file contents
* @param user_data Passed to callback
* @return Task handle or NULL
*/
TaskHandle io_read_file_async(const char *path, IoCallback callback, void *user_data);
/**
* Write file asynchronously
*/
TaskHandle io_write_file_async(const char *path, const void *data, size_t size,
IoCallback callback, void *user_data);
/**
* Read directory contents asynchronously
* Result is a null-separated list of filenames
*/
TaskHandle io_read_dir_async(const char *path, IoCallback callback, void *user_data);
/**
* HTTP GET request asynchronously
* @param url URL to fetch
* @param callback Called with response body
* @param user_data Passed to callback
* @return Task handle or NULL
*/
TaskHandle io_http_get_async(const char *url, IoCallback callback, void *user_data);
/**
* HTTP POST request asynchronously
*/
TaskHandle io_http_post_async(const char *url, const void *data, size_t size,
IoCallback callback, void *user_data);
/**
* Download file with progress callback
*/
TaskHandle io_download_async(const char *url, const char *dest_path,
IoProgressCallback progress,
IoCallback callback, void *user_data);
/* ============================================================================
* Compute Worker - CPU-Intensive Tasks
* ============================================================================ */
typedef void (*ComputeCallback)(void *result, void *user_data);
/**
* Initialize compute worker subsystem
*/
bool compute_worker_init(void);
/**
* Shutdown compute worker subsystem
*/
void compute_worker_shutdown(void);
/**
* Parallel for - distribute work across threads
* @param start Start index
* @param end End index (exclusive)
* @param func Function called for each index
* @param user_data Passed to each invocation
*/
void compute_parallel_for(int start, int end,
void (*func)(int index, void *user_data),
void *user_data);
/**
* Map operation - apply function to each element
*/
void* compute_map(void *array, size_t count, size_t elem_size,
void (*func)(void *in, void *out, void *user_data),
void *user_data);
/**
* Reduce operation - aggregate values
*/
void* compute_reduce(void *array, size_t count, size_t elem_size,
void (*func)(void *accum, void *elem, void *user_data),
void *initial, void *user_data);
/**
* Submit compute task
*/
TaskHandle compute_submit(void (*func)(void *user_data), void *user_data,
ComputeCallback callback, void *callback_data);
/* ============================================================================
* System Worker - System Monitoring
* ============================================================================ */
typedef struct {
float cpu_percent;
uint64_t memory_used;
uint64_t memory_total;
float memory_percent;
float load_avg[3];
uint64_t uptime_seconds;
} SystemStats;
typedef struct {
uint32_t pid;
char name[64];
float cpu_percent;
uint64_t memory_bytes;
} ProcessInfo;
typedef void (*SystemStatsCallback)(const SystemStats *stats, void *user_data);
typedef void (*ProcessListCallback)(ProcessInfo *processes, uint32_t count, void *user_data);
/**
* Initialize system worker
*/
bool system_worker_init(void);
/**
* Shutdown system worker
*/
void system_worker_shutdown(void);
/**
* Get cached system stats (non-blocking, updated periodically)
*/
bool system_worker_get_cached_stats(SystemStats *stats);
/**
* Get fresh system stats (blocking)
*/
Future* system_worker_get_stats_async(void);
/**
* Get process list asynchronously
*/
TaskHandle system_worker_get_processes_async(ProcessListCallback callback, void *user_data);
/**
* Subscribe to periodic stats updates
* @param interval_ms Update interval (0 to disable)
* @param callback Called with new stats
* @param user_data Passed to callback
* @return Subscription ID
*/
uint32_t system_worker_subscribe_stats(uint32_t interval_ms,
SystemStatsCallback callback,
void *user_data);
/**
* Unsubscribe from stats updates
*/
bool system_worker_unsubscribe_stats(uint32_t subscription_id);
/* ============================================================================
* Deferred Worker - Delayed Execution
* ============================================================================ */
typedef uint64_t DeferredId;
typedef void (*DeferredCallback)(DeferredId id, void *user_data);
/**
* Initialize deferred worker
*/
bool deferred_worker_init(void);
/**
* Shutdown deferred worker
*/
void deferred_worker_shutdown(void);
/**
* Execute after delay
* @param delay_ms Milliseconds to wait
* @param callback Function to call
* @param user_data Passed to callback
* @return Deferred ID
*/
DeferredId deferred_after(uint64_t delay_ms, DeferredCallback callback, void *user_data);
/**
* Execute at regular interval
*/
DeferredId deferred_every(uint64_t interval_ms, DeferredCallback callback, void *user_data);
/**
* Cancel deferred execution
*/
bool deferred_cancel(DeferredId id);
/**
* Process deferred tasks (call from main loop)
* @return Number of tasks executed
*/
uint32_t deferred_process(void);
/**
* Get poll fd for select()
*/
int deferred_get_poll_fd(void);
/* ============================================================================
* High-Level Integration API
* ============================================================================ */
/**
* Initialize all worker subsystems
*/
bool thread_workers_init(void);
/**
* Shutdown all worker subsystems
*/
void thread_workers_shutdown(void);
/**
* Process all pending async work (call from main loop)
* @return true if work was processed
*/
bool thread_workers_process_all(void);
/**
* Get combined poll fds for select()
* @param fds Array to fill (size at least 4)
* @return Number of fds filled
*/
int thread_workers_get_poll_fds(int *fds);
#endif /* DWN_THREAD_WORKERS_H */
-704
View File
@@ -1,704 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Extensive Abstract Threading Framework
*
* This module provides a comprehensive, abstract threading system with:
* - Lock-free data structures
* - Thread pools with work stealing
* - Async/await pattern support
* - Thread-safe event bus
* - Memory barriers and atomic operations
*/
#ifndef DWN_THREADING_H
#define DWN_THREADING_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <pthread.h>
#include <stdatomic.h>
/* ============================================================================
* Platform Abstraction Layer
* ============================================================================ */
/* Cache line size (commonly 64 bytes on x86_64) */
#define CACHE_LINE_SIZE 64
/* Align to cache line to prevent false sharing */
#define CACHE_ALIGN __attribute__((aligned(CACHE_LINE_SIZE)))
/* Memory ordering shortcuts */
#define ATOMIC_RELAXED memory_order_relaxed
#define ATOMIC_ACQUIRE memory_order_acquire
#define ATOMIC_RELEASE memory_order_release
#define ATOMIC_ACQ_REL memory_order_acq_rel
#define ATOMIC_SEQ_CST memory_order_seq_cst
/* ============================================================================
* Core Types and Status Codes
* ============================================================================ */
typedef enum {
THREAD_OK = 0,
THREAD_ERROR = -1,
THREAD_ERROR_NOMEM = -2,
THREAD_ERROR_BUSY = -3,
THREAD_ERROR_CLOSED = -4,
THREAD_ERROR_TIMEOUT = -5,
THREAD_ERROR_INVALID = -6
} ThreadStatus;
typedef enum {
TASK_PRIORITY_CRITICAL = 0, /* UI-critical, must execute immediately */
TASK_PRIORITY_HIGH = 1, /* User-initiated actions */
TASK_PRIORITY_NORMAL = 2, /* Default background work */
TASK_PRIORITY_LOW = 3, /* Maintenance tasks */
TASK_PRIORITY_IDLE = 4, /* Only when system idle */
TASK_PRIORITY_COUNT
} TaskPriority;
typedef enum {
TASK_STATE_PENDING = 0,
TASK_STATE_RUNNING = 1,
TASK_STATE_COMPLETED = 2,
TASK_STATE_CANCELLED = 3,
TASK_STATE_ERROR = 4
} TaskState;
/* Forward declarations */
typedef struct ThreadPool ThreadPool;
typedef struct Task Task;
typedef struct Channel Channel;
typedef struct Future Future;
typedef struct EventBus EventBus;
typedef struct AsyncContext AsyncContext;
typedef struct Worker Worker;
/* ============================================================================
* Task System - Abstract Unit of Work
* ============================================================================ */
/* Task function signature - receives user data and cancellation flag */
typedef void (*TaskFunc)(void *user_data, atomic_int *cancelled);
/* Callback for task completion (called in worker thread) */
typedef void (*TaskCallback)(Task *task, void *user_data, ThreadStatus status);
/* Task handle - opaque pointer */
typedef Task* TaskHandle;
/**
* Create a new task
* @param func The function to execute
* @param user_data Data passed to the function
* @param priority Task priority level
* @return Task handle or NULL on error
*/
TaskHandle task_create(TaskFunc func, void *user_data, TaskPriority priority);
/**
* Create a task with completion callback
*/
TaskHandle task_create_with_callback(TaskFunc func, void *user_data,
TaskPriority priority,
TaskCallback on_complete,
void *callback_data);
/**
* Destroy a task (only if not submitted)
*/
void task_destroy(TaskHandle task);
/**
* Cancel a task (best effort - may already be running)
*/
bool task_cancel(TaskHandle task);
/**
* Get current task state
*/
TaskState task_get_state(TaskHandle task);
/**
* Wait for task completion (blocking)
*/
ThreadStatus task_wait(TaskHandle task, uint64_t timeout_ms);
/**
* Check if task was cancelled
*/
bool task_is_cancelled(TaskHandle task);
/* ============================================================================
* Thread Pool - Manage Worker Threads
* ============================================================================ */
/* Thread pool configuration */
typedef struct {
uint32_t min_threads; /* Minimum threads to keep alive */
uint32_t max_threads; /* Maximum threads allowed */
uint32_t queue_capacity; /* Task queue capacity per priority */
uint32_t steal_attempts; /* Work stealing attempts before blocking */
uint64_t idle_timeout_ms; /* Time before idle thread terminates */
bool enable_work_stealing; /* Enable work stealing between queues */
} ThreadPoolConfig;
/* Default configuration */
#define THREAD_POOL_DEFAULT_CONFIG ((ThreadPoolConfig){ \
.min_threads = 2, \
.max_threads = 8, \
.queue_capacity = 256, \
.steal_attempts = 4, \
.idle_timeout_ms = 60000, \
.enable_work_stealing = true \
})
/**
* Create a thread pool
*/
ThreadPool* thread_pool_create(const ThreadPoolConfig *config);
/**
* Destroy thread pool, cancelling pending tasks
*/
void thread_pool_destroy(ThreadPool *pool);
/**
* Submit a task to the pool
*/
ThreadStatus thread_pool_submit(ThreadPool *pool, TaskHandle task);
/**
* Submit a function as a task (convenience)
*/
ThreadStatus thread_pool_submit_func(ThreadPool *pool, TaskFunc func,
void *user_data, TaskPriority priority);
/**
* Get number of active threads
*/
uint32_t thread_pool_active_count(ThreadPool *pool);
/**
* Get number of pending tasks
*/
uint32_t thread_pool_pending_count(ThreadPool *pool);
/**
* Shutdown pool gracefully, waiting for tasks to complete
*/
ThreadStatus thread_pool_shutdown(ThreadPool *pool, uint64_t timeout_ms);
/**
* Get the default/global thread pool
*/
ThreadPool* thread_pool_default(void);
/**
* Initialize the default thread pool
*/
ThreadStatus thread_pool_init_default(const ThreadPoolConfig *config);
/**
* Shutdown the default thread pool
*/
void thread_pool_shutdown_default(void);
/* ============================================================================
* Lock-Free Queue - Single Producer Single Consumer
* ============================================================================ */
#define SPSC_QUEUE_SIZE 1024
typedef struct {
_Atomic uint64_t head CACHE_ALIGN; /* Write index - producer only */
_Atomic uint64_t tail CACHE_ALIGN; /* Read index - consumer only */
void *buffer[SPSC_QUEUE_SIZE];
} SpscQueue;
/**
* Initialize SPSC queue
*/
void spsc_queue_init(SpscQueue *q);
/**
* Push item (producer only)
* @return false if queue full
*/
bool spsc_queue_push(SpscQueue *q, void *item);
/**
* Pop item (consumer only)
* @return false if queue empty
*/
bool spsc_queue_pop(SpscQueue *q, void **item);
/**
* Check if queue is empty (consumer only)
*/
bool spsc_queue_empty(SpscQueue *q);
/**
* Get approximate size (not synchronized)
*/
uint64_t spsc_queue_size_approx(SpscQueue *q);
/* ============================================================================
* Lock-Free Queue - Multi Producer Single Consumer
* ============================================================================ */
typedef struct MpscQueue MpscQueue;
/**
* Create MPSC queue
*/
MpscQueue* mpsc_queue_create(uint32_t capacity);
/**
* Destroy MPSC queue
*/
void mpsc_queue_destroy(MpscQueue *q);
/**
* Push item (thread-safe, any producer)
* @return false if queue full
*/
bool mpsc_queue_push(MpscQueue *q, void *item);
/**
* Pop item (consumer only - single thread)
* @return false if queue empty
*/
bool mpsc_queue_pop(MpscQueue *q, void **item);
/**
* Check if queue is empty
*/
bool mpsc_queue_empty(MpscQueue *q);
/* ============================================================================
* Channel - Thread-Safe Communication
* ============================================================================ */
typedef enum {
CHANNEL_UNBUFFERED = 0, /* Synchronous - sender blocks until received */
CHANNEL_BUFFERED = 1 /* Asynchronous - uses internal buffer */
} ChannelType;
/**
* Create a channel
* @param capacity Buffer size (0 for unbuffered synchronous channel)
*/
Channel* channel_create(uint32_t capacity);
/**
* Destroy channel
*/
void channel_destroy(Channel *ch);
/**
* Send data through channel (blocking)
* @return THREAD_OK on success, THREAD_ERROR_CLOSED if closed
*/
ThreadStatus channel_send(Channel *ch, void *data);
/**
* Send with timeout
*/
ThreadStatus channel_send_timeout(Channel *ch, void *data, uint64_t timeout_ms);
/**
* Try send (non-blocking)
*/
ThreadStatus channel_try_send(Channel *ch, void *data);
/**
* Receive from channel (blocking)
*/
ThreadStatus channel_recv(Channel *ch, void **data);
/**
* Receive with timeout
*/
ThreadStatus channel_recv_timeout(Channel *ch, void **data, uint64_t timeout_ms);
/**
* Try receive (non-blocking)
*/
ThreadStatus channel_try_recv(Channel *ch, void **data);
/**
* Close channel (no more sends allowed)
*/
void channel_close(Channel *ch);
/**
* Check if channel is closed
*/
bool channel_is_closed(Channel *ch);
/**
* Select on multiple channels (like Go's select)
* Returns index of ready channel or -1 on timeout
*/
int channel_select(Channel **channels, uint32_t count, uint64_t timeout_ms,
void **out_data);
/* ============================================================================
* Future/Promise - Async Result Handling
* ============================================================================ */
typedef void* FutureResult;
typedef void (*FutureCallback)(Future *f, FutureResult result, void *user_data);
/**
* Create a future
*/
Future* future_create(void);
/**
* Destroy future
*/
void future_destroy(Future *f);
/**
* Set the result (called by producer)
*/
void future_set_result(Future *f, FutureResult result);
/**
* Set error result
*/
void future_set_error(Future *f, int error_code);
/**
* Get result (blocking)
*/
FutureResult future_get(Future *f, ThreadStatus *status);
/**
* Get result with timeout
*/
FutureResult future_get_timeout(Future *f, uint64_t timeout_ms, ThreadStatus *status);
/**
* Check if future is ready
*/
bool future_is_ready(Future *f);
/**
* Attach callback to be called when ready (thread-safe)
*/
void future_then(Future *f, FutureCallback callback, void *user_data);
/**
* Create future that completes when all given futures complete
*/
Future* future_all(Future **futures, uint32_t count);
/**
* Create future that completes when any given future completes
*/
Future* future_any(Future **futures, uint32_t count);
/* ============================================================================
* Event Bus - Thread-Safe Pub/Sub
* ============================================================================ */
typedef uint32_t EventType;
typedef uint32_t SubscriptionId;
/* Event handler signature */
typedef void (*EventHandler)(EventType type, void *event_data, void *user_data);
/* Event filter - return true to allow event */
typedef bool (*EventFilter)(EventType type, void *event_data, void *user_data);
/**
* Create event bus
*/
EventBus* event_bus_create(void);
/**
* Destroy event bus
*/
void event_bus_destroy(EventBus *bus);
/**
* Subscribe to event type
* @return Subscription ID or 0 on error
*/
SubscriptionId event_bus_subscribe(EventBus *bus, EventType type,
EventHandler handler, void *user_data);
/**
* Subscribe with filter
*/
SubscriptionId event_bus_subscribe_filtered(EventBus *bus, EventType type,
EventHandler handler, void *user_data,
EventFilter filter, void *filter_data);
/**
* Unsubscribe
*/
bool event_bus_unsubscribe(EventBus *bus, SubscriptionId id);
/**
* Publish event (thread-safe)
*/
void event_bus_publish(EventBus *bus, EventType type, void *event_data);
/**
* Publish event with custom free function
*/
void event_bus_publish_owned(EventBus *bus, EventType type, void *event_data,
void (*free_fn)(void*));
/**
* Process pending events (call from main thread)
* @return number of events processed
*/
uint32_t event_bus_process(EventBus *bus);
/**
* Set processing limit per call
*/
void event_bus_set_batch_size(EventBus *bus, uint32_t batch_size);
/**
* Get the global/default event bus
*/
EventBus* event_bus_default(void);
/**
* Initialize default event bus
*/
bool event_bus_init_default(void);
/**
* Shutdown default event bus
*/
void event_bus_shutdown_default(void);
/* ============================================================================
* Async Context - Per-Module Async State
* ============================================================================ */
/* Context for managing async operations within a module */
struct AsyncContext {
ThreadPool *pool;
EventBus *event_bus;
Channel *completion_channel;
atomic_int operation_count;
atomic_int shutdown_requested;
pthread_mutex_t mutex;
};
/**
* Create async context
*/
AsyncContext* async_context_create(const char *name);
/**
* Destroy async context (cancels all pending operations)
*/
void async_context_destroy(AsyncContext *ctx);
/**
* Submit work to context's thread pool
*/
TaskHandle async_submit(AsyncContext *ctx, TaskFunc func, void *user_data,
TaskPriority priority);
/**
* Submit work and get future
*/
Future* async_submit_future(AsyncContext *ctx, TaskFunc func, void *user_data,
TaskPriority priority);
/**
* Check for completed operations (call from main thread)
*/
uint32_t async_poll(AsyncContext *ctx);
/**
* Get file descriptor for select() integration
* Returns -1 if not available
*/
int async_get_poll_fd(AsyncContext *ctx);
/* ============================================================================
* Timer/Scheduler - Delayed Execution
* ============================================================================ */
typedef uint64_t TimerId;
typedef void (*TimerCallback)(TimerId id, void *user_data);
/**
* Schedule one-shot timer
*/
TimerId timer_schedule(uint64_t delay_ms, TimerCallback callback, void *user_data);
/**
* Schedule repeating timer
*/
TimerId timer_schedule_repeating(uint64_t interval_ms, TimerCallback callback,
void *user_data);
/**
* Cancel timer
*/
bool timer_cancel(TimerId id);
/**
* Check if timer exists
*/
bool timer_exists(TimerId id);
/**
* Process timer events (call from main thread)
* @return Number of timers fired
*/
uint32_t timer_process(void);
/**
* Get timer file descriptor for select()
*/
int timer_get_poll_fd(void);
/**
* Initialize timer subsystem
*/
bool timer_init(void);
/**
* Shutdown timer subsystem
*/
void timer_shutdown(void);
/* ============================================================================
* Thread-Local Storage Abstraction
* ============================================================================ */
typedef struct TlsKey TlsKey;
/**
* Create TLS key
*/
TlsKey* tls_create(void (*destructor)(void*));
/**
* Destroy TLS key
*/
void tls_destroy(TlsKey *key);
/**
* Set TLS value
*/
void tls_set(TlsKey *key, void *value);
/**
* Get TLS value
*/
void* tls_get(TlsKey *key);
/* ============================================================================
* Read-Write Lock Wrapper
* ============================================================================ */
typedef struct RwLock RwLock;
RwLock* rwlock_create(void);
void rwlock_destroy(RwLock *lock);
void rwlock_read_lock(RwLock *lock);
bool rwlock_read_trylock(RwLock *lock);
void rwlock_read_unlock(RwLock *lock);
void rwlock_write_lock(RwLock *lock);
bool rwlock_write_trylock(RwLock *lock);
void rwlock_write_unlock(RwLock *lock);
/* ============================================================================
* Barrier and Synchronization
* ============================================================================ */
typedef struct Barrier Barrier;
Barrier* barrier_create(uint32_t count);
void barrier_destroy(Barrier *b);
bool barrier_wait(Barrier *b, uint64_t timeout_ms);
/* ============================================================================
* Initialization and Cleanup
* ============================================================================ */
/**
* Initialize entire threading subsystem
*/
bool threading_init(void);
/**
* Shutdown entire threading subsystem
*/
void threading_shutdown(void);
/**
* Get number of hardware threads
*/
uint32_t threading_hw_concurrency(void);
/**
* Current thread ID
*/
uint64_t threading_current_thread_id(void);
/**
* Set current thread name (for debugging)
*/
void threading_set_name(const char *name);
/**
* Yield current thread
*/
void threading_yield(void);
/* ============================================================================
* Utility Macros
* ============================================================================ */
/* Run function in background */
#define ASYNC(func, data) \
thread_pool_submit_func(thread_pool_default(), (func), (data), TASK_PRIORITY_NORMAL)
/* Run function with priority */
#define ASYNC_PRIORITY(func, data, prio) \
thread_pool_submit_func(thread_pool_default(), (func), (data), (prio))
/* Create future and submit */
#define ASYNC_FUTURE(ctx, func, data) \
async_submit_future((ctx), (func), (data), TASK_PRIORITY_NORMAL)
/* Synchronized block using mutex */
#define WITH_MUTEX(mutex, code) do { \
pthread_mutex_lock(&(mutex)); \
code; \
pthread_mutex_unlock(&(mutex)); \
} while(0)
/* Read lock block */
#define WITH_READ_LOCK(rwlock, code) do { \
rwlock_read_lock((rwlock)); \
code; \
rwlock_read_unlock((rwlock)); \
} while(0)
/* Write lock block */
#define WITH_WRITE_LOCK(rwlock, code) do { \
rwlock_write_lock((rwlock)); \
code; \
rwlock_write_unlock((rwlock)); \
} while(0)
#endif /* DWN_THREADING_H */
-75
View File
@@ -1,75 +0,0 @@
/*
* DWN - Desktop Window Manager
* retoor <retoor@molodetz.nl>
* Threading Integration Header
*/
#ifndef DWN_THREADING_INTEGRATION_H
#define DWN_THREADING_INTEGRATION_H
#include "threading.h"
#include "thread_workers.h"
#include <sys/select.h>
/**
* Initialize threading integration with main loop
*/
bool threading_integration_init(void);
/**
* Shutdown threading integration
*/
void threading_integration_shutdown(void);
/**
* Prepare fd_set for select() - adds threading fds
*/
void threading_integration_prepare_select(int xfd, fd_set *fds, int *max_fd);
/**
* Process threading events after select() returns
*/
void threading_integration_process(fd_set *fds);
/**
* Non-blocking poll for async work
*/
void threading_integration_poll(void);
/**
* Run one iteration with integrated threading
*/
void threading_integration_run_iteration(int xfd, void (*handle_xevent)(void));
/**
* Get stats string for display
*/
void threading_integration_get_stats(char *buf, size_t bufsize);
/**
* Initialize per-module async contexts
*/
bool threading_integration_init_module_contexts(void);
/**
* Cleanup per-module async contexts
*/
void threading_integration_cleanup_module_contexts(void);
/**
* Get module-specific async contexts
*/
AsyncContext* threading_integration_get_ai_context(void);
AsyncContext* threading_integration_get_screenshot_context(void);
AsyncContext* threading_integration_get_ocr_context(void);
/**
* High-level task submission
*/
TaskHandle threading_integration_submit_ai(TaskFunc func, void *user_data);
TaskHandle threading_integration_submit_screenshot(TaskFunc func, void *user_data);
TaskHandle threading_integration_submit_ocr(TaskFunc func, void *user_data);
TaskHandle threading_integration_submit_ui(TaskFunc func, void *user_data);
TaskHandle threading_integration_submit_background(TaskFunc func, void *user_data);
#endif /* DWN_THREADING_INTEGRATION_H */
+14 -34
View File
@@ -11,11 +11,12 @@
#include <stddef.h>
#include <stdarg.h>
#include <assert.h>
#include <string.h>
/* Contract assertion macro - use for programmer errors */
#define DWN_ASSERT(cond) assert(cond)
#define DWN_ASSERT_MSG(cond, msg) assert((cond) && (msg))
/* Logging levels */
typedef enum {
LOG_DEBUG,
LOG_INFO,
@@ -23,72 +24,51 @@ typedef enum {
LOG_ERROR
} LogLevel;
/* Async Logging - non-blocking with max file size (5MB) and rotation */
void log_init(const char *log_file);
void log_close(void);
void log_set_level(LogLevel level);
void log_flush(void);
void log_flush(void); /* Force flush pending logs (call before exit/crash) */
void log_msg(LogLevel level, const char *fmt, ...);
/* Convenience macros */
#define LOG_DEBUG(...) log_msg(LOG_DEBUG, __VA_ARGS__)
#define LOG_INFO(...) log_msg(LOG_INFO, __VA_ARGS__)
#define LOG_WARN(...) log_msg(LOG_WARN, __VA_ARGS__)
#define LOG_ERROR(...) log_msg(LOG_ERROR, __VA_ARGS__)
/* Memory allocation with error checking */
void *dwn_malloc(size_t size);
void *dwn_calloc(size_t nmemb, size_t size);
void *dwn_realloc(void *ptr, size_t size);
char *dwn_strdup(const char *s);
void dwn_free(void *ptr);
void secure_wipe(void *ptr, size_t size);
void secure_wipe(void *ptr, size_t size); /* Securely wipe sensitive data */
/* String utilities */
char *str_trim(char *str);
bool str_starts_with(const char *str, const char *prefix);
bool str_ends_with(const char *str, const char *suffix);
int str_split(char *str, char delim, char **parts, int max_parts);
char *shell_escape(const char *str);
/* Safe string copy that always null-terminates */
static inline void safe_strncpy(char *dest, const char *src, size_t n)
{
if (n == 0) return;
size_t src_len = strlen(src);
size_t copy_len = (src_len < n - 1) ? src_len : n - 1;
memcpy(dest, src, copy_len);
dest[copy_len] = '\0';
}
/* Check if value is within valid range */
static inline bool in_range_int(int val, int min, int max)
{
return val >= min && val <= max;
}
static inline bool in_range_size_t(size_t val, size_t min, size_t max)
{
return val >= min && val <= max;
}
char *shell_escape(const char *str); /* Escape string for safe shell use */
/* File utilities */
bool file_exists(const char *path);
char *file_read_all(const char *path);
bool file_write_all(const char *path, const char *content);
char *expand_path(const char *path);
/* Color utilities */
unsigned long parse_color(const char *color_str);
void color_to_rgb(unsigned long color, int *r, int *g, int *b);
unsigned long rgb_to_pixel(int r, int g, int b);
unsigned long generate_unique_color(void);
unsigned long adjust_color_for_background(unsigned long color, unsigned long bg);
unsigned long interpolate_color(unsigned long from, unsigned long to, float progress);
unsigned long dim_color(unsigned long color, float factor);
unsigned long glow_color(unsigned long base, float phase);
unsigned long ambient_glow_bg(unsigned long base, float phase);
unsigned long ambient_glow_accent(unsigned long base, float phase);
/* Time utilities */
long get_time_ms(void);
void sleep_ms(int ms);
/* Process utilities */
int spawn(const char *cmd);
int spawn_async(const char *cmd);
char *spawn_capture(const char *cmd);
#endif
#endif /* DWN_UTIL_H */
+10 -9
View File
@@ -10,23 +10,28 @@
#include "dwn.h"
#include <stdbool.h>
/* Workspace initialization */
void workspace_init(void);
void workspace_cleanup(void);
/* Workspace access */
Workspace *workspace_get(int index);
Workspace *workspace_get_current(void);
int workspace_get_current_index(void);
/* Workspace switching */
void workspace_switch(int index);
void workspace_switch_next(void);
void workspace_switch_prev(void);
/* Client management within workspaces */
void workspace_add_client(int workspace, Client *client);
void workspace_remove_client(int workspace, Client *client);
void workspace_move_client(Client *client, int new_workspace);
Client *workspace_get_first_client(int workspace);
Client *workspace_get_focused_client(int workspace);
/* Layout */
void workspace_set_layout(int workspace, LayoutType layout);
LayoutType workspace_get_layout(int workspace);
void workspace_cycle_layout(int workspace);
@@ -35,27 +40,23 @@ void workspace_adjust_master_ratio(int workspace, float delta);
void workspace_set_master_count(int workspace, int count);
void workspace_adjust_master_count(int workspace, int delta);
/* Arrangement */
void workspace_arrange(int workspace);
void workspace_arrange_current(void);
/* Visibility */
void workspace_show(int workspace);
void workspace_hide(int workspace);
/* Properties */
void workspace_set_name(int workspace, const char *name);
const char *workspace_get_name(int workspace);
int workspace_client_count(int workspace);
bool workspace_is_empty(int workspace);
/* Focus cycling within workspace */
void workspace_focus_next(void);
void workspace_focus_prev(void);
void workspace_focus_master(void);
void workspace_alt_tab_next(void);
void workspace_alt_tab_prev(void);
void workspace_end_alt_tab(void);
void workspace_mru_push(int workspace, Client *client);
void workspace_mru_remove(int workspace, Client *client);
Client *workspace_mru_get_previous(int workspace, Client *current);
#endif
#endif /* DWN_WORKSPACE_H */
-305
View File
@@ -1,305 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Abstraction Layer - 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">v2.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="abstraction-layer.html" class="nav-link active">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</aside>
<main class="main-content">
<div class="content">
<div class="page-header">
<h1>Abstraction Layer</h1>
<p class="lead">Backend-agnostic architecture for future extensibility</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="#core-types">Core Types</a></li>
<li><a href="#backend-interface">Backend Interface</a></li>
<li><a href="#client-abstraction">Client Abstraction</a></li>
<li><a href="#containers">Container Types</a></li>
<li><a href="#migration">Migration Strategy</a></li>
</ul>
</div>
<h2 id="overview">Overview</h2>
<p>DWN v2.0 introduces a comprehensive abstraction layer that separates the window manager logic from backend-specific implementations. This architecture enables:</p>
<ul>
<li><strong>Backend Portability</strong> - Clean migration path from X11 to Wayland</li>
<li><strong>Type Safety</strong> - Strongly typed handles eliminate void* casting</li>
<li><strong>Memory Safety</strong> - Abstract strings and containers prevent buffer overflows</li>
<li><strong>Plugin Extensibility</strong> - Dynamic loading of layouts and widgets</li>
<li><strong>100% Compatibility</strong> - Existing code continues to work unchanged</li>
</ul>
<h2 id="core-types">Core Types</h2>
<p>The abstraction layer provides type-safe replacements for backend-specific types:</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Abstract Type</th>
<th>X11 Equivalent</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>WmWindowHandle</code></td>
<td><code>Window</code></td>
<td>Opaque window reference</td>
</tr>
<tr>
<td><code>WmClientId</code></td>
<td>-</td>
<td>Unique client identifier</td>
</tr>
<tr>
<td><code>WmWorkspaceId</code></td>
<td><code>int</code></td>
<td>Workspace identifier</td>
</tr>
<tr>
<td><code>WmRect</code></td>
<td>-</td>
<td>Rectangle geometry (x, y, w, h)</td>
</tr>
<tr>
<td><code>WmColor</code></td>
<td><code>unsigned long</code></td>
<td>RGBA color value</td>
</tr>
</tbody>
</table>
</div>
<h3>Geometry Operations</h3>
<p>Inline functions for rectangle operations:</p>
<div class="code-block">
<pre><code>WmRect rect = wm_rect_make(0, 0, 1920, 1080);
bool contains = wm_rect_contains_point(&rect, 100, 100);
bool intersects = wm_rect_intersects(&rect1, &rect2);
WmRect intersection = wm_rect_intersection(&rect1, &rect2);</code></pre>
</div>
<h3>Color Operations</h3>
<div class="code-block">
<pre><code>WmColor color = wm_color_rgb(255, 0, 128); // RGB
WmColor color = wm_color_rgba(255, 0, 128, 200); // RGBA
uint8_t r = wm_color_get_red(color);
uint8_t a = wm_color_get_alpha(color);</code></pre>
</div>
<h2 id="backend-interface">Backend Interface</h2>
<p>The backend interface defines a vtable of operations that any backend must implement:</p>
<div class="code-block">
<pre><code>typedef struct BackendInterface {
/* Identification */
const char *name;
WmBackendInfo (*get_info)(void);
/* Lifecycle */
bool (*init)(void *config);
void (*shutdown)(void);
/* Window Management */
void (*window_move)(WmWindowHandle window, int x, int y);
void (*window_resize)(WmWindowHandle window, int width, int height);
void (*window_focus)(WmWindowHandle window);
/* Events */
bool (*poll_event)(WmBackendEvent *event_out);
/* ... 80+ operations */
} BackendInterface;</code></pre>
</div>
<h3>X11 Backend</h3>
<p>The X11 backend is the reference implementation, translating abstract operations to X11 calls:</p>
<ul>
<li>Event translation (X11 → abstract events)</li>
<li>Protocol support (ICCCM, EWMH)</li>
<li>Property management with atom caching</li>
<li>Error handling with custom handlers</li>
</ul>
<h3>Future Backends</h3>
<p>The architecture supports multiple backends:</p>
<ul>
<li><strong>X11</strong> - Current, fully implemented</li>
<li><strong>Wayland</strong> - Planned for future</li>
<li><strong>Headless</strong> - For testing and CI</li>
</ul>
<h2 id="client-abstraction">Client Abstraction</h2>
<p>The <code>AbstractClient</code> type provides a backend-agnostic representation of a managed window:</p>
<div class="code-block">
<pre><code>/* Create from native window */
AbstractClient* client = wm_client_create(window, WM_CLIENT_TYPE_NORMAL);
/* State management */
wm_client_set_state(client, WM_CLIENT_STATE_FULLSCREEN);
wm_client_add_state(client, WM_CLIENT_STATE_FLOATING);
bool is_floating = wm_client_is_floating(client);
/* Geometry */
WmRect geom = wm_client_get_geometry(client);
wm_client_set_geometry(client, &new_geom);
wm_client_move_resize(client, x, y, width, height);
/* Properties */
wm_client_set_title(client, "New Title");
const char* title = wm_client_get_title(client);</code></pre>
</div>
<h3>Legacy Compatibility</h3>
<p>Abstract clients maintain bidirectional synchronization with legacy <code>Client</code> structures:</p>
<div class="code-block">
<pre><code>/* Wrap existing legacy client */
AbstractClient* abs_client = wm_client_from_legacy(legacy_client);
/* Access legacy client when needed */
Client* legacy = wm_client_get_legacy(abs_client);</code></pre>
</div>
<h2 id="containers">Container Types</h2>
<p>Safe, dynamic container implementations:</p>
<h3>WmString (Dynamic Strings)</h3>
<div class="code-block">
<pre><code>WmString* str = wm_string_new("Hello");
wm_string_append(str, " World");
wm_string_append_printf(str, " %d", 42);
const char* cstr = wm_string_cstr(str);
bool empty = wm_string_is_empty(str);
wm_string_destroy(str);</code></pre>
</div>
<h3>WmList (Dynamic Arrays)</h3>
<div class="code-block">
<pre><code>WmList* list = wm_list_new();
wm_list_append(list, item1);
wm_list_prepend(list, item2);
void* item = wm_list_get(list, 0);
wm_list_foreach(list, my_callback, user_data);
wm_list_destroy(list);</code></pre>
</div>
<h3>WmHashMap</h3>
<div class="code-block">
<pre><code>WmHashMap* map = wm_hashmap_new_string_key();
wm_hashmap_insert_string(map, "key", value);
void* value = wm_hashmap_get_string(map, "key");
bool exists = wm_hashmap_contains_string(map, "key");
wm_hashmap_destroy(map);</code></pre>
</div>
<h2 id="migration">Migration Strategy</h2>
<p>The abstraction layer uses an incremental migration approach:</p>
<ol>
<li><strong>Phase 1: Infrastructure</strong> (Complete)
<ul>
<li>Core types defined</li>
<li>Backend interface specified</li>
<li>X11 backend implemented</li>
</ul>
</li>
<li><strong>Phase 2: Client Migration</strong> (Complete)
<ul>
<li>AbstractClient type created</li>
<li>Bidirectional sync with legacy Client</li>
<li>Client manager with MRU tracking</li>
</ul>
</li>
<li><strong>Phase 3: Plugin System</strong> (Complete)
<ul>
<li>Layout plugin API</li>
<li>Widget plugin API</li>
<li>4 built-in layout plugins</li>
</ul>
</li>
<li><strong>Phase 4: Future</strong>
<ul>
<li>Gradual migration of existing code</li>
<li>Wayland backend implementation</li>
<li>Legacy code deprecation (long-term)</li>
</ul>
</li>
</ol>
<div class="alert alert-info">
<strong>Note:</strong> The abstraction layer is fully backward compatible. Existing code using X11 types continues to work unchanged.
</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>
-340
View File
@@ -1,340 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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>
-879
View File
@@ -1,879 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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="#fade-control">Fade Effect Control</a></li>
<li><a href="#events">Event Subscription</a></li>
<li><a href="#automation">Automation Recipes</a></li>
<li><a href="#browser-ocr">Browser Automation with OCR</a></li>
</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/ws")
def send_command(command, **params):
request = {"command": command, **params}
ws.send(json.dumps(request))
return json.loads(ws.recv())
# List all clients
result = send_command("get_clients")
for client in result["clients"]:
print(f"{client['title']} ({client['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 clients
clients = client.get_clients()
print(f"Found {len(clients)} clients")
# Focus client by title
for c in clients:
if "Firefox" in c["title"]:
client.focus_client(c["window"])
break
# Switch to workspace 3
client.switch_workspace(2)
# Type some text
client.key_type("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>Client Arrangement Script</h3>
<div class="code-block">
<pre><code>from dwn_api_client import DWNClient
def arrange_coding_setup(client):
"""Arrange clients for coding: editor left, terminal right"""
clients = client.get_clients()
# Find VS Code and terminal
vscode = None
terminal = None
for c in clients:
if "code" in c["class"].lower():
vscode = c
elif "terminal" in c["class"].lower():
terminal = c
if vscode:
client.move_client(vscode["window"], 0, 32)
client.resize_client(vscode["window"], 960, 1048)
if terminal:
client.move_client(terminal["window"], 960, 32)
client.resize_client(terminal["window"], 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/ws") as ws:
# Send command
await ws.send(json.dumps({"command": "get_clients"}))
# Receive response
response = json.loads(await ws.recv())
for client in response["clients"]:
print(f"Client: {client['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/ws') {
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 getClients() {
this.send('get_clients');
}
async focusClient(windowId) {
this.send('focus_client', { window: windowId });
}
async keyType(text) {
this.send('key_type', { text });
}
async screenshot(mode = 'fullscreen') {
this.send('screenshot', { mode });
}
}
// Usage
const client = new DWNClient();
await client.connect();
await client.getClients();</code></pre>
</div>
<h3>Node.js Client</h3>
<div class="code-block">
<pre><code>const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:8777/ws');
ws.on('open', () => {
console.log('Connected to DWN');
// List clients
ws.send(JSON.stringify({ command: 'get_clients' }));
});
ws.on('message', (data) => {
const response = JSON.parse(data);
if (response.clients) {
response.clients.forEach(c => {
console.log(`${c.title} - ${c.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 clients
echo '{"command": "get_clients"}' | websocat ws://localhost:8777/ws
# Focus client by ID
echo '{"command": "focus_client", "window": 12345678}' | websocat ws://localhost:8777/ws
# Switch workspace
echo '{"command": "switch_workspace", "workspace": 2}' | websocat ws://localhost:8777/ws
# Take screenshot and save
echo '{"command": "screenshot", "mode": "fullscreen"}' | \
websocat ws://localhost:8777/ws | \
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": "get_clients"}' | wscat -c ws://localhost:8777/ws -w 1
# Interactive session
wscat -c ws://localhost:8777/ws
# 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/ws
}
# Get focused client
dwn_command '{"command": "get_focused_client"}' | jq '.client.title'
# Type text
dwn_command '{"command": "key_type", "text": "Hello!"}'
# Launch application
dwn_command '{"command": "run_command", "exec": "firefox"}'</code></pre>
</div>
<h3>Fade Effect Control</h3>
<p>Control DWN's fade animation effects via API:</p>
<div class="code-block">
<pre><code>import json
import websocket
ws = websocket.create_connection("ws://localhost:8777/ws")
# Get current fade settings
ws.send(json.dumps({"command": "get_fade_settings"}))
response = json.loads(ws.recv())
print(f"Speed: {response['fade_speed']}, Intensity: {response['fade_intensity']}")
# Set fade animation speed (0.1 - 3.0)
ws.send(json.dumps({"command": "set_fade_speed", "speed": 1.5}))
# Set fade glow intensity (0.0 - 1.0)
ws.send(json.dumps({"command": "set_fade_intensity", "intensity": 0.8}))
# Subscribe to fade change events
ws.send(json.dumps({
"command": "subscribe",
"events": ["fade_speed_changed", "fade_intensity_changed"]
}))
# Listen for events
while True:
event = json.loads(ws.recv())
if event.get("type") == "event":
print(f"Event: {event['event']}")
print(f"Data: {event['data']}")</code></pre>
</div>
<p>Using the provided fade control demo script:</p>
<div class="code-block">
<pre><code># Get current fade settings
python3 examples/fade_control_demo.py
# Set fade speed
python3 examples/fade_control_demo.py --speed 1.5
# Set fade intensity
python3 examples/fade_control_demo.py --intensity 0.8
# Listen for fade events
python3 examples/fade_control_demo.py --listen --duration 30</code></pre>
</div>
<h2 id="events">Event Subscription</h2>
<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');
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 Client 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 clients...")
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 Clients 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()
clients = client.get_clients()
for c in clients:
wm_class = c["class"].lower()
for pattern, rules in LAYOUT_RULES.items():
if pattern in wm_class:
if c["workspace"] != rules["workspace"]:
client.move_client_to_workspace(
c["window"], rules["workspace"]
)
if c["floating"] != rules["floating"]:
client.float_client(c["window"], 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>Client Focus Logger</h3>
<div class="code-block">
<pre><code>import time
from datetime import datetime
from dwn_api_client import DWNClient
def focus_logger(log_file="focus_log.txt"):
"""Log client focus changes"""
client = DWNClient()
client.connect()
last_focused = None
try:
with open(log_file, "a") as f:
while True:
clients = client.get_clients()
focused = next((c for c in clients if c["focused"]), None)
if focused and focused["window"] != last_focused:
timestamp = datetime.now().isoformat()
entry = f"{timestamp} | {focused['title']} ({focused['class']})\n"
f.write(entry)
f.flush()
print(entry.strip())
last_focused = focused["window"]
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_client():
"""Extract and print text from active client"""
client = DWNClient()
client.connect()
# Capture active client
screenshot = client.screenshot("active")
# Extract text
ocr_result = client.ocr(screenshot["data"])
print(f"Text from active client (confidence: {ocr_result['confidence']:.0%}):")
print("-" * 40)
print(ocr_result["text"])
client.disconnect()
read_active_client()</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>
-350
View File
@@ -1,350 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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/ws</code> (or your configured port). The <code>/ws</code> path is required.</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/ws
# Send a command
> {"command": "get_clients"}
< {"status": "ok", "clients": [...]}</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": "get_clients"}' | websocat ws://localhost:8777/ws</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>Clients</td>
<td>get_clients, find_clients, get_focused_client, focus_client, focus_next, focus_prev, focus_master, close_client, kill_client, move_client, resize_client, minimize_client, restore_client, maximize_client, fullscreen_client, float_client, raise_client, lower_client, snap_client</td>
</tr>
<tr>
<td>Workspaces</td>
<td>get_workspaces, switch_workspace, switch_workspace_next, switch_workspace_prev, move_client_to_workspace</td>
</tr>
<tr>
<td>Layout</td>
<td>set_layout, cycle_layout, set_master_ratio, adjust_master_ratio, set_master_count, adjust_master_count</td>
</tr>
<tr>
<td>Keyboard</td>
<td>key_press, key_release, key_tap, key_type, get_keybindings</td>
</tr>
<tr>
<td>Mouse</td>
<td>mouse_move, mouse_move_relative, mouse_click, mouse_press, mouse_release, mouse_scroll, get_mouse_position</td>
</tr>
<tr>
<td>Screenshot</td>
<td>screenshot</td>
</tr>
<tr>
<td>OCR</td>
<td>ocr</td>
</tr>
<tr>
<td>System</td>
<td>get_status, get_screen_info, run_command, show_desktop, get_config, reload_config</td>
</tr>
<tr>
<td>Notifications</td>
<td>notify, close_notification, get_notifications</td>
</tr>
<tr>
<td>System Tray</td>
<td>get_battery_state, get_audio_state, set_audio_volume, toggle_audio_mute, get_wifi_state</td>
</tr>
<tr>
<td>Panels</td>
<td>get_panel_state, show_panel, hide_panel, toggle_panel</td>
</tr>
<tr>
<td>AI</td>
<td>ai_is_available, ai_command, exa_is_available, exa_search</td>
</tr>
<tr>
<td>News</td>
<td>get_news, news_next, news_prev, news_open</td>
</tr>
<tr>
<td>Demo/Tutorial</td>
<td>start_demo, stop_demo, get_demo_state, start_tutorial, stop_tutorial, get_tutorial_state</td>
</tr>
<tr>
<td>Events</td>
<td>subscribe, unsubscribe, list_events</td>
</tr>
<tr>
<td>Fade Effects</td>
<td>get_fade_settings, set_fade_speed, set_fade_intensity</td>
</tr>
</tbody>
</table>
</div>
<h2 id="quick-start">Quick Start</h2>
<h3>List Clients</h3>
<div class="code-block">
<pre><code>// Request
{"command": "get_clients"}
// Response
{
"status": "ok",
"clients": [
{
"window": 12345678,
"title": "Firefox",
"class": "firefox",
"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 a Client</h3>
<div class="code-block">
<pre><code>{"command": "focus_client", "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": "key_type", "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 clients
clients = client.get_clients()
# Focus a client
client.focus_client(clients[0]['window'])
# 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');
ws.onopen = () => {
ws.send(JSON.stringify({command: 'get_clients'}));
};
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>
File diff suppressed because it is too large Load Diff
-545
View File
@@ -1,545 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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="#abstraction">Abstraction Layer</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. Version 2.0 introduces a comprehensive abstraction layer enabling backend portability and plugin extensibility.</p>
<h2 id="abstraction">Abstraction Layer (v2.0)</h2>
<p>The new abstraction layer provides backend-agnostic types and a plugin system:</p>
<div class="code-block">
<pre><code>┌─────────────────────────────────────────────────────────┐
│ DWN Application │
├─────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Layout │ │ Widget │ │ AI Provider │ │
│ │ Plugin │ │ Plugin │ │ API │ │
│ │ System │ │ System │ │ (future) │ │
│ └──────┬──────┘ └──────┬──────┘ └─────────────────┘ │
│ │ │ │
│ ┌──────┴────────────────┴───────────────────────────┐ │
│ │ Abstract Client Manager │ │
│ │ (wm_client.h/c - bidirectional sync) │ │
│ └───────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌───────────────────────┴───────────────────────────┐ │
│ │ Backend Abstraction Interface │ │
│ │ (backend_interface.h - vtable-based) │ │
│ └───────────────────────┬───────────────────────────┘ │
│ │ │
│ ┌───────────────────────┴───────────────────────────┐ │
│ │ X11 Backend │ Wayland Backend │ Headless │ │
│ │ (complete) │ (future) │ (future) │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘</code></pre>
</div>
<h3>Core Abstractions</h3>
<ul>
<li><strong>wm_types.h</strong> - Abstract handles, geometry, colors, events</li>
<li><strong>wm_string.h/c</strong> - Safe dynamic strings</li>
<li><strong>wm_list.h/c</strong> - Dynamic arrays</li>
<li><strong>wm_hashmap.h/c</strong> - Hash tables</li>
<li><strong>wm_client.h/c</strong> - Abstract client type</li>
</ul>
<h3>Plugin System</h3>
<ul>
<li><strong>Layout Plugins</strong> - Custom window arrangements (tiling, floating, monocle, grid)</li>
<li><strong>Widget Plugins</strong> - Panel components (taskbar, clock, system stats)</li>
<li><strong>Dynamic Loading</strong> - Load plugins from shared libraries</li>
</ul>
<h3>Backend Interface</h3>
<p>The backend interface defines 80+ operations for window management, events, and rendering. Current implementations:</p>
<ul>
<li><strong>X11</strong> - Full implementation with event translation</li>
<li><strong>Wayland</strong> - Planned for future</li>
<li><strong>Headless</strong> - For testing and CI</li>
</ul>
<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>
-440
View File
@@ -1,440 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link 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>
-518
View File
@@ -1,518 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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>
<h3 id="rules">[rules] - Window Rules</h3>
<p>Automatically apply settings to windows based on their class or title. Rules use glob patterns for matching.</p>
<div class="code-block">
<pre><code>[rules]
Firefox = workspace:2, floating:false
class:*terminal* = workspace:1
title:*Calculator* = floating:true, width:400, height:300
class:Telegram = workspace:9, sticky:true
Gimp = floating:true
class:mpv = fullscreen:true</code></pre>
</div>
<h4>Rule Syntax</h4>
<p>Each rule has the format: <code>pattern = property:value, property:value, ...</code></p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Pattern Prefix</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>class:</code></td>
<td>Match by WM_CLASS (default if no prefix)</td>
</tr>
<tr>
<td><code>title:</code></td>
<td>Match by window title</td>
</tr>
<tr>
<td>(no prefix)</td>
<td>Treated as class pattern</td>
</tr>
</tbody>
</table>
</div>
<h4>Available Properties</h4>
<div class="table-container">
<table>
<thead>
<tr>
<th>Property</th>
<th>Values</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>workspace</code></td>
<td>1-9</td>
<td>Move to specific workspace</td>
</tr>
<tr>
<td><code>floating</code></td>
<td>true/false</td>
<td>Set floating mode</td>
</tr>
<tr>
<td><code>sticky</code></td>
<td>true/false</td>
<td>Visible on all workspaces</td>
</tr>
<tr>
<td><code>fullscreen</code></td>
<td>true/false</td>
<td>Start in fullscreen mode</td>
</tr>
<tr>
<td><code>width</code></td>
<td>pixels</td>
<td>Set initial width</td>
</tr>
<tr>
<td><code>height</code></td>
<td>pixels</td>
<td>Set initial height</td>
</tr>
<tr>
<td><code>x</code></td>
<td>pixels</td>
<td>Set initial X position</td>
</tr>
<tr>
<td><code>y</code></td>
<td>pixels</td>
<td>Set initial Y position</td>
</tr>
</tbody>
</table>
</div>
<h4>Pattern Matching</h4>
<ul>
<li><code>*</code> - Matches any sequence of characters</li>
<li><code>?</code> - Matches any single character</li>
<li><code>[abc]</code> - Matches any character in brackets</li>
<li>Matching is case-insensitive</li>
</ul>
<h4>Examples</h4>
<div class="code-block">
<pre><code>[rules]
# Open Firefox on workspace 2
Firefox = workspace:2
# All terminals on workspace 1
class:*terminal* = workspace:1
# Calculator always floating with specific size
title:*Calculator* = floating:true, width:400, height:300
# Picture-in-picture windows
title:Picture-in-Picture = floating:true, sticky:true
# Gimp dialogs floating
class:Gimp = floating:true
# Video players fullscreen
class:mpv = fullscreen:true
class:vlc = fullscreen:true</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
@@ -1,611 +0,0 @@
/* 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;
}
-408
View File
@@ -1,408 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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="#window-rules">Window Rules</a></li>
<li><a href="#window-marks">Window Marks</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>
<h3>Keyboard Window Control</h3>
<p>Move and resize floating windows without leaving the keyboard:</p>
<ul>
<li><code>Super+Alt+Arrow</code> - Move window by 20 pixels</li>
<li><code>Super+Ctrl+Arrow</code> - Resize window by 20 pixels</li>
</ul>
<h2 id="window-rules">Window Rules</h2>
<p>Automatically apply settings to windows based on their class or title. Rules are matched using glob patterns with case-insensitive matching.</p>
<h3>Matching Criteria</h3>
<ul>
<li><strong>Class Pattern</strong> - Match by WM_CLASS (e.g., <code>Firefox</code>, <code>*terminal*</code>)</li>
<li><strong>Title Pattern</strong> - Match by window title (e.g., <code>*Calculator*</code>)</li>
<li><strong>Combined</strong> - Match both class and title simultaneously</li>
</ul>
<h3>Available Actions</h3>
<div class="table-container">
<table>
<thead>
<tr>
<th>Property</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>workspace</code></td>
<td>Move window to specific workspace (1-9)</td>
</tr>
<tr>
<td><code>floating</code></td>
<td>Set floating state (true/false)</td>
</tr>
<tr>
<td><code>sticky</code></td>
<td>Make window visible on all workspaces</td>
</tr>
<tr>
<td><code>fullscreen</code></td>
<td>Start window in fullscreen mode</td>
</tr>
<tr>
<td><code>width</code>, <code>height</code></td>
<td>Set initial window size</td>
</tr>
<tr>
<td><code>x</code>, <code>y</code></td>
<td>Set initial window position</td>
</tr>
</tbody>
</table>
</div>
<p>See <a href="configuration.html#rules">Configuration</a> for rule syntax.</p>
<h2 id="window-marks">Window Marks</h2>
<p>Mark windows with letters (a-z) for instant navigation, inspired by Vim marks.</p>
<div class="feature-grid">
<div class="feature-card">
<div class="feature-title">Set Mark</div>
<div class="feature-desc">Press <code>Super+M</code> then <code>a-z</code> to mark the focused window.</div>
</div>
<div class="feature-card">
<div class="feature-title">Jump to Mark</div>
<div class="feature-desc">Press <code>Super+'</code> then <code>a-z</code> to instantly focus the marked window.</div>
</div>
<div class="feature-card">
<div class="feature-title">Cross-Workspace</div>
<div class="feature-desc">Marks work across workspaces, automatically switching if needed.</div>
</div>
<div class="feature-card">
<div class="feature-title">26 Slots</div>
<div class="feature-desc">Use any letter a-z for marks. Reassigning overwrites the previous mark.</div>
</div>
</div>
<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>Six 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>
<h3>Centered Master Layout</h3>
<p>The master window is centered on screen with stack windows divided on left and right sides. Great for wide monitors.</p>
<h3>Columns Layout</h3>
<p>All windows arranged in equal-width vertical columns. Each window takes full height.</p>
<h3>Fibonacci Layout</h3>
<p>Windows arranged in a spiral pattern using recursive splitting. Creates a visually interesting and space-efficient arrangement.</p>
<h3>Grid Layout</h3>
<p>Windows arranged in an automatically-calculated grid pattern. Perfect for comparing multiple documents or monitoring many terminals simultaneously.</p>
<h3>Plugin System</h3>
<p>DWN v2.0 supports custom layout plugins. Create your own window arrangements using the Layout Plugin API. Both built-in and dynamically loaded plugins are supported.</p>
<p>See <a href="layouts.html">Layouts</a> and <a href="plugin-development.html">Plugin Development</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>Abstraction Layer (v2.0)</h2>
<p>DWN v2.0 introduces a comprehensive abstraction layer enabling backend portability and plugin extensibility:</p>
<ul>
<li><strong>Backend Interface</strong> - 80+ operation vtable for X11/Wayland portability</li>
<li><strong>Type Safety</strong> - Strongly typed handles replacing void* casts</li>
<li><strong>Memory Safety</strong> - Safe string and container abstractions</li>
<li><strong>Plugin API</strong> - Extensible architecture for layouts and widgets</li>
<li><strong>100% Compatible</strong> - Existing code works unchanged</li>
</ul>
<p>See <a href="abstraction-layer.html">Abstraction Layer</a> for technical details.</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>
-180
View File
@@ -1,180 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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, WebSocket API, and plugin extensibility</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, monocle, and grid layouts with per-workspace configuration. Plugin API for custom layouts.</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 class="feature-card">
<div class="feature-icon">🔧</div>
<div class="feature-title">Plugin System</div>
<div class="feature-desc">Extensible plugin architecture for layouts and widgets. Dynamic loading support.</div>
</div>
<div class="feature-card">
<div class="feature-icon">📐</div>
<div class="feature-title">Abstraction Layer</div>
<div class="feature-desc">Backend-agnostic architecture. Ready for X11, Wayland, and headless backends.</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>
-242
View File
@@ -1,242 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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
@@ -1,119 +0,0 @@
/* 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' });
}
}
-444
View File
@@ -1,444 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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="#centered-master">Centered Master Layout</a></li>
<li><a href="#columns">Columns Layout</a></li>
<li><a href="#fibonacci">Fibonacci Layout</a></li>
<li><a href="#grid">Grid Layout</a></li>
<li><a href="#plugin-system">Plugin System</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 six 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 → Centered Master → Columns → Fibonacci → 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="centered-master">Centered Master Layout</h2>
<p>The master window is centered on screen with stack windows divided on left and right sides.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+--------+------------------+--------+
| Stack | Master | Stack |
| 1 | Window | 3 |
+--------+ +--------+
| Stack | | Stack |
| 2 | | 4 |
+--------+------------------+--------+</code></pre>
</div>
<ul>
<li>The <strong>master window</strong> occupies the center of the screen</li>
<li>Stack windows are divided between left and right sides</li>
<li>Odd-numbered stack windows go left, even go right</li>
<li>Excellent for wide monitors and ultrawide displays</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Keeping primary work centered while referencing side content</li>
<li>Video editing with timeline and tools on sides</li>
<li>Development with editor centered and documentation on sides</li>
</ul>
<h2 id="columns">Columns Layout</h2>
<p>All windows arranged in equal-width vertical columns spanning the full height.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+----------+----------+----------+----------+
| | | | |
| Window | Window | Window | Window |
| 1 | 2 | 3 | 4 |
| | | | |
| | | | |
+----------+----------+----------+----------+</code></pre>
</div>
<ul>
<li>Each window gets an equal-width column</li>
<li>Windows span the full usable height</li>
<li>Simple and predictable arrangement</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Comparing multiple files side-by-side</li>
<li>Monitoring multiple log files</li>
<li>Multi-column text editing</li>
<li>Concurrent terminal sessions</li>
</ul>
<h2 id="fibonacci">Fibonacci Layout</h2>
<p>Windows arranged in a spiral pattern using recursive splitting, inspired by the Fibonacci sequence.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+------------------+----------+
| | |
| Window 1 | Window 2 |
| +----+-----+
| | W3 | |
+------------------+----+ W4 |
| Window 5 | |
+-----------------------+-----+</code></pre>
</div>
<ul>
<li>First window takes half the screen</li>
<li>Each subsequent window takes half the remaining space</li>
<li>Alternates between horizontal and vertical splits</li>
<li>Creates a visually interesting spiral pattern</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Hierarchical window importance (larger = more important)</li>
<li>Primary workspace with progressively smaller utilities</li>
<li>Creative workflows with main canvas and tool windows</li>
</ul>
<h2 id="grid">Grid Layout</h2>
<p>Windows are arranged in a grid pattern with automatic row/column calculation.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+----------+----------+----------+
| Window | Window | Window |
| 1 | 2 | 3 |
+----------+----------+----------+
| Window | Window |
| 4 | 5 |
+----------+----------+</code></pre>
</div>
<ul>
<li>Automatically calculates optimal rows and columns</li>
<li>Uses square root for balanced grid</li>
<li>All windows have equal size</li>
<li>Great for viewing multiple documents simultaneously</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Comparing multiple documents or files</li>
<li>Monitoring multiple terminals</li>
<li>Dashboard-style workflows</li>
<li>Multi-way video calls</li>
</ul>
<h2 id="plugin-system">Plugin System (v2.0)</h2>
<p>DWN v2.0 introduces a layout plugin system that allows custom layout algorithms to be loaded dynamically or built-in.</p>
<h3>Built-in Layouts</h3>
<ul>
<li><strong>tiling</strong> - Master-stack tiling (default)</li>
<li><strong>floating</strong> - Traditional floating windows</li>
<li><strong>monocle</strong> - Single maximized window</li>
<li><strong>centered-master</strong> - Master centered with stacks on sides</li>
<li><strong>columns</strong> - Equal-width vertical columns</li>
<li><strong>fibonacci</strong> - Spiral recursive splitting</li>
<li><strong>grid</strong> - Grid arrangement</li>
</ul>
<h3>Custom Layouts</h3>
<p>Developers can create custom layout plugins using the Layout Plugin API. See <a href="plugin-development.html">Plugin Development</a> for details.</p>
<div class="alert alert-info">
<strong>Plugin API:</strong> Layouts implement the <code>LayoutPluginInterface</code> vtable with an <code>arrange()</code> method that calculates window geometries.
</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, monocle, centered-master, columns, or fibonacci</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: Centered-master for wide monitor development</li>
<li>Workspace 5: Columns layout for log monitoring</li>
<li>Workspace 6: Fibonacci for hierarchical work</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 layout for new workspaces
# Options: tiling, floating, monocle, centered-master, columns, fibonacci
default = tiling
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>
-87
View File
@@ -1,87 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Plugin Development - 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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link active">Plugin Development</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>Plugin Development</h1>
<p class="lead">Create custom layouts and widgets for DWN</p>
</div>
<h2>Overview</h2>
<p>DWN v2.0 introduces a plugin system for extending functionality. Two types of plugins are supported:</p>
<ul>
<li><strong>Layout Plugins</strong> - Custom window arrangement algorithms</li>
<li><strong>Widget Plugins</strong> - Panel components like taskbar, clock, system monitors</li>
</ul>
<h2>Layout Plugins</h2>
<p>Layout plugins implement the LayoutPluginInterface vtable. See the abstraction-layer.html documentation for details.</p>
<h2>Widget Plugins</h2>
<p>Widget plugins create panel components with custom rendering and event handling.</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>
-243
View File
@@ -1,243 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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>
-410
View File
@@ -1,410 +0,0 @@
<!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">v2.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="abstraction-layer.html" class="nav-link">Abstraction Layer</a>
<a href="plugin-development.html" class="nav-link">Plugin Development</a>
<a href="building.html" class="nav-link">Building from Source</a>
</div>
</nav>
</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="#keyboard-control">Keyboard Window Control</a></li>
<li><a href="#marks">Window Marks</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="keyboard-control">Keyboard Window Control</h2>
<p>Move and resize floating windows without using the mouse.</p>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+Alt+Left</code></td>
<td>Move window left by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Alt+Right</code></td>
<td>Move window right by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Alt+Up</code></td>
<td>Move window up by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Alt+Down</code></td>
<td>Move window down by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Ctrl+Left</code></td>
<td>Shrink window width by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Ctrl+Right</code></td>
<td>Grow window width by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Ctrl+Up</code></td>
<td>Shrink window height by 20 pixels</td>
</tr>
<tr>
<td><code>Super+Ctrl+Down</code></td>
<td>Grow window height by 20 pixels</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-info">
<strong>Note:</strong> Window must be floating for keyboard move/resize to work. Toggle floating with <code>Super+F9</code>.
</div>
<h2 id="marks">Window Marks</h2>
<p>Mark windows with letters (a-z) for instant switching, similar to Vim marks.</p>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+M</code> then <code>a-z</code></td>
<td>Mark current window with letter</td>
</tr>
<tr>
<td><code>Super+'</code> then <code>a-z</code></td>
<td>Jump to window marked with letter</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-info">
<strong>Tip:</strong> Use marks to quickly switch between frequently used windows. For example, mark your editor with <code>e</code> and terminal with <code>t</code>.
</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 → centered-master → columns → fibonacci)</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>
@@ -1,8 +1,8 @@
#!/bin/sh
# retoor <retoor@molodetz.nl>
# Example .xinitrc for starting DWN with startx
# Copy this to ~/.xinitrc and modify as needed
# Set environment variables
export XDG_SESSION_TYPE=x11
export XDG_CURRENT_DESKTOP=DWN
@@ -10,6 +10,7 @@ export XDG_CURRENT_DESKTOP=DWN
# export OPENROUTER_API_KEY="your-api-key-here"
# export EXA_API_KEY="your-exa-key-here"
# Optional: Start background services
# Start D-Bus session bus if not already running
if [ -z "$DBUS_SESSION_BUS_ADDRESS" ]; then
eval $(dbus-launch --sh-syntax)
@@ -21,6 +22,9 @@ fi
# Optional: Set wallpaper
# feh --bg-scale ~/.wallpaper.png &
# Optional: Start notification daemon (DWN has built-in)
# Note: DWN includes its own notification daemon
# Optional: Start XFCE components for additional functionality
# xfce4-power-manager &
# xfsettingsd &
@@ -34,4 +38,5 @@ fi
# Optional: Start clipboard manager
# xfce4-clipman &
# Start DWN
exec dwn
+458
View File
@@ -0,0 +1,458 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="AI integration in DWN window manager - command palette, semantic search, and context analysis.">
<title>AI Features - DWN Window Manager</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header>
<nav>
<a href="index.html" class="logo">
<span class="logo-icon">D</span>
<span>DWN</span>
</a>
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Install</a></li>
<li class="dropdown">
<a href="documentation.html" class="active">Docs</a>
<div class="dropdown-menu">
<a href="documentation.html">Getting Started</a>
<a href="shortcuts.html">Keyboard Shortcuts</a>
<a href="configuration.html">Configuration</a>
<a href="ai-features.html">AI Features</a>
<a href="architecture.html">Architecture</a>
</div>
</li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
<div class="nav-toggle" onclick="toggleNav()">
<span></span>
<span></span>
<span></span>
</div>
</nav>
</header>
<main>
<section class="hero" style="padding: 8rem 0 4rem;">
<div class="container hero-content">
<h1>AI Integration</h1>
<p class="subtitle">
Control your desktop with natural language and get intelligent assistance.
</p>
</div>
</section>
<section class="section">
<div class="container">
<div class="alert alert-info" style="margin-bottom: 2rem;">
<strong class="alert-title">Optional Features</strong>
<p style="margin: 0;">AI features are completely optional and require external API keys.
DWN works perfectly without them.</p>
</div>
<h2>Overview</h2>
<p>
DWN integrates with two AI services to provide intelligent desktop assistance:
</p>
<ul style="margin-bottom: 2rem;">
<li><strong>OpenRouter API</strong> - Powers the AI command palette and context analysis</li>
<li><strong>Exa API</strong> - Provides semantic web search capabilities</li>
</ul>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">&#129302;</div>
<h3>AI Command Palette</h3>
<p>Type natural language commands to control your desktop.
Launch apps, query system info, and more.</p>
<p style="margin-top: 1rem;"><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd></p>
</div>
<div class="feature-card">
<div class="feature-icon">&#128065;</div>
<h3>Context Analysis</h3>
<p>AI analyzes your current workspace to understand your task
and provide relevant suggestions.</p>
<p style="margin-top: 1rem;"><kbd>Super</kbd> + <kbd>A</kbd></p>
</div>
<div class="feature-card">
<div class="feature-icon">&#128269;</div>
<h3>Semantic Search</h3>
<p>Search the web using meaning, not just keywords.
Find relevant content instantly.</p>
<p style="margin-top: 1rem;"><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd></p>
</div>
</div>
<!-- Setup OpenRouter -->
<h2 id="openrouter" style="margin-top: 4rem;">Setting Up OpenRouter</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
OpenRouter provides access to multiple AI models through a single API.
You can use free models or paid ones depending on your needs.
</p>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h4>Get an API Key</h4>
<p>Visit <a href="https://openrouter.ai/keys" target="_blank">https://openrouter.ai/keys</a>
and create a free account to get your API key.</p>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h4>Set the Environment Variable</h4>
<p>Add to your shell profile (~/.bashrc, ~/.zshrc, etc.):</p>
<div class="code-header">
<span>~/.bashrc</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>export OPENROUTER_API_KEY="sk-or-v1-your-key-here"</code></pre>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h4>Choose a Model (Optional)</h4>
<p>Configure the AI model in your DWN config file:</p>
<div class="code-header">
<span>~/.config/dwn/config</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[ai]
model = google/gemini-2.0-flash-exp:free</code></pre>
<p style="margin-top: 0.5rem; font-size: 0.875rem; color: var(--text-muted);">
Browse available models at <a href="https://openrouter.ai/models" target="_blank">openrouter.ai/models</a>
</p>
</div>
</div>
</div>
<div class="card" style="margin-top: 2rem;">
<h3>Recommended Free Models</h3>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Model ID</th>
<th>Provider</th>
<th>Best For</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>google/gemini-2.0-flash-exp:free</code></td>
<td>Google</td>
<td>Fast responses, good general use</td>
</tr>
<tr>
<td><code>meta-llama/llama-3.2-3b-instruct:free</code></td>
<td>Meta</td>
<td>Quick commands, lightweight</td>
</tr>
<tr>
<td><code>mistralai/mistral-7b-instruct:free</code></td>
<td>Mistral</td>
<td>Balanced performance</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- AI Command Palette -->
<h2 id="command-palette" style="margin-top: 4rem;">AI Command Palette</h2>
<p>
Press <kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> to open the command palette.
Type natural language commands and press Enter.
</p>
<h3>Supported Commands</h3>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Category</th>
<th>Examples</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Launch Applications</strong></td>
<td>
"open firefox"<br>
"run terminal"<br>
"launch file manager"<br>
"start chrome"
</td>
</tr>
<tr>
<td><strong>System Queries</strong></td>
<td>
"what time is it"<br>
"how much memory is free"<br>
"what's my IP address"<br>
"show disk usage"
</td>
</tr>
<tr>
<td><strong>General Questions</strong></td>
<td>
"how do I resize the master area"<br>
"what's the shortcut for fullscreen"<br>
"explain tiling mode"
</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-success" style="margin-top: 1.5rem;">
<strong class="alert-title">Pro Tip</strong>
<p style="margin: 0;">The AI understands context. You can say "open browser" instead of
remembering the exact application name - it will figure out what you mean.</p>
</div>
<!-- Context Analysis -->
<h2 id="context" style="margin-top: 4rem;">Context Analysis</h2>
<p>
Press <kbd>Super</kbd> + <kbd>A</kbd> to see AI-powered analysis of your current workspace.
</p>
<div class="card">
<h3>What It Shows</h3>
<ul style="padding-left: 1.25rem;">
<li><strong>Task Type</strong> - Coding, browsing, communication, etc.</li>
<li><strong>Focused Window</strong> - Currently active application</li>
<li><strong>Suggestions</strong> - Relevant shortcuts or actions based on context</li>
<li><strong>Workspace Summary</strong> - Overview of open applications</li>
</ul>
</div>
<!-- Setup Exa -->
<h2 id="exa" style="margin-top: 4rem;">Setting Up Exa Search</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Exa provides semantic search - finding content based on meaning rather than exact keywords.
</p>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h4>Get an Exa API Key</h4>
<p>Visit <a href="https://dashboard.exa.ai/api-keys" target="_blank">https://dashboard.exa.ai/api-keys</a>
and create an account to get your API key.</p>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h4>Set the Environment Variable</h4>
<div class="code-header">
<span>~/.bashrc</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>export EXA_API_KEY="your-exa-key-here"</code></pre>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h4>Start Searching</h4>
<p>Press <kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd> and type your query.</p>
</div>
</div>
</div>
<!-- Semantic Search -->
<h2 id="search" style="margin-top: 4rem;">Using Semantic Search</h2>
<p>
Unlike traditional search, Exa understands the meaning of your query.
</p>
<div class="comparison" style="grid-template-columns: repeat(2, 1fr);">
<div class="comparison-card">
<h3>Traditional Search</h3>
<p style="color: var(--text-muted);">Keyword matching</p>
<ul>
<li>Exact keyword matches</li>
<li>Boolean operators needed</li>
<li>Miss relevant results</li>
</ul>
<p style="margin-top: 1rem; font-style: italic; color: var(--text-muted);">
"nginx reverse proxy setup tutorial"
</p>
</div>
<div class="comparison-card featured">
<h3>Semantic Search</h3>
<p style="color: var(--text-muted);">Meaning-based</p>
<ul>
<li>Understands intent</li>
<li>Natural language</li>
<li>Finds related content</li>
</ul>
<p style="margin-top: 1rem; font-style: italic; color: var(--text-muted);">
"how to configure nginx as a reverse proxy"
</p>
</div>
</div>
<h3 style="margin-top: 2rem;">Search Tips</h3>
<ul>
<li>Use natural, conversational queries</li>
<li>Be specific about what you're looking for</li>
<li>Results appear in a dmenu/rofi list - select to open in browser</li>
<li>Search includes articles, documentation, tutorials, and more</li>
</ul>
<!-- Privacy -->
<h2 id="privacy" style="margin-top: 4rem;">Privacy Considerations</h2>
<div class="alert alert-warning">
<strong class="alert-title">Data Sent to External Services</strong>
<p style="margin: 0;">When using AI features, the following data is sent to external APIs:</p>
</div>
<div class="table-wrapper" style="margin-top: 1rem;">
<table>
<thead>
<tr>
<th>Feature</th>
<th>Data Sent</th>
<th>Service</th>
</tr>
</thead>
<tbody>
<tr>
<td>Command Palette</td>
<td>Your typed command</td>
<td>OpenRouter (then to model provider)</td>
</tr>
<tr>
<td>Context Analysis</td>
<td>Window titles, app names</td>
<td>OpenRouter (then to model provider)</td>
</tr>
<tr>
<td>Semantic Search</td>
<td>Your search query</td>
<td>Exa</td>
</tr>
</tbody>
</table>
</div>
<p style="margin-top: 1rem; color: var(--text-muted);">
If you're concerned about privacy, you can:
</p>
<ul style="color: var(--text-muted);">
<li>Not configure API keys (AI features simply won't work)</li>
<li>Use OpenRouter with privacy-focused models</li>
<li>Only use AI features when needed</li>
</ul>
<!-- Troubleshooting -->
<h2 id="troubleshooting" style="margin-top: 4rem;">Troubleshooting</h2>
<div class="faq-item">
<button class="faq-question" onclick="toggleFaq(this)">
AI commands don't work - "API key not configured"
</button>
<div class="faq-answer">
<div class="faq-answer-content">
<p>Make sure your API key is properly set:</p>
<ol style="padding-left: 1.25rem; margin-top: 0.5rem;">
<li>Check the environment variable: <code>echo $OPENROUTER_API_KEY</code></li>
<li>Ensure the variable is exported in your shell profile</li>
<li>Log out and back in, or source your profile: <code>source ~/.bashrc</code></li>
<li>Restart DWN</li>
</ol>
</div>
</div>
</div>
<div class="faq-item">
<button class="faq-question" onclick="toggleFaq(this)">
Slow responses from AI
</button>
<div class="faq-answer">
<div class="faq-answer-content">
<p>Try using a faster model. Free models can sometimes be slow due to rate limiting.
Gemini Flash is usually the fastest free option.</p>
</div>
</div>
</div>
<div class="faq-item">
<button class="faq-question" onclick="toggleFaq(this)">
Exa search returns no results
</button>
<div class="faq-answer">
<div class="faq-answer-content">
<p>Check your Exa API key and ensure you have remaining credits.
Visit the <a href="https://dashboard.exa.ai">Exa dashboard</a> to check your usage.</p>
</div>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<div class="footer-grid">
<div class="footer-section">
<h4>DWN Window Manager</h4>
<p style="color: var(--text-muted);">
A modern, production-ready X11 window manager with XFCE-like
functionality and optional AI integration.
</p>
</div>
<div class="footer-section">
<h4>Documentation</h4>
<ul>
<li><a href="documentation.html">Getting Started</a></li>
<li><a href="shortcuts.html">Keyboard Shortcuts</a></li>
<li><a href="configuration.html">Configuration</a></li>
<li><a href="architecture.html">Architecture</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Resources</h4>
<ul>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="ai-features.html">AI Integration</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Community</h4>
<ul>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/issues">Issue Tracker</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/discussions">Discussions</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/CONTRIBUTING.md">Contributing</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/LICENSE">License (MIT)</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>DWN Window Manager by retoor &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
+565
View File
@@ -0,0 +1,565 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Technical architecture documentation for DWN window manager - codebase structure, modules, and internals.">
<title>Architecture - DWN Window Manager</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header>
<nav>
<a href="index.html" class="logo">
<span class="logo-icon">D</span>
<span>DWN</span>
</a>
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Install</a></li>
<li class="dropdown">
<a href="documentation.html" class="active">Docs</a>
<div class="dropdown-menu">
<a href="documentation.html">Getting Started</a>
<a href="shortcuts.html">Keyboard Shortcuts</a>
<a href="configuration.html">Configuration</a>
<a href="ai-features.html">AI Features</a>
<a href="architecture.html">Architecture</a>
</div>
</li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
<div class="nav-toggle" onclick="toggleNav()">
<span></span>
<span></span>
<span></span>
</div>
</nav>
</header>
<main>
<section class="hero" style="padding: 8rem 0 4rem;">
<div class="container hero-content">
<h1>Architecture</h1>
<p class="subtitle">
Technical documentation for developers and contributors.
</p>
</div>
</section>
<section class="section">
<div class="container">
<h2>Overview</h2>
<p>
DWN is written in ANSI C (C99) and follows a modular single-responsibility architecture.
A global <code>DWNState</code> singleton manages all state, and the main event loop
dispatches X11 events to specialized modules.
</p>
<div class="card" style="margin: 2rem 0;">
<h3>Project Statistics</h3>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 1rem; text-align: center;">
<div>
<div style="font-size: 2rem; font-weight: 700; color: var(--primary);">~10K</div>
<div style="color: var(--text-muted);">Lines of Code</div>
</div>
<div>
<div style="font-size: 2rem; font-weight: 700; color: var(--primary);">12</div>
<div style="color: var(--text-muted);">Core Modules</div>
</div>
<div>
<div style="font-size: 2rem; font-weight: 700; color: var(--primary);">C99</div>
<div style="color: var(--text-muted);">Standard</div>
</div>
<div>
<div style="font-size: 2rem; font-weight: 700; color: var(--primary);">MIT</div>
<div style="color: var(--text-muted);">License</div>
</div>
</div>
</div>
<!-- Directory Structure -->
<h2 id="structure" style="margin-top: 3rem;">Directory Structure</h2>
<div class="code-header">
<span>Project Layout</span>
</div>
<pre><code>dwn/
├── src/ # Source files (.c)
│ ├── main.c # Entry point, event loop
│ ├── client.c # Window management
│ ├── workspace.c # Virtual desktops
│ ├── layout.c # Tiling algorithms
│ ├── decorations.c # Title bars, borders
│ ├── panel.c # Top/bottom panels
│ ├── systray.c # System tray widgets
│ ├── notifications.c # D-Bus notifications
│ ├── atoms.c # X11 atoms (EWMH/ICCCM)
│ ├── keys.c # Keyboard handling
│ ├── config.c # INI parser
│ ├── ai.c # AI integration
│ └── util.c # Utilities
├── include/ # Header files (.h)
├── site/ # Documentation website
├── Makefile # Build system
├── CLAUDE.md # AI assistant context
└── README.md # Project readme</code></pre>
<!-- Core Modules -->
<h2 id="modules" style="margin-top: 3rem;">Core Modules</h2>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Module</th>
<th>File</th>
<th>Responsibility</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Main</strong></td>
<td><code>main.c</code></td>
<td>X11 initialization, event loop, signal handling, module orchestration</td>
</tr>
<tr>
<td><strong>Client</strong></td>
<td><code>client.c</code></td>
<td>Window lifecycle, focus management, frame creation, client list</td>
</tr>
<tr>
<td><strong>Workspace</strong></td>
<td><code>workspace.c</code></td>
<td>9 virtual desktops, per-workspace state, window assignment</td>
</tr>
<tr>
<td><strong>Layout</strong></td>
<td><code>layout.c</code></td>
<td>Tiling (master+stack), floating, monocle layout algorithms</td>
</tr>
<tr>
<td><strong>Decorations</strong></td>
<td><code>decorations.c</code></td>
<td>Window title bars, borders, decoration rendering</td>
</tr>
<tr>
<td><strong>Panel</strong></td>
<td><code>panel.c</code></td>
<td>Top panel (taskbar, workspace indicators), bottom panel (clock)</td>
</tr>
<tr>
<td><strong>Systray</strong></td>
<td><code>systray.c</code></td>
<td>System tray with WiFi/audio/battery indicators, dropdowns</td>
</tr>
<tr>
<td><strong>Notifications</strong></td>
<td><code>notifications.c</code></td>
<td>D-Bus notification daemon (org.freedesktop.Notifications)</td>
</tr>
<tr>
<td><strong>Atoms</strong></td>
<td><code>atoms.c</code></td>
<td>X11 EWMH/ICCCM atom management and property handling</td>
</tr>
<tr>
<td><strong>Keys</strong></td>
<td><code>keys.c</code></td>
<td>Keyboard shortcut capture, keybinding registry, callbacks</td>
</tr>
<tr>
<td><strong>Config</strong></td>
<td><code>config.c</code></td>
<td>INI-style config loading and parsing</td>
</tr>
<tr>
<td><strong>AI</strong></td>
<td><code>ai.c</code></td>
<td>Async OpenRouter API integration, Exa semantic search</td>
</tr>
<tr>
<td><strong>Util</strong></td>
<td><code>util.c</code></td>
<td>Logging, memory allocation, string utilities, file helpers</td>
</tr>
</tbody>
</table>
</div>
<!-- Module Dependencies -->
<h2 id="dependencies" style="margin-top: 3rem;">Module Dependencies</h2>
<div class="card">
<pre style="margin: 0; background: transparent; border: none; padding: 0;"><code>main.c (orchestrator)
├── client.c
│ ├── decorations.c
│ ├── config.c
│ └── atoms.c
├── workspace.c
│ ├── client.c
│ ├── layout.c
│ └── atoms.c
├── panel.c
│ ├── client.c
│ └── config.c
├── systray.c
│ └── config.c
├── notifications.c (independent)
├── ai.c (independent)
└── keys.c
└── config.c</code></pre>
</div>
<!-- Global State -->
<h2 id="state" style="margin-top: 3rem;">Global State (DWNState)</h2>
<p>
All window manager state is centralized in a single <code>DWNState</code> structure.
This simplifies state management and makes the codebase easier to understand.
</p>
<div class="code-header">
<span>include/dwn.h (simplified)</span>
</div>
<pre><code>typedef struct {
Display *display; // X11 connection
Window root; // Root window
int screen; // Default screen
Client *clients[MAX_CLIENTS]; // All managed windows
int client_count;
Workspace workspaces[MAX_WORKSPACES]; // Virtual desktops
int current_workspace;
Panel top_panel;
Panel bottom_panel;
Config config; // User configuration
KeyBinding keys[MAX_KEYBINDINGS];
// EWMH atoms
Atom atoms[ATOM_COUNT];
} DWNState;
extern DWNState *dwn; // Global singleton</code></pre>
<!-- Event Loop -->
<h2 id="events" style="margin-top: 3rem;">Event Loop</h2>
<p>
DWN uses a traditional X11 event loop with XNextEvent. Events are dispatched
to appropriate handlers based on type.
</p>
<div class="code-header">
<span>main.c (simplified)</span>
</div>
<pre><code>int main(int argc, char *argv[]) {
dwn_init(); // Initialize X11, atoms, config
setup_keybindings(); // Register keyboard shortcuts
setup_panels(); // Create panel windows
XEvent event;
while (running) {
XNextEvent(dwn->display, &event);
switch (event.type) {
case MapRequest:
handle_map_request(&event.xmaprequest);
break;
case UnmapNotify:
handle_unmap_notify(&event.xunmap);
break;
case KeyPress:
handle_key_press(&event.xkey);
break;
case ButtonPress:
handle_button_press(&event.xbutton);
break;
case ConfigureRequest:
handle_configure_request(&event.xconfigurerequest);
break;
// ... more event types
}
}
dwn_cleanup();
return 0;
}</code></pre>
<!-- Key Constants -->
<h2 id="constants" style="margin-top: 3rem;">Key Constants</h2>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Constant</th>
<th>Value</th>
<th>Purpose</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>Number of virtual desktops</td>
</tr>
<tr>
<td><code>MAX_MONITORS</code></td>
<td>8</td>
<td>Multi-monitor support limit</td>
</tr>
<tr>
<td><code>MAX_NOTIFICATIONS</code></td>
<td>32</td>
<td>Concurrent notifications</td>
</tr>
<tr>
<td><code>MAX_KEYBINDINGS</code></td>
<td>64</td>
<td>Registered keyboard shortcuts</td>
</tr>
</tbody>
</table>
</div>
<!-- Coding Conventions -->
<h2 id="conventions" style="margin-top: 3rem;">Coding Conventions</h2>
<div class="features-grid" style="grid-template-columns: repeat(2, 1fr);">
<div class="card">
<h3>Naming</h3>
<ul style="padding-left: 1.25rem;">
<li><code>snake_case</code> for functions and variables</li>
<li><code>CamelCase</code> for types and structs</li>
<li>Module prefix for functions (e.g., <code>client_focus()</code>)</li>
<li>Constants in <code>UPPER_SNAKE_CASE</code></li>
</ul>
</div>
<div class="card">
<h3>Style</h3>
<ul style="padding-left: 1.25rem;">
<li>4-space indentation</li>
<li>K&R brace style</li>
<li>Max 100 characters per line</li>
<li>clang-format for consistency</li>
</ul>
</div>
</div>
<div class="code-header" style="margin-top: 1.5rem;">
<span>Example Function</span>
</div>
<pre><code>void client_focus(Client *c) {
if (!c) return;
// Unfocus previous
if (dwn->focused && dwn->focused != c) {
client_unfocus(dwn->focused);
}
dwn->focused = c;
XSetInputFocus(dwn->display, c->window, RevertToPointerRoot, CurrentTime);
XRaiseWindow(dwn->display, c->frame);
decorations_update(c);
atoms_set_active_window(c->window);
}</code></pre>
<!-- EWMH/ICCCM -->
<h2 id="protocols" style="margin-top: 3rem;">EWMH/ICCCM Support</h2>
<p>
DWN implements key Extended Window Manager Hints and ICCCM protocols
for compatibility with modern applications.
</p>
<div class="features-grid" style="grid-template-columns: repeat(2, 1fr);">
<div class="card">
<h3>EWMH Atoms</h3>
<ul style="padding-left: 1.25rem; font-family: var(--font-mono); font-size: 0.875rem;">
<li>_NET_SUPPORTED</li>
<li>_NET_CLIENT_LIST</li>
<li>_NET_CLIENT_LIST_STACKING</li>
<li>_NET_ACTIVE_WINDOW</li>
<li>_NET_CURRENT_DESKTOP</li>
<li>_NET_NUMBER_OF_DESKTOPS</li>
<li>_NET_WM_STATE</li>
<li>_NET_WM_STATE_FULLSCREEN</li>
<li>_NET_WM_STATE_MAXIMIZED_*</li>
<li>_NET_WM_WINDOW_TYPE</li>
<li>_NET_WM_NAME</li>
</ul>
</div>
<div class="card">
<h3>ICCCM Support</h3>
<ul style="padding-left: 1.25rem; font-family: var(--font-mono); font-size: 0.875rem;">
<li>WM_STATE</li>
<li>WM_PROTOCOLS</li>
<li>WM_DELETE_WINDOW</li>
<li>WM_TAKE_FOCUS</li>
<li>WM_NORMAL_HINTS</li>
<li>WM_SIZE_HINTS</li>
<li>WM_CLASS</li>
<li>WM_NAME</li>
<li>WM_TRANSIENT_FOR</li>
</ul>
</div>
</div>
<!-- Build System -->
<h2 id="build" style="margin-top: 3rem;">Build System</h2>
<p>
DWN uses a simple Makefile-based build system with pkg-config for dependency detection.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Target</th>
<th>Command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Build (release)</td>
<td><code>make</code></td>
<td>Optimized build with -O2</td>
</tr>
<tr>
<td>Build (debug)</td>
<td><code>make debug</code></td>
<td>Debug symbols, -DDEBUG flag</td>
</tr>
<tr>
<td>Install</td>
<td><code>sudo make install</code></td>
<td>Install to PREFIX (/usr/local)</td>
</tr>
<tr>
<td>Clean</td>
<td><code>make clean</code></td>
<td>Remove build artifacts</td>
</tr>
<tr>
<td>Format</td>
<td><code>make format</code></td>
<td>Run clang-format on sources</td>
</tr>
<tr>
<td>Check</td>
<td><code>make check</code></td>
<td>Run cppcheck static analysis</td>
</tr>
<tr>
<td>Test</td>
<td><code>make run</code></td>
<td>Run in Xephyr nested server</td>
</tr>
<tr>
<td>Dependencies</td>
<td><code>make deps</code></td>
<td>Auto-install for your distro</td>
</tr>
</tbody>
</table>
</div>
<!-- Contributing -->
<h2 id="contributing" style="margin-top: 3rem;">Contributing</h2>
<p>
Contributions are welcome! Here's how to get started:
</p>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h4>Fork & Clone</h4>
<p>Fork the repository and clone your fork locally.</p>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h4>Create a Branch</h4>
<p>Create a feature branch: <code>git checkout -b feature/my-feature</code></p>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h4>Make Changes</h4>
<p>Follow coding conventions. Run <code>make format</code> and <code>make check</code>.</p>
</div>
</div>
<div class="step">
<div class="step-number">4</div>
<div class="step-content">
<h4>Test</h4>
<p>Test your changes with <code>make run</code> in a nested X server.</p>
</div>
</div>
<div class="step">
<div class="step-number">5</div>
<div class="step-content">
<h4>Submit PR</h4>
<p>Push your branch and open a pull request with a clear description.</p>
</div>
</div>
</div>
</div>
</section>
</main>
<footer>
<div class="container">
<div class="footer-grid">
<div class="footer-section">
<h4>DWN Window Manager</h4>
<p style="color: var(--text-muted);">
A modern, production-ready X11 window manager with XFCE-like
functionality and optional AI integration.
</p>
</div>
<div class="footer-section">
<h4>Documentation</h4>
<ul>
<li><a href="documentation.html">Getting Started</a></li>
<li><a href="shortcuts.html">Keyboard Shortcuts</a></li>
<li><a href="configuration.html">Configuration</a></li>
<li><a href="architecture.html">Architecture</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Resources</h4>
<ul>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="ai-features.html">AI Integration</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Community</h4>
<ul>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/issues">Issue Tracker</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/discussions">Discussions</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/CONTRIBUTING.md">Contributing</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/LICENSE">License (MIT)</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>DWN Window Manager by retoor &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
+520
View File
@@ -0,0 +1,520 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Complete configuration guide for DWN window manager - customize colors, behavior, and more.">
<title>Configuration - DWN Window Manager</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header>
<nav>
<a href="index.html" class="logo">
<span class="logo-icon">D</span>
<span>DWN</span>
</a>
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Install</a></li>
<li class="dropdown">
<a href="documentation.html" class="active">Docs</a>
<div class="dropdown-menu">
<a href="documentation.html">Getting Started</a>
<a href="shortcuts.html">Keyboard Shortcuts</a>
<a href="configuration.html">Configuration</a>
<a href="ai-features.html">AI Features</a>
<a href="architecture.html">Architecture</a>
</div>
</li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
<div class="nav-toggle" onclick="toggleNav()">
<span></span>
<span></span>
<span></span>
</div>
</nav>
</header>
<main>
<section class="hero" style="padding: 8rem 0 4rem;">
<div class="container hero-content">
<h1>Configuration Guide</h1>
<p class="subtitle">
Customize every aspect of DWN to match your workflow and style.
</p>
</div>
</section>
<section class="section">
<div class="container">
<h2>Configuration File</h2>
<p>
DWN reads its configuration from <code>~/.config/dwn/config</code> using an INI-style format.
Changes take effect on restart (or you can reload in a future version).
</p>
<div class="alert alert-info">
<strong class="alert-title">First Run</strong>
<p style="margin: 0;">DWN creates a default configuration file on first run if one doesn't exist.
You can also copy the example config from the source repository.</p>
</div>
<!-- General Section -->
<h2 id="general" style="margin-top: 3rem;">[general] - Core Settings</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Basic behavior settings for applications and focus handling.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>terminal</code></td>
<td><code>xfce4-terminal</code></td>
<td>Terminal emulator launched with Ctrl+Alt+T</td>
</tr>
<tr>
<td><code>launcher</code></td>
<td><code>dmenu_run</code></td>
<td>Application launcher for Alt+F2</td>
</tr>
<tr>
<td><code>file_manager</code></td>
<td><code>thunar</code></td>
<td>File manager for Super+E</td>
</tr>
<tr>
<td><code>focus_mode</code></td>
<td><code>click</code></td>
<td><code>click</code> or <code>follow</code> (sloppy focus)</td>
</tr>
<tr>
<td><code>decorations</code></td>
<td><code>true</code></td>
<td>Show window title bars and borders</td>
</tr>
</tbody>
</table>
</div>
<div class="code-header">
<span>Example</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[general]
terminal = alacritty
launcher = rofi -show run
file_manager = nautilus
focus_mode = click
decorations = true</code></pre>
<!-- Appearance Section -->
<h2 id="appearance" style="margin-top: 3rem;">[appearance] - Visual Settings</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Control the visual appearance of windows, panels, and gaps.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>border_width</code></td>
<td><code>2</code></td>
<td>0-50</td>
<td>Window border width in pixels</td>
</tr>
<tr>
<td><code>title_height</code></td>
<td><code>24</code></td>
<td>0-100</td>
<td>Title bar height in pixels</td>
</tr>
<tr>
<td><code>panel_height</code></td>
<td><code>28</code></td>
<td>0-100</td>
<td>Top/bottom panel height</td>
</tr>
<tr>
<td><code>gap</code></td>
<td><code>4</code></td>
<td>0-100</td>
<td>Gap between tiled windows</td>
</tr>
<tr>
<td><code>font</code></td>
<td><code>fixed</code></td>
<td>-</td>
<td>X11 font name for text</td>
</tr>
</tbody>
</table>
</div>
<div class="code-header">
<span>Example</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[appearance]
border_width = 2
title_height = 28
panel_height = 32
gap = 8
font = DejaVu Sans-10</code></pre>
<!-- Layout Section -->
<h2 id="layout" style="margin-top: 3rem;">[layout] - Layout Behavior</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Configure the default layout mode and tiling parameters.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Range</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>default</code></td>
<td><code>tiling</code></td>
<td>-</td>
<td><code>tiling</code>, <code>floating</code>, or <code>monocle</code></td>
</tr>
<tr>
<td><code>master_ratio</code></td>
<td><code>0.55</code></td>
<td>0.1-0.9</td>
<td>Portion of screen for master area</td>
</tr>
<tr>
<td><code>master_count</code></td>
<td><code>1</code></td>
<td>1-10</td>
<td>Number of windows in master area</td>
</tr>
</tbody>
</table>
</div>
<div class="code-header">
<span>Example</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[layout]
default = tiling
master_ratio = 0.60
master_count = 1</code></pre>
<!-- Panels Section -->
<h2 id="panels" style="margin-top: 3rem;">[panels] - Panel Visibility</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Control which panels are displayed.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>top</code></td>
<td><code>true</code></td>
<td>Show top panel (workspaces, taskbar, systray)</td>
</tr>
<tr>
<td><code>bottom</code></td>
<td><code>true</code></td>
<td>Show bottom panel (clock)</td>
</tr>
</tbody>
</table>
</div>
<div class="code-header">
<span>Example - Minimal Setup</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[panels]
top = true
bottom = false</code></pre>
<!-- Colors Section -->
<h2 id="colors" style="margin-top: 3rem;">[colors] - Color Scheme</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Customize all colors using hex format (#RRGGBB).
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Option</th>
<th>Default</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>panel_bg</code></td>
<td><code>#1a1a2e</code></td>
<td>Panel background color</td>
</tr>
<tr>
<td><code>panel_fg</code></td>
<td><code>#e0e0e0</code></td>
<td>Panel text color</td>
</tr>
<tr>
<td><code>workspace_active</code></td>
<td><code>#4a90d9</code></td>
<td>Active workspace indicator</td>
</tr>
<tr>
<td><code>workspace_inactive</code></td>
<td><code>#3a3a4e</code></td>
<td>Inactive workspace indicator</td>
</tr>
<tr>
<td><code>workspace_urgent</code></td>
<td><code>#d94a4a</code></td>
<td>Urgent workspace indicator</td>
</tr>
<tr>
<td><code>title_focused_bg</code></td>
<td><code>#2d3a4a</code></td>
<td>Focused window title background</td>
</tr>
<tr>
<td><code>title_focused_fg</code></td>
<td><code>#ffffff</code></td>
<td>Focused window title text</td>
</tr>
<tr>
<td><code>title_unfocused_bg</code></td>
<td><code>#1a1a1a</code></td>
<td>Unfocused window title background</td>
</tr>
<tr>
<td><code>title_unfocused_fg</code></td>
<td><code>#808080</code></td>
<td>Unfocused window title text</td>
</tr>
<tr>
<td><code>border_focused</code></td>
<td><code>#4a90d9</code></td>
<td>Focused window border</td>
</tr>
<tr>
<td><code>border_unfocused</code></td>
<td><code>#333333</code></td>
<td>Unfocused window border</td>
</tr>
<tr>
<td><code>notification_bg</code></td>
<td><code>#2a2a3e</code></td>
<td>Notification background</td>
</tr>
<tr>
<td><code>notification_fg</code></td>
<td><code>#ffffff</code></td>
<td>Notification text</td>
</tr>
</tbody>
</table>
</div>
<div class="code-header">
<span>Example - Nord Theme</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[colors]
panel_bg = #2e3440
panel_fg = #eceff4
workspace_active = #88c0d0
workspace_inactive = #4c566a
workspace_urgent = #bf616a
title_focused_bg = #3b4252
title_focused_fg = #eceff4
title_unfocused_bg = #2e3440
title_unfocused_fg = #4c566a
border_focused = #88c0d0
border_unfocused = #3b4252
notification_bg = #3b4252
notification_fg = #eceff4</code></pre>
<!-- AI Section -->
<h2 id="ai" style="margin-top: 3rem;">[ai] - AI Integration</h2>
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
Configure AI features. See <a href="ai-features.html">AI Features</a> for full setup instructions.
</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Option</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>model</code></td>
<td>OpenRouter model ID (e.g., <code>google/gemini-2.0-flash-exp:free</code>)</td>
</tr>
<tr>
<td><code>openrouter_api_key</code></td>
<td>Your OpenRouter API key (or use environment variable)</td>
</tr>
<tr>
<td><code>exa_api_key</code></td>
<td>Your Exa API key (or use environment variable)</td>
</tr>
</tbody>
</table>
</div>
<div class="code-header">
<span>Example</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code>[ai]
model = google/gemini-2.0-flash-exp:free
openrouter_api_key = sk-or-v1-your-key-here
exa_api_key = your-exa-key-here</code></pre>
<div class="alert alert-warning" style="margin-top: 1rem;">
<strong class="alert-title">Security Note</strong>
<p style="margin: 0;">For better security, use environment variables instead of storing API keys in the config file:
<code>export OPENROUTER_API_KEY=sk-or-v1-...</code></p>
</div>
<!-- Complete Example -->
<h2 style="margin-top: 3rem;">Complete Configuration Example</h2>
<div class="code-header">
<span>~/.config/dwn/config</span>
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
</div>
<pre><code># DWN Window Manager Configuration
# https://dwn.github.io
[general]
terminal = alacritty
launcher = rofi -show drun
file_manager = thunar
focus_mode = click
decorations = true
[appearance]
border_width = 2
title_height = 24
panel_height = 28
gap = 6
font = DejaVu Sans-10
[layout]
default = tiling
master_ratio = 0.55
master_count = 1
[panels]
top = true
bottom = true
[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
[ai]
model = google/gemini-2.0-flash-exp:free
# API keys via environment variables recommended</code></pre>
</div>
</section>
</main>
<footer>
<div class="container">
<div class="footer-grid">
<div class="footer-section">
<h4>DWN Window Manager</h4>
<p style="color: var(--text-muted);">
A modern, production-ready X11 window manager with XFCE-like
functionality and optional AI integration.
</p>
</div>
<div class="footer-section">
<h4>Documentation</h4>
<ul>
<li><a href="documentation.html">Getting Started</a></li>
<li><a href="shortcuts.html">Keyboard Shortcuts</a></li>
<li><a href="configuration.html">Configuration</a></li>
<li><a href="architecture.html">Architecture</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Resources</h4>
<ul>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="ai-features.html">AI Integration</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Community</h4>
<ul>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/issues">Issue Tracker</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/discussions">Discussions</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/CONTRIBUTING.md">Contributing</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/LICENSE">License (MIT)</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>DWN Window Manager by retoor &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
+1085
View File
File diff suppressed because it is too large Load Diff
+425
View File
@@ -0,0 +1,425 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Getting started with DWN window manager - learn the basics and become productive quickly.">
<title>Documentation - DWN Window Manager</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header>
<nav>
<a href="index.html" class="logo">
<span class="logo-icon">D</span>
<span>DWN</span>
</a>
<ul class="nav-links">
<li><a href="index.html">Home</a></li>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Install</a></li>
<li class="dropdown">
<a href="documentation.html" class="active">Docs</a>
<div class="dropdown-menu">
<a href="documentation.html">Getting Started</a>
<a href="shortcuts.html">Keyboard Shortcuts</a>
<a href="configuration.html">Configuration</a>
<a href="ai-features.html">AI Features</a>
<a href="architecture.html">Architecture</a>
</div>
</li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
<div class="nav-toggle" onclick="toggleNav()">
<span></span>
<span></span>
<span></span>
</div>
</nav>
</header>
<div class="docs-layout container">
<aside class="docs-sidebar">
<ul>
<li>
<span class="section-title">Getting Started</span>
<ul>
<li><a href="#introduction" class="active">Introduction</a></li>
<li><a href="#first-steps">First Steps</a></li>
<li><a href="#basic-concepts">Basic Concepts</a></li>
<li><a href="#tutorial">Interactive Tutorial</a></li>
</ul>
</li>
<li>
<span class="section-title">Core Usage</span>
<ul>
<li><a href="#windows">Managing Windows</a></li>
<li><a href="#workspaces">Using Workspaces</a></li>
<li><a href="#layouts">Layout Modes</a></li>
<li><a href="#panels">Panels & Systray</a></li>
</ul>
</li>
<li>
<span class="section-title">Reference</span>
<ul>
<li><a href="shortcuts.html">Keyboard Shortcuts</a></li>
<li><a href="configuration.html">Configuration</a></li>
<li><a href="ai-features.html">AI Features</a></li>
<li><a href="architecture.html">Architecture</a></li>
</ul>
</li>
</ul>
</aside>
<main class="docs-content">
<h1 id="introduction">Getting Started with DWN</h1>
<p class="lead">
Learn the fundamentals of DWN and become productive in minutes.
</p>
<h2 id="first-steps">First Steps</h2>
<p>
After <a href="installation.html">installing DWN</a> and starting your session,
you'll see a clean desktop with two panels: a top panel with workspace indicators,
taskbar, and system tray, and a bottom panel showing the clock.
</p>
<h3>Opening Your First Application</h3>
<p>Start by launching a terminal and application launcher:</p>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>T</kbd></td>
<td>Open terminal</td>
</tr>
<tr>
<td><kbd>Alt</kbd> + <kbd>F2</kbd></td>
<td>Open application launcher (dmenu/rofi)</td>
</tr>
<tr>
<td><kbd>Super</kbd> + <kbd>E</kbd></td>
<td>Open file manager</td>
</tr>
<tr>
<td><kbd>Super</kbd> + <kbd>B</kbd></td>
<td>Open web browser</td>
</tr>
</tbody>
</table>
</div>
<div class="alert alert-info">
<strong class="alert-title">Tip: Run the Tutorial</strong>
<p style="margin: 0;">Press <kbd>Super</kbd> + <kbd>T</kbd> to start an interactive
tutorial that will guide you through all essential shortcuts.</p>
</div>
<h2 id="basic-concepts">Basic Concepts</h2>
<h3>The Super Key</h3>
<p>
Most DWN shortcuts use the <kbd>Super</kbd> key (often the Windows key or Command key).
This keeps shortcuts separate from application shortcuts that typically use
<kbd>Ctrl</kbd> or <kbd>Alt</kbd>.
</p>
<h3>Focus Model</h3>
<p>
By default, DWN uses "click to focus" - you click on a window to focus it.
You can change this to "focus follows mouse" (sloppy focus) in the configuration.
</p>
<h3>Window Decorations</h3>
<p>
Each window has a title bar showing its name. The title bar color indicates focus:
</p>
<ul>
<li><strong>Blue title bar</strong> - Focused window</li>
<li><strong>Gray title bar</strong> - Unfocused window</li>
</ul>
<h2 id="tutorial">Interactive Tutorial</h2>
<p>
DWN includes a built-in interactive tutorial that teaches you essential shortcuts
step by step. The tutorial:
</p>
<ul>
<li>Shows instructions for each shortcut</li>
<li>Waits for you to press the correct keys</li>
<li>Automatically advances when you complete each step</li>
<li>Covers all essential shortcuts from basic to advanced</li>
</ul>
<div class="card">
<h3>Start the Tutorial</h3>
<p>Press <kbd>Super</kbd> + <kbd>T</kbd> at any time to start or resume the tutorial.</p>
</div>
<h2 id="windows">Managing Windows</h2>
<h3>Window Operations</h3>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><kbd>Alt</kbd> + <kbd>F4</kbd></td>
<td>Close focused window</td>
</tr>
<tr>
<td><kbd>Alt</kbd> + <kbd>Tab</kbd></td>
<td>Cycle to next window</td>
</tr>
<tr>
<td><kbd>Alt</kbd> + <kbd>Shift</kbd> + <kbd>Tab</kbd></td>
<td>Cycle to previous window</td>
</tr>
<tr>
<td><kbd>Alt</kbd> + <kbd>F10</kbd></td>
<td>Toggle maximize</td>
</tr>
<tr>
<td><kbd>Alt</kbd> + <kbd>F11</kbd></td>
<td>Toggle fullscreen</td>
</tr>
<tr>
<td><kbd>Super</kbd> + <kbd>F9</kbd></td>
<td>Toggle floating for current window</td>
</tr>
</tbody>
</table>
</div>
<h3>Moving and Resizing</h3>
<p>In floating mode, you can move and resize windows with the mouse:</p>
<ul>
<li><strong>Move</strong> - Click and drag the title bar</li>
<li><strong>Resize</strong> - Drag any window edge or corner</li>
</ul>
<h2 id="workspaces">Using Workspaces</h2>
<p>
DWN provides 9 virtual workspaces to organize your windows. You can see which
workspaces are active in the top panel.
</p>
<h3>Workspace Navigation</h3>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><kbd>F1</kbd> - <kbd>F9</kbd></td>
<td>Switch to workspace 1-9</td>
</tr>
<tr>
<td><kbd>Shift</kbd> + <kbd>F1</kbd> - <kbd>F9</kbd></td>
<td>Move window to workspace 1-9</td>
</tr>
<tr>
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Right</kbd></td>
<td>Next workspace</td>
</tr>
<tr>
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Left</kbd></td>
<td>Previous workspace</td>
</tr>
</tbody>
</table>
</div>
<h3>Workspace Organization Tips</h3>
<ul>
<li><strong>Workspace 1</strong> - Main work (editor, terminal)</li>
<li><strong>Workspace 2</strong> - Web browser, documentation</li>
<li><strong>Workspace 3</strong> - Communication (email, chat)</li>
<li><strong>Workspace 4-9</strong> - Project-specific contexts</li>
</ul>
<h2 id="layouts">Layout Modes</h2>
<p>
DWN supports three layout modes. Press <kbd>Super</kbd> + <kbd>Space</kbd> to cycle
through them.
</p>
<div class="features-grid" style="grid-template-columns: repeat(3, 1fr); gap: 1rem; margin: 1.5rem 0;">
<div class="card" style="padding: 1.5rem;">
<h4>Tiling</h4>
<p style="color: var(--text-muted); font-size: 0.9rem;">
Windows automatically arranged in master-stack layout.
Perfect for development workflows.
</p>
</div>
<div class="card" style="padding: 1.5rem;">
<h4>Floating</h4>
<p style="color: var(--text-muted); font-size: 0.9rem;">
Traditional overlapping windows.
Move and resize freely.
</p>
</div>
<div class="card" style="padding: 1.5rem;">
<h4>Monocle</h4>
<p style="color: var(--text-muted); font-size: 0.9rem;">
One fullscreen window at a time.
Great for focused work.
</p>
</div>
</div>
<h3>Tiling Layout Controls</h3>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><kbd>Super</kbd> + <kbd>H</kbd></td>
<td>Shrink master area</td>
</tr>
<tr>
<td><kbd>Super</kbd> + <kbd>L</kbd></td>
<td>Expand master area</td>
</tr>
<tr>
<td><kbd>Super</kbd> + <kbd>I</kbd></td>
<td>Increase master window count</td>
</tr>
<tr>
<td><kbd>Super</kbd> + <kbd>D</kbd></td>
<td>Decrease master window count</td>
</tr>
</tbody>
</table>
</div>
<h2 id="panels">Panels & System Tray</h2>
<h3>Top Panel</h3>
<p>The top panel contains:</p>
<ul>
<li><strong>Workspace indicators</strong> - Click to switch, highlighted when occupied</li>
<li><strong>Taskbar</strong> - Shows windows on current workspace</li>
<li><strong>System tray</strong> - Battery, volume, WiFi (see below)</li>
</ul>
<h3>System Tray</h3>
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Indicator</th>
<th>Click</th>
<th>Right-click</th>
<th>Scroll</th>
</tr>
</thead>
<tbody>
<tr>
<td>Volume</td>
<td>Show slider</td>
<td>Toggle mute</td>
<td>Adjust volume</td>
</tr>
<tr>
<td>WiFi</td>
<td>Show networks</td>
<td>Disconnect</td>
<td>-</td>
</tr>
<tr>
<td>Battery</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
<h3>Bottom Panel</h3>
<p>
The bottom panel shows the current time and a scrolling news ticker. Navigate
news articles with <kbd>Super</kbd> + <kbd>Up</kbd>/<kbd>Down</kbd> and open
the current article with <kbd>Super</kbd> + <kbd>Return</kbd>. Both panels can
be hidden in the configuration if you prefer a minimal setup.
</p>
<h2>Next Steps</h2>
<p>Now that you know the basics, explore these topics:</p>
<ul>
<li><a href="shortcuts.html">Complete Keyboard Shortcuts Reference</a></li>
<li><a href="configuration.html">Customizing DWN</a></li>
<li><a href="ai-features.html">Using AI Features</a></li>
</ul>
</main>
</div>
<footer>
<div class="container">
<div class="footer-grid">
<div class="footer-section">
<h4>DWN Window Manager</h4>
<p style="color: var(--text-muted);">
A modern, production-ready X11 window manager with XFCE-like
functionality and optional AI integration.
</p>
</div>
<div class="footer-section">
<h4>Documentation</h4>
<ul>
<li><a href="documentation.html">Getting Started</a></li>
<li><a href="shortcuts.html">Keyboard Shortcuts</a></li>
<li><a href="configuration.html">Configuration</a></li>
<li><a href="architecture.html">Architecture</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Resources</h4>
<ul>
<li><a href="features.html">Features</a></li>
<li><a href="installation.html">Installation</a></li>
<li><a href="ai-features.html">AI Integration</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Git</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Community</h4>
<ul>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/issues">Issue Tracker</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/discussions">Discussions</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/CONTRIBUTING.md">Contributing</a></li>
<li><a href="https://retoor.molodetz.nl/retoor/dwn/blob/main/LICENSE">License (MIT)</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>DWN Window Manager by retoor &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More