docs: update architecture, configuration, and FAQs
- Expand architecture overview with plugin system details - Update configuration guide with new spotlight settings - Improve FAQs with troubleshooting tips - Remove docs/README.md (content merged into main README)
This commit is contained in:
4 files changed
+187
-91
No files matched your search
@@ -1,39 +0,0 @@
|
||||
# Modus Documentation
|
||||
|
||||
Welcome to the official documentation for Modus!
|
||||
|
||||
Modus is a highly customizable, hackable shell environment designed specifically for Hyprland on Wayland, built using Python and the [Fabric](https://github.com/Fabric-Development/fabric/) framework.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. **[Installation Guide](installation.md)**
|
||||
- Automated installation via `install.sh`.
|
||||
- Manual installation and compiling C-extensions.
|
||||
- Post-installation steps and recommended themes.
|
||||
|
||||
2. **[Configuration Guide](configuration.md)**
|
||||
- How to configure the shell using `config.toml`.
|
||||
- Setting up custom panel buttons with `mods.toml`.
|
||||
- Pinning apps to the dock via `dock.toml`.
|
||||
|
||||
3. **[Styling Guide](styling.md)**
|
||||
- How Matugen dynamically generates colors based on your wallpaper.
|
||||
- Customizing CSS components in `src/shared/styles/`.
|
||||
|
||||
4. **[Architecture Overview](architecture.md)**
|
||||
- Deep dive into how Modus is built.
|
||||
- The Wayland Window Switcher C-Backend (`AppCapture`).
|
||||
- The Global Menu DBus implementation.
|
||||
|
||||
5. **[FAQs & Tips](faqs_tips.md)**
|
||||
- Troubleshooting common issues (Dock occlusion, CPU usage, OSD).
|
||||
- Pro tips for keyboard shortcuts and live-reloading.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Modus is open-source and hackable by design. If you want to contribute to the project:
|
||||
1. Familiarize yourself with the [Architecture Overview](architecture.md).
|
||||
2. Write Python code using the Fabric framework widgets.
|
||||
3. Ensure you test your changes by running `uv run start` before opening a pull request.
|
||||
+84
-28
@@ -1,43 +1,99 @@
|
||||
# Architecture Overview
|
||||
|
||||
Modus is built using **Python** and the **Fabric** framework (a GTK/Wayland wrapper), along with custom C extensions for high-performance Wayland interactions.
|
||||
Modus is built with Python and the [Fabric](https://github.com/Fabric-Development/fabric/)
|
||||
framework (GTK/Wayland wrapper), plus custom C extensions for high-performance
|
||||
Wayland interactions.
|
||||
|
||||
## 1. Core Application Loop
|
||||
|
||||
The entry point of Modus is `start.py`, which delegates to `src/main.py`.
|
||||
`main.py` instantiates the `fabric.Application` object. This central application manages the lifecycle of various floating Wayland windows (called "widgets" or "panels" in Modus).
|
||||
Entry point is `start.py`, delegates to `src/main.py`. `main.py` instantiates
|
||||
`fabric.Application`, which manages lifecycle of floating Wayland windows:
|
||||
|
||||
Key windows include:
|
||||
- **Panel**: The top bar (`src/window/panel/`)
|
||||
- **Dock**: The macOS-style bottom dock (`src/window/dock/`)
|
||||
- **Control Center**: The right-side quick settings panel (`src/window/controlcenter/`)
|
||||
- **App Switcher**: The Alt-Tab window switcher (`src/window/switcher/`)
|
||||
- **Lock Screen**: The session lock screen (`src/window/lock.py`)
|
||||
- **Panel**: Top bar (`src/window/panel/`)
|
||||
- **Dock**: macOS-style bottom dock (`src/window/dock/`)
|
||||
- **Control Center**: Right-side quick settings (`src/window/controlcenter/`)
|
||||
- **App Switcher**: Alt-Tab window switcher (`src/window/switcher/`)
|
||||
- **Lock Screen**: Session lock (`src/window/lock.py`)
|
||||
- **Spotlight Search**: Super+D search overlay (`src/window/spotlight/`)
|
||||
|
||||
## 2. High-Performance App Switcher (AppCapture)
|
||||
## 2. Spotlight Search Engine
|
||||
|
||||
The App Switcher (`Alt+Tab`) needs to display live video previews of running Wayland windows. Doing this in pure Python via DBus polling would be far too slow and resource-intensive.
|
||||
The spotlight (`src/window/spotlight/`) is a plugin-powered search overlay.
|
||||
It has three layers:
|
||||
|
||||
To solve this, Modus uses a custom C-extension located in `src/window/switcher/app-capture/`.
|
||||
- **How it works**: It uses the `hyprland-toplevel-export-v1` Wayland protocol to ask Hyprland for direct memory-mapped buffers (dmabuf/shm) of the window contents.
|
||||
- **Python Integration**: The C-backend is compiled to `libappcapture.so` and exposed to Python via GObject Introspection.
|
||||
- **Optimization**: To prevent CPU spikes when capturing 4K windows, the capture framerate is intentionally throttled by Python using `invoke_repeater`, controlled by `switcher_live_preview_delay_ms` in `config.toml`.
|
||||
### Layer 1: UI (`main.py`)
|
||||
|
||||
## 3. Global Menu (DBus)
|
||||
A GTK window with an entry field and scrollable result list. Renders results
|
||||
from plugins. Manages keyboard navigation, viewport sizing, and result
|
||||
selection. Uses a 200ms debounce on the entry field to avoid searching on
|
||||
every keystroke.
|
||||
|
||||
Modus features a macOS-style Global Menu (the application menu bar sits in the top panel instead of inside the app window).
|
||||
### Layer 2: Search Pipeline (`core/search.py`)
|
||||
|
||||
- **How it works**: Apps export their menus over the `com.canonical.dbusmenu` DBus interface.
|
||||
- **The Shim**: To force GTK apps to export their menus even if they weren't natively designed to, Modus preloads a custom C library (`src/globalmenu/libmenu_button_shim.c`). This shim hooks into GTK's menu generation and forces the export.
|
||||
- **Python Daemon**: The Python side (`src/globalmenu/service.py`) acts as a DBus server, listens for these exported menus, and dynamically renders them as Fabric widgets in the top panel.
|
||||
On each keystroke:
|
||||
|
||||
## 4. State Management & Services
|
||||
1. Routes the query — if it starts with a keyword (e.g., `gg ` for Google),
|
||||
the query goes exclusively to that plugin
|
||||
2. Otherwise, dispatches to all `searchable` plugins in parallel via daemon
|
||||
threads
|
||||
3. Collects results via `GLib.idle_add` (main thread callback)
|
||||
4. Merges results sorted by plugin priority then result score
|
||||
5. Cancels previous in-flight searches with `threading.Event` tokens
|
||||
|
||||
Modus relies heavily on asynchronous event-driven services located in `src/services/`.
|
||||
These services monitor system states and emit signals when things change, updating the UI reactively:
|
||||
- `battery.py`: Uses UPower over DBus to track battery state.
|
||||
- `bluetooth.py`: Uses BlueZ over DBus.
|
||||
- `brightness.py`: Uses `brightnessctl` or `ddcutil`.
|
||||
- `network.py`: Uses NetworkManager over DBus.
|
||||
### Layer 3: Plugin System (`core/loader.py`, `core/manager.py`, `core/registry.py`)
|
||||
|
||||
By using signals (`connect("changed", update_ui)`), Modus ensures that it uses virtually 0% CPU while idle.
|
||||
- **Loader**: Scans `config/plugins/` for `.py` files and package directories.
|
||||
Handles per-plugin virtualenvs via `uv` for dependency isolation. Supports
|
||||
deep reload (re-import from disk without restart).
|
||||
- **Manager**: Orchestrates lifecycle — instantiate, enable/disable, reload,
|
||||
release memory. Persists disabled plugin list to `plugins.json`.
|
||||
- **Registry**: Stores plugin metadata keyed by id. Maintains keyword-to-plugin
|
||||
map for fast routing.
|
||||
|
||||
### Data flow
|
||||
|
||||
```
|
||||
User types → debounce (200ms) → route query
|
||||
├─ keyword match → single plugin search → render
|
||||
└─ global search → dispatch to threads → collect → merge → render
|
||||
|
||||
User selects result → action callback → (copy, launch, etc.)
|
||||
User presses Escape → cancel searches → release plugin memory → hide
|
||||
```
|
||||
|
||||
## 3. High-Performance App Switcher (AppCapture)
|
||||
|
||||
The App Switcher (Alt+Tab) needs live video previews of Wayland windows. Pure
|
||||
Python DBus polling is too slow, so Modus uses a C-extension at
|
||||
`src/window/switcher/app-capture/`.
|
||||
|
||||
- Uses `hyprland-toplevel-export-v1` Wayland protocol to get dmabuf/shm buffers
|
||||
from Hyprland
|
||||
- Compiled to `libappcapture.so`, exposed to Python via GObject Introspection
|
||||
- Framerate throttled by Python using `invoke_repeater`, controlled by
|
||||
`switcher_live_preview_delay_ms` in `config.toml`
|
||||
|
||||
## 4. Global Menu (DBus)
|
||||
|
||||
macOS-style menu bar in the top panel.
|
||||
|
||||
- Apps export menus over `com.canonical.dbusmenu` DBus
|
||||
- Custom C shim (`src/globalmenu/libmenu_button_shim.c`) forces GTK apps to
|
||||
export menus even if they weren't designed to
|
||||
- Python daemon (`src/globalmenu/service.py`) listens for exported menus and
|
||||
renders them as Fabric widgets in the panel
|
||||
|
||||
## 5. State Management & Services
|
||||
|
||||
Services in `src/services/` monitor system state asynchronously and emit
|
||||
signals:
|
||||
|
||||
| Service | Source | What it tracks |
|
||||
|---------|--------|---------------|
|
||||
| `battery.py` | UPower over DBus | Battery percentage, charging state |
|
||||
| `bluetooth.py` | BlueZ over DBus | Bluetooth devices |
|
||||
| `brightness.py` | `brightnessctl` / `ddcutil` | Screen brightness |
|
||||
| `network.py` | NetworkManager over DBus | WiFi, ethernet state |
|
||||
|
||||
Uses signals (`connect("changed", update_ui)`) for reactive UI — virtually 0%
|
||||
CPU while idle.
|
||||
+14
-1
@@ -8,7 +8,6 @@ Modus is highly configurable using TOML files located in the `config/` directory
|
||||
|------|---------|
|
||||
| `config.toml` | Main application configuration (modules, features, toggles). |
|
||||
| `mods.toml` | Custom panel button definitions (menus, shell commands). |
|
||||
| `launcher.toml` | Settings specific to the application launcher/spotlight. |
|
||||
| `dock.toml` | Pinned applications for the dock. |
|
||||
|
||||
---
|
||||
@@ -31,6 +30,20 @@ The `config.toml` file controls which widgets and services are enabled. Most cha
|
||||
- `switcher_live_preview_delay_ms`: Framerate delay for previews (e.g., `200` = ~5fps). Lower is smoother but uses more CPU.
|
||||
- `osd`: Enables on-screen displays for volume and brightness changes.
|
||||
|
||||
**Spotlight Search Keybinds** (defined in `config/hypr/modus.lua`):
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Super + D` | Open spotlight search (default) |
|
||||
| `Super + E` | Open emoji picker |
|
||||
| `Super + V` | Open clipboard history |
|
||||
| `Super + W` | Open wallpaper browser |
|
||||
| `Alt + Shift + W` | Set random wallpaper |
|
||||
|
||||
These can be customized in `config/hypr/modus.lua`. The spotlight `.toggle()`
|
||||
method accepts optional arguments to pre-fill keywords: `toggle('em')` opens
|
||||
the emoji picker directly.
|
||||
|
||||
---
|
||||
|
||||
## `mods.toml` (Custom Panel Buttons)
|
||||
|
||||
+89
-23
@@ -3,35 +3,101 @@
|
||||
## Frequently Asked Questions
|
||||
|
||||
### 1. The Global Menu isn't showing up for some apps?
|
||||
Modus uses a custom `libmenu_button_shim.so` along with `appmenu-gtk-module` to export GTK menus over DBus.
|
||||
- Ensure `appmenu-gtk-module` is installed.
|
||||
- Ensure the shim was compiled correctly during installation.
|
||||
- Note that non-GTK apps (like Electron apps or Qt apps) might need specific flags (like `export QT_QPA_PLATFORMTHEME=appmenu-qt5`) to export their menus.
|
||||
|
||||
### 2. High CPU Usage when the Application Switcher is open?
|
||||
The Modus App Switcher uses a custom Wayland C-Backend (`app-capture`) to generate live video previews of your running windows.
|
||||
- The previews are software-rendered via Cairo.
|
||||
- If you have many 4k windows open, generating live thumbnails might cause a CPU spike.
|
||||
- **Fix:** You can throttle the framerate in `config.toml` by increasing `switcher_live_preview_delay_ms` (e.g., set to `200` for ~5 FPS), or entirely disable live previews by setting `switcher_live_preview = false`.
|
||||
Modus uses a custom `libmenu_button_shim.so` with `appmenu-gtk-module` to
|
||||
export GTK menus over DBus.
|
||||
- Ensure `appmenu-gtk-module` is installed
|
||||
- Ensure the shim was compiled correctly during installation
|
||||
- Non-GTK apps (Electron, Qt) may need flags like
|
||||
`export QT_QPA_PLATFORMTHEME=appmenu-qt5`
|
||||
|
||||
### 3. The Dock is covering my fullscreen games/videos?
|
||||
Ensure you have `dock_auto_hide = true` in your `config.toml`. The dock intelligently hides when a window is maximized or goes fullscreen.
|
||||
### 2. High CPU usage when App Switcher is open?
|
||||
|
||||
### 4. Why isn't my Volume or Brightness OSD showing up?
|
||||
Ensure that `osd = true` is set in your `config.toml`. The On-Screen Display relies on `brightnessctl` for brightness and standard audio services (like Pipewire/Wireplumber) for volume. Make sure those underlying tools are working correctly on your system.
|
||||
The App Switcher uses a custom C-backend to capture live window previews.
|
||||
- 4K windows with many open can spike CPU
|
||||
- **Fix**: Increase `switcher_live_preview_delay_ms` in `config.toml` (e.g., `200`
|
||||
for ~5 FPS) or set `switcher_live_preview = false` to disable previews entirely
|
||||
|
||||
### 5. The Weather widget is showing the wrong location?
|
||||
You can easily fix this by updating the `weather_location` setting in your `config.toml`. Simply type your city and country (e.g., `"London, UK"` or `"Patan, Nepal"`), and the widget will fetch the correct local weather.
|
||||
### 3. Dock covering fullscreen games/videos?
|
||||
|
||||
Set `dock_auto_hide = true` in `config.toml`. The dock hides automatically
|
||||
when a window is maximized or fullscreen.
|
||||
|
||||
### 4. Volume/Brightness OSD not showing?
|
||||
|
||||
Set `osd = true` in `config.toml`. Requires `brightnessctl` for brightness and
|
||||
Pipewire/Wireplumber for audio.
|
||||
|
||||
### 5. Weather widget shows wrong location?
|
||||
|
||||
Update `weather_location` in `config.toml` (e.g., `"London, UK"` or
|
||||
`"Patan, Nepal"`).
|
||||
|
||||
### 6. Spotlight plugin isn't showing up?
|
||||
|
||||
- Check the file is in `config/plugins/` (not `examples/plugins/`)
|
||||
- Check `config/plugins/` is NOT in `.gitignore` for your own repos
|
||||
- Run `journalctl --user -f -t modus` and look for `[PluginLoader]` errors
|
||||
- Make sure the file has `PLUGIN = YourClassName` at the bottom
|
||||
- If using dependencies, ensure `uv` is installed
|
||||
- After editing, run `fabric-cli exec modus1 'deep_reload your_plugin_id'`
|
||||
|
||||
### 7. Plugin has dependencies but they aren't installed?
|
||||
|
||||
- Check `uv` is installed (`which uv`)
|
||||
- Check `requirements.txt` is next to your `.py` file or inside your package dir
|
||||
- The venv lives at `config/plugins/.venvs/<plugin_name>/`
|
||||
- Delete that directory to force re-install on next load
|
||||
|
||||
---
|
||||
|
||||
## Pro Tips
|
||||
|
||||
- **Live Reload for Mods:** The `config/mods.toml` file is actively monitored. Any changes you make to custom panel buttons or dropdown menus are instantly loaded—no shell restart required!
|
||||
- **Pinning Apps Correctly:** When pinning apps in `dock.toml`, ensure you use the exact `.desktop` file name (excluding the `.desktop` extension). For example, use `"org.gnome.Nautilus"` instead of just `"nautilus"` or `"Files"`.
|
||||
- **Custom Scripts in Panel:** You aren't limited to simple commands in `mods.toml`. You can point the `on-clicked` actions to complex bash scripts stored in your home directory to create advanced panel utilities.
|
||||
- **Forcing a Theme Update:** Because Modus uses Matugen for styling, the entire shell's color scheme is tethered to your wallpaper. If you want to completely change the look of your desktop, simply set a new wallpaper! Modus will automatically generate a new palette and hot-reload the UI without dropping your session.
|
||||
- **Keyboard Shortcuts:**
|
||||
- `uv run start` — Starts the main shell daemon.
|
||||
- `uv run lock` — Triggers the session lock screen.
|
||||
- **Wayland Screencapture:** You can quickly toggle the screen recording/capture widget from the panel or bind it to a key using your Hyprland config (e.g. `bind = SUPER, S, exec, fabric-cli exec modus 'screencapture.toggle()'`).
|
||||
- **Live Reload for Mods**: `config/mods.toml` is monitored for changes. Edits
|
||||
apply instantly — no restart needed
|
||||
|
||||
- **Pinning Apps**: Use exact `.desktop` filename in `dock.toml` (e.g.,
|
||||
`"org.gnome.Nautilus"`, not `"Files"`)
|
||||
|
||||
- **Custom Scripts**: `on-clicked` in `mods.toml` can point to bash scripts in
|
||||
your home directory
|
||||
|
||||
- **Theme Updates**: Modus uses Matugen tied to your wallpaper. Set a new
|
||||
wallpaper and the shell recolors automatically
|
||||
|
||||
- **Spotlight Keyboard Shortcuts** (from `config/hypr/modus.lua`):
|
||||
|
||||
| Key | Action |
|
||||
|-----|--------|
|
||||
| `Super + D` | Open spotlight search |
|
||||
| `Super + E` | Open emoji picker |
|
||||
| `Super + V` | Open clipboard history |
|
||||
| `Super + W` | Open wallpaper browser |
|
||||
|
||||
- **Deep Reload a Plugin**: After editing `config/plugins/hello.py`:
|
||||
|
||||
```bash
|
||||
fabric-cli exec modus1 'deep_reload hello'
|
||||
```
|
||||
|
||||
No Modus restart required. Works with all plugins.
|
||||
|
||||
- **Test a Plugin from Terminal**:
|
||||
|
||||
```bash
|
||||
fabric-cli exec modus1 'wth london'
|
||||
```
|
||||
|
||||
Routes to the weather plugin's `search()` and prints results to the Modus log.
|
||||
|
||||
- **Check Plugin Load Log**:
|
||||
|
||||
```bash
|
||||
journalctl --user -f -t modus
|
||||
```
|
||||
|
||||
Filters to Modus messages only. Shows plugin discovery, load errors, and
|
||||
search crashes.
|
||||
|
||||
- **Wayland Screencapture**: Toggle from panel or bind in Hyprland:
|
||||
`bind = SUPER, S, exec, fabric-cli exec modus 'screencapture.toggle()'`
|
||||
Reference in new issue
Block a user