How Hyprland Screencasting Permissions Work: Dynamic Permission System Explained

Hyprland screencasting permissions are enforced by the CDynamicPermissionManager, which intercepts Wayland screencopy protocol requests and either allows, denies, or prompts the user based on configurable Lua rules.

The hyprwm/Hyprland compositor implements a dynamic permission system that governs which clients can capture the screen through the Wayland screencopy protocol. Instead of unconditionally granting access, Hyprland evaluates every screencasting request against user-defined rules and runtime prompts. This article breaks down the internal flow from permission identification to the final rendered frame, referencing the actual source implementation.

The Screencast Permission Pipeline

When a client such as grim or xdg-desktop-portal-hyprland initiates a screen capture, the request enters a dedicated permission pipeline before any pixel data is shared.

Identifying the Permission Type

Hyprland classifies screen-sharing operations under the PERMISSION_TYPE_SCREENCOPY enumerator. This value is defined in the eDynamicPermissionType enum inside src/managers/permissions/DynamicPermissionManager.hpp. Every incoming screencopy request is tagged with this type so the compositor can apply the correct policy rules.

Looking Up Rules in CDynamicPermissionManager

The core logic resides in CDynamicPermissionManager, implemented across src/managers/permissions/DynamicPermissionManager.cpp. When a client request arrives, the manager calls clientPermissionMode to evaluate the following:

  • Config rules loaded from the Lua configuration via hl.permission entries and the ecosystem:enforce_permissions setting. These are stored and checked through CConfigValue<Config::INTEGER>("ecosystem:enforce_permissions") and loaded via addConfigPermissionRule.
  • Cached decisions if the user has already responded to a prompt for the client's binary path.
  • Default behavior of ask if no static rule exists, which triggers a one-time user dialog through CAsyncDialogBox and remembers the result.

If a rule explicitly allows or denies the binary pattern, the manager returns immediately. Otherwise, the user is prompted and the decision is cached for future requests from the same binary.

Configuring Hyprland Screencasting Permissions

Administrators and users control screen-sharing behavior through Lua configuration bindings. The system is exposed via hl.permission in src/config/lua/bindings/LuaBindingsConfigRules.cpp.

Setting Static Allow Rules

To bypass interactive prompts for trusted capture tools, add explicit allow rules in your Hyprland Lua config:

-- Allow grim and the xdg-desktop-portal-hyprland implementation to capture without prompting
hl.permission("/usr/(bin|local/bin)/grim", "screencopy", "allow")
hl.permission("/usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland", "screencopy", "allow")

These patterns match the client binary path and permanently grant Hyprland screencasting permissions for the specified executables.

Denying Screencopy for Specific Clients

Conversely, you can block problematic or untrusted applications:

-- Prevent a specific tool from ever capturing the screen
hl.permission("/opt/badtool/capture", "screencopy", "deny")

Preserving the Ask Behavior

Setting the policy to ask forces Hyprland to request confirmation on the first execution and then cache the answer:

-- Prompt once per binary, then remember the decision
hl.permission(".*", "screencopy", "ask")

If no rule is configured, this is the default behavior.

Internal Handling of Allowed and Denied Requests

Once CDynamicPermissionManager returns a verdict, the screencopy protocol implementation reacts differently depending on the outcome.

Allowed Screencopy Execution

If the permission mode resolves to allow, the request proceeds normally through src/protocols/Screencopy.cpp. The compositor captures the frame buffer and delivers it to the client without obstruction.

Denied Request Rendering

If access is denied, Hyprland does not crash or silently fail. Instead, the renderer constructs a dedicated denial texture. Inside src/render/Renderer.cpp, the m_screencopyDeniedTexture resource is created to display a "Permission denied to share screen" message.

When the ScreenshareFrame prepares output in src/managers/screenshare/ScreenshareFrame.cpp, it detects the denied state and draws this fallback texture in place of the actual desktop contents. This provides clear visual feedback that the screencasting permission was rejected.

Programmatic Permission Checks

Extensions and protocol implementations can query the current permission state before attempting capture. The following pattern illustrates how Hyprland evaluates a client at runtime:

auto mode = PROTO::screencopy->clientPermissionMode(client, PERMISSION_TYPE_SCREENCOPY);
if (mode == PERMISSION_RULE_ALLOW_MODE_ALLOW) {
    // proceed with screencopy
} else {
    // render denied texture (handled internally)
}

This check ensures that enforcement remains centralized and consistent across all screen-sharing entry points.

Summary

Frequently Asked Questions

How do I permanently allow a screen recorder in Hyprland?

Add a Lua config rule using hl.permission with the "allow" action. For example, grant access to grim by writing hl.permission("/usr/bin/grim", "screencopy", "allow") in your Hyprland configuration. The binary path is matched against a regex pattern, and the decision is persisted across sessions.

What happens when a screencast permission is denied?

Hyprland substitutes a dedicated denial texture instead of the desktop image. The texture m_screencopyDeniedTexture is created in src/render/Renderer.cpp and drawn by the ScreenshareFrame class in src/managers/screenshare/ScreenshareFrame.cpp, clearly indicating that screen sharing is blocked.

Where does Hyprland store screencasting permission rules?

Static rules are defined in the Lua configuration through the hl.permission binding exposed in src/config/lua/bindings/LuaBindingsConfigRules.cpp. Dynamic, cached answers from user prompts are managed at runtime by CDynamicPermissionManager in src/managers/permissions/DynamicPermissionManager.cpp.

Can I set a global ask policy for all screencopy clients?

Yes. Use hl.permission(".*", "screencopy", "ask") to match all binaries. Because the default behavior is already to ask once per unrecognized binary and remember the response, this rule explicitly enforces the prompt policy across every client.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →