Hyprland IPC Socket Communication Explained: Event and Command Socket Architecture
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, the CEventManager constructor creates this socket using a non-blocking stream socket configuration (lines 14‑22):
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 (lines 84‑99), the hyprctl utility constructs the socket path by combining the runtime directory and instance signature:
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 (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 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:
# 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:
#!/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:
#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 (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 (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.sockfor events and.socket.sockfor commands. - The event socket uses
CEventManagerinsrc/managers/EventManager.cppto broadcast state changes asynchronously using theevent>>data\nformat. - The command socket accepts text commands via
hyprctlor 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →