Skip to content
HalffdPublic

About

Havel is a scripting language for desktop automation: hotkeys, window control, input simulation, and game macros. Write concise scripts that run on X11 and Wayland

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Latest commit

 

History

7,024 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Havel is a powerful utility for managing windows and hotkeys across multiple platforms, with a focus on Linux X11 environments. It allows for complex window management, hotkey configurations, and automated tasks via its own Havel scripting language.

📋 Table of Contents

Features

  • Global hotkey registration and management
  • Dynamic conditional hotkeys with runtime evaluation
  • Window tracking and manipulation
  • Havel scripting language for complex automation (replacing Lua)
  • Configurable via text files
  • Cross-platform support

Installation

Prerequisites

  • C++17 compatible compiler (GCC 9+ or Clang 10+)
  • CMake 3.10+
  • X11 development libraries (on Linux)
  • Qt6 development libraries

Quick Install (Pre-built Packages)

Debian/Ubuntu (.deb)

# Download the latest .deb from releases or build locally
sudo apt install ./havel-1.0.0-Linux-x86_64.deb
# Or from GitHub releases:
# wget https://github.com/Halffd/Havel/releases/latest/download/havel-1.0.0-Linux-x86_64.deb
# sudo apt install ./havel-1.0.0-Linux-x86_64.deb

Fedora/RHEL (.rpm)

# Download the latest .rpm from releases or build locally
sudo dnf install ./havel-1.0.0-Linux-x86_64.rpm
# Or from GitHub releases:
# wget https://github.com/Halffd/Havel/releases/latest/download/havel-1.0.0-Linux-x86_64.rpm
# sudo dnf install ./havel-1.0.0-Linux-x86_64.rpm

Arch Linux (AUR/PKGBUILD)

# Using the provided PKGBUILD
git clone https://github.com/Halffd/Havel
cd Havel
makepkg -si
# Or install from AUR (when available):
# yay -S havel

Build from Source

git clone https://github.com/Halffd/Havel
cd Havel

# Detect system dependencies
./build.sh detect

# Build modes (see Build System for full table)
# Default (mode 6): Debug, tests, Havel Lang, no LLVM
./build.sh build

# Full release with LLVM JIT (mode 5)
./build.sh 5 build

# Release without LLVM, portable binaries (mode 9 equivalent)
./build.sh 9 build

# Create .deb and .rpm packages
./packaging/package.sh build-release

Build Modes

# Mode 0: Debug + Tests + Havel Lang + LLVM
./build.sh 0 build

# Mode 5: Release + Tests + Havel Lang + LLVM (full features)
./build.sh 5 build

# Mode 6: Debug + Tests + Havel Lang + no LLVM (default, fast)
./build.sh 6 build

# Mode 9: Release + Tests + Havel Lang + no LLVM
./build.sh 9 build

# Headless (no Qt/GUI) — server/embedded use
./build.sh 12 build  # Debug headless
./build.sh 15 build  # Release headless

# ThreadSanitizer
./build.sh 16 build

Environment Variables

# Parallel jobs (default: all cores)
THREADS=8 ./build.sh build

# Sanitizer levels (Debug builds only)
./build.sh --asan-level full build      # strict ASAN
./build.sh --ubsan-full build           # all UBSAN checks
./build.sh --tsan build                 # ThreadSanitizer (mode 16)
./build.sh --no-asan build              # disable ASAN

Portable Builds (for Distribution)

# Build portable binaries without -march=native
cmake -B build-release -DCMAKE_BUILD_TYPE=Release -DPORTABLE_BUILD=ON
cmake --build build-release

Building on Windows

Status: the Windows build does not compile yet. The port is in progress. Do not expect a working havel.exe from this tree. This section documents how to try, and what is known to be missing.

Prerequisites:

  • Visual Studio 2022 or newer with the Desktop development with C++ workload
  • CMake 3.20 or newer
  • No X11, Wayland, libevdev, D-Bus or libcurl. Those are Linux subsystems and are either gated out or not yet ported on Windows.
# Headless is the only UI backend that builds: Qt is not wired up on Windows.
cmake -S . -B build-msvc -G "Visual Studio 2022" `
  -DUI_BACKEND=HEADLESS -DENABLE_QT_UI_BACKEND=OFF
cmake --build build-msvc --config Release --parallel 2

Use --parallel 2 or lower. The default (all cores) exhausts the MSVC heap and the link step fails rather than the compile.

Current error count on a clean configure is around 320, down from 132 when the port started. That number rose partway through because MSVC stops a translation unit at 100 errors and MSBuild halts dependent projects, so earlier counts were not seeing most of the tree at all.

What works today:

  • The C runtime layer (src/c): logger and config compile clean on Windows, using CRITICAL_SECTION, FindFirstFile, and GetDiskFreeSpaceEx.
  • Brightness: gamma is driven through SetDeviceGammaRamp / GetDeviceGammaRamp, with monitors enumerated through DisplayManager.
  • Shell pipelines: pipe/fork/execvp are replaced with CreatePipe and CreateProcessW.

What does not, in priority order:

  1. Hotkey parsing (KeyMap.cpp) still uses X11 keysyms (XK_*). It needs to map onto Win32 virtual-key codes. This is the highest-risk item: a wrong mapping selects the wrong key silently instead of failing loudly.
  2. X11 window management (WindowManager, WindowManagerDetector, WindowService, X11Backend) and Wayland are unported.
  3. curl, dbus and fnmatch are not available and have no Windows path yet.

See docs/build/windows.md for the toolchain details, docs/windows-modules.md for the per-module triage, and docs/plat001-investigation.md for the full history including the wrong turns.

One thing to know before writing code: Windows headers define several macros that collide with ordinary C++ names. ERROR, near, min, max and Yield() have all broken this tree. If something suddenly fails to compile after including a Windows header, suspect a macro before suspecting the code.

Verify Installation

# Run the binary
havel --version

# Run a script
havel scripts/test_basic.hv

# Start REPL
havel --repl

System-Wide Install (from Build)

# From build-release (mode 5 or 9)
sudo cmake --install build-release

# Verify
havel --version

Basic Hotkey Mapping

hotkey => action

Pipeline Transformations

data | transform1 | transform2

Blocks

hotkey => {
    // multiple statements
}

Variables

let variable_name = value

Conditional Logic

if condition { ... } else { ... }

Keywords

Keyword Purpose Example
let Variable declaration let x = 5
if/else Conditional execution if x > 0 { ... } else { ... }
when Conditional block when condition { ... }
fn Function definition fn name(args) => ...
return Function return return value
import Module import import module from "path"
config Configuration block config { ... }
devices Device configuration devices { ... }
modes Mode configuration modes { ... }

Conditional Hotkeys

Postfix Conditional Syntax

hotkey => action if condition

Prefix Conditional Syntax

hotkey if condition => action

When Blocks

when condition {
    hotkey1 => action1
    hotkey2 => action2
}

Nested Conditions

when outer_condition {
    hotkey if inner_condition => action
}

Example:

F1 when mode gaming => { print("Gaming mode active") }
!C if remapCapslock == false => map("capslock", "esc")

When Blocks

Group multiple hotkeys under a shared condition:

when mode gaming {
    ^!A => click()
    ^!B => click("right")
    F1 if health < 50 => send("e")
}

All conditions are evaluated dynamically at runtime, allowing variables to change and trigger hotkeys accordingly.

New Features (Latest)

Traits

Interface-based polymorphism without inheritance:

trait Drawable { fn draw() }
impl Drawable for Circle { fn draw() { ... } }
let c = Circle(10)
c.draw()  // Calls impl method

const

Immutable variable bindings:

const x = 10
x = 20  // Error!

Enhanced repeat

Now accepts variables and expressions:

let n = 3
repeat n { print(n) }
repeat 2 + 3 { print("expression") }

Shell Commands

$ firefox              // Fire-and-forget
let out = `echo hello` // Capture output
print(out.stdout)

Screenshot with Image Data

let r = screenshot.full()
print(r.path)   // File path
print(r.data)   // Base64 image data
print(r.width)  // Dimensions

Struct Methods

struct Point {
  x, y
  fn init(x, y) { this.x = x; this.y = y }
  fn move(dx, dy) { this.x += dx; this.y += dy }
}
let p = Point(10, 20)  // Constructor sugar
p.move(5, 5)

Type Conversions

int(3.9)      // 3
str(123)      // "123"
list(1,2,3)   // [1, 2, 3]
set_(1,2,2)   // [1, 2] (unique)

Fuzzy Float Comparison

approx(0.1 + 0.2, 0.3)  // true

Config Sections (Hyprland-Style)

let step = 2
brightness {
  step = num(step) / 10
  current = 0.5
}
monitor "HDMI-0" {
  brightness = 0.8
}
print(config.brightness.step)  // 0.2

Debounced Config Saves

// Automatic 500ms debounce prevents write storms
config.set("Key", "value", true)  // Single save after batch
config.forceSave()                 // Immediate save

Examples

Basic Hotkey

F1 => send("Hello World!")

With Conditional Logic

^V when title "Discord" => {
    let clip = clipboard.get
    if clip | matches("^\d+") || has("secret") {
        let text = ""
        let length = len(clip)
        for i in 1..length {
            text += "*"
        }
        send("$clip")
    } else {
        send(clip)
    }
}

Conditional Hotkey

^!A if window.active.title.contains("Chrome") => send("^F5")

When Block

when mode coding {
    ^!S => send("^s")
    ^!F => send("^f")
    ^!G => send("^g")
}

With Built-in Help

The language includes a built-in help() function for interactive learning:

  • help() - Show main help page
  • help("syntax") - Show syntax reference
  • help("keywords") - Show all keywords
  • help("hotkeys") - Show hotkey features
  • help("modules") - Show available modules

Building the Project

# Build release
./build.sh 1 build

# Run havel script file
./build-release/havel script.hv

# Run REPL
./build-release/havwl

Alternatively, you can use the Makefile:

make

Troubleshooting

If you encounter build issues, please refer to the BUILD_ISSUES.md file for common problems and solutions.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Directory Structure

  • /src - Source code
  • /include - Header files
  • /config - Configuration files
  • /log - Log files
  • /build - Build directory
  • /scripts - Scripts examples

Embedding Havel

Havel can be easily embedded in C++ applications (games, window managers, etc.):

#include <Havel.hpp>

int main() {
    havel::VM vm;
    
    // Load and run code
    vm.load(R"(
        fn add(a, b) { return a + b }
        print(add(5, 3))
    )");
    
    return 0;
}

See embed/README.md for full documentation.

Examples

C API

A C API is also available for embedding in other languages:

#include <Havel-cAPI.h>

HavelVM* vm = havel_vm_create();
havel_vm_load(vm, "print(\"Hello from C!\")", "script");
havel_vm_destroy(vm);

See include/Havel-cAPI.h for the full C API.

Wayland support

Future plan is embedding it into a compositor Havel-wm currently in developme

About

Havel is a scripting language for desktop automation: hotkeys, window control, input simulation, and game macros. Write concise scripts that run on X11 and Wayland

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages