Compare commits
34
Commits
beda6ec573
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3073db38f1 | ||
|
|
a6ff84dfca | ||
|
|
cd9e55dc64 | ||
|
|
8e86b2d106 | ||
|
|
a462643110 | ||
|
|
1ba5fae79b | ||
|
|
5404adfcd5 | ||
|
|
2303b53c70 | ||
|
|
a0d5b1b091 | ||
|
|
2f096a55ac | ||
|
|
915870875d | ||
|
|
725267fa00 | ||
|
|
ce6729c53c | ||
|
|
eaa41d70ea | ||
|
|
fb28931535 | ||
|
|
51c3d4b919 | ||
|
|
c32bda853c | ||
|
|
a6eeeb978e | ||
|
|
6cd3a50f37 | ||
|
|
1724f17e92 | ||
|
|
115c69f1bc | ||
|
|
daaef6fa17 | ||
|
|
eb492cd5ff | ||
|
|
4793d372ac | ||
|
|
54565b21bd | ||
|
|
8a5454fda0 | ||
|
|
91cfecb6e5 | ||
|
|
ab36b7dd2c | ||
|
|
64f9761116 | ||
|
|
3364464b43 | ||
|
|
c927e23f23 | ||
|
|
dc522d5912 | ||
|
|
41fc47abac | ||
|
|
21e8bcc88a |
@@ -3,8 +3,9 @@
|
||||
|
||||
# Compiler settings
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -O2 -I./include
|
||||
LDFLAGS = -lX11 -lXext -lXinerama -lXrandr -lXft -lfontconfig -ldbus-1 -lcurl -lm -lpthread
|
||||
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
|
||||
|
||||
# Directories
|
||||
SRC_DIR = src
|
||||
@@ -12,9 +13,9 @@ INC_DIR = include
|
||||
BUILD_DIR = build
|
||||
BIN_DIR = bin
|
||||
|
||||
# Find all source files automatically
|
||||
SRCS = $(wildcard $(SRC_DIR)/*.c)
|
||||
OBJS = $(SRCS:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)
|
||||
# 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))
|
||||
DEPS = $(OBJS:.o=.d)
|
||||
|
||||
# Output binary
|
||||
@@ -27,8 +28,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 2>/dev/null)
|
||||
PKG_LIBS := $(shell pkg-config --libs x11 xext xinerama xrandr xft fontconfig dbus-1 2>/dev/null)
|
||||
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)
|
||||
|
||||
# Use pkg-config if available
|
||||
ifneq ($(PKG_LIBS),)
|
||||
@@ -42,7 +43,7 @@ endif
|
||||
# MAIN TARGETS
|
||||
# =============================================================================
|
||||
|
||||
.PHONY: all help clean install uninstall debug run test deps check-deps
|
||||
.PHONY: all help clean install uninstall debug sanitize run test deps check-deps
|
||||
|
||||
# Default target - show help if first time, otherwise build
|
||||
all: check-deps $(TARGET)
|
||||
@@ -69,6 +70,7 @@ 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"
|
||||
@@ -76,6 +78,11 @@ 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"
|
||||
@@ -91,6 +98,13 @@ 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..."
|
||||
@@ -99,6 +113,7 @@ $(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
|
||||
@@ -119,7 +134,7 @@ $(BIN_DIR):
|
||||
install: $(TARGET)
|
||||
@echo "Installing DWN..."
|
||||
@install -Dm755 $(TARGET) $(DESTDIR)$(BINDIR)/dwn
|
||||
@install -Dm644 scripts/dwn.desktop $(DESTDIR)$(DATADIR)/xsessions/dwn.desktop
|
||||
@install -Dm644 examples/dwn.desktop $(DESTDIR)$(DATADIR)/xsessions/dwn.desktop
|
||||
@mkdir -p $(DESTDIR)$(SYSCONFDIR)/dwn
|
||||
@install -Dm644 config/config.example $(DESTDIR)$(SYSCONFDIR)/dwn/config.example
|
||||
@echo ""
|
||||
@@ -195,9 +210,15 @@ 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 ""; \
|
||||
@@ -211,8 +232,13 @@ 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 ""; \
|
||||
@@ -226,8 +252,13 @@ deps:
|
||||
libxext \
|
||||
libxinerama \
|
||||
libxrandr \
|
||||
libxtst \
|
||||
dbus \
|
||||
curl \
|
||||
libpng \
|
||||
tesseract \
|
||||
tesseract-data-eng \
|
||||
leptonica \
|
||||
xorg-server-xephyr \
|
||||
dmenu; \
|
||||
echo ""; \
|
||||
@@ -238,7 +269,7 @@ deps:
|
||||
echo "Please install these packages manually:"; \
|
||||
echo " - GCC and Make"; \
|
||||
echo " - pkg-config"; \
|
||||
echo " - X11, Xext, Xinerama, Xrandr development libraries"; \
|
||||
echo " - X11, Xext, Xinerama, Xrandr, Xtst development libraries"; \
|
||||
echo " - D-Bus development library"; \
|
||||
echo " - libcurl development library"; \
|
||||
echo " - Xephyr (for testing)"; \
|
||||
@@ -246,6 +277,60 @@ 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)
|
||||
# =============================================================================
|
||||
|
||||
@@ -1,269 +1,352 @@
|
||||
# DWN - Desktop Window Manager
|
||||
|
||||
Author: retoor <retoor@molodetz.nl>
|
||||
retoor <retoor@molodetz.nl>
|
||||
|
||||
A lightweight, AI-enhanced window manager for Linux with tiling, floating, and fullscreen layouts.
|
||||
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.
|
||||
|
||||
## Quick Start (Copy & Paste These Commands)
|
||||
## Design Philosophy
|
||||
|
||||
```bash
|
||||
# 1. Install dependencies (enter your password when asked)
|
||||
make deps
|
||||
DWN prioritizes a seamless, distraction-free desktop experience:
|
||||
|
||||
# 2. Build DWN
|
||||
make
|
||||
- **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
|
||||
|
||||
# 3. Test it (opens in a safe window - won't affect your desktop)
|
||||
make run
|
||||
```
|
||||
|
||||
That's it! Press `Super+Backspace` to exit the test window.
|
||||
|
||||
---
|
||||
|
||||
## What is DWN?
|
||||
|
||||
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.
|
||||
|
||||
**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
|
||||
- XDG Autostart support (automatically starts nm-applet, blueman, etc.)
|
||||
- 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
|
||||
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
### Step 1: Install Dependencies
|
||||
|
||||
Run this command (works on Ubuntu, Debian, Fedora, and Arch):
|
||||
|
||||
```bash
|
||||
make deps
|
||||
```
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
You should see "Build successful!" at the end.
|
||||
|
||||
### Step 3: Test (Recommended!)
|
||||
|
||||
Before installing, test DWN in a safe window:
|
||||
|
||||
```bash
|
||||
make run
|
||||
```
|
||||
|
||||
This opens DWN inside a window on your current desktop. You can try it out without changing anything. Press `Super+Backspace` to close it.
|
||||
|
||||
### Step 4: Install
|
||||
|
||||
```bash
|
||||
sudo make install
|
||||
```
|
||||
|
||||
### Step 5: Use DWN
|
||||
|
||||
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!
|
||||
|
||||
---
|
||||
|
||||
## Basic Controls
|
||||
|
||||
### Essential Shortcuts (Memorize These!)
|
||||
|
||||
| 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** |
|
||||
| `Super + Shift + D` | **Demo mode** (automated feature showcase) |
|
||||
|
||||
### 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 |
|
||||
## Features
|
||||
|
||||
### Window Management
|
||||
|
||||
| Keys | What it does |
|
||||
|------|--------------|
|
||||
| `Alt + F9` | Toggle minimize/restore |
|
||||
| `Alt + F10` | Toggle maximize |
|
||||
| `Alt + F11` | Toggle fullscreen |
|
||||
| `Super + F9` | Toggle floating mode |
|
||||
| `Alt + Tab` | Cycle windows forward |
|
||||
| `Alt + Shift + Tab` | Cycle windows backward |
|
||||
**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
|
||||
|
||||
### Layout Control (Super key shortcuts)
|
||||
**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)
|
||||
|
||||
| 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 |
|
||||
| `Super + Left` | Snap window to left half (50% width) |
|
||||
| `Super + Right` | Snap window to right half (50% width) |
|
||||
**Directional Resizing**
|
||||
- Resize windows from any edge or corner
|
||||
- Respects layout bounds and snap constraints
|
||||
|
||||
### AI Features (Optional)
|
||||
**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
|
||||
|
||||
| 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 |
|
||||
### 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
|
||||
|
||||
The bottom panel displays a scrolling news ticker. Navigate articles with these shortcuts:
|
||||
- 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)
|
||||
|
||||
| Keys | What it does |
|
||||
|------|--------------|
|
||||
| `Super + Down` | Next news article |
|
||||
| `Super + Up` | Previous news article |
|
||||
| `Super + Return` | Open current article in browser |
|
||||
### AI Integration
|
||||
|
||||
### Other Shortcuts
|
||||
**Command Palette (Super+Shift+A)**
|
||||
- Natural language command execution
|
||||
- OpenRouter API with configurable model selection
|
||||
- Context-aware responses based on current workspace
|
||||
|
||||
| Keys | What it does |
|
||||
|------|--------------|
|
||||
| `Print` | Take screenshot (xfce4-screenshooter) |
|
||||
**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
|
||||
|
||||
## System Tray
|
||||
### Screenshot and OCR
|
||||
|
||||
The top panel includes a system tray on the right side with support for external application icons plus built-in battery, audio, and WiFi indicators.
|
||||
**Screenshot API**
|
||||
- Fullscreen capture
|
||||
- Active window capture
|
||||
- Area selection capture
|
||||
- Async capture with callbacks
|
||||
- Base64 encoding for API transmission
|
||||
- PNG output
|
||||
|
||||
### XEmbed System Tray (External Application Icons)
|
||||
**OCR API**
|
||||
- Tesseract-based text extraction
|
||||
- Multi-language support
|
||||
- Confidence scoring
|
||||
- Async processing
|
||||
|
||||
DWN implements the freedesktop.org XEmbed System Tray protocol, allowing external applications to dock their status icons in the panel - just like XFCE, GNOME, or KDE. This means applications like:
|
||||
### WebSocket API
|
||||
|
||||
- **Telegram** - Shows notification icon when messages arrive
|
||||
- **nm-applet** - NetworkManager GUI applet
|
||||
- **blueman-applet** - Bluetooth manager
|
||||
- **pasystray** - PulseAudio volume control
|
||||
- **udiskie** - USB device automounter
|
||||
- **clipit/parcellite** - Clipboard managers
|
||||
Programmatic control on port 8777:
|
||||
|
||||
...will automatically appear in your system tray when launched.
|
||||
| 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) |
|
||||
|
||||
Simply start any application with tray icon support and it will dock itself in the panel. Click on icons to interact with them - clicks are forwarded to the application.
|
||||
**Event Subscription**
|
||||
|
||||
### Battery Indicator
|
||||
Subscribe to real-time events including fade changes:
|
||||
|
||||
Shows battery percentage on laptops with color coding:
|
||||
- **Green**: > 50%
|
||||
- **Yellow**: 20-50%
|
||||
- **Red**: < 20%
|
||||
- **Blue**: Charging
|
||||
```bash
|
||||
# Subscribe to fade events
|
||||
python3 examples/dwn_api_client.py subscribe fade_speed_changed fade_intensity_changed
|
||||
|
||||
### WiFi Indicator
|
||||
# Listen for all events
|
||||
python3 examples/dwn_api_client.py listen
|
||||
```
|
||||
|
||||
Located in the top-right corner of the panel, showing your current connection status.
|
||||
**Fade Control Example**
|
||||
|
||||
| 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 |
|
||||
```bash
|
||||
# Get current fade settings
|
||||
python3 examples/dwn_api_client.py fade-settings
|
||||
|
||||
The dropdown shows:
|
||||
- Available WiFi networks with signal strength
|
||||
- `●` marker next to the currently connected network
|
||||
- Networks are sorted and updated automatically
|
||||
# Set fade speed (faster animation)
|
||||
python3 examples/dwn_api_client.py fade-speed 1.5
|
||||
|
||||
**Note:** Requires NetworkManager (`nmcli`) to be installed.
|
||||
# Set fade intensity (dimmer glow)
|
||||
python3 examples/dwn_api_client.py fade-intensity 0.5
|
||||
|
||||
### Audio Indicator
|
||||
# Run interactive demo
|
||||
python3 examples/fade_control_demo.py
|
||||
```
|
||||
|
||||
Shows current volume level next to the WiFi indicator.
|
||||
### Automation
|
||||
|
||||
| Action | What it does |
|
||||
|--------|--------------|
|
||||
| **Left-click** | Toggle mute/unmute |
|
||||
| **Scroll up** | Increase volume by 5% |
|
||||
| **Scroll down** | Decrease volume by 5% |
|
||||
**XDG Autostart**
|
||||
- Scans `/etc/xdg/autostart/` and `~/.config/autostart/`
|
||||
- Parses `.desktop` files
|
||||
- Custom autostart directory support
|
||||
|
||||
**Note:** Requires ALSA utilities (`amixer`) to be installed.
|
||||
**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
|
||||
|
||||
Required libraries (via pkg-config):
|
||||
- X11, Xext, Xinerama, Xrandr, Xft, Xi
|
||||
- fontconfig
|
||||
- libdbus-1
|
||||
- libcurl
|
||||
- libpng
|
||||
- tesseract (optional, for OCR)
|
||||
|
||||
### Build
|
||||
|
||||
```bash
|
||||
make deps # Auto-install dependencies (apt/dnf/pacman)
|
||||
make # Build with -O2 optimization
|
||||
make install # Install to /usr/local/bin (PREFIX configurable)
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
make run # Launch in nested Xephyr window
|
||||
```
|
||||
|
||||
### Build Targets
|
||||
|
||||
| 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 |
|
||||
|
||||
## Configuration
|
||||
|
||||
DWN can be customized by editing a config file.
|
||||
Configuration file: `~/.config/dwn/config` (INI format)
|
||||
|
||||
### Create Your Config
|
||||
### General
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/dwn
|
||||
cp /etc/dwn/config.example ~/.config/dwn/config
|
||||
```
|
||||
|
||||
### Edit Your Config
|
||||
|
||||
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
|
||||
terminal = xfce4-terminal
|
||||
launcher = dmenu_run
|
||||
file_manager = thunar
|
||||
focus_mode = click # click or follow
|
||||
focus_follow_delay = 100 # 0-1000ms
|
||||
decorations = true
|
||||
```
|
||||
|
||||
**Change layout:**
|
||||
```ini
|
||||
[layout]
|
||||
default = floating
|
||||
```
|
||||
Options: `tiling`, `floating`, `monocle`
|
||||
### Appearance
|
||||
|
||||
**Change window gaps:**
|
||||
```ini
|
||||
[appearance]
|
||||
gap = 10
|
||||
border_width = 2
|
||||
border_width = 0 # 0-50px
|
||||
title_height = 28 # 0-100px
|
||||
panel_height = 32 # 0-100px
|
||||
gap = 0 # 0-100px
|
||||
font = fixed
|
||||
```
|
||||
|
||||
**Configure autostart:**
|
||||
### 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
|
||||
```
|
||||
|
||||
API keys:
|
||||
- OpenRouter: https://openrouter.ai/keys
|
||||
- Exa: https://dashboard.exa.ai/api-keys
|
||||
|
||||
### Autostart
|
||||
|
||||
```ini
|
||||
[autostart]
|
||||
enabled = true
|
||||
@@ -271,161 +354,273 @@ xdg_autostart = true
|
||||
path = ~/.config/dwn/autostart.d
|
||||
```
|
||||
|
||||
DWN automatically starts applications from XDG autostart directories:
|
||||
- `/etc/xdg/autostart/` - System apps (nm-applet, blueman, etc.)
|
||||
- `~/.config/autostart/` - User apps
|
||||
- `~/.config/dwn/autostart.d/` - DWN-specific symlinks
|
||||
### API
|
||||
|
||||
To add custom autostart apps:
|
||||
```bash
|
||||
mkdir -p ~/.config/dwn/autostart.d
|
||||
ln -s /usr/bin/telegram-desktop ~/.config/dwn/autostart.d/
|
||||
```ini
|
||||
[api]
|
||||
enabled = true
|
||||
port = 8777
|
||||
```
|
||||
|
||||
---
|
||||
### Demo
|
||||
|
||||
## 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
|
||||
```ini
|
||||
[demo]
|
||||
step_delay = 4000 # 1000-30000ms
|
||||
ai_timeout = 15000 # 5000-60000ms
|
||||
window_timeout = 5000 # 1000-30000ms
|
||||
```
|
||||
|
||||
4. Make sure dmenu is installed: `sudo apt install dmenu`
|
||||
## Keyboard Shortcuts
|
||||
|
||||
### Exa Semantic Search Setup
|
||||
### Application Launchers
|
||||
|
||||
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:
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| `Ctrl+Alt+T` | Terminal |
|
||||
| `Super` / `Alt+F2` | Application launcher |
|
||||
| `Super+E` | File manager |
|
||||
| `Super+B` | Web browser |
|
||||
| `Print` | Screenshot |
|
||||
|
||||
```bash
|
||||
echo 'export EXA_API_KEY="your-key-here"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
### 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 |
|
||||
|
||||
### Workspace Navigation
|
||||
|
||||
| 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 |
|
||||
|
||||
### Layout Control
|
||||
|
||||
| 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 |
|
||||
|
||||
### Window Snapping
|
||||
|
||||
| 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%) |
|
||||
|
||||
### AI Features
|
||||
|
||||
| Shortcut | Action |
|
||||
|----------|--------|
|
||||
| `Super+A` | Context analysis |
|
||||
| `Super+Shift+A` | Command palette |
|
||||
| `Super+Shift+E` | Exa semantic search |
|
||||
|
||||
### News and Help
|
||||
|
||||
| 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
|
||||
|
||||
### Module Structure
|
||||
|
||||
| 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 |
|
||||
|
||||
### New Abstraction Layer (v2.0)
|
||||
|
||||
DWN now includes a modern abstraction layer for future extensibility:
|
||||
|
||||
**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
|
||||
|
||||
**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
|
||||
|
||||
**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 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
|
||||
|
||||
### Design Patterns
|
||||
|
||||
**Encapsulation**
|
||||
- Opaque pointer types hide internal structures
|
||||
- Header exposes only public API
|
||||
- Implementation details remain private
|
||||
|
||||
**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
|
||||
|
||||
### Design Patterns
|
||||
|
||||
**Encapsulation**
|
||||
- Opaque pointer types hide internal structures
|
||||
- Header exposes only public API
|
||||
- Implementation details remain private
|
||||
|
||||
**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()
|
||||
```
|
||||
|
||||
### Usage
|
||||
### Response Format
|
||||
|
||||
- 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
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"data": {
|
||||
"clients": [
|
||||
{
|
||||
"window": 12345678,
|
||||
"title": "Firefox",
|
||||
"class": "firefox",
|
||||
"workspace": 1,
|
||||
"x": 0, "y": 32,
|
||||
"width": 960, "height": 540,
|
||||
"focused": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### "X11 libraries not found" or build errors
|
||||
## Project Structure
|
||||
|
||||
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
|
||||
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
|
||||
```
|
||||
|
||||
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 - 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/)
|
||||
MIT License - see LICENSE file.
|
||||
|
||||
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
-1
@@ -19,7 +19,7 @@ build/client.o: src/client.c include/client.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/layout.h \
|
||||
include/panel.h
|
||||
include/panel.h include/api.h include/rules.h include/marks.h
|
||||
include/client.h:
|
||||
include/dwn.h:
|
||||
include/atoms.h:
|
||||
@@ -48,3 +48,6 @@ include/notifications.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:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+3
-1
@@ -1,7 +1,9 @@
|
||||
build/decorations.o: src/decorations.c include/decorations.h \
|
||||
include/dwn.h include/client.h include/config.h include/util.h
|
||||
include/dwn.h include/client.h include/config.h include/util.h \
|
||||
include/workspace.h
|
||||
include/decorations.h:
|
||||
include/dwn.h:
|
||||
include/client.h:
|
||||
include/config.h:
|
||||
include/util.h:
|
||||
include/workspace.h:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+4
-1
@@ -19,7 +19,7 @@ build/keys.o: src/keys.c include/keys.h include/dwn.h include/client.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/layout.h include/api.h include/marks.h include/panel.h
|
||||
include/keys.h:
|
||||
include/dwn.h:
|
||||
include/client.h:
|
||||
@@ -51,3 +51,6 @@ include/applauncher.h:
|
||||
include/decorations.h:
|
||||
include/demo.h:
|
||||
include/layout.h:
|
||||
include/api.h:
|
||||
include/marks.h:
|
||||
include/panel.h:
|
||||
|
||||
Binary file not shown.
+40
-1
@@ -1,8 +1,47 @@
|
||||
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/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/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:
|
||||
|
||||
Binary file not shown.
+20
-2
@@ -19,8 +19,12 @@ 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/news.h include/applauncher.h include/ai.h include/autostart.h \
|
||||
include/demo.h include/util.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/dwn.h:
|
||||
include/config.h:
|
||||
include/dwn.h:
|
||||
@@ -51,9 +55,23 @@ 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:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -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/util.h include/api.h
|
||||
include/notifications.h:
|
||||
include/dwn.h:
|
||||
/usr/include/dbus-1.0/dbus/dbus.h:
|
||||
@@ -40,3 +40,4 @@ 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.
+3
-1
@@ -1,6 +1,7 @@
|
||||
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/news.h
|
||||
include/util.h include/atoms.h include/systray.h include/slider.h \
|
||||
include/news.h
|
||||
include/panel.h:
|
||||
include/dwn.h:
|
||||
include/workspace.h:
|
||||
@@ -10,4 +11,5 @@ include/config.h:
|
||||
include/util.h:
|
||||
include/atoms.h:
|
||||
include/systray.h:
|
||||
include/slider.h:
|
||||
include/news.h:
|
||||
|
||||
Binary file not shown.
+5
-3
@@ -1,6 +1,6 @@
|
||||
build/systray.o: src/systray.c include/systray.h include/dwn.h \
|
||||
include/panel.h include/config.h include/util.h include/notifications.h \
|
||||
/usr/include/dbus-1.0/dbus/dbus.h \
|
||||
include/slider.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,9 +17,10 @@ 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
|
||||
/usr/include/dbus-1.0/dbus/dbus-threads.h include/atoms.h include/api.h
|
||||
include/systray.h:
|
||||
include/dwn.h:
|
||||
include/slider.h:
|
||||
include/panel.h:
|
||||
include/config.h:
|
||||
include/util.h:
|
||||
@@ -43,3 +44,4 @@ include/notifications.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:
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+3
-1
@@ -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/config.h include/panel.h include/api.h include/decorations.h
|
||||
include/workspace.h:
|
||||
include/dwn.h:
|
||||
include/client.h:
|
||||
@@ -9,3 +9,5 @@ include/atoms.h:
|
||||
include/util.h:
|
||||
include/config.h:
|
||||
include/panel.h:
|
||||
include/api.h:
|
||||
include/decorations.h:
|
||||
|
||||
Binary file not shown.
@@ -18,17 +18,17 @@ focus_mode = click
|
||||
decorations = true
|
||||
|
||||
[appearance]
|
||||
# Border width in pixels (default: 2)
|
||||
border_width = 2
|
||||
# Border width in pixels (default: 0)
|
||||
border_width = 0
|
||||
|
||||
# Title bar height in pixels (default: 24)
|
||||
title_height = 24
|
||||
# Title bar height in pixels (default: 28)
|
||||
title_height = 28
|
||||
|
||||
# Panel height in pixels (default: 28)
|
||||
panel_height = 28
|
||||
# Panel height in pixels (default: 32)
|
||||
panel_height = 32
|
||||
|
||||
# Gap between windows in pixels (default: 4)
|
||||
gap = 4
|
||||
# Gap between windows in pixels (default: 0)
|
||||
gap = 0
|
||||
|
||||
# Font for titles and panels (default: fixed)
|
||||
# Use xlsfonts to list available fonts
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,188 @@
|
||||
<!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>
|
||||
@@ -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,7 +10,6 @@ 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)
|
||||
@@ -22,9 +21,6 @@ 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 &
|
||||
@@ -38,5 +34,4 @@ fi
|
||||
# Optional: Start clipboard manager
|
||||
# xfce4-clipman &
|
||||
|
||||
# Start DWN
|
||||
exec dwn
|
||||
@@ -10,6 +10,11 @@
|
||||
#include "dwn.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
/* Forward declarations for libcurl types */
|
||||
struct curl_slist;
|
||||
struct Curl_easy;
|
||||
typedef struct Curl_easy DWN_CURL;
|
||||
|
||||
typedef enum {
|
||||
AI_STATE_IDLE,
|
||||
AI_STATE_PENDING,
|
||||
@@ -24,6 +29,8 @@ 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;
|
||||
|
||||
typedef struct {
|
||||
@@ -74,6 +81,8 @@ typedef struct ExaRequest {
|
||||
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);
|
||||
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -110,6 +110,7 @@ extern ICCCMAtoms icccm;
|
||||
extern MiscAtoms misc_atoms;
|
||||
|
||||
void atoms_init(Display *display);
|
||||
void atoms_cleanup(void);
|
||||
|
||||
void atoms_setup_ewmh(void);
|
||||
void atoms_update_client_list(void);
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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 */
|
||||
+6
-1
@@ -18,7 +18,7 @@ void client_unmanage(Client *client);
|
||||
Client *client_find_by_window(Window window);
|
||||
Client *client_find_by_frame(Window frame);
|
||||
|
||||
void client_focus(Client *client);
|
||||
void client_focus(Client *client, bool update_mru);
|
||||
void client_unfocus(Client *client);
|
||||
void client_raise(Client *client);
|
||||
void client_lower(Client *client);
|
||||
@@ -70,4 +70,9 @@ 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
|
||||
|
||||
@@ -60,9 +60,31 @@ struct Config {
|
||||
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];
|
||||
};
|
||||
|
||||
Config *config_create(void);
|
||||
@@ -70,6 +92,7 @@ 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);
|
||||
|
||||
const char *config_get_terminal(void);
|
||||
const char *config_get_launcher(void);
|
||||
@@ -78,6 +101,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);
|
||||
|
||||
typedef void (*ConfigCallback)(const char *section, const char *key,
|
||||
const char *value, void *user_data);
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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 */
|
||||
+79
-5
@@ -25,10 +25,15 @@
|
||||
#define MAX_NOTIFICATIONS 32
|
||||
#define MAX_KEYBINDINGS 64
|
||||
|
||||
#define DEFAULT_BORDER_WIDTH 2
|
||||
#define DEFAULT_TITLE_HEIGHT 24
|
||||
#define DEFAULT_PANEL_HEIGHT 28
|
||||
#define DEFAULT_GAP 4
|
||||
#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
|
||||
|
||||
typedef enum {
|
||||
DWN_OK = 0,
|
||||
@@ -44,6 +49,9 @@ typedef enum {
|
||||
LAYOUT_TILING,
|
||||
LAYOUT_FLOATING,
|
||||
LAYOUT_MONOCLE,
|
||||
LAYOUT_CENTERED_MASTER,
|
||||
LAYOUT_COLUMNS,
|
||||
LAYOUT_FIBONACCI,
|
||||
LAYOUT_COUNT
|
||||
} LayoutType;
|
||||
|
||||
@@ -59,7 +67,8 @@ typedef enum {
|
||||
CLIENT_URGENT = (1 << 2),
|
||||
CLIENT_MINIMIZED = (1 << 3),
|
||||
CLIENT_STICKY = (1 << 4),
|
||||
CLIENT_MAXIMIZED = (1 << 5)
|
||||
CLIENT_MAXIMIZED = (1 << 5),
|
||||
CLIENT_UNMANAGING = (1 << 6) /* Being destroyed, skip processing */
|
||||
} ClientFlags;
|
||||
|
||||
typedef enum {
|
||||
@@ -82,6 +91,21 @@ typedef struct {
|
||||
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;
|
||||
|
||||
typedef struct Client Client;
|
||||
typedef struct Workspace Workspace;
|
||||
typedef struct Monitor Monitor;
|
||||
@@ -101,6 +125,10 @@ struct Client {
|
||||
char title[256];
|
||||
char class[64];
|
||||
SnapConstraint snap;
|
||||
bool floating_before_maximize;
|
||||
unsigned long taskbar_color;
|
||||
ColorAnimation title_anim;
|
||||
TextGlowAnimation text_glow;
|
||||
Client *next;
|
||||
Client *prev;
|
||||
Client *mru_next;
|
||||
@@ -161,6 +189,7 @@ typedef struct {
|
||||
GC gc;
|
||||
XFontStruct *font;
|
||||
XftFont *xft_font;
|
||||
XftFont *xft_font_bold;
|
||||
Colormap colormap;
|
||||
|
||||
Client *drag_client;
|
||||
@@ -168,11 +197,56 @@ typedef struct {
|
||||
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
|
||||
|
||||
extern DWNState *dwn;
|
||||
|
||||
int dwn_init(void);
|
||||
|
||||
@@ -86,6 +86,21 @@ 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);
|
||||
|
||||
void tutorial_start(void);
|
||||
void tutorial_stop(void);
|
||||
|
||||
@@ -13,6 +13,9 @@ 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);
|
||||
|
||||
int layout_get_usable_area(int *x, int *y, int *width, int *height);
|
||||
int layout_count_tiled_clients(int workspace);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -22,6 +22,7 @@ struct Panel {
|
||||
int width, height;
|
||||
bool visible;
|
||||
Pixmap buffer;
|
||||
bool dirty;
|
||||
};
|
||||
|
||||
Panel *panel_create(PanelPosition position);
|
||||
@@ -31,6 +32,13 @@ void panels_cleanup(void);
|
||||
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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
|
||||
+36
-32
@@ -8,10 +8,11 @@
|
||||
#define DWN_SYSTRAY_H
|
||||
|
||||
#include "dwn.h"
|
||||
#include "slider.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MAX_WIFI_NETWORKS 20
|
||||
#define MAX_TRAY_ICONS 32
|
||||
#define MAX_BATTERIES 4
|
||||
#define TRAY_ICON_SIZE 22
|
||||
#define TRAY_ICON_SPACING 4
|
||||
|
||||
@@ -41,21 +42,11 @@ extern TrayIcon tray_icons[MAX_TRAY_ICONS];
|
||||
extern int tray_icon_count;
|
||||
extern Window tray_selection_owner;
|
||||
|
||||
typedef struct {
|
||||
char ssid[64];
|
||||
int signal;
|
||||
char security[32];
|
||||
bool connected;
|
||||
} WifiNetwork;
|
||||
|
||||
typedef struct {
|
||||
bool enabled;
|
||||
bool connected;
|
||||
char current_ssid[64];
|
||||
int signal_strength;
|
||||
WifiNetwork networks[MAX_WIFI_NETWORKS];
|
||||
int network_count;
|
||||
long last_scan;
|
||||
} WifiState;
|
||||
|
||||
typedef struct {
|
||||
@@ -70,6 +61,22 @@ typedef struct {
|
||||
int time_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;
|
||||
|
||||
typedef struct {
|
||||
Window window;
|
||||
int x, y;
|
||||
@@ -78,29 +85,26 @@ typedef struct {
|
||||
bool dragging;
|
||||
} VolumeSlider;
|
||||
|
||||
/* Generic slider wrapper for fade controls */
|
||||
typedef struct {
|
||||
Window window;
|
||||
int x, y;
|
||||
int width, height;
|
||||
int item_count;
|
||||
int hovered_item;
|
||||
bool visible;
|
||||
void (*on_select)(int index);
|
||||
} DropdownMenu;
|
||||
GenericSlider *slider;
|
||||
int icon_x; /* Position for popup */
|
||||
} FadeControl;
|
||||
|
||||
extern WifiState wifi_state;
|
||||
extern AudioState audio_state;
|
||||
extern BatteryState battery_state;
|
||||
extern DropdownMenu *wifi_menu;
|
||||
extern MultiBatteryState multi_battery_state;
|
||||
extern VolumeSlider *volume_slider;
|
||||
|
||||
/* Fade control externs */
|
||||
extern FadeControl fade_speed_control;
|
||||
extern FadeControl fade_intensity_control;
|
||||
|
||||
void systray_init(void);
|
||||
void systray_cleanup(void);
|
||||
|
||||
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);
|
||||
|
||||
void audio_update_state(void);
|
||||
@@ -120,15 +124,14 @@ 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);
|
||||
|
||||
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);
|
||||
/* 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);
|
||||
|
||||
void systray_render(Panel *panel, int x, int *width);
|
||||
int systray_get_width(void);
|
||||
@@ -141,6 +144,7 @@ void systray_lock(void);
|
||||
void systray_unlock(void);
|
||||
|
||||
BatteryState systray_get_battery_snapshot(void);
|
||||
MultiBatteryState systray_get_multi_battery_snapshot(void);
|
||||
AudioState systray_get_audio_snapshot(void);
|
||||
|
||||
void xembed_init(void);
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,704 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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 */
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#define DWN_ASSERT(cond) assert(cond)
|
||||
#define DWN_ASSERT_MSG(cond, msg) assert((cond) && (msg))
|
||||
@@ -46,6 +47,27 @@ 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;
|
||||
}
|
||||
|
||||
bool file_exists(const char *path);
|
||||
char *file_read_all(const char *path);
|
||||
bool file_write_all(const char *path, const char *content);
|
||||
@@ -53,6 +75,14 @@ char *expand_path(const char *path);
|
||||
|
||||
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);
|
||||
|
||||
long get_time_ms(void);
|
||||
void sleep_ms(int ms);
|
||||
|
||||
@@ -50,6 +50,10 @@ 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);
|
||||
|
||||
@@ -0,0 +1,305 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,340 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,879 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,350 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,545 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,440 @@
|
||||
<!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 &
|
||||
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 &
|
||||
DISPLAY=:2 ./bin/dwn</code></pre>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<p>DWN Window Manager - retoor <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,518 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,611 @@
|
||||
/* retoor <retoor@molodetz.nl> */
|
||||
/* DWN Documentation Styles */
|
||||
|
||||
:root {
|
||||
--bg-primary: #1a1a2e;
|
||||
--bg-secondary: #16213e;
|
||||
--bg-tertiary: #0f3460;
|
||||
--bg-code: #0d1117;
|
||||
--text-primary: #e0e0e0;
|
||||
--text-secondary: #a0a0a0;
|
||||
--text-muted: #666;
|
||||
--accent: #4a90d9;
|
||||
--accent-hover: #5da0e9;
|
||||
--border-color: #2d3748;
|
||||
--success: #48bb78;
|
||||
--warning: #ed8936;
|
||||
--error: #f56565;
|
||||
--sidebar-width: 280px;
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
||||
background-color: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
line-height: 1.6;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: var(--accent-hover);
|
||||
}
|
||||
|
||||
.layout {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: var(--sidebar-width);
|
||||
background: var(--bg-secondary);
|
||||
border-right: 1px solid var(--border-color);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-header h1 {
|
||||
font-size: 1.5rem;
|
||||
color: var(--accent);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.sidebar-header .version {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.sidebar-nav {
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.nav-section {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.nav-section-title {
|
||||
padding: 8px 20px;
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
display: block;
|
||||
padding: 8px 20px 8px 30px;
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
transition: all 0.2s;
|
||||
border-left: 3px solid transparent;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--accent);
|
||||
border-left-color: var(--accent);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
margin-left: var(--sidebar-width);
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 40px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.page-header .lead {
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.75rem;
|
||||
margin: 40px 0 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.35rem;
|
||||
margin: 30px 0 15px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 1.1rem;
|
||||
margin: 20px 0 10px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
margin: 0 0 20px 25px;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
|
||||
background: var(--bg-code);
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.9em;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
pre {
|
||||
background: var(--bg-code);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
overflow-x: auto;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
pre code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
th, td {
|
||||
padding: 12px 15px;
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
th {
|
||||
background: var(--bg-secondary);
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
background: var(--bg-secondary);
|
||||
}
|
||||
|
||||
.card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 25px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 20px;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 25px;
|
||||
transition: transform 0.2s, border-color 0.2s;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.feature-title {
|
||||
font-size: 1.1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.feature-desc {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.shortcut-table code {
|
||||
background: var(--bg-tertiary);
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.badge-get { background: var(--success); color: #000; }
|
||||
.badge-post { background: var(--accent); color: #fff; }
|
||||
.badge-required { background: var(--error); color: #fff; }
|
||||
.badge-optional { background: var(--text-muted); color: #fff; }
|
||||
|
||||
.method-block {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
margin: 25px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.method-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 15px 20px;
|
||||
background: var(--bg-tertiary);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.method-name {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.method-body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.param-table {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.param-table th {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.example-tabs {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.example-tab {
|
||||
padding: 8px 16px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-bottom: none;
|
||||
border-radius: 6px 6px 0 0;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.example-tab:hover,
|
||||
.example-tab.active {
|
||||
background: var(--bg-code);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.example-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.example-content.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: 15px 20px;
|
||||
border-radius: 8px;
|
||||
margin: 20px 0;
|
||||
border-left: 4px solid;
|
||||
}
|
||||
|
||||
.alert-info {
|
||||
background: rgba(74, 144, 217, 0.1);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: rgba(237, 137, 54, 0.1);
|
||||
border-color: var(--warning);
|
||||
}
|
||||
|
||||
.alert-success {
|
||||
background: rgba(72, 187, 120, 0.1);
|
||||
border-color: var(--success);
|
||||
}
|
||||
|
||||
.toc {
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.toc-title {
|
||||
font-weight: 600;
|
||||
margin-bottom: 10px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.toc-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.toc-list li {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.toc-list a {
|
||||
color: var(--text-secondary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
padding: 5px 10px;
|
||||
background: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 4px;
|
||||
color: var(--text-secondary);
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.copy-btn:hover {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 10px 15px;
|
||||
background: var(--bg-primary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.search-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.search-input::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 60px;
|
||||
padding: 30px 0;
|
||||
border-top: 1px solid var(--border-color);
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.sidebar {
|
||||
transform: translateX(-100%);
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.sidebar.open {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.main-content {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.mobile-menu-btn {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 15px;
|
||||
left: 15px;
|
||||
z-index: 200;
|
||||
padding: 10px;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 6px;
|
||||
color: var(--text-primary);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 769px) {
|
||||
.mobile-menu-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.hero {
|
||||
text-align: center;
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.hero h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.hero .tagline {
|
||||
font-size: 1.3rem;
|
||||
color: var(--text-secondary);
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
display: inline-block;
|
||||
padding: 12px 24px;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
border-radius: 6px;
|
||||
font-weight: 600;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
background: var(--accent-hover);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-outline {
|
||||
background: transparent;
|
||||
border: 2px solid var(--accent);
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.btn-outline:hover {
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.quick-start {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 30px;
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
.quick-start h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 15px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.step-num {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background: var(--accent);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.anchor {
|
||||
color: var(--text-muted);
|
||||
margin-left: 8px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
h2:hover .anchor,
|
||||
h3:hover .anchor {
|
||||
opacity: 1;
|
||||
}
|
||||
@@ -0,0 +1,408 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,180 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,242 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,119 @@
|
||||
/* retoor <retoor@molodetz.nl> */
|
||||
/* DWN Documentation Scripts */
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
highlightCurrentPage();
|
||||
setupCopyButtons();
|
||||
setupMobileMenu();
|
||||
setupSearch();
|
||||
setupTabs();
|
||||
});
|
||||
|
||||
function highlightCurrentPage() {
|
||||
const currentPath = window.location.pathname.split('/').pop() || 'index.html';
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
const href = link.getAttribute('href');
|
||||
if (href === currentPath || (currentPath === '' && href === 'index.html')) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupCopyButtons() {
|
||||
const codeBlocks = document.querySelectorAll('pre code');
|
||||
|
||||
codeBlocks.forEach(block => {
|
||||
const wrapper = block.closest('.code-block') || block.parentElement;
|
||||
wrapper.style.position = 'relative';
|
||||
|
||||
const btn = document.createElement('button');
|
||||
btn.className = 'copy-btn';
|
||||
btn.textContent = 'Copy';
|
||||
btn.addEventListener('click', function() {
|
||||
const text = block.textContent;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
btn.textContent = 'Copied!';
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'Copy';
|
||||
}, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
wrapper.appendChild(btn);
|
||||
});
|
||||
}
|
||||
|
||||
function setupMobileMenu() {
|
||||
const menuBtn = document.querySelector('.mobile-menu-btn');
|
||||
const sidebar = document.querySelector('.sidebar');
|
||||
|
||||
if (menuBtn && sidebar) {
|
||||
menuBtn.addEventListener('click', function() {
|
||||
sidebar.classList.toggle('open');
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
if (!sidebar.contains(e.target) && !menuBtn.contains(e.target)) {
|
||||
sidebar.classList.remove('open');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setupSearch() {
|
||||
const searchInput = document.querySelector('.search-input');
|
||||
if (!searchInput) return;
|
||||
|
||||
searchInput.addEventListener('input', function(e) {
|
||||
const query = e.target.value.toLowerCase();
|
||||
const navLinks = document.querySelectorAll('.nav-link');
|
||||
|
||||
navLinks.forEach(link => {
|
||||
const text = link.textContent.toLowerCase();
|
||||
const section = link.closest('.nav-section');
|
||||
|
||||
if (query === '' || text.includes(query)) {
|
||||
link.style.display = 'block';
|
||||
} else {
|
||||
link.style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function setupTabs() {
|
||||
const tabGroups = document.querySelectorAll('.example-tabs');
|
||||
|
||||
tabGroups.forEach(group => {
|
||||
const tabs = group.querySelectorAll('.example-tab');
|
||||
const container = group.nextElementSibling;
|
||||
|
||||
tabs.forEach(tab => {
|
||||
tab.addEventListener('click', function() {
|
||||
const target = this.dataset.tab;
|
||||
|
||||
tabs.forEach(t => t.classList.remove('active'));
|
||||
this.classList.add('active');
|
||||
|
||||
if (container) {
|
||||
const contents = container.querySelectorAll('.example-content');
|
||||
contents.forEach(c => {
|
||||
c.classList.remove('active');
|
||||
if (c.dataset.tab === target) {
|
||||
c.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function scrollToSection(id) {
|
||||
const element = document.getElementById(id);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
<!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>><></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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,87 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,243 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,410 @@
|
||||
<!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 <retoor@molodetz.nl></p>
|
||||
</footer>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,422 +0,0 @@
|
||||
<!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>
|
||||
<a href="design-patterns.html">Design Patterns</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">🤖</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">👁</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">🔍</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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,530 +0,0 @@
|
||||
<!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>
|
||||
<a href="design-patterns.html">Design Patterns</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);">13</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>
|
||||
<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>
|
||||
<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>Autostart</strong></td>
|
||||
<td><code>autostart.c</code></td>
|
||||
<td>XDG Autostart support, .desktop file parsing, concurrent app launch</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>
|
||||
<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)
|
||||
├── autostart.c
|
||||
│ └── config.c
|
||||
└── keys.c
|
||||
└── config.c</code></pre>
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,578 +0,0 @@
|
||||
<!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>
|
||||
<a href="design-patterns.html">Design Patterns</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>
|
||||
<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>focus_follow_delay</code></td>
|
||||
<td><code>100</code></td>
|
||||
<td>Delay in ms before focus switches in follow mode (0-1000)</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 = follow
|
||||
focus_follow_delay = 100
|
||||
decorations = true</code></pre>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<h2 id="autostart" style="margin-top: 3rem;">[autostart] - XDG Autostart</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
|
||||
Configure automatic application startup. DWN follows the XDG Autostart specification.
|
||||
</p>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>enabled</code></td>
|
||||
<td>Enable/disable all autostart functionality (default: <code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>xdg_autostart</code></td>
|
||||
<td>Scan XDG .desktop files from /etc/xdg/autostart and ~/.config/autostart (default: <code>true</code>)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>path</code></td>
|
||||
<td>Additional directory for symlinks/scripts (default: <code>~/.config/dwn/autostart.d</code>)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>[autostart]
|
||||
enabled = true
|
||||
xdg_autostart = true
|
||||
path = ~/.config/dwn/autostart.d</code></pre>
|
||||
<p style="color: var(--text-muted); margin-top: 1rem;">
|
||||
<strong>Directories scanned:</strong>
|
||||
</p>
|
||||
<ul style="color: var(--text-muted);">
|
||||
<li><code>/etc/xdg/autostart/</code> - System defaults (nm-applet, blueman, power-manager)</li>
|
||||
<li><code>~/.config/autostart/</code> - User XDG autostart entries</li>
|
||||
<li><code>~/.config/dwn/autostart.d/</code> - DWN-specific symlinks and scripts</li>
|
||||
</ul>
|
||||
<h2 id="demo" style="margin-top: 3rem;">[demo] - Demo Mode</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 1.5rem;">
|
||||
Configure demo mode timing. The demo showcases DWN features including live AI and search functionality.
|
||||
</p>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Option</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>step_delay</code></td>
|
||||
<td>Time between demo steps in milliseconds (1000-30000, default: 4000)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>ai_timeout</code></td>
|
||||
<td>Timeout for AI/Exa API responses in milliseconds (5000-60000, default: 15000)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>window_timeout</code></td>
|
||||
<td>Timeout for window spawn operations in milliseconds (1000-30000, default: 5000)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>[demo]
|
||||
step_delay = 4000
|
||||
ai_timeout = 15000
|
||||
window_timeout = 5000</code></pre>
|
||||
<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
|
||||
focus_follow_delay = 100
|
||||
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
|
||||
[autostart]
|
||||
enabled = true
|
||||
xdg_autostart = true
|
||||
path = ~/.config/dwn/autostart.d
|
||||
[demo]
|
||||
step_delay = 4000
|
||||
ai_timeout = 15000
|
||||
window_timeout = 5000</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
-1085
File diff suppressed because it is too large
Load Diff
@@ -1,779 +0,0 @@
|
||||
<!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="DWN Design Patterns - Comprehensive documentation of design patterns, architectural decisions, and research sources used in the DWN window manager.">
|
||||
<meta name="keywords" content="design patterns, C programming, opaque pointer, vtable, factory pattern, observer pattern, EWMH, ICCCM, XEmbed">
|
||||
<title>Design Patterns - 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">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>
|
||||
<a href="design-patterns.html" class="active">Design Patterns</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 hero-small">
|
||||
<div class="container">
|
||||
<h1>Design Patterns & Architecture</h1>
|
||||
<p class="subtitle">
|
||||
Comprehensive documentation of the design patterns, architectural decisions,
|
||||
and research that shaped DWN's implementation.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container docs-container">
|
||||
<aside class="docs-sidebar">
|
||||
<h3>Contents</h3>
|
||||
<ul>
|
||||
<li><a href="#overview">Overview</a></li>
|
||||
<li><a href="#c-patterns">C Design Patterns</a></li>
|
||||
<li><a href="#opaque-pointer">Opaque Pointer</a></li>
|
||||
<li><a href="#goto-cleanup">Goto Cleanup</a></li>
|
||||
<li><a href="#vtable">Vtable Polymorphism</a></li>
|
||||
<li><a href="#factory">Factory Pattern</a></li>
|
||||
<li><a href="#observer">Observer Pattern</a></li>
|
||||
<li><a href="#singleton">Singleton Pattern</a></li>
|
||||
<li><a href="#double-fork">Double Fork Daemon</a></li>
|
||||
<li><a href="#x11-protocols">X11 Protocols</a></li>
|
||||
<li><a href="#ewmh">EWMH Specification</a></li>
|
||||
<li><a href="#icccm">ICCCM Standard</a></li>
|
||||
<li><a href="#xembed">XEmbed Protocol</a></li>
|
||||
<li><a href="#systray">System Tray Protocol</a></li>
|
||||
<li><a href="#xdg">XDG Specifications</a></li>
|
||||
<li><a href="#async">Async Patterns</a></li>
|
||||
<li><a href="#modular">Modular Architecture</a></li>
|
||||
<li><a href="#defensive">Defensive Programming</a></li>
|
||||
<li><a href="#sources">Research Sources</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<div class="docs-content">
|
||||
<h2 id="overview">Overview</h2>
|
||||
<p>
|
||||
DWN is built using professional C design patterns that provide encapsulation,
|
||||
modularity, and maintainability without the overhead of C++. This document
|
||||
details the patterns used, the rationale behind architectural decisions,
|
||||
and links to authoritative sources.
|
||||
</p>
|
||||
<div class="alert alert-info">
|
||||
<strong class="alert-title">Design Philosophy</strong>
|
||||
<p style="margin: 0;">
|
||||
DWN prioritizes simplicity, readability, and defensive programming.
|
||||
Each module has a single responsibility with well-defined interfaces.
|
||||
The codebase follows the principle: "Simple is better than complex."
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h2 id="c-patterns" style="margin-top: 3rem;">C Design Patterns</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 2rem;">
|
||||
While C lacks native object-oriented features, these patterns provide
|
||||
equivalent functionality with minimal overhead.
|
||||
</p>
|
||||
|
||||
<h3 id="opaque-pointer">Opaque Pointer Pattern</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Hide implementation details from API consumers, enabling changes without recompilation.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>The header declares a pointer to an incomplete type. The struct definition
|
||||
exists only in the implementation file, preventing direct member access.</p>
|
||||
<div class="code-header">
|
||||
<span>Header (public)</span>
|
||||
</div>
|
||||
<pre><code>typedef struct config_t *Config;
|
||||
Config config_create(void);
|
||||
void config_destroy(Config cfg);</code></pre>
|
||||
<div class="code-header">
|
||||
<span>Implementation (private)</span>
|
||||
</div>
|
||||
<pre><code>struct config_t {
|
||||
int border_width;
|
||||
char terminal[128];
|
||||
};</code></pre>
|
||||
<h4>Benefits</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>API consumers cannot access internal fields directly</li>
|
||||
<li>Implementation can change without breaking client code</li>
|
||||
<li>Enforces encapsulation at compile time</li>
|
||||
</ul>
|
||||
<h4>Used In</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>config.c</code> - Configuration management
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://interrupt.memfault.com/blog/opaque-pointers" target="_blank">Practical Design Patterns: Opaque Pointers and Objects in C</a> - Memfault</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Opaque_pointer" target="_blank">Opaque Pointer</a> - Wikipedia</li>
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/DCL12-C.+Implement+abstract+data+types+using+opaque+types" target="_blank">DCL12-C: Implement abstract data types using opaque types</a> - SEI CERT C Coding Standard</li>
|
||||
<li><a href="https://blog.mbedded.ninja/programming/design-patterns/opaque-pointers/" target="_blank">Opaque Pointers</a> - mbedded.ninja</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="goto-cleanup">Goto Cleanup Pattern</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Centralize resource cleanup in functions that acquire multiple resources,
|
||||
preventing memory leaks and ensuring proper deallocation on all code paths.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>Resources are initialized to safe values (NULL). On error, execution jumps
|
||||
to a cleanup label. The cleanup section safely releases all resources.</p>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
</div>
|
||||
<pre><code>int process_file(const char *path) {
|
||||
char *buf = NULL;
|
||||
FILE *f = NULL;
|
||||
int status = -1;
|
||||
|
||||
buf = malloc(1024);
|
||||
if (!buf) goto cleanup;
|
||||
|
||||
f = fopen(path, "r");
|
||||
if (!f) goto cleanup;
|
||||
|
||||
// ... processing ...
|
||||
status = 0;
|
||||
|
||||
cleanup:
|
||||
free(buf); // safe: free(NULL) is no-op
|
||||
if (f) fclose(f);
|
||||
return status;
|
||||
}</code></pre>
|
||||
<h4>Benefits</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Single cleanup point prevents code duplication</li>
|
||||
<li>All error paths properly release resources</li>
|
||||
<li>Used extensively in Linux kernel and SQLite</li>
|
||||
</ul>
|
||||
<h4>Used In</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>config.c</code>, <code>news.c</code>, <code>ai.c</code> - File and network operations
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/MEM12-C.+Consider+using+a+goto+chain+when+leaving+a+function+on+error+when+using+and+releasing+resources" target="_blank">MEM12-C: Using goto chain for error handling</a> - SEI CERT C Coding Standard</li>
|
||||
<li><a href="https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c" target="_blank">Using goto for error handling in C</a> - Eli Bendersky</li>
|
||||
<li><a href="https://www.geeksforgeeks.org/c/using-goto-for-exception-handling-in-c/" target="_blank">Using goto for Exception Handling in C</a> - GeeksforGeeks</li>
|
||||
<li><a href="https://ayende.com/blog/183521-C/error-handling-via-goto-in-c" target="_blank">Error handling via GOTO in C</a> - Ayende Rahien</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="vtable">Vtable Polymorphism</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Enable runtime polymorphism in C using function pointer tables,
|
||||
allowing different implementations to share a common interface.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>A struct of function pointers (vtable) defines the interface. Objects contain
|
||||
a pointer to their vtable. Calling through the vtable invokes the correct implementation.</p>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
</div>
|
||||
<pre><code>typedef struct Widget Widget;
|
||||
typedef struct {
|
||||
void (*draw)(Widget *self);
|
||||
void (*destroy)(Widget *self);
|
||||
} WidgetVtable;
|
||||
|
||||
struct Widget {
|
||||
const WidgetVtable *vtable;
|
||||
int x, y, width, height;
|
||||
};
|
||||
|
||||
// Usage: widget->vtable->draw(widget);</code></pre>
|
||||
<h4>Benefits</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Runtime dispatch without language support</li>
|
||||
<li>New implementations added without modifying existing code</li>
|
||||
<li>Same pattern used by C++ compilers internally</li>
|
||||
</ul>
|
||||
<h4>Used In</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>panel.c</code> - Widget rendering system
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://en.wikipedia.org/wiki/Virtual_method_table" target="_blank">Virtual Method Table</a> - Wikipedia</li>
|
||||
<li><a href="https://embeddedartistry.com/fieldatlas/technique-inheritance-and-polymorphism-in-c/" target="_blank">Inheritance and Polymorphism in C</a> - Embedded Artistry</li>
|
||||
<li><a href="https://www.state-machine.com/doc/AN_Simple_OOP_in_C.pdf" target="_blank">Object-Oriented Programming in C</a> - Quantum Leaps (PDF)</li>
|
||||
<li><a href="https://www.embedded.com/programming-embedded-systems-polymorphism-in-c-2/" target="_blank">Programming embedded systems: polymorphism in C</a> - Embedded.com</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="factory">Factory Pattern</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Encapsulate object creation logic, allowing the system to create objects
|
||||
without specifying their exact types.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>A factory function takes parameters describing what to create and returns
|
||||
a pointer to the appropriate object type.</p>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
</div>
|
||||
<pre><code>typedef enum { WIDGET_BUTTON, WIDGET_LABEL } WidgetType;
|
||||
|
||||
Widget *widget_create(WidgetType type) {
|
||||
switch (type) {
|
||||
case WIDGET_BUTTON: return button_create();
|
||||
case WIDGET_LABEL: return label_create();
|
||||
default: return NULL;
|
||||
}
|
||||
}</code></pre>
|
||||
<h4>Benefits</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Centralizes object creation logic</li>
|
||||
<li>New types added without changing client code</li>
|
||||
<li>Supports Open/Closed Principle</li>
|
||||
</ul>
|
||||
<h4>Used In</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>config.c</code>, <code>client.c</code>, <code>workspace.c</code> - Object lifecycle management
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://refactoring.guru/design-patterns/factory-method" target="_blank">Factory Method Pattern</a> - Refactoring Guru</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Factory_method_pattern" target="_blank">Factory Method Pattern</a> - Wikipedia</li>
|
||||
<li><a href="https://sourcemaking.com/design_patterns" target="_blank">Design Patterns</a> - SourceMaking</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="observer">Observer Pattern</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Implement event-driven communication where subjects notify observers
|
||||
of state changes without tight coupling.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>Observers register callback functions with subjects. When events occur,
|
||||
the subject iterates through registered callbacks and invokes them.</p>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
</div>
|
||||
<pre><code>typedef void (*EventCallback)(void *data);
|
||||
|
||||
void events_register(EventType type, EventCallback cb, void *data);
|
||||
void events_emit(EventType type);
|
||||
|
||||
// Notification triggers all registered callbacks</code></pre>
|
||||
<h4>Benefits</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Loose coupling between event sources and handlers</li>
|
||||
<li>Observers added/removed without modifying subjects</li>
|
||||
<li>Foundation of event-driven programming</li>
|
||||
</ul>
|
||||
<h4>Used In</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>keys.c</code> - Keyboard event handling, <code>notifications.c</code> - D-Bus signals
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://refactoring.guru/design-patterns/observer" target="_blank">Observer Pattern</a> - Refactoring Guru</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Observer_pattern" target="_blank">Observer Pattern</a> - Wikipedia</li>
|
||||
<li><a href="https://learn.microsoft.com/en-us/dotnet/standard/events/observer-design-pattern" target="_blank">Observer Design Pattern</a> - Microsoft Learn</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="singleton">Singleton / Global State</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Provide a single, globally accessible instance of the window manager state,
|
||||
simplifying module communication.</p>
|
||||
<h4>Implementation in DWN</h4>
|
||||
<p>DWN uses a single <code>DWNState</code> structure accessible via the global
|
||||
<code>dwn</code> pointer. This is appropriate for a window manager where
|
||||
exactly one instance exists per X11 session.</p>
|
||||
<div class="code-header">
|
||||
<span>Example</span>
|
||||
</div>
|
||||
<pre><code>// Global state pointer
|
||||
extern DWNState *dwn;
|
||||
|
||||
// Access from any module
|
||||
if (dwn != NULL && dwn->config != NULL) {
|
||||
return dwn->config->terminal;
|
||||
}</code></pre>
|
||||
<h4>Rationale</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Window manager is inherently a singleton per X session</li>
|
||||
<li>Simplifies inter-module communication</li>
|
||||
<li>All state centralized for easier debugging</li>
|
||||
</ul>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://refactoring.guru/design-patterns/singleton" target="_blank">Singleton Pattern</a> - Refactoring Guru</li>
|
||||
<li><a href="https://gameprogrammingpatterns.com/singleton.html" target="_blank">Singleton</a> - Game Programming Patterns</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Singleton_pattern" target="_blank">Singleton Pattern</a> - Wikipedia</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="double-fork">Double Fork Daemon Pattern</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Launch background processes that are fully detached from the parent,
|
||||
preventing zombie processes and terminal reattachment.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>The pattern uses two fork() calls: the first creates a child that calls
|
||||
setsid() to become a session leader, then forks again. The grandchild
|
||||
cannot reacquire a controlling terminal.</p>
|
||||
<div class="code-header">
|
||||
<span>Implementation in DWN</span>
|
||||
</div>
|
||||
<pre><code>int spawn_async(const char *cmd) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
setsid(); // New session
|
||||
pid_t pid2 = fork(); // Second fork
|
||||
if (pid2 == 0) {
|
||||
execl("/bin/sh", "sh", "-c", cmd, NULL);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
_exit(EXIT_SUCCESS); // Intermediate exits
|
||||
}
|
||||
waitpid(pid, &status, 0); // Only wait for intermediate
|
||||
return 0;
|
||||
}</code></pre>
|
||||
<h4>Benefits</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Process fully detached from parent</li>
|
||||
<li>No zombie processes (intermediate is reaped immediately)</li>
|
||||
<li>Cannot reacquire controlling terminal</li>
|
||||
<li>Non-blocking for the caller</li>
|
||||
</ul>
|
||||
<h4>Used In</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>util.c</code> - spawn_async(), <code>autostart.c</code>, <code>applauncher.c</code>
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://0xjet.github.io/3OHA/2022/04/11/post.html" target="_blank">UNIX daemonization and the double fork</a> - Juan Tapiador</li>
|
||||
<li><a href="https://www.digitalbunker.dev/understanding-daemons-unix/" target="_blank">Understanding Daemons</a> - Digital Bunker</li>
|
||||
<li><a href="https://lloydrochester.com/post/c/unix-daemon-example/" target="_blank">Daemon Example in C</a> - Lloyd Rochester</li>
|
||||
<li><a href="https://goral.net.pl/post/double-fork/" target="_blank">Double Fork</a> - Michal Goral</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="x11-protocols" style="margin-top: 3rem;">X11 Window Manager Protocols</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 2rem;">
|
||||
DWN implements several freedesktop.org specifications for cross-desktop compatibility.
|
||||
</p>
|
||||
|
||||
<h3 id="ewmh">Extended Window Manager Hints (EWMH)</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Define interactions between window managers, compositing managers, applications,
|
||||
and desktop utilities in a standardized way.</p>
|
||||
<h4>Key Features Implemented</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>_NET_SUPPORTED</code> - List of supported hints</li>
|
||||
<li><code>_NET_CLIENT_LIST</code> - List of managed windows</li>
|
||||
<li><code>_NET_CURRENT_DESKTOP</code> - Active workspace</li>
|
||||
<li><code>_NET_WM_STATE</code> - Window states (fullscreen, maximized)</li>
|
||||
<li><code>_NET_ACTIVE_WINDOW</code> - Currently focused window</li>
|
||||
<li><code>_NET_WM_WINDOW_TYPE</code> - Window type classification</li>
|
||||
</ul>
|
||||
<h4>Implementation</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>atoms.c</code> manages X11 atom creation and EWMH property updates.
|
||||
Properties are updated on window focus changes, workspace switches, and state changes.
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/wm/latest/" target="_blank">Extended Window Manager Hints Specification</a> - freedesktop.org</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Extended_Window_Manager_Hints" target="_blank">Extended Window Manager Hints</a> - Wikipedia</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="icccm">Inter-Client Communication Conventions Manual (ICCCM)</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Define low-level conventions for X11 client communication, including
|
||||
selections, window management, and session management.</p>
|
||||
<h4>Key Features Implemented</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>WM_PROTOCOLS</code> - Window close handling</li>
|
||||
<li><code>WM_DELETE_WINDOW</code> - Graceful window closing</li>
|
||||
<li><code>WM_NAME</code> / <code>_NET_WM_NAME</code> - Window titles</li>
|
||||
<li><code>WM_CLASS</code> - Application classification</li>
|
||||
<li><code>WM_HINTS</code> - Window hints (urgency, input model)</li>
|
||||
</ul>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html" target="_blank">Inter-Client Communication Conventions Manual</a> - X.Org</li>
|
||||
<li><a href="https://tronche.com/gui/x/icccm/" target="_blank">ICCCM Reference</a> - Christophe Tronche</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="xembed">XEmbed Protocol</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Enable embedding of controls from one application into another,
|
||||
forming the basis of the system tray implementation.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>The embedder (DWN panel) acts as a window manager for embedded clients.
|
||||
Client windows are reparented into the embedder, and events are coordinated
|
||||
through XEMBED messages.</p>
|
||||
<h4>Key Messages</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>XEMBED_EMBEDDED_NOTIFY</code> - Sent when embedding completes</li>
|
||||
<li><code>XEMBED_FOCUS_IN/OUT</code> - Focus coordination</li>
|
||||
<li><code>XEMBED_WINDOW_ACTIVATE</code> - Window activation</li>
|
||||
</ul>
|
||||
<h4>Implementation</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>systray.c</code> implements the embedder side, reparenting tray icons
|
||||
and forwarding click events.
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/xembed-spec/latest/" target="_blank">XEmbed Protocol Specification</a> - freedesktop.org</li>
|
||||
<li><a href="https://www.freedesktop.org/wiki/Specifications/xembed-spec/" target="_blank">XEmbed Spec Wiki</a> - freedesktop.org</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3 id="systray">System Tray Protocol</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Enable applications to display status icons in a desktop panel,
|
||||
providing a standardized notification area.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>DWN acquires the <code>_NET_SYSTEM_TRAY_S0</code> selection to become the
|
||||
tray manager. Applications send <code>SYSTEM_TRAY_REQUEST_DOCK</code> messages
|
||||
to dock their icons.</p>
|
||||
<h4>Docking Process</h4>
|
||||
<ol style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>DWN acquires <code>_NET_SYSTEM_TRAY_S0</code> selection</li>
|
||||
<li>Application sends <code>SYSTEM_TRAY_REQUEST_DOCK</code> client message</li>
|
||||
<li>DWN creates embedding window and reparents icon</li>
|
||||
<li>DWN sends <code>XEMBED_EMBEDDED_NOTIFY</code> to icon</li>
|
||||
<li>Click events forwarded to icon window</li>
|
||||
</ol>
|
||||
<h4>Supported Applications</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
nm-applet, blueman-applet, Telegram, pasystray, udiskie, and any XEmbed-compatible tray icon.
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-0.3.html" target="_blank">System Tray Protocol Specification</a> - freedesktop.org</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="xdg" style="margin-top: 3rem;">XDG Specifications</h2>
|
||||
|
||||
<h3>Desktop Entry Specification</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Standard format for application metadata files (.desktop files) used
|
||||
by application launchers and autostart systems.</p>
|
||||
<h4>Key Fields Parsed</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>Exec</code> - Command to execute</li>
|
||||
<li><code>TryExec</code> - Check if binary exists</li>
|
||||
<li><code>Hidden</code> - Entry is disabled</li>
|
||||
<li><code>OnlyShowIn</code> / <code>NotShowIn</code> - Desktop environment filters</li>
|
||||
<li><code>Terminal</code> - Run in terminal</li>
|
||||
</ul>
|
||||
<h4>Implementation</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>applauncher.c</code> parses .desktop files for the application menu.
|
||||
<code>autostart.c</code> parses autostart entries.
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/" target="_blank">Desktop Entry Specification</a> - freedesktop.org</li>
|
||||
<li><a href="https://wiki.archlinux.org/title/Desktop_entries" target="_blank">Desktop Entries</a> - ArchWiki</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3>Desktop Application Autostart Specification</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Define standard locations and format for applications that should
|
||||
start automatically when the user logs in.</p>
|
||||
<h4>Directories Scanned</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>/etc/xdg/autostart/</code> - System-wide autostart</li>
|
||||
<li><code>~/.config/autostart/</code> - User autostart</li>
|
||||
<li><code>~/.config/dwn/autostart.d/</code> - DWN-specific (symlinks)</li>
|
||||
</ul>
|
||||
<h4>Key Behavior</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>User entries override system entries with same filename</li>
|
||||
<li><code>Hidden=true</code> disables an entry</li>
|
||||
<li><code>TryExec</code> prevents running if binary missing</li>
|
||||
</ul>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html" target="_blank">Desktop Application Autostart Specification</a> - freedesktop.org</li>
|
||||
<li><a href="https://wiki.archlinux.org/title/XDG_Autostart" target="_blank">XDG Autostart</a> - ArchWiki</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3>Desktop Notifications Specification</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Standard D-Bus interface for applications to display passive notifications
|
||||
to users without blocking.</p>
|
||||
<h4>D-Bus Interface</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
DWN implements <code>org.freedesktop.Notifications</code> on the session bus
|
||||
at path <code>/org/freedesktop/Notifications</code>.
|
||||
</p>
|
||||
<h4>Key Methods</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>Notify</code> - Display a notification</li>
|
||||
<li><code>CloseNotification</code> - Dismiss a notification</li>
|
||||
<li><code>GetCapabilities</code> - Query supported features</li>
|
||||
<li><code>GetServerInformation</code> - Server metadata</li>
|
||||
</ul>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/notification-spec/latest/" target="_blank">Desktop Notifications Specification</a> - freedesktop.org</li>
|
||||
<li><a href="https://wiki.archlinux.org/title/Desktop_notifications" target="_blank">Desktop Notifications</a> - ArchWiki</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="async" style="margin-top: 3rem;">Async Programming Patterns</h2>
|
||||
|
||||
<h3>libcurl Multi Interface</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Perform HTTP requests asynchronously without blocking the main event loop,
|
||||
essential for AI features and news fetching.</p>
|
||||
<h4>How It Works</h4>
|
||||
<p>Instead of blocking on network I/O, the multi interface allows the main
|
||||
loop to poll for completion. <code>curl_multi_perform()</code> advances
|
||||
transfers incrementally.</p>
|
||||
<div class="code-header">
|
||||
<span>Integration Pattern</span>
|
||||
</div>
|
||||
<pre><code>// In event loop (16ms intervals)
|
||||
curl_multi_perform(multi_handle, &running);
|
||||
CURLMsg *msg = curl_multi_info_read(multi_handle, &msgs_left);
|
||||
if (msg && msg->msg == CURLMSG_DONE) {
|
||||
// Request completed, process response
|
||||
}</code></pre>
|
||||
<h4>Implementation</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>ai.c</code> uses curl_multi for OpenRouter API calls.
|
||||
Responses processed in <code>ai_process_pending()</code> called from main loop.
|
||||
</p>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://curl.se/libcurl/c/libcurl-multi.html" target="_blank">libcurl multi interface overview</a> - curl.se</li>
|
||||
<li><a href="https://curl.se/libcurl/c/libcurl-tutorial.html" target="_blank">libcurl programming tutorial</a> - curl.se</li>
|
||||
<li><a href="https://curl.se/libcurl/c/multi-app.html" target="_blank">multi-app.c example</a> - curl.se</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h3>pthread for Background I/O</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Purpose</h4>
|
||||
<p>Offload blocking operations to separate threads when async APIs
|
||||
are unavailable or impractical.</p>
|
||||
<h4>Implementation</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
<code>news.c</code> spawns a detached thread for RSS fetching.
|
||||
Mutex guards shared state, atomic flags prevent concurrent fetches.
|
||||
</p>
|
||||
<div class="code-header">
|
||||
<span>Pattern</span>
|
||||
</div>
|
||||
<pre><code>static pthread_mutex_t news_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
static atomic_int fetch_running = 0;
|
||||
|
||||
void news_fetch_async(void) {
|
||||
if (atomic_load(&fetch_running)) return;
|
||||
atomic_store(&fetch_running, 1);
|
||||
pthread_create(&fetch_thread, NULL, fetch_thread_func, NULL);
|
||||
}</code></pre>
|
||||
</div>
|
||||
|
||||
<h2 id="modular" style="margin-top: 3rem;">Modular Architecture</h2>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Design Principle</h4>
|
||||
<p>Each module has a single responsibility with well-defined interfaces.
|
||||
Modules communicate through the global <code>dwn</code> state and
|
||||
function calls, never through shared mutable state.</p>
|
||||
<h4>Module Structure</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>include/module.h</code> - Public API declarations</li>
|
||||
<li><code>src/module.c</code> - Private implementation</li>
|
||||
<li>Static functions for internal logic</li>
|
||||
<li>module_init() / module_cleanup() lifecycle</li>
|
||||
</ul>
|
||||
<h4>Module Responsibilities</h4>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Module</th>
|
||||
<th>Responsibility</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>main.c</td><td>Event loop, initialization orchestration</td></tr>
|
||||
<tr><td>client.c</td><td>Window management, focus handling</td></tr>
|
||||
<tr><td>workspace.c</td><td>Virtual desktop management</td></tr>
|
||||
<tr><td>layout.c</td><td>Tiling algorithms</td></tr>
|
||||
<tr><td>panel.c</td><td>UI panels and widgets</td></tr>
|
||||
<tr><td>systray.c</td><td>System tray protocol</td></tr>
|
||||
<tr><td>notifications.c</td><td>D-Bus notification daemon</td></tr>
|
||||
<tr><td>autostart.c</td><td>XDG autostart support</td></tr>
|
||||
<tr><td>config.c</td><td>Configuration parsing</td></tr>
|
||||
<tr><td>ai.c</td><td>AI integration</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://en.wikipedia.org/wiki/Modular_programming" target="_blank">Modular Programming</a> - Wikipedia</li>
|
||||
<li><a href="https://en.wikipedia.org/wiki/Separation_of_concerns" target="_blank">Separation of Concerns</a> - Wikipedia</li>
|
||||
<li><a href="https://thecloudstrap.com/chapter-14-modular-programming-in-c/" target="_blank">Modular Programming in C</a> - TheCloudStrap</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="defensive" style="margin-top: 3rem;">Defensive Programming</h2>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h4>Core Principles</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><strong>Null Checks</strong> - All pointer parameters validated before use</li>
|
||||
<li><strong>Bounds Checking</strong> - Array indices and string lengths verified</li>
|
||||
<li><strong>Return Value Checking</strong> - malloc(), fopen(), etc. checked for failure</li>
|
||||
<li><strong>String Safety</strong> - strncpy() with size-1, explicit null termination</li>
|
||||
<li><strong>Assertions</strong> - assert() for programmer errors in debug builds</li>
|
||||
</ul>
|
||||
<h4>Examples in DWN</h4>
|
||||
<div class="code-header">
|
||||
<span>Null Check Pattern</span>
|
||||
</div>
|
||||
<pre><code>const char *config_get_terminal(void) {
|
||||
if (dwn != NULL && dwn->config != NULL) {
|
||||
return dwn->config->terminal;
|
||||
}
|
||||
return "xterm"; // Safe fallback
|
||||
}</code></pre>
|
||||
<div class="code-header">
|
||||
<span>String Safety Pattern</span>
|
||||
</div>
|
||||
<pre><code>strncpy(cfg->terminal, value, sizeof(cfg->terminal) - 1);
|
||||
cfg->terminal[sizeof(cfg->terminal) - 1] = '\0'; // Guarantee null termination</code></pre>
|
||||
<h4>Research Sources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/API00-C.+Functions+should+validate+their+parameters" target="_blank">API00-C: Functions should validate their parameters</a> - SEI CERT</li>
|
||||
<li><a href="https://enterprisecraftsmanship.com/posts/defensive-programming/" target="_blank">Defensive programming: the good, the bad and the ugly</a> - Enterprise Craftsmanship</li>
|
||||
<li><a href="https://www.cse.psu.edu/~gxt29/teaching/cs447s19/slides/05defensiveProg.pdf" target="_blank">Defensive Programming</a> - Penn State (PDF)</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2 id="sources" style="margin-top: 3rem;">Complete Research Sources</h2>
|
||||
<div class="card">
|
||||
<h4>C Design Patterns</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://interrupt.memfault.com/blog/opaque-pointers" target="_blank">Practical Design Patterns: Opaque Pointers and Objects in C</a> - Memfault</li>
|
||||
<li><a href="https://wiki.sei.cmu.edu/confluence/display/c/" target="_blank">SEI CERT C Coding Standard</a> - Carnegie Mellon University</li>
|
||||
<li><a href="https://refactoring.guru/design-patterns" target="_blank">Design Patterns Catalog</a> - Refactoring Guru</li>
|
||||
<li><a href="https://sourcemaking.com/design_patterns" target="_blank">Design Patterns</a> - SourceMaking</li>
|
||||
<li><a href="https://embeddedartistry.com/fieldatlas/" target="_blank">Embedded Artistry Field Atlas</a> - Embedded Artistry</li>
|
||||
<li><a href="https://www.state-machine.com/doc/AN_Simple_OOP_in_C.pdf" target="_blank">Object-Oriented Programming in C</a> - Quantum Leaps</li>
|
||||
</ul>
|
||||
<h4>X11 and freedesktop.org</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://specifications.freedesktop.org/wm/latest/" target="_blank">Extended Window Manager Hints (EWMH)</a> - freedesktop.org</li>
|
||||
<li><a href="https://specifications.freedesktop.org/xembed-spec/latest/" target="_blank">XEmbed Protocol</a> - freedesktop.org</li>
|
||||
<li><a href="https://specifications.freedesktop.org/systemtray-spec/systemtray-spec-0.3.html" target="_blank">System Tray Protocol</a> - freedesktop.org</li>
|
||||
<li><a href="https://specifications.freedesktop.org/notification-spec/latest/" target="_blank">Desktop Notifications</a> - freedesktop.org</li>
|
||||
<li><a href="https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html" target="_blank">Desktop Application Autostart</a> - freedesktop.org</li>
|
||||
<li><a href="https://specifications.freedesktop.org/desktop-entry-spec/latest/" target="_blank">Desktop Entry Specification</a> - freedesktop.org</li>
|
||||
</ul>
|
||||
<h4>Libraries and APIs</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://curl.se/libcurl/c/libcurl-multi.html" target="_blank">libcurl multi interface</a> - curl.se</li>
|
||||
<li><a href="https://dbus.freedesktop.org/doc/dbus-specification.html" target="_blank">D-Bus Specification</a> - freedesktop.org</li>
|
||||
<li><a href="https://www.x.org/releases/current/doc/" target="_blank">X Window System Documentation</a> - X.Org</li>
|
||||
</ul>
|
||||
<h4>Unix Programming</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://0xjet.github.io/3OHA/2022/04/11/post.html" target="_blank">UNIX daemonization and the double fork</a> - Juan Tapiador</li>
|
||||
<li><a href="https://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c" target="_blank">Using goto for error handling in C</a> - Eli Bendersky</li>
|
||||
<li><a href="https://gameprogrammingpatterns.com/" target="_blank">Game Programming Patterns</a> - Robert Nystrom</li>
|
||||
</ul>
|
||||
<h4>Community Resources</h4>
|
||||
<ul style="padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><a href="https://wiki.archlinux.org/" target="_blank">ArchWiki</a> - Comprehensive Linux documentation</li>
|
||||
<li><a href="https://en.wikipedia.org/" target="_blank">Wikipedia</a> - Design pattern overviews</li>
|
||||
</ul>
|
||||
</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>Resources</h4>
|
||||
<ul>
|
||||
<li><a href="https://retoor.molodetz.nl/retoor/dwn">Source Code</a></li>
|
||||
<li><a href="https://retoor.molodetz.nl/retoor/dwn/issues">Issue Tracker</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer-section">
|
||||
<h4>Author</h4>
|
||||
<p style="color: var(--text-muted);">
|
||||
retoor <retoor@molodetz.nl>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer-bottom">
|
||||
<p>DWN Window Manager - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,399 +0,0 @@
|
||||
<!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>
|
||||
<a href="design-patterns.html">Design Patterns</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,465 +0,0 @@
|
||||
<!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="Explore the powerful features of DWN window manager - tiling layouts, workspaces, AI integration, and more.">
|
||||
<title>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" class="active">Features</a></li>
|
||||
<li><a href="installation.html">Install</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="documentation.html">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>
|
||||
<a href="design-patterns.html">Design Patterns</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>Powerful Features</h1>
|
||||
<p class="subtitle">
|
||||
Everything you need for a productive desktop experience, without the bloat.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Window Management</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
DWN provides flexible window management that adapts to your workflow, whether you prefer
|
||||
the precision of tiling or the freedom of floating windows.
|
||||
</p>
|
||||
<div class="features-grid">
|
||||
<div class="card">
|
||||
<h3><span class="card-icon">☷</span> Tiling Layout</h3>
|
||||
<p>Master-stack tiling with configurable master area ratio. Windows automatically
|
||||
organize into a primary area and a stack, maximizing screen real estate.</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Adjustable master area ratio (0.1 - 0.9)</li>
|
||||
<li>Multiple windows in master area</li>
|
||||
<li>Smart stack arrangement</li>
|
||||
<li>Configurable gaps between windows</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><span class="card-icon">❏</span> Floating Layout</h3>
|
||||
<p>Traditional floating window management with drag-and-drop positioning.
|
||||
Perfect for workflows that need overlapping windows or free-form arrangement.</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Click and drag to move windows</li>
|
||||
<li>Resize from any edge or corner</li>
|
||||
<li>Window snapping support</li>
|
||||
<li>Respect minimum size hints</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><span class="card-icon">☐</span> Monocle Layout</h3>
|
||||
<p>Full-screen single window mode for focused work. Each window takes up
|
||||
the entire workspace, perfect for presentations or deep concentration.</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Maximize focused window</li>
|
||||
<li>Quick window cycling</li>
|
||||
<li>Ideal for single-task focus</li>
|
||||
<li>Works great on small screens</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-info" style="margin-top: 2rem;">
|
||||
<strong class="alert-title">Pro Tip</strong>
|
||||
<p style="margin: 0;">Switch layouts instantly with <kbd>Super</kbd> + <kbd>Space</kbd>.
|
||||
Your window arrangement is preserved when switching back.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2>Virtual Workspaces</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
Nine virtual desktops give you unlimited room to organize your work.
|
||||
Each workspace maintains its own window state and layout preferences.
|
||||
</p>
|
||||
<div class="features-grid" style="grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">1-9</div>
|
||||
<h3>9 Workspaces</h3>
|
||||
<p>Quick access via F1-F9 keys. Organize projects, contexts, or tasks across
|
||||
dedicated spaces.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">↔</div>
|
||||
<h3>Window Transfer</h3>
|
||||
<p>Move windows between workspaces with Shift+F1-F9. Quick and keyboard-driven.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">📈</div>
|
||||
<h3>Per-Workspace State</h3>
|
||||
<p>Each workspace remembers its layout mode, window positions, and focused window.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">👁</div>
|
||||
<h3>Visual Indicators</h3>
|
||||
<p>Panel shows active and occupied workspaces at a glance with color coding.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Panels & System Tray</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
Built-in panels provide essential information and quick access to common functions
|
||||
without needing external tools or status bars.
|
||||
</p>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 2rem;">
|
||||
<div class="card">
|
||||
<h3>Top Panel</h3>
|
||||
<p>The top panel contains your main controls and information:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><strong>Workspace Indicators</strong> - Click or use shortcuts to switch</li>
|
||||
<li><strong>Taskbar</strong> - Shows windows on current workspace</li>
|
||||
<li><strong>System Tray</strong> - Battery, volume, WiFi indicators</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Bottom Panel</h3>
|
||||
<p>Optional bottom panel for additional information:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><strong>Clock Display</strong> - Time and date</li>
|
||||
<li><strong>News Ticker</strong> - Scrolling news feed with navigation</li>
|
||||
<li><strong>Customizable</strong> - Can be hidden in config</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h3 style="margin-top: 3rem; margin-bottom: 1.5rem;">News Ticker</h3>
|
||||
<div class="card">
|
||||
<p>The bottom panel includes a scrolling news ticker that displays headlines from a news feed.
|
||||
Navigate through articles using keyboard shortcuts:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><kbd>Super</kbd> + <kbd>Down</kbd> - Next article</li>
|
||||
<li><kbd>Super</kbd> + <kbd>Up</kbd> - Previous article</li>
|
||||
<li><kbd>Super</kbd> + <kbd>Return</kbd> - Open in browser</li>
|
||||
</ul>
|
||||
<p style="margin-top: 1rem; color: var(--text-muted);">
|
||||
The ticker updates automatically and caches up to 50 articles. Smooth scrolling animation
|
||||
at 80 pixels per second keeps you informed without distraction.
|
||||
</p>
|
||||
</div>
|
||||
<h3 style="margin-top: 3rem; margin-bottom: 1.5rem;">XEmbed System Tray</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h3>📱 External Application Icons</h3>
|
||||
<p>DWN implements the freedesktop.org XEmbed System Tray protocol, allowing external applications
|
||||
to dock their status icons in the panel - just like XFCE, GNOME, or KDE.</p>
|
||||
<p style="margin-top: 1rem; color: var(--text-muted);">Supported applications include:</p>
|
||||
<ul style="margin-top: 0.5rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><strong>Telegram</strong> - Notification icons for messages</li>
|
||||
<li><strong>nm-applet</strong> - NetworkManager GUI</li>
|
||||
<li><strong>blueman-applet</strong> - Bluetooth manager</li>
|
||||
<li><strong>pasystray</strong> - PulseAudio control</li>
|
||||
<li><strong>udiskie</strong> - USB automounter</li>
|
||||
<li>Any application with tray icon support</li>
|
||||
</ul>
|
||||
<p style="margin-top: 1rem; color: var(--text-muted);">
|
||||
Simply launch any tray-enabled application and its icon will automatically appear in the panel.
|
||||
Click on icons to interact - all events are forwarded to the application.
|
||||
</p>
|
||||
</div>
|
||||
<h3 style="margin-top: 3rem; margin-bottom: 1.5rem;">XDG Autostart</h3>
|
||||
<div class="card" style="margin-bottom: 2rem;">
|
||||
<h3>Automatic Application Startup</h3>
|
||||
<p>DWN follows the XDG Autostart specification, automatically starting system services
|
||||
and tray applications - just like traditional desktop environments.</p>
|
||||
<p style="margin-top: 1rem; color: var(--text-muted);">Directories scanned at startup:</p>
|
||||
<ul style="margin-top: 0.5rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li><code>/etc/xdg/autostart/</code> - System defaults (nm-applet, blueman, power-manager)</li>
|
||||
<li><code>~/.config/autostart/</code> - User XDG autostart entries</li>
|
||||
<li><code>~/.config/dwn/autostart.d/</code> - DWN-specific symlinks and scripts</li>
|
||||
</ul>
|
||||
<p style="margin-top: 1rem; color: var(--text-muted);">
|
||||
All applications launch concurrently for fastest boot time. Properly handles .desktop
|
||||
file fields including Hidden, TryExec, OnlyShowIn, and NotShowIn.
|
||||
</p>
|
||||
</div>
|
||||
<h3 style="margin-top: 2rem; margin-bottom: 1.5rem;">Built-in Widgets</h3>
|
||||
<div class="features-grid">
|
||||
<div class="card">
|
||||
<h3>🔋 Battery Monitor</h3>
|
||||
<p>Shows current battery percentage with color-coded status:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Red when below 20%</li>
|
||||
<li>Blue when charging</li>
|
||||
<li>Auto-hides on desktops</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>🔊 Volume Control</h3>
|
||||
<p>Full audio control at your fingertips:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Click for volume slider</li>
|
||||
<li>Scroll to adjust</li>
|
||||
<li>Right-click to mute</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>📶 WiFi Manager</h3>
|
||||
<p>Network management made simple:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Click for network list</li>
|
||||
<li>Signal strength indicators</li>
|
||||
<li>Current SSID display</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2>Notification System</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
Built-in D-Bus notification daemon following freedesktop.org standards.
|
||||
No need for external notification tools like dunst or notify-osd.
|
||||
</p>
|
||||
<div class="features-grid" style="grid-template-columns: repeat(2, 1fr);">
|
||||
<div class="card">
|
||||
<h3>Standards Compliant</h3>
|
||||
<p>Implements the org.freedesktop.Notifications D-Bus interface.
|
||||
Works seamlessly with any application that sends desktop notifications.</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Customizable Appearance</h3>
|
||||
<p>Configure notification colors and positioning through the config file.
|
||||
Notifications match your overall color scheme automatically.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-success" style="margin-top: 2rem;">
|
||||
<strong class="alert-title">Capacity</strong>
|
||||
<p style="margin: 0;">DWN can display up to 32 notifications simultaneously,
|
||||
with automatic queuing and timeout management.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>AI Integration</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
Optional AI features powered by OpenRouter API and Exa semantic search.
|
||||
Control your desktop with natural language and get intelligent assistance.
|
||||
</p>
|
||||
<div class="features-grid">
|
||||
<div class="card">
|
||||
<h3>🤖 AI Command Palette</h3>
|
||||
<p>Press <kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd> and type natural
|
||||
language commands like "open firefox" or "launch terminal".</p>
|
||||
<a href="ai-features.html" class="btn btn-sm btn-secondary" style="margin-top: 1rem;">Learn More</a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>🔍 Semantic Web Search</h3>
|
||||
<p>Search the web semantically with Exa integration. Find relevant content based
|
||||
on meaning, not just keywords.</p>
|
||||
<a href="ai-features.html" class="btn btn-sm btn-secondary" style="margin-top: 1rem;">Learn More</a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>🎓 Context Analysis</h3>
|
||||
<p>AI analyzes your current workspace to understand what you're working on
|
||||
and provides relevant suggestions.</p>
|
||||
<a href="ai-features.html" class="btn btn-sm btn-secondary" style="margin-top: 1rem;">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2>Standards Compliance</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
DWN implements EWMH and ICCCM protocols for maximum compatibility with X11 applications.
|
||||
</p>
|
||||
<div class="features-grid" style="grid-template-columns: repeat(2, 1fr);">
|
||||
<div class="card">
|
||||
<h3>EWMH Support</h3>
|
||||
<p>Extended Window Manager Hints for modern application features:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>_NET_WM_STATE (fullscreen, maximized, etc.)</li>
|
||||
<li>_NET_ACTIVE_WINDOW</li>
|
||||
<li>_NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING</li>
|
||||
<li>_NET_CURRENT_DESKTOP and _NET_NUMBER_OF_DESKTOPS</li>
|
||||
<li>_NET_WM_WINDOW_TYPE</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>ICCCM Compliance</h3>
|
||||
<p>Inter-Client Communication Conventions Manual support:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>WM_STATE management</li>
|
||||
<li>WM_PROTOCOLS (WM_DELETE_WINDOW, WM_TAKE_FOCUS)</li>
|
||||
<li>WM_NORMAL_HINTS (size hints)</li>
|
||||
<li>WM_CLASS for window matching</li>
|
||||
<li>WM_NAME and _NET_WM_NAME</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Technical Specifications</h2>
|
||||
<div class="table-wrapper">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Specification</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Language</td>
|
||||
<td>ANSI C (C99)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Maximum Clients</td>
|
||||
<td>256 windows</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Workspaces</td>
|
||||
<td>9 virtual desktops</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Monitor Support</td>
|
||||
<td>Up to 8 monitors (Xinerama/Xrandr)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Notifications</td>
|
||||
<td>32 concurrent</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Keybindings</td>
|
||||
<td>64 configurable shortcuts</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Memory Usage</td>
|
||||
<td>< 5MB typical</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Configuration</td>
|
||||
<td>INI-style (~/.config/dwn/config)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Learning DWN</h2>
|
||||
<p style="color: var(--text-muted); max-width: 700px; margin-bottom: 2rem;">
|
||||
Two built-in modes help you learn DWN quickly: an interactive tutorial and
|
||||
an automated demo that showcases all features.
|
||||
</p>
|
||||
<div class="features-grid" style="grid-template-columns: repeat(2, 1fr);">
|
||||
<div class="card">
|
||||
<h3><span class="card-icon">📚</span> Interactive Tutorial</h3>
|
||||
<p>Press <kbd>Super</kbd> + <kbd>T</kbd> to start a hands-on tutorial that:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Guides you through essential shortcuts step-by-step</li>
|
||||
<li>Waits for you to press the correct key combination</li>
|
||||
<li>Automatically advances when you complete each step</li>
|
||||
<li>Can be restarted at any time</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3><span class="card-icon">🎬</span> Demo Mode</h3>
|
||||
<p>Press <kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>D</kbd> for an automated showcase:</p>
|
||||
<ul style="margin-top: 1rem; padding-left: 1.25rem; color: var(--text-muted);">
|
||||
<li>Demonstrates window management, workspaces, and layouts</li>
|
||||
<li>Shows panel features and system tray</li>
|
||||
<li>Highlights AI integration and news ticker</li>
|
||||
<li>Displays complete keyboard shortcut reference</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container" style="text-align: center;">
|
||||
<h2>Ready to Try DWN?</h2>
|
||||
<p style="color: var(--text-muted); max-width: 500px; margin: 0 auto 2rem;">
|
||||
Get started in minutes with our simple installation process.
|
||||
</p>
|
||||
<div class="hero-buttons" style="justify-content: center;">
|
||||
<a href="installation.html" class="btn btn-primary btn-lg">Install Now</a>
|
||||
<a href="shortcuts.html" class="btn btn-secondary btn-lg">View Shortcuts</a>
|
||||
</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
-288
@@ -1,288 +0,0 @@
|
||||
<!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="DWN - A modern, production-ready X11 window manager with XFCE-like functionality and optional AI integration.">
|
||||
<meta name="keywords" content="window manager, X11, Linux, tiling, floating, EWMH, AI, productivity">
|
||||
<title>DWN Window Manager - Modern X11 Window Management</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" class="active">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">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>
|
||||
<a href="design-patterns.html">Design Patterns</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">
|
||||
<div class="container hero-content">
|
||||
<h1>Modern Window Management for X11</h1>
|
||||
<p class="subtitle">
|
||||
DWN is a production-ready window manager written in ANSI C with XFCE-like functionality,
|
||||
powerful tiling layouts, and optional AI integration. Fast, flexible, and fully featured.
|
||||
</p>
|
||||
<div class="hero-buttons">
|
||||
<a href="installation.html" class="btn btn-primary btn-lg">Get Started</a>
|
||||
<a href="features.html" class="btn btn-secondary btn-lg">Explore Features</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 style="text-align: center; margin-bottom: 1rem;">Why Choose DWN?</h2>
|
||||
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 3rem;">
|
||||
DWN combines the simplicity of traditional floating window managers with the productivity
|
||||
of tiling layouts, all wrapped in a modern, customizable package.
|
||||
</p>
|
||||
<div class="features-grid">
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">☰</div>
|
||||
<h3>Multiple Layouts</h3>
|
||||
<p>Switch seamlessly between tiling, floating, and monocle layouts.
|
||||
Resize master areas and organize windows exactly how you work.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⚙</div>
|
||||
<h3>9 Workspaces</h3>
|
||||
<p>Organize your workflow across 9 virtual desktops with per-workspace
|
||||
state. Move windows between workspaces with a single keystroke.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🎨</div>
|
||||
<h3>Fully Customizable</h3>
|
||||
<p>INI-style configuration with extensive theming options. Customize colors,
|
||||
fonts, borders, gaps, and behavior to match your style.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">💡</div>
|
||||
<h3>AI Integration</h3>
|
||||
<p>Optional AI command palette and semantic web search. Control your desktop
|
||||
with natural language and get intelligent suggestions.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">🔔</div>
|
||||
<h3>Notification Daemon</h3>
|
||||
<p>Built-in D-Bus notification support following freedesktop.org standards.
|
||||
No need for external notification daemons.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">💻</div>
|
||||
<h3>System Tray</h3>
|
||||
<p>XEmbed protocol for external app icons (Telegram, Bluetooth, etc.) plus
|
||||
built-in battery, volume, and WiFi widgets with interactive controls.</p>
|
||||
</div>
|
||||
<div class="feature-card">
|
||||
<div class="feature-icon">⚡</div>
|
||||
<h3>XDG Autostart</h3>
|
||||
<p>Automatic startup of system services and tray applications following the
|
||||
XDG Autostart spec. Works with nm-applet, blueman, and more.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<h3>~15K</h3>
|
||||
<p>Lines of Pure C</p>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<h3>0</h3>
|
||||
<p>Runtime Dependencies</p>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<h3><5MB</h3>
|
||||
<p>Memory Footprint</p>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<h3>43+</h3>
|
||||
<p>Keyboard Shortcuts</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 style="text-align: center;">Get Up and Running in Minutes</h2>
|
||||
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 3rem;">
|
||||
DWN is designed for easy installation and immediate productivity.
|
||||
Build from source or use your distribution's package manager.
|
||||
</p>
|
||||
<div class="card" style="max-width: 700px; margin: 0 auto;">
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Clone the repository
|
||||
git clone https://retoor.molodetz.nl/retoor/dwn.git
|
||||
cd dwn
|
||||
# Install dependencies (auto-detects your distro)
|
||||
make deps
|
||||
# Build and install
|
||||
make
|
||||
sudo make install
|
||||
# Add to your .xinitrc
|
||||
echo "exec dwn" >> ~/.xinitrc</code></pre>
|
||||
</div>
|
||||
<p style="text-align: center; margin-top: 2rem;">
|
||||
<a href="installation.html" class="btn btn-primary">Full Installation Guide</a>
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2 style="text-align: center;">See DWN in Action</h2>
|
||||
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 3rem;">
|
||||
A clean, modern interface that stays out of your way while providing everything you need.
|
||||
</p>
|
||||
<div class="screenshot-grid">
|
||||
<div class="screenshot">
|
||||
<div class="screenshot-placeholder">
|
||||
[Tiling Layout with Terminal and Editor]
|
||||
</div>
|
||||
<div class="screenshot-caption">Master-stack tiling layout perfect for development</div>
|
||||
</div>
|
||||
<div class="screenshot">
|
||||
<div class="screenshot-placeholder">
|
||||
[System Tray and Notifications]
|
||||
</div>
|
||||
<div class="screenshot-caption">Integrated system tray with volume and WiFi controls</div>
|
||||
</div>
|
||||
<div class="screenshot">
|
||||
<div class="screenshot-placeholder">
|
||||
[AI Command Palette]
|
||||
</div>
|
||||
<div class="screenshot-caption">AI-powered command palette for natural language control</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2 style="text-align: center;">How DWN Compares</h2>
|
||||
<p style="text-align: center; color: var(--text-muted); max-width: 600px; margin: 0 auto 3rem;">
|
||||
DWN bridges the gap between minimal tiling managers and full desktop environments.
|
||||
</p>
|
||||
<div class="comparison">
|
||||
<div class="comparison-card">
|
||||
<h3>Minimal Tiling WMs</h3>
|
||||
<p style="color: var(--text-muted);">dwm, i3, bspwm</p>
|
||||
<ul>
|
||||
<li>Lightweight and fast</li>
|
||||
<li>Keyboard-driven workflow</li>
|
||||
<li>Highly customizable</li>
|
||||
<li>Steep learning curve</li>
|
||||
<li>Requires additional tools</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="comparison-card featured">
|
||||
<h3>DWN</h3>
|
||||
<p style="color: var(--text-muted);">Best of both worlds</p>
|
||||
<ul>
|
||||
<li>Lightweight and fast</li>
|
||||
<li>Keyboard-driven workflow</li>
|
||||
<li>Highly customizable</li>
|
||||
<li>Interactive tutorial</li>
|
||||
<li>Built-in panels and systray</li>
|
||||
<li>AI integration optional</li>
|
||||
<li>Notification daemon included</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="comparison-card">
|
||||
<h3>Desktop Environments</h3>
|
||||
<p style="color: var(--text-muted);">XFCE, GNOME, KDE</p>
|
||||
<ul>
|
||||
<li>Feature complete</li>
|
||||
<li>User-friendly</li>
|
||||
<li>Heavy resource usage</li>
|
||||
<li>Less customizable</li>
|
||||
<li>Slower performance</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container" style="text-align: center;">
|
||||
<h2>Ready to Try DWN?</h2>
|
||||
<div class="hero-buttons" style="justify-content: center;">
|
||||
<a href="installation.html" class="btn btn-primary btn-lg">Install DWN</a>
|
||||
<a href="documentation.html" class="btn btn-secondary btn-lg">Read the Docs</a>
|
||||
</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,561 +0,0 @@
|
||||
<!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="Install DWN window manager - step by step guide for Debian, Ubuntu, Fedora, Arch Linux and more.">
|
||||
<title>Installation - 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" class="active">Install</a></li>
|
||||
<li class="dropdown">
|
||||
<a href="documentation.html">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>
|
||||
<a href="design-patterns.html">Design Patterns</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>Installation Guide</h1>
|
||||
<p class="subtitle">
|
||||
Get DWN running on your system in just a few minutes.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Requirements</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 2rem;">
|
||||
DWN requires X11 and a few common libraries. Most Linux distributions include these by default.
|
||||
</p>
|
||||
<div class="card">
|
||||
<h3>Required Dependencies</h3>
|
||||
<div class="table-wrapper" style="margin-top: 1rem;">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Library</th>
|
||||
<th>Purpose</th>
|
||||
<th>Package (Debian/Ubuntu)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>libX11</td>
|
||||
<td>X Window System client library</td>
|
||||
<td><code>libx11-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>libXext</td>
|
||||
<td>X extensions library</td>
|
||||
<td><code>libxext-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>libXinerama</td>
|
||||
<td>Multi-monitor support</td>
|
||||
<td><code>libxinerama-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>libXrandr</td>
|
||||
<td>Display configuration</td>
|
||||
<td><code>libxrandr-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>libXft</td>
|
||||
<td>Font rendering</td>
|
||||
<td><code>libxft-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>fontconfig</td>
|
||||
<td>Font configuration</td>
|
||||
<td><code>libfontconfig1-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>libdbus-1</td>
|
||||
<td>D-Bus for notifications</td>
|
||||
<td><code>libdbus-1-dev</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>libcurl</td>
|
||||
<td>AI features (optional)</td>
|
||||
<td><code>libcurl4-openssl-dev</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2>Quick Installation</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 2rem;">
|
||||
The fastest way to get started. Our build system auto-detects your distribution.
|
||||
</p>
|
||||
<div class="steps">
|
||||
<div class="step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-content">
|
||||
<h4>Clone the Repository</h4>
|
||||
<p>Download the latest source code from GitHub.</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>git clone https://retoor.molodetz.nl/retoor/dwn.git
|
||||
cd dwn</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-content">
|
||||
<h4>Install Dependencies</h4>
|
||||
<p>Automatically install required packages for your distribution.</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>make deps</code></pre>
|
||||
<p style="margin-top: 0.5rem; font-size: 0.875rem; color: var(--text-muted);">
|
||||
Supports Debian, Ubuntu, Fedora, Arch, openSUSE, and Void Linux.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-content">
|
||||
<h4>Build DWN</h4>
|
||||
<p>Compile the window manager with optimizations.</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>make</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-content">
|
||||
<h4>Install System-wide</h4>
|
||||
<p>Install the binary to your system PATH.</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>sudo make install</code></pre>
|
||||
<p style="margin-top: 0.5rem; font-size: 0.875rem; color: var(--text-muted);">
|
||||
Default location: <code>/usr/local/bin/dwn</code>. Override with <code>PREFIX=/custom/path make install</code>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">5</div>
|
||||
<div class="step-content">
|
||||
<h4>Configure Your Session</h4>
|
||||
<p>Add DWN to your X session startup.</p>
|
||||
<div class="code-header">
|
||||
<span>~/.xinitrc</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>exec dwn</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Distribution-Specific Instructions</h2>
|
||||
<div class="tabs">
|
||||
<button class="tab active" onclick="showTab('debian')">Debian/Ubuntu</button>
|
||||
<button class="tab" onclick="showTab('fedora')">Fedora</button>
|
||||
<button class="tab" onclick="showTab('arch')">Arch Linux</button>
|
||||
<button class="tab" onclick="showTab('void')">Void Linux</button>
|
||||
</div>
|
||||
<div id="debian" class="tab-content active">
|
||||
<h3>Debian / Ubuntu / Linux Mint</h3>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Install dependencies
|
||||
sudo apt update
|
||||
sudo apt install -y \
|
||||
build-essential \
|
||||
libx11-dev \
|
||||
libxext-dev \
|
||||
libxinerama-dev \
|
||||
libxrandr-dev \
|
||||
libxft-dev \
|
||||
libfontconfig1-dev \
|
||||
libdbus-1-dev \
|
||||
libcurl4-openssl-dev \
|
||||
pkg-config
|
||||
# Build and install
|
||||
git clone https://retoor.molodetz.nl/retoor/dwn.git
|
||||
cd dwn
|
||||
make
|
||||
sudo make install</code></pre>
|
||||
</div>
|
||||
<div id="fedora" class="tab-content">
|
||||
<h3>Fedora / RHEL / CentOS</h3>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Install dependencies
|
||||
sudo dnf install -y \
|
||||
gcc \
|
||||
make \
|
||||
libX11-devel \
|
||||
libXext-devel \
|
||||
libXinerama-devel \
|
||||
libXrandr-devel \
|
||||
libXft-devel \
|
||||
fontconfig-devel \
|
||||
dbus-devel \
|
||||
libcurl-devel \
|
||||
pkg-config
|
||||
# Build and install
|
||||
git clone https://retoor.molodetz.nl/retoor/dwn.git
|
||||
cd dwn
|
||||
make
|
||||
sudo make install</code></pre>
|
||||
</div>
|
||||
<div id="arch" class="tab-content">
|
||||
<h3>Arch Linux / Manjaro</h3>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Install dependencies
|
||||
sudo pacman -S --needed \
|
||||
base-devel \
|
||||
libx11 \
|
||||
libxext \
|
||||
libxinerama \
|
||||
libxrandr \
|
||||
libxft \
|
||||
fontconfig \
|
||||
dbus \
|
||||
curl \
|
||||
pkg-config
|
||||
# Build and install
|
||||
git clone https://retoor.molodetz.nl/retoor/dwn.git
|
||||
cd dwn
|
||||
make
|
||||
sudo make install</code></pre>
|
||||
<div class="alert alert-info" style="margin-top: 1rem;">
|
||||
<strong>AUR Package</strong>
|
||||
<p style="margin: 0;">An AUR package <code>dwn-git</code> may also be available:
|
||||
<code>yay -S dwn-git</code></p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="void" class="tab-content">
|
||||
<h3>Void Linux</h3>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Install dependencies
|
||||
sudo xbps-install -S \
|
||||
base-devel \
|
||||
libX11-devel \
|
||||
libXext-devel \
|
||||
libXinerama-devel \
|
||||
libXrandr-devel \
|
||||
libXft-devel \
|
||||
fontconfig-devel \
|
||||
dbus-devel \
|
||||
libcurl-devel \
|
||||
pkg-config
|
||||
# Build and install
|
||||
git clone https://retoor.molodetz.nl/retoor/dwn.git
|
||||
cd dwn
|
||||
make
|
||||
sudo make install</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2>Session Setup</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 2rem;">
|
||||
Configure your display manager or xinit to start DWN.
|
||||
</p>
|
||||
<div class="features-grid" style="grid-template-columns: repeat(2, 1fr);">
|
||||
<div class="card">
|
||||
<h3>Using xinit / startx</h3>
|
||||
<p>For minimal setups using startx:</p>
|
||||
<div class="code-header">
|
||||
<span>~/.xinitrc</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Optional: set display settings
|
||||
xrandr --output DP-1 --mode 2560x1440
|
||||
# Optional: set wallpaper
|
||||
feh --bg-fill ~/wallpaper.jpg
|
||||
# Start DWN
|
||||
exec dwn</code></pre>
|
||||
<p style="margin-top: 1rem; font-size: 0.875rem; color: var(--text-muted);">
|
||||
Then run <code>startx</code> from a TTY.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card">
|
||||
<h3>Using a Display Manager</h3>
|
||||
<p>Create a desktop entry for GDM, LightDM, etc:</p>
|
||||
<div class="code-header">
|
||||
<span>/usr/share/xsessions/dwn.desktop</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>[Desktop Entry]
|
||||
Name=DWN
|
||||
Comment=DWN Window Manager
|
||||
Exec=dwn
|
||||
Type=Application
|
||||
DesktopNames=DWN</code></pre>
|
||||
<p style="margin-top: 1rem; font-size: 0.875rem; color: var(--text-muted);">
|
||||
DWN will appear in your display manager's session menu.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Testing in a Nested X Server</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 2rem;">
|
||||
Test DWN without leaving your current session using Xephyr.
|
||||
</p>
|
||||
<div class="card">
|
||||
<h3>Using make run</h3>
|
||||
<p>The easiest way to test DWN safely:</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Make sure Xephyr is installed
|
||||
# Debian/Ubuntu: sudo apt install xserver-xephyr
|
||||
# Fedora: sudo dnf install xorg-x11-server-Xephyr
|
||||
# Arch: sudo pacman -S xorg-server-xephyr
|
||||
# Run DWN in a nested window
|
||||
make run</code></pre>
|
||||
<p style="margin-top: 1rem; color: var(--text-muted);">
|
||||
This opens a 1280x720 window running DWN. Perfect for experimenting with configuration changes.
|
||||
</p>
|
||||
</div>
|
||||
<div class="card" style="margin-top: 1.5rem;">
|
||||
<h3>Manual Xephyr Setup</h3>
|
||||
<p>For more control over the test environment:</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code># Start Xephyr on display :1
|
||||
Xephyr :1 -screen 1920x1080 &
|
||||
# Run DWN on that display
|
||||
DISPLAY=:1 ./dwn
|
||||
# Open a terminal in the test environment
|
||||
DISPLAY=:1 xterm &</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container">
|
||||
<h2>Post-Installation</h2>
|
||||
<div class="steps">
|
||||
<div class="step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-content">
|
||||
<h4>Create Configuration Directory</h4>
|
||||
<p>DWN will create this automatically on first run, but you can set it up in advance:</p>
|
||||
<div class="code-header">
|
||||
<span>Terminal</span>
|
||||
<button class="copy-btn" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<pre><code>mkdir -p ~/.config/dwn</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-content">
|
||||
<h4>Run the Interactive Tutorial</h4>
|
||||
<p>Once DWN is running, press <kbd>Super</kbd> + <kbd>T</kbd> to start the built-in tutorial
|
||||
that will teach you all the essential shortcuts.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-content">
|
||||
<h4>View All Shortcuts</h4>
|
||||
<p>Press <kbd>Super</kbd> + <kbd>S</kbd> to see a complete list of keyboard shortcuts.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-content">
|
||||
<h4>Customize Your Setup</h4>
|
||||
<p>See the <a href="configuration.html">Configuration Guide</a> to personalize DWN to your liking.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<h2>Troubleshooting</h2>
|
||||
<div class="faq-item">
|
||||
<button class="faq-question" onclick="toggleFaq(this)">
|
||||
DWN doesn't start - "cannot open display"
|
||||
</button>
|
||||
<div class="faq-answer">
|
||||
<div class="faq-answer-content">
|
||||
<p>This error means DWN can't connect to an X server. Make sure:</p>
|
||||
<ul style="padding-left: 1.25rem; margin-top: 0.5rem;">
|
||||
<li>You're running from a TTY with <code>startx</code>, not from within another X session</li>
|
||||
<li>The DISPLAY environment variable is set correctly</li>
|
||||
<li>X server is installed and working (<code>Xorg -configure</code> to test)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<button class="faq-question" onclick="toggleFaq(this)">
|
||||
Build fails - "pkg-config: command not found"
|
||||
</button>
|
||||
<div class="faq-answer">
|
||||
<div class="faq-answer-content">
|
||||
<p>Install pkg-config for your distribution:</p>
|
||||
<pre style="margin-top: 0.5rem;"><code>sudo apt install pkg-config # Debian/Ubuntu
|
||||
sudo dnf install pkg-config # Fedora
|
||||
sudo pacman -S pkg-config # Arch</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<button class="faq-question" onclick="toggleFaq(this)">
|
||||
Missing header files during build
|
||||
</button>
|
||||
<div class="faq-answer">
|
||||
<div class="faq-answer-content">
|
||||
<p>Make sure you have the development packages installed, not just the runtime libraries.
|
||||
On Debian/Ubuntu, install packages ending with <code>-dev</code>.
|
||||
Run <code>make deps</code> to auto-install everything.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<button class="faq-question" onclick="toggleFaq(this)">
|
||||
Keyboard shortcuts don't work
|
||||
</button>
|
||||
<div class="faq-answer">
|
||||
<div class="faq-answer-content">
|
||||
<p>Check for conflicts with other programs grabbing keys:</p>
|
||||
<ul style="padding-left: 1.25rem; margin-top: 0.5rem;">
|
||||
<li>Make sure no other window manager is running</li>
|
||||
<li>Check if compositor (like picom) is grabbing keys</li>
|
||||
<li>Verify keyboard layout is correct with <code>setxkbmap</code></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="faq-item">
|
||||
<button class="faq-question" onclick="toggleFaq(this)">
|
||||
Fonts look bad or missing
|
||||
</button>
|
||||
<div class="faq-answer">
|
||||
<div class="faq-answer-content">
|
||||
<p>DWN uses Xft for font rendering. Install some good fonts:</p>
|
||||
<pre style="margin-top: 0.5rem;"><code>sudo apt install fonts-dejavu fonts-liberation # Debian/Ubuntu
|
||||
sudo dnf install dejavu-fonts-all liberation-fonts # Fedora</code></pre>
|
||||
<p style="margin-top: 0.5rem;">You can configure the font in <code>~/.config/dwn/config</code>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section section-alt">
|
||||
<div class="container" style="text-align: center;">
|
||||
<h2>Installation Complete?</h2>
|
||||
<p style="color: var(--text-muted); max-width: 500px; margin: 0 auto 2rem;">
|
||||
Learn how to use DWN effectively with our documentation.
|
||||
</p>
|
||||
<div class="hero-buttons" style="justify-content: center;">
|
||||
<a href="documentation.html" class="btn btn-primary btn-lg">Getting Started Guide</a>
|
||||
<a href="shortcuts.html" class="btn btn-secondary btn-lg">Keyboard Shortcuts</a>
|
||||
</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - MIT License</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
-304
@@ -1,304 +0,0 @@
|
||||
/**
|
||||
* DWN Window Manager - Website JavaScript
|
||||
* Vanilla JS for interactivity
|
||||
*/
|
||||
|
||||
// Mobile Navigation Toggle
|
||||
function toggleNav() {
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
const toggle = document.querySelector('.nav-toggle');
|
||||
|
||||
navLinks.classList.toggle('active');
|
||||
toggle.classList.toggle('active');
|
||||
}
|
||||
|
||||
// Close mobile nav when clicking outside
|
||||
document.addEventListener('click', function(e) {
|
||||
const nav = document.querySelector('nav');
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
|
||||
if (navLinks && navLinks.classList.contains('active')) {
|
||||
if (!nav.contains(e.target)) {
|
||||
navLinks.classList.remove('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Close mobile nav when clicking a link
|
||||
document.querySelectorAll('.nav-links a').forEach(link => {
|
||||
link.addEventListener('click', () => {
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
if (navLinks) {
|
||||
navLinks.classList.remove('active');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Tab functionality
|
||||
function showTab(tabId) {
|
||||
// Hide all tab contents
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.classList.remove('active');
|
||||
});
|
||||
|
||||
// Deactivate all tabs
|
||||
document.querySelectorAll('.tab').forEach(tab => {
|
||||
tab.classList.remove('active');
|
||||
});
|
||||
|
||||
// Show selected tab content
|
||||
const selectedContent = document.getElementById(tabId);
|
||||
if (selectedContent) {
|
||||
selectedContent.classList.add('active');
|
||||
}
|
||||
|
||||
// Activate clicked tab
|
||||
event.target.classList.add('active');
|
||||
}
|
||||
|
||||
// FAQ Accordion
|
||||
function toggleFaq(button) {
|
||||
const faqItem = button.closest('.faq-item');
|
||||
const isActive = faqItem.classList.contains('active');
|
||||
|
||||
// Close all FAQ items
|
||||
document.querySelectorAll('.faq-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
|
||||
// Open clicked item if it wasn't already open
|
||||
if (!isActive) {
|
||||
faqItem.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
// Copy to clipboard functionality
|
||||
function copyCode(button) {
|
||||
const codeBlock = button.closest('.code-header').nextElementSibling;
|
||||
const code = codeBlock.querySelector('code');
|
||||
|
||||
if (code) {
|
||||
const text = code.textContent;
|
||||
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
// Show feedback
|
||||
const originalText = button.textContent;
|
||||
button.textContent = 'Copied!';
|
||||
button.style.background = 'var(--success)';
|
||||
|
||||
setTimeout(() => {
|
||||
button.textContent = originalText;
|
||||
button.style.background = '';
|
||||
}, 2000);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy:', err);
|
||||
button.textContent = 'Error';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Shortcut search/filter functionality
|
||||
function filterShortcuts() {
|
||||
const searchInput = document.getElementById('shortcut-search');
|
||||
if (!searchInput) return;
|
||||
|
||||
const filter = searchInput.value.toLowerCase();
|
||||
const tables = document.querySelectorAll('.shortcuts-table');
|
||||
|
||||
tables.forEach(table => {
|
||||
const rows = table.querySelectorAll('tbody tr');
|
||||
let visibleCount = 0;
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
if (text.includes(filter)) {
|
||||
row.style.display = '';
|
||||
visibleCount++;
|
||||
} else {
|
||||
row.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Show/hide the entire table section if no matches
|
||||
const section = table.closest('.table-wrapper');
|
||||
const header = section ? section.previousElementSibling : null;
|
||||
|
||||
if (section && header && header.tagName === 'H2') {
|
||||
if (visibleCount === 0 && filter !== '') {
|
||||
section.style.display = 'none';
|
||||
header.style.display = 'none';
|
||||
} else {
|
||||
section.style.display = '';
|
||||
header.style.display = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Smooth scroll for anchor links
|
||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||||
anchor.addEventListener('click', function(e) {
|
||||
const href = this.getAttribute('href');
|
||||
if (href === '#') return;
|
||||
|
||||
e.preventDefault();
|
||||
const target = document.querySelector(href);
|
||||
|
||||
if (target) {
|
||||
const headerOffset = 80; // Account for fixed header
|
||||
const elementPosition = target.getBoundingClientRect().top;
|
||||
const offsetPosition = elementPosition + window.pageYOffset - headerOffset;
|
||||
|
||||
window.scrollTo({
|
||||
top: offsetPosition,
|
||||
behavior: 'smooth'
|
||||
});
|
||||
|
||||
// Update URL without scrolling
|
||||
history.pushState(null, null, href);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Highlight active section in docs sidebar
|
||||
function updateActiveSection() {
|
||||
const sidebar = document.querySelector('.docs-sidebar');
|
||||
if (!sidebar) return;
|
||||
|
||||
const sections = document.querySelectorAll('h2[id], h3[id]');
|
||||
const links = sidebar.querySelectorAll('a[href^="#"]');
|
||||
|
||||
let currentSection = '';
|
||||
const scrollPos = window.scrollY + 100;
|
||||
|
||||
sections.forEach(section => {
|
||||
if (section.offsetTop <= scrollPos) {
|
||||
currentSection = section.id;
|
||||
}
|
||||
});
|
||||
|
||||
links.forEach(link => {
|
||||
link.classList.remove('active');
|
||||
if (link.getAttribute('href') === '#' + currentSection) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Throttle scroll events
|
||||
let scrollTimeout;
|
||||
window.addEventListener('scroll', () => {
|
||||
if (scrollTimeout) return;
|
||||
|
||||
scrollTimeout = setTimeout(() => {
|
||||
updateActiveSection();
|
||||
scrollTimeout = null;
|
||||
}, 100);
|
||||
});
|
||||
|
||||
// Header background on scroll
|
||||
function updateHeaderBackground() {
|
||||
const header = document.querySelector('header');
|
||||
if (!header) return;
|
||||
|
||||
if (window.scrollY > 50) {
|
||||
header.style.background = 'rgba(26, 26, 46, 0.98)';
|
||||
} else {
|
||||
header.style.background = 'rgba(26, 26, 46, 0.95)';
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', updateHeaderBackground);
|
||||
|
||||
// Animate elements on scroll (intersection observer)
|
||||
function initScrollAnimations() {
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach(entry => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add('fade-in');
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, {
|
||||
threshold: 0.1,
|
||||
rootMargin: '0px 0px -50px 0px'
|
||||
});
|
||||
|
||||
// Observe feature cards and other elements
|
||||
document.querySelectorAll('.feature-card, .card, .comparison-card, .testimonial').forEach(el => {
|
||||
el.style.opacity = '0';
|
||||
observer.observe(el);
|
||||
});
|
||||
}
|
||||
|
||||
// Keyboard shortcut for search (/)
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === '/' && !['INPUT', 'TEXTAREA'].includes(document.activeElement.tagName)) {
|
||||
const searchInput = document.getElementById('shortcut-search');
|
||||
if (searchInput) {
|
||||
e.preventDefault();
|
||||
searchInput.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Escape to close search/nav
|
||||
if (e.key === 'Escape') {
|
||||
const searchInput = document.getElementById('shortcut-search');
|
||||
if (searchInput && document.activeElement === searchInput) {
|
||||
searchInput.blur();
|
||||
searchInput.value = '';
|
||||
filterShortcuts();
|
||||
}
|
||||
|
||||
const navLinks = document.querySelector('.nav-links');
|
||||
if (navLinks) {
|
||||
navLinks.classList.remove('active');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// External link handling - open in new tab
|
||||
document.querySelectorAll('a[href^="http"]').forEach(link => {
|
||||
if (!link.hostname.includes(window.location.hostname)) {
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('rel', 'noopener noreferrer');
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize on DOM ready
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
initScrollAnimations();
|
||||
updateActiveSection();
|
||||
updateHeaderBackground();
|
||||
|
||||
// Set current year in footer if needed
|
||||
const yearSpan = document.querySelector('.current-year');
|
||||
if (yearSpan) {
|
||||
yearSpan.textContent = new Date().getFullYear();
|
||||
}
|
||||
});
|
||||
|
||||
// Print-friendly handling
|
||||
window.addEventListener('beforeprint', () => {
|
||||
// Expand all FAQs for printing
|
||||
document.querySelectorAll('.faq-item').forEach(item => {
|
||||
item.classList.add('active');
|
||||
});
|
||||
|
||||
// Show all tab contents
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('afterprint', () => {
|
||||
// Restore FAQ state
|
||||
document.querySelectorAll('.faq-item').forEach(item => {
|
||||
item.classList.remove('active');
|
||||
});
|
||||
|
||||
// Restore tab state
|
||||
document.querySelectorAll('.tab-content').forEach(content => {
|
||||
content.style.display = '';
|
||||
});
|
||||
});
|
||||
@@ -1,460 +0,0 @@
|
||||
<!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 keyboard shortcuts reference for DWN window manager.">
|
||||
<title>Keyboard Shortcuts - 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>
|
||||
<a href="design-patterns.html">Design Patterns</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>Keyboard Shortcuts</h1>
|
||||
<p class="subtitle">
|
||||
Complete reference for all DWN keyboard shortcuts.
|
||||
Press <kbd>Super</kbd> + <kbd>S</kbd> in DWN to view this list.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
<div class="search-box">
|
||||
<input type="text" id="shortcut-search" placeholder="Search shortcuts..." onkeyup="filterShortcuts()">
|
||||
</div>
|
||||
<h2 id="launchers">Application Launchers</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>T</kbd></td>
|
||||
<td>Open terminal (configurable)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> / <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 (configurable)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>B</kbd></td>
|
||||
<td>Open web browser</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Print</kbd></td>
|
||||
<td>Take screenshot (xfce4-screenshooter)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="windows">Window Management</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">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>F9</kbd></td>
|
||||
<td>Toggle minimize/restore</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 mode for focused window</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="workspaces">Workspace Navigation</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>F1</kbd></td>
|
||||
<td>Switch to workspace 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F2</kbd></td>
|
||||
<td>Switch to workspace 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F3</kbd></td>
|
||||
<td>Switch to workspace 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F4</kbd></td>
|
||||
<td>Switch to workspace 4</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F5</kbd></td>
|
||||
<td>Switch to workspace 5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F6</kbd></td>
|
||||
<td>Switch to workspace 6</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F7</kbd></td>
|
||||
<td>Switch to workspace 7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F8</kbd></td>
|
||||
<td>Switch to workspace 8</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>F9</kbd></td>
|
||||
<td>Switch to workspace 9</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F1</kbd></td>
|
||||
<td>Move focused window to workspace 1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F2</kbd></td>
|
||||
<td>Move focused window to workspace 2</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F3</kbd></td>
|
||||
<td>Move focused window to workspace 3</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F4</kbd></td>
|
||||
<td>Move focused window to workspace 4</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F5</kbd></td>
|
||||
<td>Move focused window to workspace 5</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F6</kbd></td>
|
||||
<td>Move focused window to workspace 6</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F7</kbd></td>
|
||||
<td>Move focused window to workspace 7</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F8</kbd></td>
|
||||
<td>Move focused window to workspace 8</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Shift</kbd> + <kbd>F9</kbd></td>
|
||||
<td>Move focused window to workspace 9</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Right</kbd></td>
|
||||
<td>Switch to next workspace</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>Left</kbd></td>
|
||||
<td>Switch to previous workspace</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="layouts">Layout Control</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Space</kbd></td>
|
||||
<td>Cycle layout mode (tiling → floating → monocle)</td>
|
||||
</tr>
|
||||
<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="snapping">Window Snapping (Composable)</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 1rem;">
|
||||
Snapping shortcuts are composable: press Super+Left then Super+Up for top-left quarter. Press the same key twice to expand to full in that axis.
|
||||
</p>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Left</kbd></td>
|
||||
<td>Snap left 50% (press twice for full width)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Right</kbd></td>
|
||||
<td>Snap right 50% (press twice for full width)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Up</kbd></td>
|
||||
<td>Snap top 50% (press twice for full height)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Down</kbd></td>
|
||||
<td>Snap bottom 50% (press twice for full height)</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="ai">AI Features</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 1rem;">
|
||||
These shortcuts require API keys to be configured. See <a href="ai-features.html">AI Features</a> for setup.
|
||||
</p>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>A</kbd></td>
|
||||
<td>Show AI context analysis</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd></td>
|
||||
<td>Open AI command palette</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd></td>
|
||||
<td>Open Exa semantic web search</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="news">News Ticker</h2>
|
||||
<p style="color: var(--text-muted); margin-bottom: 1rem;">
|
||||
The scrolling news ticker is displayed in the bottom panel.
|
||||
</p>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Return</kbd></td>
|
||||
<td>Open current article in browser</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<h2 id="system">Help & System</h2>
|
||||
<div class="table-wrapper">
|
||||
<table class="shortcuts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 40%;">Shortcut</th>
|
||||
<th>Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>S</kbd></td>
|
||||
<td>Show all keyboard shortcuts</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>T</kbd></td>
|
||||
<td>Start/continue interactive tutorial</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>D</kbd></td>
|
||||
<td>Start/stop demo mode</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><kbd>Super</kbd> + <kbd>Backspace</kbd></td>
|
||||
<td>Quit DWN</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="card" style="margin-top: 3rem;">
|
||||
<h3>Printable Quick Reference</h3>
|
||||
<p>Essential shortcuts to memorize when starting with DWN:</p>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 1.5rem; margin-top: 1.5rem;">
|
||||
<div>
|
||||
<h4 style="color: var(--primary); margin-bottom: 0.75rem;">Must Know</h4>
|
||||
<ul style="list-style: none; padding: 0;">
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>T</kbd> Terminal
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Alt</kbd>+<kbd>F2</kbd> Launcher
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Alt</kbd>+<kbd>F4</kbd> Close window
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Alt</kbd>+<kbd>Tab</kbd> Switch windows
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0;">
|
||||
<kbd>F1</kbd>-<kbd>F9</kbd> Workspaces
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<h4 style="color: var(--primary); margin-bottom: 0.75rem;">Power User</h4>
|
||||
<ul style="list-style: none; padding: 0;">
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Super</kbd>+<kbd>Space</kbd> Change layout
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Super</kbd>+<kbd>H</kbd>/<kbd>L</kbd> Resize master
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Super</kbd>+Arrows Composable snap
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0; border-bottom: 1px solid var(--border-color);">
|
||||
<kbd>Shift</kbd>+<kbd>F1-9</kbd> Move to workspace
|
||||
</li>
|
||||
<li style="padding: 0.5rem 0;">
|
||||
<kbd>Super</kbd>+<kbd>S</kbd> Show shortcuts
|
||||
</li>
|
||||
</ul>
|
||||
</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>
|
||||
<li><a href="design-patterns.html">Design Patterns</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 <retoor@molodetz.nl> - 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
Reference in New Issue
Block a user