Compare commits

..
34 Commits
Author SHA1 Message Date
retoor 3073db38f1 fix: skip unmap notifications for clients undergoing fullscreen unmanagement
Add CLIENT_UNMANAGING flag to prevent redundant unmap processing when client_set_fullscreen transitions a client out of fullscreen mode. Set flag before clearing CLIENT_FULLSCREEN and clear it after decorations render, with early return in handle_unmap_notify when flag is active.
2026-02-19 20:48:46 +00:00
retoor a6ff84dfca feat: add expose all windows keybinding and Alt-drag window manipulation with floating state tracking
Implement key_expose_all function bound to Alt+Space that unminimizes, unmaximizes, and unfullscreens all windows on current workspace then rearranges. Add floating_before_maximize boolean to Client struct to preserve floating state when toggling maximize. Enable Alt+LeftDrag for moving and Alt+RightDrag for resizing windows from any position with directional cursor support. Increase resize edge detection from 8 to 12 pixels. Add panel.h dependency to keys build target and update shortcut help text with new bindings.
2026-02-16 01:24:05 +00:00
retoor cd9e55dc64 build: add -ldl linker flag, recursive source discovery, and API test targets to Makefile 2026-02-07 12:04:52 +00:00
retoor 8e86b2d106 revert: remove dpi detection and configuration for mouse distance feature
This reverts commit df34716ea5, undoing the addition of dpi detection logic and related configuration options for mouse distance calculations.
2026-01-29 15:39:43 +00:00
retoor a462643110 revert: remove sysmon panel toggle click handling and map event integration
This reverts commit aee7030b30, stripping all sysmon references from panel click handlers, map/unmap event logic, expose rendering, button/motion event dispatch, and the Monitor struct sysmon field, along with the sysmon.h include chain in main.c and main.d dependency file.
2026-01-29 15:38:55 +00:00
retoor 1ba5fae79b feat: add system monitor feature with panel toggle and event handling
- Introduce SystemMonitor struct and typedef in dwn.h, add sysmon pointer to Monitor struct
- Include sysmon.h in main.c, integrate sysmon_find_by_window and sysmon_set_client into map request handling
- Add sysmon_render call in expose handler and sysmon_handle_event in button press and motion notify handlers
- Implement clickable stats and top process areas in bottom panel to toggle system monitor via sysmon_toggle
- Update build/main.d dependency file to include sysmon.h
2026-01-29 13:36:28 +00:00
retoor 5404adfcd5 feat: add dpi detection and configuration for mouse distance
Implement automatic DPI detection from EDID display data and a manual `mouse_distance_dpi` config option to convert mouse movement pixels into real-world centimeters. The new `calculate_pixels_per_cm()` function in main.c falls back to manual DPI override, then EDID-based auto-detection with sanity checks, and finally a default 96 DPI (37.8 px/cm). The `Config` struct gains a `mouse_distance_dpi` field parsed from config files, and `DWNState` stores the computed `pixels_per_cm` and a `dpi_auto_detected` flag. The panel renderer now uses `dwn->pixels_per_cm` instead of the hardcoded 37.8 constant for mouse distance display.
2026-01-28 15:55:00 +00:00
retoor 2303b53c70 feat: implement fixed-width toggling top process display for CPU and memory
Add TOP_PROCESS_WIDGET_WIDTH constant and process_show_cpu flag to SystemStats struct, enabling fixed-width rendering of top process panel widget that toggles between CPU and memory usage display. Remove dynamic width calculation function panel_calculate_top_process_width and integrate top process rendering into the main panel layout with consistent spacing.
2026-01-27 11:18:45 +00:00
retoor a0d5b1b091 docs: expand README with design philosophy, features, and architecture sections while adding panel dirty tracking, Xft color caching, and XInput2 mouse distance monitoring 2026-01-27 01:50:47 +00:00
retoor 2f096a55ac perf: increase typing activity speed multiplier from 3.0 to 10.0 in dwn_run
The speed_multiplier variable in dwn_run() is raised from 3.0f to 10.0f when the time since last typing activity is under 500ms, accelerating ambient phase progression during active typing sessions.
2026-01-26 23:51:19 +00:00
retoor 915870875d build: add XInput2 library dependency for raw key event monitoring 2026-01-26 23:47:23 +00:00
retoor 725267fa00 feat: add text glow animation with sinusoidal color cycling and alt-tab bold font rendering 2026-01-26 22:36:06 +00:00
retoor ce6729c53c feat: add animated focus transitions for window titles and taskbar colors 2026-01-26 22:16:04 +00:00
retoor eaa41d70ea feat: add directional window resize constants and multi-battery monitoring with per-battery breakdown in panel 2026-01-26 19:40:08 +00:00
retoor fb28931535 build: add libxtst, libpng, tesseract, and leptonica dependencies to Makefile for screenshot and ocr support 2026-01-24 15:17:47 +00:00
retoor 51c3d4b919 build: enable address/undefined sanitizer build target and add -Wpedantic -Wshadow -fstack-protector-strong flags 2026-01-24 12:26:45 +00:00
retoor c32bda853c chore: add alt-tab navigation with update_mru flag and MRU-based taskbar rendering 2026-01-16 17:24:59 +00:00
retoor a6eeeb978e feat: add 13 color configuration fields for panel, workspace, title, border, and notification themes
Add color customization support to the Config struct with 13 new char[16] fields covering panel background/foreground, workspace active/inactive/urgent, title focused/unfocused background/foreground, border focused/unfocused, and notification background/foreground. Implement default value initialization in config_set_defaults using strncpy with DEFAULT_* constants, and add parsing logic in handle_config_entry for the new "colors" section to populate these fields from configuration files.
2026-01-16 05:10:17 +00:00
retoor 6cd3a50f37 chore: remove unused c and o source files from repository root
Remove the stale binary and object files from the repository root directory
that are no longer referenced by the build system. Additionally, update the
atoms_get_window_name function to use AnyPropertyType instead of specific
atom types for XGetWindowProperty calls, and add a nil check on nitems to
prevent potential null pointer dereferences. Modify panel_handle_click to
minimize the currently focused client when clicking on it, rather than
keeping it focused, improving window management behavior.
2026-01-16 04:41:39 +00:00
retoor 1724f17e92 chore: remove outdated example configs, html docs, and binary artifacts from root and site directories 2026-01-09 13:47:47 +00:00
retoor 115c69f1bc chore: update default color scheme to neon theme and add window constraint logic 2026-01-08 23:41:45 +00:00
retoor daaef6fa17 chore: add web_remote.html with workspace grid and quick launch UI for DWN control 2026-01-08 23:19:20 +00:00
retoor eb492cd5ff chore: add WebSocket API documentation and auto-organization feature cards to site pages 2026-01-08 23:18:43 +00:00
retoor 4793d372ac chore: add WebSocket API with Mongoose, service management, and AI command execution
Add new WebSocket API module (api.c/api.h) using Mongoose library for real-time
window manager control, including workspace and client status endpoints.
Introduce service management module (services.h) with init/run/cleanup lifecycle.
Extend AI module to parse [WM_CMD:] directives from responses and forward JSON
commands to the API handler. Update config with api.enabled, api.port, and
services.path settings. Integrate new modules into main init/run/cleanup flow.
2026-01-08 22:58:19 +00:00
retoor 54565b21bd chore: swap super+a and super+shift+a bindings for ai command and context in keys.c and docs 2026-01-08 22:37:00 +00:00
retoor 8a5454fda0 fix: skip unmanage for maximized or fullscreen windows on UnmapNotify in handle_unmap_notify
Add early return guard in handle_unmap_notify to ignore UnmapNotify events
when the client has CLIENT_MAXIMIZED or CLIENT_FULLSCREEN flags set,
preventing browsers and other apps from becoming orphaned during temporary
unmap transitions like resizing or state changes.
2026-01-08 22:34:10 +00:00
retoor 91cfecb6e5 fix: grab button events on unfocus and ungrab on focus for proper click handling 2026-01-08 22:24:45 +00:00
retoor ab36b7dd2c feat: implement composable window snapping with dual-axis constraints and toggle-to-full behavior
Add SnapHorizontal/SnapVertical enums and SnapConstraint struct to Client for tracking snap state. Introduce layout_apply_snap_constraint() to position windows into quadrants or full axes based on stored snap flags. Extend key_snap_left/right with toggle logic that expands to full width on second press, and add new key_snap_up/down handlers for vertical snapping. Update shortcut documentation and help overlay to describe composable two-key sequences (e.g. Super+Left then Super+Up for top-left quarter).
2025-12-28 10:21:03 +00:00
retoor 64f9761116 feat: add maximize window support with MRU focus tracking and demo timing configs
Implement client maximize/unmaximize toggle and state tracking via CLIENT_MAXIMIZED flag, add MRU (most-recently-used) doubly-linked list per workspace for intelligent focus restoration on client unmanage, introduce configurable focus-follow delay (0-1000ms) for sloppy focus mode, and add demo mode timing parameters (step_delay, ai_timeout, window_timeout) with corresponding wait condition enum and configuration parsing in the [demo] section.
2025-12-28 09:23:03 +00:00
retoor 3364464b43 docs: update README with autostart, demo mode, XEmbed tray, window snapping, and Alt+F9 minimize keybinding 2025-12-28 08:26:53 +00:00
retoor c927e23f23 style: remove all block comments from header files 2025-12-28 04:01:46 +00:00
retoor dc522d5912 docs: add comprehensive readme with installation, features, and usage guide for DWN window manager 2025-12-28 03:34:24 +00:00
retoor 41fc47abac feat: add news ticker with sentiment analysis and error codes to dwn.h and news.h headers
- Add DwnStatus enum with six error codes (OK, ERROR, INVALID_ARG, NO_MEMORY, NOT_FOUND, DISPLAY, IO) to include/dwn.h
- Extend NewsArticle struct with NewsSentiment enum (NEUTRAL, POSITIVE, NEGATIVE) and sentiment_score float field in include/news.h
- Insert DWN_ASSERT and DWN_ASSERT_MSG contract assertion macros plus assert.h include into include/util.h
2025-12-28 03:30:10 +00:00
retoor 21e8bcc88a chore: add MIT license, Makefile, and initial build artifacts for DWN project 2025-12-28 02:14:31 +00:00
205 changed files with 53287 additions and 7406 deletions
+95 -10
View File
@@ -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)
# =============================================================================
+545 -350
View File
@@ -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
View File
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+4 -1
View File
@@ -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:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3 -1
View File
@@ -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.
BIN
View File
Binary file not shown.
+4 -1
View File
@@ -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:
BIN
View File
Binary file not shown.
+40 -1
View File
@@ -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:
BIN
View File
Binary file not shown.
+20 -2
View File
@@ -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:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+2 -1
View File
@@ -17,7 +17,7 @@ build/notifications.o: src/notifications.c include/notifications.h \
/usr/include/dbus-1.0/dbus/dbus-signature.h \
/usr/include/dbus-1.0/dbus/dbus-syntax.h \
/usr/include/dbus-1.0/dbus/dbus-threads.h include/config.h \
include/util.h
include/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
View File
@@ -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:
BIN
View File
Binary file not shown.
+5 -3
View File
@@ -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:
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3 -1
View File
@@ -1,6 +1,6 @@
build/workspace.o: src/workspace.c include/workspace.h include/dwn.h \
include/client.h include/layout.h include/atoms.h include/util.h \
include/config.h include/panel.h
include/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:
BIN
View File
Binary file not shown.
+8 -8
View File
@@ -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
+188
View File
@@ -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
+9
View File
@@ -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
View File
@@ -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
+1
View File
@@ -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);
+311
View File
@@ -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 */
+35
View File
@@ -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
View File
@@ -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
+27
View File
@@ -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);
+523
View File
@@ -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 */
+220
View File
@@ -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 */
+265
View File
@@ -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 */
+107
View File
@@ -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 */
+123
View File
@@ -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 */
+222
View File
@@ -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 */
+490
View File
@@ -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 */
+161
View File
@@ -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
View File
@@ -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);
+15
View File
@@ -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);
+3
View File
@@ -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);
+33
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+70
View File
@@ -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
+8
View File
@@ -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);
+57
View File
@@ -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 */
+365
View File
@@ -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 */
+435
View File
@@ -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 */
+61
View File
@@ -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
+84
View File
@@ -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
+13
View File
@@ -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
+72
View File
@@ -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
View File
@@ -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);
+263
View File
@@ -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 */
+704
View File
@@ -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 */
+75
View File
@@ -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 */
+30
View File
@@ -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);
+4
View File
@@ -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);
+305
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+340
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+879
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+350
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
File diff suppressed because it is too large Load Diff
+545
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+440
View File
@@ -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 &amp;
DISPLAY=:1 ./bin/dwn</code></pre>
</div>
<h3>Code Formatting</h3>
<div class="code-block">
<pre><code>make format</code></pre>
</div>
<p>Uses clang-format with project style.</p>
<h3>Static Analysis</h3>
<div class="code-block">
<pre><code>make check</code></pre>
</div>
<p>Runs cppcheck for common issues.</p>
<h3>Debug Logging</h3>
<p>Debug builds write to <code>~/.local/share/dwn/dwn.log</code>:</p>
<div class="code-block">
<pre><code>tail -f ~/.local/share/dwn/dwn.log</code></pre>
</div>
<h3>Directory Structure</h3>
<div class="code-block">
<pre><code>dwn/
├── include/ # Header files
│ ├── dwn.h # Main state struct
│ ├── client.h # Window management
│ ├── workspace.h # Virtual desktops
│ ├── layout.h # Layout algorithms
│ ├── panel.h # Panels
│ ├── api.h # WebSocket API
│ ├── screenshot.h
│ ├── ocr.h
│ └── ...
├── src/ # Implementation
│ ├── main.c # Entry point, event loop
│ ├── client.c
│ ├── workspace.c
│ ├── layout.c
│ ├── panel.c
│ ├── api.c
│ ├── screenshot.c
│ ├── ocr.c
│ └── ...
├── build/ # Object files
├── bin/ # Output binary
├── examples/ # Client examples
├── manual/ # Documentation
├── Makefile
└── CLAUDE.md # Development guide</code></pre>
</div>
<h3>Adding a New Module</h3>
<ol>
<li>Create <code>include/newmodule.h</code> with public API</li>
<li>Create <code>src/newmodule.c</code> with implementation</li>
<li>Add <code>newmodule_init()</code> call to <code>main.c:dwn_init()</code></li>
<li>Add <code>newmodule_cleanup()</code> call to <code>main.c:dwn_cleanup()</code></li>
<li>Makefile automatically picks up new .c files</li>
</ol>
<h2 id="troubleshooting">Troubleshooting</h2>
<h3>Missing pkg-config</h3>
<div class="code-block">
<pre><code># Check if library is found
pkg-config --cflags --libs x11
# If not found, install dev package
sudo apt install libx11-dev # Debian/Ubuntu</code></pre>
</div>
<h3>Tesseract not found</h3>
<div class="code-block">
<pre><code># Check installation
pkg-config --cflags --libs tesseract
# Install if missing
sudo apt install libtesseract-dev tesseract-ocr tesseract-ocr-eng</code></pre>
</div>
<h3>Linker errors</h3>
<p>Ensure all -dev packages are installed. The Makefile uses pkg-config to find libraries.</p>
<h3>Runtime errors</h3>
<ol>
<li>Check log file: <code>~/.local/share/dwn/dwn.log</code></li>
<li>Build with debug: <code>make clean && make debug</code></li>
<li>Build with sanitizers: <code>make clean && make sanitize</code></li>
<li>Run in GDB: <code>DISPLAY=:1 gdb ./bin/dwn</code></li>
</ol>
<h3>X11 errors</h3>
<p>Run with synchronous X11 for detailed errors:</p>
<div class="code-block">
<pre><code>DISPLAY=:1 ./bin/dwn --sync</code></pre>
</div>
<h3>Xephyr not starting</h3>
<div class="code-block">
<pre><code># Check if display :1 is in use
ls /tmp/.X11-unix/
# Use different display
Xephyr :2 -screen 1920x1080 &amp;
DISPLAY=:2 ./bin/dwn</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+518
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+611
View File
@@ -0,0 +1,611 @@
/* retoor <retoor@molodetz.nl> */
/* DWN Documentation Styles */
:root {
--bg-primary: #1a1a2e;
--bg-secondary: #16213e;
--bg-tertiary: #0f3460;
--bg-code: #0d1117;
--text-primary: #e0e0e0;
--text-secondary: #a0a0a0;
--text-muted: #666;
--accent: #4a90d9;
--accent-hover: #5da0e9;
--border-color: #2d3748;
--success: #48bb78;
--warning: #ed8936;
--error: #f56565;
--sidebar-width: 280px;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background-color: var(--bg-primary);
color: var(--text-primary);
line-height: 1.6;
min-height: 100vh;
}
a {
color: var(--accent);
text-decoration: none;
transition: color 0.2s;
}
a:hover {
color: var(--accent-hover);
}
.layout {
display: flex;
min-height: 100vh;
}
.sidebar {
width: var(--sidebar-width);
background: var(--bg-secondary);
border-right: 1px solid var(--border-color);
position: fixed;
top: 0;
left: 0;
height: 100vh;
overflow-y: auto;
z-index: 100;
}
.sidebar-header {
padding: 20px;
border-bottom: 1px solid var(--border-color);
}
.sidebar-header h1 {
font-size: 1.5rem;
color: var(--accent);
margin-bottom: 5px;
}
.sidebar-header .version {
font-size: 0.85rem;
color: var(--text-muted);
}
.sidebar-nav {
padding: 15px 0;
}
.nav-section {
margin-bottom: 10px;
}
.nav-section-title {
padding: 8px 20px;
font-size: 0.75rem;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--text-muted);
font-weight: 600;
}
.nav-link {
display: block;
padding: 8px 20px 8px 30px;
color: var(--text-secondary);
font-size: 0.9rem;
transition: all 0.2s;
border-left: 3px solid transparent;
}
.nav-link:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.nav-link.active {
background: var(--bg-tertiary);
color: var(--accent);
border-left-color: var(--accent);
}
.main-content {
flex: 1;
margin-left: var(--sidebar-width);
min-width: 0;
}
.content {
max-width: 900px;
margin: 0 auto;
padding: 40px;
}
.page-header {
margin-bottom: 40px;
padding-bottom: 20px;
border-bottom: 1px solid var(--border-color);
}
.page-header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
color: var(--text-primary);
}
.page-header .lead {
font-size: 1.2rem;
color: var(--text-secondary);
}
h2 {
font-size: 1.75rem;
margin: 40px 0 20px;
padding-bottom: 10px;
border-bottom: 1px solid var(--border-color);
color: var(--text-primary);
}
h3 {
font-size: 1.35rem;
margin: 30px 0 15px;
color: var(--text-primary);
}
h4 {
font-size: 1.1rem;
margin: 20px 0 10px;
color: var(--text-secondary);
}
p {
margin-bottom: 15px;
}
ul, ol {
margin: 0 0 20px 25px;
}
li {
margin-bottom: 8px;
}
code {
font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;
background: var(--bg-code);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.9em;
color: var(--accent);
}
pre {
background: var(--bg-code);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
overflow-x: auto;
margin: 20px 0;
}
pre code {
background: none;
padding: 0;
color: var(--text-primary);
font-size: 0.85rem;
line-height: 1.5;
}
.table-container {
overflow-x: auto;
margin: 20px 0;
}
table {
width: 100%;
border-collapse: collapse;
font-size: 0.9rem;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background: var(--bg-secondary);
font-weight: 600;
color: var(--text-primary);
}
tr:hover {
background: var(--bg-secondary);
}
.card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 25px;
margin: 20px 0;
}
.card-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 10px;
color: var(--text-primary);
}
.feature-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 20px;
margin: 30px 0;
}
.feature-card {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 25px;
transition: transform 0.2s, border-color 0.2s;
}
.feature-card:hover {
transform: translateY(-2px);
border-color: var(--accent);
}
.feature-icon {
font-size: 2rem;
margin-bottom: 15px;
}
.feature-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 10px;
}
.feature-desc {
font-size: 0.9rem;
color: var(--text-secondary);
}
.shortcut-table code {
background: var(--bg-tertiary);
padding: 4px 8px;
border-radius: 4px;
font-weight: 500;
}
.badge {
display: inline-block;
padding: 3px 8px;
border-radius: 4px;
font-size: 0.75rem;
font-weight: 600;
text-transform: uppercase;
}
.badge-get { background: var(--success); color: #000; }
.badge-post { background: var(--accent); color: #fff; }
.badge-required { background: var(--error); color: #fff; }
.badge-optional { background: var(--text-muted); color: #fff; }
.method-block {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
margin: 25px 0;
overflow: hidden;
}
.method-header {
display: flex;
align-items: center;
gap: 15px;
padding: 15px 20px;
background: var(--bg-tertiary);
border-bottom: 1px solid var(--border-color);
}
.method-name {
font-family: 'JetBrains Mono', monospace;
font-size: 1rem;
font-weight: 600;
}
.method-body {
padding: 20px;
}
.param-table {
margin: 15px 0;
}
.param-table th {
background: var(--bg-primary);
}
.example-tabs {
display: flex;
gap: 5px;
margin-bottom: -1px;
}
.example-tab {
padding: 8px 16px;
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-bottom: none;
border-radius: 6px 6px 0 0;
cursor: pointer;
font-size: 0.85rem;
color: var(--text-secondary);
transition: all 0.2s;
}
.example-tab:hover,
.example-tab.active {
background: var(--bg-code);
color: var(--text-primary);
}
.example-content {
display: none;
}
.example-content.active {
display: block;
}
.alert {
padding: 15px 20px;
border-radius: 8px;
margin: 20px 0;
border-left: 4px solid;
}
.alert-info {
background: rgba(74, 144, 217, 0.1);
border-color: var(--accent);
}
.alert-warning {
background: rgba(237, 137, 54, 0.1);
border-color: var(--warning);
}
.alert-success {
background: rgba(72, 187, 120, 0.1);
border-color: var(--success);
}
.toc {
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
padding: 20px;
margin: 20px 0;
}
.toc-title {
font-weight: 600;
margin-bottom: 10px;
color: var(--text-primary);
}
.toc-list {
list-style: none;
margin: 0;
padding: 0;
}
.toc-list li {
margin: 5px 0;
}
.toc-list a {
color: var(--text-secondary);
font-size: 0.9rem;
}
.copy-btn {
position: absolute;
top: 10px;
right: 10px;
padding: 5px 10px;
background: var(--bg-tertiary);
border: 1px solid var(--border-color);
border-radius: 4px;
color: var(--text-secondary);
cursor: pointer;
font-size: 0.75rem;
transition: all 0.2s;
}
.copy-btn:hover {
background: var(--accent);
color: #fff;
}
.code-block {
position: relative;
}
.search-box {
padding: 15px 20px;
border-bottom: 1px solid var(--border-color);
}
.search-input {
width: 100%;
padding: 10px 15px;
background: var(--bg-primary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
font-size: 0.9rem;
}
.search-input:focus {
outline: none;
border-color: var(--accent);
}
.search-input::placeholder {
color: var(--text-muted);
}
footer {
margin-top: 60px;
padding: 30px 0;
border-top: 1px solid var(--border-color);
text-align: center;
color: var(--text-muted);
font-size: 0.85rem;
}
@media (max-width: 768px) {
.sidebar {
transform: translateX(-100%);
transition: transform 0.3s;
}
.sidebar.open {
transform: translateX(0);
}
.main-content {
margin-left: 0;
}
.content {
padding: 20px;
}
.mobile-menu-btn {
display: block;
position: fixed;
top: 15px;
left: 15px;
z-index: 200;
padding: 10px;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 6px;
color: var(--text-primary);
cursor: pointer;
}
}
@media (min-width: 769px) {
.mobile-menu-btn {
display: none;
}
}
.hero {
text-align: center;
padding: 60px 0;
}
.hero h1 {
font-size: 3rem;
margin-bottom: 20px;
}
.hero .tagline {
font-size: 1.3rem;
color: var(--text-secondary);
margin-bottom: 30px;
}
.btn {
display: inline-block;
padding: 12px 24px;
background: var(--accent);
color: #fff;
border-radius: 6px;
font-weight: 600;
transition: background 0.2s;
}
.btn:hover {
background: var(--accent-hover);
color: #fff;
}
.btn-outline {
background: transparent;
border: 2px solid var(--accent);
color: var(--accent);
}
.btn-outline:hover {
background: var(--accent);
color: #fff;
}
.btn-group {
display: flex;
gap: 15px;
justify-content: center;
flex-wrap: wrap;
}
.quick-start {
background: var(--bg-secondary);
border-radius: 8px;
padding: 30px;
margin: 40px 0;
}
.quick-start h3 {
margin-top: 0;
}
.step {
display: flex;
align-items: flex-start;
gap: 15px;
margin: 20px 0;
}
.step-num {
width: 30px;
height: 30px;
background: var(--accent);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
flex-shrink: 0;
}
.step-content {
flex: 1;
}
.anchor {
color: var(--text-muted);
margin-left: 8px;
opacity: 0;
transition: opacity 0.2s;
}
h2:hover .anchor,
h3:hover .anchor {
opacity: 1;
}
+408
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+180
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+242
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+119
View File
@@ -0,0 +1,119 @@
/* retoor <retoor@molodetz.nl> */
/* DWN Documentation Scripts */
document.addEventListener('DOMContentLoaded', function() {
highlightCurrentPage();
setupCopyButtons();
setupMobileMenu();
setupSearch();
setupTabs();
});
function highlightCurrentPage() {
const currentPath = window.location.pathname.split('/').pop() || 'index.html';
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const href = link.getAttribute('href');
if (href === currentPath || (currentPath === '' && href === 'index.html')) {
link.classList.add('active');
}
});
}
function setupCopyButtons() {
const codeBlocks = document.querySelectorAll('pre code');
codeBlocks.forEach(block => {
const wrapper = block.closest('.code-block') || block.parentElement;
wrapper.style.position = 'relative';
const btn = document.createElement('button');
btn.className = 'copy-btn';
btn.textContent = 'Copy';
btn.addEventListener('click', function() {
const text = block.textContent;
navigator.clipboard.writeText(text).then(() => {
btn.textContent = 'Copied!';
setTimeout(() => {
btn.textContent = 'Copy';
}, 2000);
});
});
wrapper.appendChild(btn);
});
}
function setupMobileMenu() {
const menuBtn = document.querySelector('.mobile-menu-btn');
const sidebar = document.querySelector('.sidebar');
if (menuBtn && sidebar) {
menuBtn.addEventListener('click', function() {
sidebar.classList.toggle('open');
});
document.addEventListener('click', function(e) {
if (!sidebar.contains(e.target) && !menuBtn.contains(e.target)) {
sidebar.classList.remove('open');
}
});
}
}
function setupSearch() {
const searchInput = document.querySelector('.search-input');
if (!searchInput) return;
searchInput.addEventListener('input', function(e) {
const query = e.target.value.toLowerCase();
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const text = link.textContent.toLowerCase();
const section = link.closest('.nav-section');
if (query === '' || text.includes(query)) {
link.style.display = 'block';
} else {
link.style.display = 'none';
}
});
});
}
function setupTabs() {
const tabGroups = document.querySelectorAll('.example-tabs');
tabGroups.forEach(group => {
const tabs = group.querySelectorAll('.example-tab');
const container = group.nextElementSibling;
tabs.forEach(tab => {
tab.addEventListener('click', function() {
const target = this.dataset.tab;
tabs.forEach(t => t.classList.remove('active'));
this.classList.add('active');
if (container) {
const contents = container.querySelectorAll('.example-content');
contents.forEach(c => {
c.classList.remove('active');
if (c.dataset.tab === target) {
c.classList.add('active');
}
});
}
});
});
});
}
function scrollToSection(id) {
const element = document.getElementById(id);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}
+444
View File
@@ -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>&gt;&lt;&gt;</code> for floating, <code>[M]</code> for monocle.
</div>
<h2 id="tiling">Tiling Layout</h2>
<p>The default layout. Windows automatically arrange themselves in a master-stack pattern.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+------------------+----------+
| | Window |
| Master | 2 |
| Window +----------+
| 1 | Window |
| | 3 |
+------------------+----------+</code></pre>
</div>
<ul>
<li>The <strong>master area</strong> (left) contains the primary window(s)</li>
<li>The <strong>stack area</strong> (right) contains secondary windows</li>
<li>Windows are automatically sized to fill the screen</li>
<li>No manual positioning required</li>
</ul>
<h3>Master Ratio</h3>
<p>Controls the width of the master area relative to the screen.</p>
<ul>
<li><code>Super+H</code> - Decrease master ratio (shrink master area)</li>
<li><code>Super+L</code> - Increase master ratio (expand master area)</li>
<li>Range: 10% to 90%</li>
<li>Default: 55%</li>
</ul>
<h3>Master Count</h3>
<p>Controls how many windows occupy the master area.</p>
<ul>
<li><code>Super+I</code> - Increase master count</li>
<li><code>Super+D</code> - Decrease master count</li>
<li>Range: 1 to 10</li>
<li>Default: 1</li>
</ul>
<p>With master count of 2:</p>
<div class="code-block">
<pre><code>+--------+---------+----------+
| Master | Master | Stack |
| 1 | 2 | 3 |
| | +----------+
| | | Stack |
| | | 4 |
+--------+---------+----------+</code></pre>
</div>
<h3>Floating Windows in Tiling</h3>
<p>Press <code>Super+F9</code> to toggle floating mode for a window. Floating windows:</p>
<ul>
<li>Are exempt from automatic tiling</li>
<li>Can be freely moved and resized</li>
<li>Float above tiled windows</li>
<li>Useful for dialogs, small utilities, reference windows</li>
</ul>
<h2 id="floating">Floating Layout</h2>
<p>Traditional overlapping window management like most desktop environments.</p>
<h3>Characteristics</h3>
<ul>
<li>Windows maintain their own position and size</li>
<li>No automatic arrangement</li>
<li>Drag title bars to move windows</li>
<li>Drag window edges to resize</li>
<li>Click to raise windows</li>
</ul>
<h3>Window Controls</h3>
<p>In floating mode, windows have full manual control:</p>
<ul>
<li><strong>Move</strong>: Drag the title bar</li>
<li><strong>Resize</strong>: Drag the window border</li>
<li><strong>Maximize</strong>: <code>Alt+F10</code> or click maximize button</li>
<li><strong>Minimize</strong>: <code>Alt+F9</code> or click minimize button</li>
</ul>
<h2 id="monocle">Monocle Layout</h2>
<p>Each window takes the full screen. Only one window visible at a time.</p>
<h3>Use Cases</h3>
<ul>
<li>Focused work on a single application</li>
<li>Small screens or high window count</li>
<li>Reading or writing documents</li>
<li>Video or media playback</li>
</ul>
<h3>Navigation</h3>
<ul>
<li><code>Alt+Tab</code> - Switch to next window</li>
<li><code>Alt+Shift+Tab</code> - Switch to previous window</li>
<li>Taskbar click - Switch to specific window</li>
</ul>
<div class="alert alert-info">
<strong>Note:</strong> Windows maintain decorations in monocle mode unless fullscreen (<code>Alt+F11</code>).
</div>
<h2 id="centered-master">Centered Master Layout</h2>
<p>The master window is centered on screen with stack windows divided on left and right sides.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+--------+------------------+--------+
| Stack | Master | Stack |
| 1 | Window | 3 |
+--------+ +--------+
| Stack | | Stack |
| 2 | | 4 |
+--------+------------------+--------+</code></pre>
</div>
<ul>
<li>The <strong>master window</strong> occupies the center of the screen</li>
<li>Stack windows are divided between left and right sides</li>
<li>Odd-numbered stack windows go left, even go right</li>
<li>Excellent for wide monitors and ultrawide displays</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Keeping primary work centered while referencing side content</li>
<li>Video editing with timeline and tools on sides</li>
<li>Development with editor centered and documentation on sides</li>
</ul>
<h2 id="columns">Columns Layout</h2>
<p>All windows arranged in equal-width vertical columns spanning the full height.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+----------+----------+----------+----------+
| | | | |
| Window | Window | Window | Window |
| 1 | 2 | 3 | 4 |
| | | | |
| | | | |
+----------+----------+----------+----------+</code></pre>
</div>
<ul>
<li>Each window gets an equal-width column</li>
<li>Windows span the full usable height</li>
<li>Simple and predictable arrangement</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Comparing multiple files side-by-side</li>
<li>Monitoring multiple log files</li>
<li>Multi-column text editing</li>
<li>Concurrent terminal sessions</li>
</ul>
<h2 id="fibonacci">Fibonacci Layout</h2>
<p>Windows arranged in a spiral pattern using recursive splitting, inspired by the Fibonacci sequence.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+------------------+----------+
| | |
| Window 1 | Window 2 |
| +----+-----+
| | W3 | |
+------------------+----+ W4 |
| Window 5 | |
+-----------------------+-----+</code></pre>
</div>
<ul>
<li>First window takes half the screen</li>
<li>Each subsequent window takes half the remaining space</li>
<li>Alternates between horizontal and vertical splits</li>
<li>Creates a visually interesting spiral pattern</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Hierarchical window importance (larger = more important)</li>
<li>Primary workspace with progressively smaller utilities</li>
<li>Creative workflows with main canvas and tool windows</li>
</ul>
<h2 id="grid">Grid Layout</h2>
<p>Windows are arranged in a grid pattern with automatic row/column calculation.</p>
<h3>How It Works</h3>
<div class="code-block">
<pre><code>+----------+----------+----------+
| Window | Window | Window |
| 1 | 2 | 3 |
+----------+----------+----------+
| Window | Window |
| 4 | 5 |
+----------+----------+</code></pre>
</div>
<ul>
<li>Automatically calculates optimal rows and columns</li>
<li>Uses square root for balanced grid</li>
<li>All windows have equal size</li>
<li>Great for viewing multiple documents simultaneously</li>
</ul>
<h3>Use Cases</h3>
<ul>
<li>Comparing multiple documents or files</li>
<li>Monitoring multiple terminals</li>
<li>Dashboard-style workflows</li>
<li>Multi-way video calls</li>
</ul>
<h2 id="plugin-system">Plugin System (v2.0)</h2>
<p>DWN v2.0 introduces a layout plugin system that allows custom layout algorithms to be loaded dynamically or built-in.</p>
<h3>Built-in Layouts</h3>
<ul>
<li><strong>tiling</strong> - Master-stack tiling (default)</li>
<li><strong>floating</strong> - Traditional floating windows</li>
<li><strong>monocle</strong> - Single maximized window</li>
<li><strong>centered-master</strong> - Master centered with stacks on sides</li>
<li><strong>columns</strong> - Equal-width vertical columns</li>
<li><strong>fibonacci</strong> - Spiral recursive splitting</li>
<li><strong>grid</strong> - Grid arrangement</li>
</ul>
<h3>Custom Layouts</h3>
<p>Developers can create custom layout plugins using the Layout Plugin API. See <a href="plugin-development.html">Plugin Development</a> for details.</p>
<div class="alert alert-info">
<strong>Plugin API:</strong> Layouts implement the <code>LayoutPluginInterface</code> vtable with an <code>arrange()</code> method that calculates window geometries.
</div>
<h2 id="per-workspace">Per-Workspace Settings</h2>
<p>Each workspace maintains independent layout settings:</p>
<div class="table-container">
<table>
<thead>
<tr>
<th>Setting</th>
<th>Scope</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>Layout Mode</td>
<td>Per-workspace</td>
<td>Tiling, floating, monocle, centered-master, columns, or fibonacci</td>
</tr>
<tr>
<td>Master Ratio</td>
<td>Per-workspace</td>
<td>Width of master area</td>
</tr>
<tr>
<td>Master Count</td>
<td>Per-workspace</td>
<td>Windows in master area</td>
</tr>
<tr>
<td>Window Floating State</td>
<td>Per-window</td>
<td>Individual floating toggle</td>
</tr>
</tbody>
</table>
</div>
<p>Example workflow:</p>
<ul>
<li>Workspace 1: Tiling layout for coding (editor + terminal)</li>
<li>Workspace 2: Floating layout for design work</li>
<li>Workspace 3: Monocle layout for focused writing</li>
<li>Workspace 4: Centered-master for wide monitor development</li>
<li>Workspace 5: Columns layout for log monitoring</li>
<li>Workspace 6: Fibonacci for hierarchical work</li>
</ul>
<h2 id="shortcuts">Layout Shortcuts</h2>
<div class="table-container">
<table class="shortcut-table">
<thead>
<tr>
<th>Shortcut</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Super+Space</code></td>
<td>Cycle layout mode</td>
</tr>
<tr>
<td><code>Super+H</code></td>
<td>Shrink master area</td>
</tr>
<tr>
<td><code>Super+L</code></td>
<td>Expand master area</td>
</tr>
<tr>
<td><code>Super+I</code></td>
<td>Increase master count</td>
</tr>
<tr>
<td><code>Super+D</code></td>
<td>Decrease master count</td>
</tr>
<tr>
<td><code>Super+F9</code></td>
<td>Toggle window floating</td>
</tr>
<tr>
<td><code>Alt+F10</code></td>
<td>Toggle maximize</td>
</tr>
<tr>
<td><code>Alt+F11</code></td>
<td>Toggle fullscreen</td>
</tr>
</tbody>
</table>
</div>
<h2>Configuration</h2>
<p>Set default layout behavior in <code>~/.config/dwn/config</code>:</p>
<div class="code-block">
<pre><code>[layout]
# Default layout for new workspaces
# Options: tiling, floating, monocle, centered-master, columns, fibonacci
default = tiling
master_ratio = 0.55 # Default master area ratio (0.1-0.9)
master_count = 1 # Default master window count (1-10)
[appearance]
gap = 5 # Gap between tiled windows (0-100)</code></pre>
</div>
<footer>
<p>DWN Window Manager - retoor &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+87
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+243
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
+410
View File
@@ -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 &lt;retoor@molodetz.nl&gt;</p>
</footer>
</div>
</main>
</div>
<script src="js/main.js"></script>
</body>
</html>
-422
View File
@@ -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">&#129302;</div>
<h3>AI Command Palette</h3>
<p>Type natural language commands to control your desktop.
Launch apps, query system info, and more.</p>
<p style="margin-top: 1rem;"><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>A</kbd></p>
</div>
<div class="feature-card">
<div class="feature-icon">&#128065;</div>
<h3>Context Analysis</h3>
<p>AI analyzes your current workspace to understand your task
and provide relevant suggestions.</p>
<p style="margin-top: 1rem;"><kbd>Super</kbd> + <kbd>A</kbd></p>
</div>
<div class="feature-card">
<div class="feature-icon">&#128269;</div>
<h3>Semantic Search</h3>
<p>Search the web using meaning, not just keywords.
Find relevant content instantly.</p>
<p style="margin-top: 1rem;"><kbd>Super</kbd> + <kbd>Shift</kbd> + <kbd>E</kbd></p>
</div>
</div>
<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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-530
View File
@@ -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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-578
View File
@@ -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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-1085
View File
File diff suppressed because it is too large Load Diff
-779
View File
@@ -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 &lt;retoor@molodetz.nl&gt;
</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>
-399
View File
@@ -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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-465
View File
@@ -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">&#9783;</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">&#10063;</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">&#9744;</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">&#8596;</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">&#128200;</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">&#128065;</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>&#128241; 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>&#128267; 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>&#128266; 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>&#128246; 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>&#129302; 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>&#128269; 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>&#127891; 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>&lt; 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">&#128218;</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">&#127916;</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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-288
View File
@@ -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">&#9776;</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">&#9881;</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">&#127912;</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">&#128161;</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">&#128276;</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">&#128187;</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">&#9889;</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>&lt;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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-561
View File
@@ -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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
-304
View File
@@ -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 = '';
});
});
-460
View File
@@ -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 &lt;retoor@molodetz.nl&gt; - MIT License</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>
+715 -326
View File
File diff suppressed because it is too large Load Diff
+3171
View File
File diff suppressed because it is too large Load Diff

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