You can forward mouse moves, button clicks, and scroll wheel turns to a View.

A View receives no input on its own— you forward each event, which the page receives as standard DOM mouse or wheel events.

> 📘 AppCore Handles Input Automatically
>
> AppCore windows (created with `Window::Create()` after `App::Create()`) forward mouse, scroll, and keyboard input to their Views automatically— you only need to forward events when you host a View directly.

For key presses and text entry, see [Keyboard Input](/docs/2.0/keyboard-input).

## Forwarding Mouse Movement

To report cursor motion, create a [`MouseEvent`](/api/cpp/2_0_0/classultralight_1_1_mouse_event.html) with type `MouseEvent::kType_MouseMoved` and pass it to `View::FireMouseEvent()`.

<!-- tabs:start -->
```cpp
MouseEvent evt;
evt.type = MouseEvent::kType_MouseMoved;
evt.x = 100;
evt.y = 100;
evt.button = MouseEvent::kButton_None;

view->FireMouseEvent(evt);
```
```c
///
/// Create the event, fire it, then destroy it.
///
ULMouseEvent evt = ulCreateMouseEvent(kMouseEventType_MouseMoved, 100, 100,
                                      kMouseButton_None);

ulViewFireMouseEvent(view, evt);
ulDestroyMouseEvent(evt);
```
<!-- tabs:end -->

Set `button` to `MouseEvent::kButton_None` when reporting movement.

When the cursor leaves the View's area, send a move event with coordinates set to (-1, -1). This clears active CSS hover states and dispatches a `mouseleave` event to the page.

## Forwarding Button Clicks

To send a click, dispatch a `MouseEvent::kType_MouseDown` event followed by a `MouseEvent::kType_MouseUp` event at the same position.

```cpp
MouseEvent evt;
evt.x = 100;
evt.y = 100;
evt.button = MouseEvent::kButton_Left;

evt.type = MouseEvent::kType_MouseDown;
view->FireMouseEvent(evt);

evt.type = MouseEvent::kType_MouseUp;
view->FireMouseEvent(evt);
```

The `button` property indicates which button changed state. These constants map directly to DOM `MouseEvent.button` values on the page:

| Button Constant | Page `button` Value |
| :--- | :--- |
| `MouseEvent::kButton_Left` | 0 |
| `MouseEvent::kButton_Middle` | 1 |
| `MouseEvent::kButton_Right` | 2 |
| `MouseEvent::kButton_Back` | 3 (first side button— the library performs no history navigation) |
| `MouseEvent::kButton_Forward` | 4 (second side button) |

The View tracks multi-click timing internally— send every button press as `MouseEvent::kType_MouseDown`, including the OS's double-click messages.

`MouseEvent` has no fields for modifier keys— the page reads properties like `shiftKey` and `ctrlKey` from the last key event you fired on the View. You should forward modifier key presses and releases as key events to keep these properties accurate (see [Keyboard Input](/docs/2.0/keyboard-input)).

### Context Menus

Forwarding a right-button click (`MouseEvent::kButton_Right`) dispatches a DOM `contextmenu` event to the page, which you can handle in JavaScript:

```js
document.addEventListener('contextmenu', (event) => {
  openMenu(event.clientX, event.clientY);  // your own HTML menu
});
```

Ultralight never displays a menu of its own, so the page doesn't need to cancel the event.

Pressing an unmodified Menu key (`KeyCodes::GK_APPS`) or Shift+F10 also dispatches `contextmenu` on the focused element. The View fires the event once per press (auto-repeat is ignored) as long as the page doesn't cancel the `keydown` event.

This only works when the View receives those key events. If you forward input yourself, you can support both keys. AppCore on Windows doesn't forward system keys— Shift+F10 never reaches the page there.

On Windows, `contextmenu` fires when you release the right button— on macOS and Linux, it fires as soon as you press it.

## Converting Coordinates

To convert window device pixels into View coordinates, subtract the View's offset and divide by `View::device_scale()`.

```cpp
///
/// Called by our window with the cursor position in device pixels. 'view_x'
/// and 'view_y' are where the View's top-left corner sits, also in device
/// pixels.
///
void OnWindowMouseMove(int x, int y, int view_x, int view_y) {
  double scale = view->device_scale();

  MouseEvent evt;
  evt.type = MouseEvent::kType_MouseMoved;
  evt.x = (int)((x - view_x) / scale);
  evt.y = (int)((y - view_y) / scale);
  evt.button = MouseEvent::kButton_None;

  view->FireMouseEvent(evt);
}
```

Coordinates passed to `MouseEvent` use logical pixels relative to the top-left corner of the View (0, 0). For example, on a display with a device scale of 2.0, a cursor at device position (200, 100) maps to logical position (100, 50).

For more on screen coordinates and display scaling, see [Windows, Monitors, and DPI](/docs/2.0/windows-monitors-and-dpi).

## Forwarding Scroll Events

To forward scroll wheel turns, create a [`ScrollEvent`](/api/cpp/2_0_0/classultralight_1_1_scroll_event.html) and pass it to `View::FireScrollEvent()`.

```cpp
ScrollEvent evt;
evt.type = ScrollEvent::kType_ScrollByPixel;
evt.delta_x = 0;
evt.delta_y = -100;  // one wheel notch, scrolling down

view->FireScrollEvent(evt);
```

When you use `ScrollEvent::kType_ScrollByPixel`, specify deltas in logical pixels. A negative `delta_y` scrolls down and a negative `delta_x` scrolls right— rolling the wheel toward you produces a positive `deltaY` on the page, as in a browser.

For a single wheel notch, send about 100 logical pixels. Pages that read the legacy `wheelDelta` property see 120 for every 100 pixels— this matches one notch in older scripts.

The wheel scrolls whichever element sits under the cursor. Setting `x` and `y` specifies that cursor position, or leaving them at -1 applies scrolling to the last cursor position you forwarded.
