# Hyprland IPC Socket Communication Explained: Event and Command Socket Architecture

> Master Hyprland IPC socket communication. Learn about event and command sockets for seamless external tool integration without DBus.

- Repository: [Hypr Development/Hyprland](https://github.com/hyprwm/Hyprland)
- Tags: deep-dive
- Published: 2026-07-26

---

**Hyprland exposes two Unix-domain sockets—`.socket2.sock` for asynchronous event streaming and `.socket.sock` for synchronous command execution—enabling external tools to query compositor state and receive real-time updates without DBus dependencies or complex Wayland protocol implementations.**

Hyprland, the dynamic Wayland compositor maintained in the **hyprwm/Hyprland** repository, implements a lightweight IPC mechanism using standard Unix-domain sockets to bridge external scripts with the compositor core. This dual-socket architecture separates one-way event broadcasts from bidirectional command processing, providing a low-latency interface located in the user's runtime directory.

## The Dual Socket Architecture

Hyprland creates two distinct sockets during initialization, each serving a specific communication pattern.

### Event Socket (.socket2.sock)

The event socket operates as a broadcast channel for compositor state changes. In [`src/managers/EventManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/EventManager.cpp), the `CEventManager` constructor creates this socket using a non-blocking stream socket configuration (lines 14‑22):

```cpp
socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)

```

The socket binds to `${instancePath}/.socket2.sock`, where `instancePath` derives from the `HYPRLAND_INSTANCE_SIGNATURE` environment variable. Once bound, the implementation registers the file descriptor with the Wayland event loop via `wl_event_loop_add_fd` (line 40), allowing the compositor to accept client connections asynchronously without blocking the main thread.

### Command Socket (.socket.sock)

The command socket handles synchronous request-response cycles initiated by external clients. According to [`src/hyprctl/src/main.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/hyprctl/src/main.cpp) (lines 84‑99), the `hyprctl` utility constructs the socket path by combining the runtime directory and instance signature:

```cpp
std::string socketPath = std::string(getenv("XDG_RUNTIME_DIR")) + "/" + instanceSignature + "/.socket.sock";

```

Clients open a standard blocking stream socket (`socket(AF_UNIX, SOCK_STREAM, 0)`) and connect to this path to issue commands like `workspace`, `activewindow`, or `dispatch`.

## Event Protocol and Broadcasting

Events flowing through `.socket2.sock` follow a strict text-based protocol designed for efficient parsing.

### Message Formatting

The `formatEvent` method in [`src/managers/EventManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/EventManager.cpp) (lines 28‑31) wraps events in a simple delimiter-separated string:

```

event>>data\n

```

The implementation replaces any newlines within the payload data with spaces to maintain single-line messages, ensuring clients can parse events using simple line-based buffering. This format allows easy splitting on the `>>` delimiter to separate event types (e.g., `workspace`, `windowtitle`, `monitoradd`) from their associated payloads.

### The Broadcasting Pipeline

When the compositor triggers a state change, it invokes `g_pEventManager->postEvent(...)`. The implementation (lines 63‑89) creates a shared `std::string` containing the formatted event and queues it for transmission to all connected clients. The method attempts an immediate flush; if the kernel write buffer blocks, the socket is marked writable and the event dispatches later via `flushClient` when the file descriptor becomes ready.

This design ensures that slow or stalled clients do not block event delivery to other listeners, maintaining low latency for responsive scripts.

## Client Implementation Patterns

External tools interact with Hyprland's IPC through standard socket operations, available in any programming language supporting Unix-domain sockets.

### Querying State with hyprctl

The reference implementation in [`src/hyprctl/src/main.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/hyprctl/src/main.cpp) demonstrates the command protocol (lines 52‑78). The client strips command-line flags from the argument string, writes the raw command to the socket, and reads the response until the server closes the connection:

```bash

# Query active window details as JSON

hyprctl activewindow -j | jq .

```

This transmits the string `"activewindow"` to the command socket and receives a JSON payload terminated by end-of-stream.

### Listening for Events in Python

The event socket accepts multiple simultaneous listeners. A Python script can monitor workspace changes by connecting to `.socket2.sock` and parsing the `>>` delimiter:

```python
#!/usr/bin/env python3
import socket
import os

runtime_dir = os.getenv('XDG_RUNTIME_DIR')
instance_sig = os.getenv('HYPRLAND_INSTANCE_SIGNATURE')
sock_path = f"{runtime_dir}/{instance_sig}/.socket2.sock"

with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as s:
    s.connect(sock_path)
    while True:
        line = s.recv(4096).decode('utf-8')
        if not line:
            break
        for ev in line.split('\n'):
            if ev:
                typ, data = ev.split('>>', 1)
                print(f"Event: {typ}, Data: {data}")

```

This mirrors the behavior of the `CEventManager` event loop, processing newline-terminated messages as they arrive.

### Building a Custom C++ Client

For performance-critical applications, a minimal C++ client replicates the `hyprctl` logic:

```cpp
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <string>

int main() {
    const char* runtime = getenv("XDG_RUNTIME_DIR");
    const char* sig     = getenv("HYPRLAND_INSTANCE_SIGNATURE");
    std::string path = std::string(runtime) + "/" + sig + "/.socket.sock";

    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
    sockaddr_un addr = {};
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, path.c_str(), sizeof(addr.sun_path)-1);

    connect(fd, reinterpret_cast<sockname*>(&addr), SUN_LEN(&addr));
    
    const char* cmd = "workspace";
    write(fd, cmd, strlen(cmd));
    
    char buf[4096] = {};
    ssize_t n = read(fd, buf, sizeof(buf)-1);
    if (n > 0) std::cout << "Reply: " << buf << "\n";
    close(fd);
}

```

This implementation directly corresponds to the socket operations in [`src/hyprctl/src/main.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/hyprctl/src/main.cpp) (lines 84‑99), using blocking I/O suitable for command-response patterns.

## Socket Lifecycle and Cleanup

Both sockets maintain proper resource hygiene during shutdown sequences. The `CEventManager` destructor in [`src/managers/EventManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/EventManager.cpp) (lines 43‑50) removes event sources from the Wayland event loop and closes the listening file descriptor, preventing stale socket files from blocking future instances. Similarly, the `hyprctl` client closes its socket after receiving the server response (lines 73‑75), ensuring no file descriptor leaks in shell scripts or long-running automation tools.

## Summary

- **Hyprland IPC socket communication** relies on two Unix-domain sockets: `.socket2.sock` for events and `.socket.sock` for commands.
- The **event socket** uses `CEventManager` in [`src/managers/EventManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/managers/EventManager.cpp) to broadcast state changes asynchronously using the `event>>data\n` format.
- The **command socket** accepts text commands via `hyprctl` or custom clients, returning JSON or plain text responses through standard blocking I/O.
- Events dispatch through the **Wayland event loop** (`wl_event_loop_add_fd`) with non-blocking sockets to prevent head-of-line blocking between clients.
- Both sockets clean up automatically on compositor exit or client disconnect, leaving no persistent files in the runtime directory.

## Frequently Asked Questions

### What is the difference between Hyprland's two IPC sockets?

The **event socket** (`.socket2.sock`) provides one-way, asynchronous streaming of compositor events like workspace changes or window focus updates, while the **command socket** (`.socket.sock`) handles synchronous request-response interactions for querying state or executing commands. External scripts typically listen to the event socket for real-time notifications and use the command socket to retrieve detailed state information on demand.

### How do I locate Hyprland's IPC sockets when writing a custom script?

Construct the socket path using the `XDG_RUNTIME_DIR` environment variable combined with `HYPRLAND_INSTANCE_SIGNATURE`. For example: `${XDG_RUNTIME_DIR}/${HYPRLAND_INSTANCE_SIGNATURE}/.socket2.sock` for events or `.socket.sock` for commands. These paths remain stable for the lifetime of the compositor instance and are cleaned up automatically on exit.

### Can multiple clients connect to Hyprland's event socket simultaneously?

Yes. The `CEventManager` implementation accepts multiple concurrent connections on `.socket2.sock` and broadcasts events to all connected clients independently. Each client maintains its own write buffer, and the compositor handles backpressure per-client through the `flushClient` mechanism, ensuring that a slow consumer does not affect event delivery to other listeners.

### What message format does Hyprland use for IPC events?

Events follow the format `event>>data\n` where `event` is the event type identifier (such as `workspace`, `windowtitle`, or `monitoradded`), `>>` serves as a delimiter, and `data` contains the payload. Newlines within the payload data are replaced with spaces to maintain single-line messages, allowing clients to parse the stream using simple line-based buffering.