Skip to main content

ShellEvent

Enum ShellEvent 

Source
#[non_exhaustive]
pub enum ShellEvent {
Show 20 variants Resized { window: WindowId, size: PhysicalSize, scale_factor: f64, }, ScaleFactorChanged { window: WindowId, scale_factor: f64, size: PhysicalSize, }, CloseRequested { window: WindowId, }, WindowDestroyed { window: WindowId, }, Focus { window: WindowId, focused: bool, }, Key { window: WindowId, device: DeviceId, time: EventTime, scancode: Scancode, key_code: Option<KeyCode>, keysym: Keysym, state: ButtonState, repeat: bool, modifiers: Modifiers, }, PointerMotion { window: WindowId, device: DeviceId, time: EventTime, abs: Option<PhysicalPoint>, raw_delta: Option<(f64, f64)>, }, PointerFocus { window: WindowId, device: DeviceId, time: EventTime, entered: bool, position: Option<PhysicalPoint>, }, Button { window: WindowId, device: DeviceId, time: EventTime, button: PointerButton, state: ButtonState, position: Option<PhysicalPoint>, modifiers: Modifiers, }, Wheel { window: WindowId, device: DeviceId, time: EventTime, delta: ScrollDelta, position: Option<PhysicalPoint>, modifiers: Modifiers, }, Touch { window: WindowId, device: DeviceId, time: EventTime, contact: ContactId, phase: TouchPhase, position: PhysicalPoint, }, TextCommit { window: WindowId, time: EventTime, text: String, }, TextPreedit { window: WindowId, time: EventTime, text: String, cursor: Option<usize>, }, MonitorsChanged, MonitorRemoved { monitor: MonitorId, }, FocusRefused { window: WindowId, }, FullScreenApp { window: WindowId, active: bool, }, FrameReady { window: WindowId, }, DroppedFile { window: WindowId, time: EventTime, path: PathBuf, position: Option<PhysicalPoint>, }, ClipboardData { window: WindowId, request: ClipboardRequestId, mime: ReceivedMime, content: ClipboardContent, },
}
Expand description

Something the window system reported.

Delivered by Shell::pump. See the module docs for the timestamp and modifier conventions.

Variants (Non-exhaustive)§

This enum is marked as non-exhaustive
Non-exhaustive enums could have additional variants added in future. Therefore, when matching against variants of non-exhaustive enums, an extra wildcard arm must be added to account for any future variants.
§

Resized

The client area changed size.

The authoritative size for the swapchain — never the size that was asked for in WindowDesc. Carries the scale factor too, because a resize and a scale change can arrive together (dragging a window between monitors of different DPI does exactly that) and a consumer that reads size from one event and scale from another will briefly hold a mismatched pair.

Fields

§window: WindowId

Which window.

§size: PhysicalSize

New client area in device pixels.

§scale_factor: f64

Device pixels per logical unit, at the same instant.

§

ScaleFactorChanged

The window’s scale factor changed — it moved to a different monitor, or the user changed the display scale.

Carries the new physical size as well: on Wayland a scale change is a configure that also restates the size, and on Win32 WM_DPICHANGED hands over a suggested rectangle. A consumer must treat this as a resize too. The windowing rules (docs/notes/backends.md) call out “scale-factor changes mid-session must not leak wrong-size swapchains” as a required test, and putting both facts in one event is what makes that testable rather than order-dependent.

Fields

§window: WindowId

Which window.

§scale_factor: f64

New device pixels per logical unit.

§size: PhysicalSize

Client area in device pixels after the change.

§

CloseRequested

The user asked to close the window — title-bar button, Alt-F4, session logout.

The window is still open. Answer with reply_close_request; until you do, the shell holds the request. See CloseReply.

Fields

§window: WindowId

Which window.

§

WindowDestroyed

The window was destroyed and its WindowId is now stale.

Follows a CloseReply::Close, an explicit destroy_window, or the compositor taking the window away — every window at once when the connection to the window system is lost.

Fields

§window: WindowId

The window that is gone.

§

Focus

Keyboard focus entered or left the window.

A consumer must treat focus loss as “every key is now up”: no platform delivers releases for keys held when focus leaves, and a game that does not clear its key state walks into a wall until the player taps the key again. The action layer (P2) does this centrally; raw consumers do it themselves.

Fields

§window: WindowId

Which window.

§focused: bool

Whether the window now has focus.

§

Key

A key went down or came up.

Three views of the same keystroke — see crcbl_core::input for why they are all here and what each is for.

A key the input method consumes while composing is not reported as a Key — neither its press nor its release. The Backspace that deletes a kana from a TextPreedit and the Enter that commits the conversion are the input method’s, and a text field that also saw them as editing keys would delete a character or submit the form. This is what a Wayland compositor already does by not sending the key at all; the Win32 backend recognizes the keystrokes its input method marks as taken. The AppKit backend cannot yet tell such a keystroke apart, and still reports it.

Fields

§window: WindowId

Which window had focus.

§device: DeviceId

Which keyboard.

§time: EventTime

When it happened; see the module docs.

§scancode: Scancode

The platform’s raw code, for keys the engine has no name for.

§key_code: Option<KeyCode>

The physical key, layout-independent. None when the scancode maps to no key this engine names — the key is still bindable through scancode. Right for game bindings (WASD stays where the fingers are), wrong for menus and shortcuts: an xkb remap such as caps:escape or a virtual keyboard’s own keymap does not change it (wtype’s Down arrives on evdev 1, which is Escape).

§keysym: Keysym

The symbol this key produces in the current layout, for menus, shortcuts, rebind menus and shortcut labels. The keys no layout moves have named constants, Keysym::ESCAPE, Keysym::DOWN and the rest.

§state: ButtonState

Down or up.

§repeat: bool

Whether this is an auto-repeat, not a fresh press.

Text fields want repeats; a jump button must not. Filtering them at the source would break the first, keeping them silently breaks the second, so the fact is carried and the consumer decides.

§modifiers: Modifiers

Modifiers held at the time; see the module docs.

§

PointerMotion

The pointer moved.

Both fields are optional, and which are present is the difference between a UI event and a camera event:

  • abs is where the pointer is, in window pixels. None under PointerMode::Locked, where there is no meaningful position.
  • raw_delta is unaccelerated, unclipped motion. None when the backend has no relative-motion source — see ShellCaps::RAW_POINTER_MOTION.

A camera must use raw_delta and must not difference successive abs values: the latter is clamped at the screen edge and has pointer acceleration already applied, which is why aim in games that get this wrong stops working when the cursor reaches the edge of the display.

Fields

§window: WindowId

Which window.

§device: DeviceId

Which pointing device.

§time: EventTime

When it happened.

§abs: Option<PhysicalPoint>

Position in the window, in device pixels.

§raw_delta: Option<(f64, f64)>

Unaccelerated relative motion in device pixels, (dx, dy).

§

PointerFocus

The pointer entered or left the window.

Not in the windowing rules’ event set, and added anyway: every backend delivers enter/leave natively (wl_pointer.enter, X11 EnterNotify, WM_MOUSELEAVE), the UI layer needs it for hover state, and the alternative — inferring it from motion events that stop arriving — cannot distinguish “the pointer left” from “the pointer stopped moving”.

Fields

§window: WindowId

Which window.

§device: DeviceId

Which pointing device.

§time: EventTime

When it happened.

§entered: bool

Whether the pointer is now inside the window.

§position: Option<PhysicalPoint>

Where it entered, if the backend said. Always None on leave.

§

Button

A pointer button went down or came up.

Fields

§window: WindowId

Which window.

§device: DeviceId

Which pointing device.

§time: EventTime

When it happened.

§button: PointerButton

Which button.

§state: ButtonState

Down or up.

§position: Option<PhysicalPoint>

Where the pointer was, if known. None under PointerMode::Locked.

§modifiers: Modifiers

Modifiers held at the time.

§

Wheel

A scroll wheel or touchpad scrolled.

Fields

§window: WindowId

Which window.

§device: DeviceId

Which pointing device.

§time: EventTime

When it happened.

§delta: ScrollDelta

How far, in the units the device reported — see ScrollDelta, which does not collapse detents into pixels.

§position: Option<PhysicalPoint>

Where the pointer was, if known.

§modifiers: Modifiers

Modifiers held at the time. Ctrl+wheel is zoom nearly everywhere, so this is load-bearing rather than informational.

§

Touch

A finger landed, moved, lifted, or had its gesture taken away.

One event per contact, so two fingers moving at once are two events and not one averaged pointer. ContactId carries the identity and TouchPhase the lifecycle; between them a consumer can hold per-finger state, which is the whole reason this variant exists and the reason a second finger used to be dropped at the backend.

§A touchscreen also produces pointer events, and that is deliberate

The primary contact — the first finger of a gesture, and the only one while there is only one — is also reported as PointerMotion and Button, exactly as a browser emulates mouse events for touch and Windows synthesizes them from WM_POINTER. So a game bound to Binding::MouseButton is playable with one finger without knowing this variant exists, and a game that wants two fingers reads Touch and ignores the emulation.

That is an obligation on a backend that sets ShellCaps::TOUCH, not an accident of one platform: a backend whose window system does not emulate must do it itself, or every existing pointer binding stops working the day it reports its first contact. Contacts that are not primary have no pointer events at all — a second finger is not the first one teleporting.

§No modifiers, and a position that is never absent

A finger carries no modifiers: no platform reports keyboard state on a touch event, and a field the backends would have to invent is a field a consumer would believe. And position is a PhysicalPoint rather than an Option — a contact is defined by where it touches, including on the release, and there is no PointerMode::Locked equivalent that could take the position away.

Fields

§window: WindowId

Which window.

§device: DeviceId

Which touchscreen. Every contact on one screen shares this — see ContactId, which is the one that tells fingers apart.

§time: EventTime

When it happened.

§contact: ContactId

Which finger.

§phase: TouchPhase

What it just did.

§position: PhysicalPoint

Where it is, in device pixels. On Cancelled this is the last place the platform knew about rather than a place the user chose.

§

TextCommit

Text was committed — by typing, by an input method, or by a paste the window system routed as text input.

Separate from Key because the relationship is not one-to-one: an IME commits several characters from one keystroke, or one character from five, and a dead key commits nothing at all. A text field that reconstructs its content from key events is a text field that cannot type in Japanese.

Fields

§window: WindowId

Which window.

§time: EventTime

When it happened.

§text: String

The committed text. Never empty.

§

TextPreedit

An input method’s composition in progress: the pre-edit a text field draws at its caret, underlined, until it is committed or abandoned.

Each one replaces the last, and an empty text means there is no pre-edit any more: the composition was committed, cancelled, or deleted back to nothing. The committed text itself arrives separately, as TextCommit, and a consumer must not synthesize it from the last pre-edit: an input method commits the conversion the user chose, which is usually not the reading they typed. The two streams are independent — a field inserts each commit at its caret and draws the latest pre-edit after it — so their relative order within one pump carries no meaning.

The keystrokes that edit a composition are the input method’s: a key it consumes while composing is not reported as a Key.

Sent where ShellCaps::TEXT_IME is set. A backend without it never sends this. Shell::set_text_input_area is how a field tells the input method where to put that window and its candidate list.

Fields

§window: WindowId

Which window.

§time: EventTime

When it happened.

§text: String

The composition so far. Empty when the pre-edit ended.

§cursor: Option<usize>

Where the input method’s cursor sits in text, as a byte offset on a char boundary, if it said. None for an empty text.

§

MonitorsChanged

The set of monitors changed — hotplug, resolution change, or a layout rearrangement.

Carries no payload: Shell::monitors is the authority, and duplicating the list into the event would let a consumer hold a stale copy that looks authoritative. A window’s own scale change arrives separately as ScaleFactorChanged.

§

MonitorRemoved

A monitor was disconnected.

Arrives before the MonitorsChanged after which Shell::monitors no longer lists it, so a consumer can act on the id while it still knows what the id named. After that the id names nothing, and it is never reused.

By the time that MonitorsChanged arrives, every panel created for this monitor has been sent a CloseRequested: a bar on an unplugged display has nowhere to be. A window system that closes the panel itself may send that request before this event. Other windows stay wherever the window system moves them.

Fields

§monitor: MonitorId

The monitor that went.

§

FocusRefused

The window asked for the keyboard and the window system refused it.

A panel that takes the keyboard outright (PanelKeyboard::Exclusive) asks when it is shown. Win32 refuses the foreground to a process the user is not working in, and AppKit can decline to make the panel the key window; then no Focus follows: keys keep going to the window the user was in. This is how a program finds out, without guessing a timeout — to retry, to say so, or to draw the user’s attention another way.

Fields

§window: WindowId

The window that asked.

§

FullScreenApp

A full-screen window opened on this panel’s monitor, or the last one closed: active says which.

For a bar that should step aside while a game or a video is full screen. Raised on Win32, for a panel that reserves its edge (Reserve::OwnExtent), from the shell’s ABN_FULLSCREENAPP. Wayland and AppKit do not need it: their compositors already draw a full-screen window over a bar, or in a space of its own. X11 does not raise it either, so a bar there that cares watches _NET_WM_STATE_FULLSCREEN itself.

Fields

§window: WindowId

The panel told.

§active: bool

Whether a full-screen window is now open on its monitor.

§

FrameReady

A good time to present the window’s next frame: the answer to a request_frame.

One per request, however many requests were made before it came, and none for a window destroyed first. Where ShellCaps::FRAME_CALLBACK is set the compositor sends it, and may hold it back for as long as nothing of the window is visible — an output it has turned off, a hidden window — which is when presenting would block or be wasted. Elsewhere it arrives at the next pump after the request.

Fields

§window: WindowId

The window that may present.

§

DroppedFile

A file was dropped on the window.

One event per file: a multi-file drop produces several. Only delivered when the window was created with WindowDesc::accept_drops and ShellCaps::DRAG_DROP is set.

Fields

§window: WindowId

Which window.

§time: EventTime

When it happened.

§path: PathBuf

The dropped path. Not guaranteed to exist or to be readable — it comes from another process, and checking is the consumer’s job.

§position: Option<PhysicalPoint>

Where in the window it was dropped, if the backend said.

§

ClipboardData

The answer to a clipboard_request.

Exactly one of these arrives for every accepted request, and it is the only thing that ever answers one. It may be several frames late: a backend holds a read until it can answer it rather than reporting an empty clipboard it has not actually looked at. See clipboard for why reads are asynchronous and why there is no “ask again later” outcome.

Fields

§window: WindowId

Which window asked.

§request: ClipboardRequestId

Which request this answers.

§mime: ReceivedMime

The format that was delivered, spelled the way the other application spelled it.

A ReceivedMime rather than a MimeType because the peer chose this string, not us: asking for text/plain;charset=utf-8 and being answered with text/plain is normal and must round-trip verbatim. Compare with ReceivedMime::matches, never with string equality.

For Empty and Unavailable there is no peer spelling to report, so this is the format that was asked for.

§content: ClipboardContent

What the read produced — bytes, an empty clipboard, or a failure. See ClipboardContent, which exists because the three are not one case.

Implementations§

Source§

impl ShellEvent

Source

pub const fn window(&self) -> Option<WindowId>

Which window this event concerns, if any.

None only for MonitorsChanged and MonitorRemoved, which are properties of the session rather than of a window. A multi-window consumer routes on this.

Source

pub const fn time(&self) -> Option<EventTime>

When this event happened, for the variants that are input samples.

None for state transitions; see the module docs for why they deliberately have no timestamp.

Source

pub const fn is_input(&self) -> bool

Whether this event feeds the input pipeline rather than the window lifecycle.

The P2 action layer subscribes to exactly this subset.

Source

pub const fn name(&self) -> &'static str

A short, stable name for logs and test assertions.

Stable because tests assert on sequences of these: a scripted HeadlessShell session compares ["Resized", "Focus", "Key", …], which stays readable in a failure message in a way that a Debug dump of the full payloads does not.

Trait Implementations§

Source§

impl Clone for ShellEvent

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for ShellEvent

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl PartialEq for ShellEvent

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl StructuralPartialEq for ShellEvent

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.