> ## Documentation Index
> Fetch the complete documentation index at: https://docs.overlayed.gg/llms.txt
> Use this file to discover all available pages before exploring further.

# Blocking Game Input

> Prevent input from reaching the game while your overlay is focused.

Picture a notes panel in your overlay: the player presses a keybind, a window with a text field opens, they start
typing - and their character sprints into a wall, because every keystroke also went to the game. This guide walks
through the fix: open a window via keybind, block game input while it's open, and hand input back when it closes.

<Info>
  This guide covers the block-while-open workflow. For the full input-blocking API (including granular per-key
  control), see [Blocking Input](/advanced/blocking-input). For per-window input options, see [Render
  Windows](/windows/render-windows).
</Info>

## How Input Blocking Works

Four things to know before writing any code:

1. **The game keeps receiving input by default.** Overlay windows render on top of the game, but they don't steal
   input from it - the game still acts on every key press and mouse click unless you explicitly block it. Blocking is
   always opt-in.
2. **There are two levels of control.** Each `RenderWindow` has its own input flags - `hasInput`, `hasInputBlock`,
   and `hasCursorOverride`, all enabled by default - that govern input on that window. Separately, the global
   `overlay.input` module blocks input to the game as a whole: all mouse input, all keyboard input, or a single key,
   plus forcing the cursor to show.
3. **Blocks are stateful and manual.** Nothing releases a block when your window hides or closes. Every block you set
   stays active until you clear it yourself.
4. **Scopes make blocks safe across windows.** `overlay.input.scope(name)` returns a handle whose blocks are
   reference-counted: a block engages when the first scope sets it and releases only after every scope that set it
   has cleared it.

## Blocking Input While a Window Is Open

<Steps>
  <Step title="Define the keybind and window">
    Set up a keybind and a hidden in-game window. This is the same wiring covered in [Show/Hide Window
    Keybinds](/guides/keybinds-show-hide-windows), so we'll keep it brief:

    ```typescript main.ts theme={null}
    export const overlay = overlayed({
    	keybinds: {
    		toggleNotes: {
    			keys: ["AltLeft", "KeyN"],
    			mode: "toggle",
    		},
    	},
    });
    ```

    ```typescript notesWindow.ts theme={null}
    import { overlay } from "./main";

    const notesWindow = overlay.windows.createInGameWindow({
    	width: 480,
    	height: 360,
    	show: false, // the keybind will reveal it
    });
    ```
  </Step>

  <Step title="Create an input scope">
    Get a scoped handle for this window's blocks. Even if you only have one window today, starting from a scope
    means a second window can block input later without the two fighting over state:

    ```typescript notesWindow.ts theme={null}
    const inputScope = overlay.input.scope("notesWindow");
    ```

    <Warning>
      Scope names are global identifiers - two calls to `overlay.input.scope("notesWindow")` return handles to
      the *same* scope, so one releasing a block frees the other's too. Give every window its own unique name.
    </Warning>
  </Step>

  <Step title="Block on show, release on hide">
    Wire the keybind to show the window and set the blocks together, and clear everything on hide:

    ```typescript notesWindow.ts theme={null}
    function showNotes() {
    	notesWindow.show();

    	// Keystrokes no longer reach the game, so typing is safe
    	inputScope.setGlobalKeyboardBlock(true);
    	// Clicks no longer reach the game
    	inputScope.setGlobalMouseBlock(true);
    	// Force the cursor to show so the user can click the text field
    	inputScope.setGlobalCursorOverride(true);
    }

    function hideNotes() {
    	notesWindow.hide();

    	inputScope.setGlobalKeyboardBlock(false);
    	inputScope.setGlobalMouseBlock(false);
    	inputScope.setGlobalCursorOverride(false);
    }

    overlay.keybinds.toggleNotes.on("toggle", () => {
    	if (notesWindow.isVisible()) {
    		hideNotes();
    	} else {
    		showNotes();
    	}
    });
    ```

    Pressing <kbd>Alt</kbd> + <kbd>N</kbd> now opens the panel with the game deaf to input, and pressing it again
    hands input back.

    <Warning>
      Pair every `set*(true)` with a `set*(false)` on your hide path. Blocks are not tied to window visibility -
      if you hide the window without releasing them, the game stays input-blocked with nothing on screen.
    </Warning>
  </Step>
</Steps>

<Tip>
  Releasing a block your scope never set is harmless - the scope just removes itself from the block's holders, and
  the block only lifts when no scope holds it. So an unconditional "reset everything" in your hide handler is always
  safe, even on paths where you never blocked.
</Tip>

## Full Block vs. Opt-In Interaction

Blocking everything while the window is visible is right for windows the user summons briefly. For windows that stay
on screen (a scoreboard, a HUD), you usually want them passive by default with an opt-in gesture to interact.

<Tabs>
  <Tab title="Full block while visible">
    The pattern above: the window's visibility and the input blocks toggle together. Use this when the window only
    appears when the user wants to interact with it - settings panels, note editors, search boxes. The window is
    interactive out of the box (`hasInput`, `hasInputBlock`, and `hasCursorOverride` all default to `true`), so the
    global blocks are the only extra wiring.
  </Tab>

  <Tab title="Opt-in with a key">
    Keep the window visible but passive, and claim only a single key as the "interact" trigger - for example the
    middle mouse button:

    ```typescript theme={null}
    const inputScope = overlay.input.scope("scoreboard");

    // While the scoreboard is visible, block only middle mouse (0x04)
    inputScope.setKeyInputBlock(0x04, true);

    overlay.windows.on("mouseDown", (event) => {
    	if (event.key !== 0x04) {
    		return;
    	}

    	// The user pressed middle mouse: now go interactive
    	inputScope.setGlobalCursorOverride(true);
    	inputScope.setGlobalMouseBlock(true);
    });
    ```

    Everything except that one key flows to the game until the user opts in. The [Blocking
    Input](/advanced/blocking-input) reference walks through the complete version of this pattern, including the
    per-window cursor override and resetting all blocks when the window hides.
  </Tab>
</Tabs>

<Tip>
  If a window is display-only and should never react to hovers or clicks, turn its input off entirely with
  `hasInput: false` at creation (or `setInput(false)` later). See [Render Windows](/windows/render-windows).
</Tip>

## Dismissing on Outside Clicks

While the mouse block is active, Overlayed still receives mouse events - only the game stops seeing them. That lets
you close the panel when the user clicks anywhere outside it. On `mouseDown` events, `event.window` is unset when the
click didn't land on any overlay window:

```typescript notesWindow.ts theme={null}
import type { MouseButtonEvent } from "@overlayed/app";

function onMouseDown(event: MouseButtonEvent) {
	// 0x01 is left mouse; no event.window means the click was outside every window
	if (event.key === 0x01 && !event.window) {
		hideNotes();
	}
}

function showNotes() {
	// ...blocks from above...
	overlay.windows.on("mouseDown", onMouseDown);
}

function hideNotes() {
	// ...releases from above...
	overlay.windows.off("mouseDown", onMouseDown);
}
```

See [Window Events](/windows/events) for the full list of global input events.

## Sharing Blocks Across Windows

When several windows can block input, naive bookkeeping breaks: window A releases the cursor override on close, and
suddenly window B - still open - loses its cursor. Scopes exist to prevent exactly this. Each block tracks which
scopes hold it; the first `set*(true)` engages it, and it only disengages when the last holder calls `set*(false)`.

Give each window its own scope and have it set and release blocks as if it were alone - the reference counting
resolves the overlap.

The scope handle only has setters. To inspect the effective state actually applied to the game, use the raw getters:

```typescript theme={null}
overlay.input.raw.getGlobalKeyboardBlock();
overlay.input.raw.getGlobalMouseBlock();
overlay.input.raw.getGlobalCursorOverride();
overlay.input.raw.getKeyInputBlock(0x04);
```

<Warning>
  `overlay.input.raw` setters write directly to the game and bypass scope reference counting - a raw
  `setGlobalMouseBlock(false)` drops a block another scope still holds. Don't mix raw setters and scopes for the
  same override; pick one model per block.
</Warning>

## FAQ

<AccordionGroup>
  <Accordion title="Do my keybinds still fire while keyboard input is blocked?">
    Yes. The blocking APIs stop the *game* from receiving input - they don't stop Overlayed. Keybinds are captured
    by Overlayed's native keyboard hook rather than through the game, so the keybind that opened your window still
    closes it while `setGlobalKeyboardBlock(true)` is active. The mouse side works the same way: the [Blocking
    Input](/advanced/blocking-input) reference example listens for `mouseDown` events while the global mouse block
    is engaged.
  </Accordion>

  <Accordion title="What's the difference between a window's input block and the global blocks?">
    The per-window `hasInputBlock` option (and `setInputBlock`) blocks input on that window from passing through to
    the game - it's about interactions with the window itself. The global `setGlobalKeyboardBlock` and
    `setGlobalMouseBlock` stop the game from receiving that kind of input entirely, whether or not it's anywhere
    near your window. Typing needs the global keyboard block: keystrokes aren't "on" a window the way clicks are.
  </Accordion>

  <Accordion title="What happens when two windows both block input?">
    If both use scopes, the block engages when the first scope sets it and releases only after both have cleared
    it - closing one window never yanks input handling out from under the other. If you bypass scopes with
    `overlay.input.raw`, the last write wins and you own the coordination problem.
  </Accordion>

  <Accordion title="How do I check whether a block is currently active?">
    Scope handles are write-only. Read the effective state with the raw getters - `overlay.input.raw.getGlobalKeyboardBlock()`,
    `getGlobalMouseBlock()`, `getGlobalCursorOverride()`, and `getKeyInputBlock(key)`. These report what is actually
    applied to the game, regardless of which scope set it.
  </Accordion>

  <Accordion title="Can I block the mouse but not the keyboard (or just one key)?">
    Yes. The mouse block, keyboard block, cursor override, and per-key blocks are all independent - set any
    combination. `setKeyInputBlock(key, true)` blocks a single key while everything else flows to the game, which
    is the basis of the opt-in interaction pattern above.
  </Accordion>

  <Accordion title="Is anything released automatically when my window hides or closes?">
    No. Blocks set through `overlay.input` are independent of window lifecycle - hiding, closing, or destroying the
    window leaves them in place. Always release blocks in the same code path that hides the window. Since releasing
    a block you never set is a no-op, resetting everything unconditionally is safe.
  </Accordion>

  <Accordion title="The cursor doesn't show over my window - why?">
    There are two cursor knobs. The per-window override (`hasCursorOverride`, on by default) forces the cursor to
    show *when it's over the window*. The global override - `setGlobalCursorOverride(true)` - forces the cursor
    game-wide, which is what you want while an interactive panel is open so the user can see the pointer travel to
    it. If the cursor isn't appearing at all, set the global override.
  </Accordion>

  <Accordion title="What key codes does setKeyInputBlock take?">
    `VirtualKey` codes, exported from `@overlayed/app`. Note these are *not* the `KeyboardEvent#code` strings that
    keybinds use - they're numeric codes, like `0x01` for left mouse and `0x04` for middle mouse, as used in the
    [Blocking Input](/advanced/blocking-input) reference examples.
  </Accordion>
</AccordionGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Blocking Input Reference" icon="hand" href="/advanced/blocking-input">
    The full input-blocking API: scopes, raw setters, and per-key blocks.
  </Card>

  <Card title="Render Windows" icon="layer-group" href="/windows/render-windows">
    Per-window input, input block, and cursor override options.
  </Card>

  <Card title="Window Events" icon="bolt" href="/windows/events">
    Global mouse, keyboard, and resolution events on the windows object.
  </Card>

  <Card title="Show/Hide Window Keybinds" icon="keyboard" href="/guides/keybinds-show-hide-windows">
    Wire the keybind that summons the window you're blocking input for.
  </Card>
</CardGroup>
