Yashiki Tiling Window Manager

repository·main·Indexed 42 days ago

https://github.com/typester/yashiki

A tiling window manager for macOS written in Rust. It features tag-based workspaces, a shell-script-based configuration system, and external layout engines via a JSON protocol. Supports integration with workspace indicators like Ranma and SketchyBar.

Tokens
23.7K
Snippets
74
Records
111
Agent score
54%

What's inside Yashiki

  1. Understand the Yashiki project structure

    main

    The project is organized into several specialized crates:

    • yashiki/: The core window manager (WM) daemon and CLI tool.
    • yashiki-ipc/: Contains the shared protocol definitions used for communication.
    • yashiki-layout-tatami/: A layout engine implementing the Master-stack layout.
    • yashiki-layout-byobu/: A layout engine implementing the Accordion layout.
  2. How the Yashiki + SketchyBar integration works

    main

    The integration uses a bridge pattern to connect Yashiki events to SketchyBar UI updates:

    1. sketchybarrc: Configures the top bar (40px height), defines 10 tag indicators, and initializes the event bridge.
    2. plugins/yashiki_bridge.sh: Acts as the subscriber. It listens to yashiki events, manages the current workspace state using jq, and triggers SketchyBar updates.
    3. plugins/workspace.sh: A per-tag plugin called during updates. It compares the current state against a bitmask to set the visual appearance of each tag.

    Tag States

    StateMeaningAppearance
    ActiveVisible on focused displayWhite icon + background
    OccupiedHas windows but not visibleWhite icon
    VacantNo windowsDimmed icon

    Interaction

    Clicking a tag executes the command yashiki tag-view <bitmask> to switch the workspace view.

  3. Extend Yashiki with custom layout engines

    main
    Yashiki supports external layout engines through a stdin/stdout JSON protocol. This allows you to implement your own layout logic in any language that can communicate via JSON over standard streams. For the full technical specification of this protocol, refer to docs/layout-engine.md.
  4. Use keybinding modes (river-style)

    main

    Yashiki supports keybinding modes, allowing you to define different sets of keybindings that are active only when a specific mode is entered. This is useful for complex tasks like resizing layouts without cluttering your primary keybindings.

    1. Declare a mode: Use yashiki declare-mode <name>.
    2. Enter a mode: Use yashiki bind <key> enter-mode <name>.
    3. Exit a mode: Use yashiki bind <key> enter-mode normal.
    4. Mode-specific bindings: Use the --mode <name> flag with yashiki bind to define keys that only work while that mode is active.

    Example: Resize Mode

    yashiki declare-mode resize
    
    # Enter resize mode
    yashiki bind alt-r enter-mode resize
    
    # Bindings active ONLY in resize mode
    yashiki bind --mode resize h layout-cmd dec-main-ratio
    yashiki bind --mode resize l layout-cmd inc-main-ratio
    
    # Exit resize mode
    yashiki bind --mode resize escape enter-mode normal
    yashiki declare-mode resize
    yashiki bind alt-r enter-mode resize
    yashiki bind --mode resize h layout-cmd dec-main-ratio
    yashiki bind --mode resize escape enter-mode normal
  5. Understand the Ranma workspace indicator architecture

    main

    The workspace indicator operates via a bridge pattern that connects Yashiki events to Ranma UI updates:

    1. init: A startup script that launches the bridge process using reap.
    2. yashiki_bridge.py: A plugin that subscribes to Yashiki events, manages the mapping of display IDs to Ranma display slots, and sends direct updates to Ranma items.

    Data Flow: yashiki subscribe $\rightarrow$ yashiki_bridge.py $\rightarrow$ ranma set (direct updates)

    Multi-Monitor Logic: The bridge maintains a mapping of Yashiki display IDs to Ranma display slots. When a display is clicked, the bridge executes yashiki tag-view --output <display_id> to ensure the correct display is targeted. This allows displays to be connected or disconnected without requiring a restart.

  6. Understand how the SketchyBar bridge works

    main

    The SketchyBar menubar example uses a bridge pattern to sync Yashiki state with SketchyBar UI.

    Architecture

    yashiki subscribe $\rightarrow$ yashiki_bridge.py $\rightarrow$ sketchybar --set (direct updates)

    Unlike minimal examples that use event triggers, this bridge directly updates SketchyBar item properties via --set commands.

    Multi-Monitor Slot Assignment

    The bridge maintains a slot assignment mapping Yashiki display IDs to SketchyBar display slots (1-3). This mapping is independent of SketchyBar's internal arrangement IDs, allowing displays to be connected/disconnected without restarting SketchyBar.

    • On display connect: New display is assigned the lowest free slot.
    • On display disconnect: Slot is freed for reuse.
    • Interaction: Each slot's click handler targets the correct Yashiki display via yashiki tag-view --output <display_id>.
  7. How Yashiki tags and bitmasks work

    main

    Unlike traditional window managers that use numbered workspaces, Yashiki uses a tag system inspired by dwm and river.

    • Multiple Tags: A window can belong to multiple tags simultaneously.
    • Tag Combination: You can view any combination of tags at once by toggling them on or off.
    • Bitmask Representation: Tags are represented as bits in a number. This allows you to view multiple tags simultaneously by using a bitmask (e.g., viewing Tag 1 and Tag 3 together uses bitmask 5 or binary 101).
    TagBitmaskBinaryShell Expression
    Tag 11001$((1<<0))
    Tag 22010$((1<<1))
    Tag 34100$((1<<2))
    Tag 481000$((1<<3))
  8. Understand the Yashiki Tag System

    main

    Yashiki uses a bitmask-based tag system, allowing windows to belong to multiple tags simultaneously. This is similar to the tagging systems in awesome or river.

    Bitmask Mapping:

    • Tag 1 = 1 (binary 001)
    • Tag 2 = 2 (binary 010)
    • Tag 3 = 4 (binary 100)
    • Tags 1+2 = 3 (binary 011)

    When writing your ~/.config/yashiki/init script, you can use shell arithmetic to generate these values:

    • $((1<<0)) = 1
    • $((1<<1)) = 2
    • $((1<<2)) = 4
  9. How layout engines work in Yashiki

    main

    Yashiki uses a 'river-style' approach where layout engines are separate processes that communicate with the Yashiki daemon via stdin/stdout using newline-delimited JSON.

    This architecture allows you to:

    • Implement custom layout algorithms without modifying the core Yashiki codebase.
    • Write layout engines in any programming language.
    • Maintain independent state within each layout engine process.

    Communication follows a request-response pattern: Yashiki sends layout requests or commands, and the engine responds with window geometries or status messages.

    // Example: Yashiki requesting a layout
    {"Layout":{"width":1920,"height":1080,"windows":[123,456,789]}}
    
    // Example: Engine responding with geometries
    {"Layout":{"windows":[{"id":123,"x":0,"y":0,"width":960,"height":1080}]}}
  10. How Yashiki layout engines work

    main

    Yashiki uses external layout engines that run as separate processes and communicate via JSON. This allows for modularity and the ability to write custom engines in any language.

    Common layout engines include:

    • tatami: A master-stack layout where one main window is paired with a stack of other windows.
    • byobu: An accordion layout where windows are stacked with a staggered effect.

    Layout engines are configured via the yashiki layout-cmd command.

  11. Understand the difference between ignore and float actions

    main

    Both ignore and float exclude windows from the tiling engine, but they serve different purposes based on how much control you want yashiki to maintain.

    Featureignorefloat
    ManagedNo (completely skipped)Yes (floating state)
    list-windowsNot shownShown
    window-focusCannot focusCan focus
    Tag operationsNo effectHidden/shown on tag switch
    Manual toggleNot possiblewindow-toggle-float works

    When to use each:

    • ignore: For windows you don't want yashiki to touch at all, such as temporary popups, tooltips, or dropdown menus (e.g., Firefox dropdowns). These windows appear/disappear frequently and are not directly interacted with via tiling.
    • float: For windows you interact with but want excluded from tiling, such as Finder, System Settings, or dialogs. These remain managed, meaning they can be focused, moved between tags, and manually toggled.
  12. How Yashiki configuration works

    main

    Yashiki does not use a static configuration file. Instead, it uses an executable configuration file located at ~/.config/yashiki/init.

    • Flexibility: The init file can be any executable (shell script, Python, Ruby, compiled binary, etc.).
    • Mechanism: When the daemon starts, it executes this file. The script configures the daemon by calling CLI commands like yashiki bind ....
    • Application: Changes to the configuration take effect after restarting the Yashiki daemon.
    #!/usr/bin/env python3
    import subprocess
    for i in range(1, 10):
        subprocess.run(["yashiki", "bind", f"alt-{i}", "tag-view", str(1 << (i-1))])