Skip to main content

Shell

Trait Shell 

Source
pub trait Shell: Debug {
Show 33 methods // Required methods fn backend(&self) -> ShellBackend; fn caps(&self) -> ShellCaps; fn create_window( &mut self, desc: &WindowDesc<'_>, ) -> Result<WindowId, ShellError>; fn destroy_window(&mut self, window: WindowId) -> Result<(), ShellError>; fn window_state(&self, window: WindowId) -> Result<WindowState, ShellError>; fn set_title( &mut self, window: WindowId, title: &str, ) -> Result<(), ShellError>; fn set_visible( &mut self, window: WindowId, visible: bool, ) -> Result<(), ShellError>; fn set_mode( &mut self, window: WindowId, mode: DisplayMode, ) -> Result<(), ShellError>; fn set_constraints( &mut self, window: WindowId, constraints: SizeConstraints, ) -> Result<(), ShellError>; fn monitors(&self) -> &[MonitorInfo]; fn pump(&mut self, sink: &mut dyn FnMut(ShellEvent)); fn keep_alive(&mut self); fn surface_target( &self, window: WindowId, ) -> Result<SurfaceTarget, ShellError>; fn set_pointer_mode( &mut self, window: WindowId, mode: PointerMode, ) -> Result<(), ShellError>; fn set_cursor( &mut self, window: WindowId, cursor: Option<CursorIcon>, ) -> Result<(), ShellError>; fn warp_pointer( &mut self, window: WindowId, position: PhysicalPoint, ) -> Result<(), ShellError>; fn reply_close_request( &mut self, window: WindowId, reply: CloseReply, ) -> Result<(), ShellError>; fn clipboard_offer( &mut self, window: WindowId, offers: &[ClipboardOffer<'_>], ) -> Result<(), ShellError>; fn clipboard_request( &mut self, window: WindowId, mime: MimeType, ) -> Result<ClipboardRequestId, ShellError>; fn request_frame(&mut self, window: WindowId) -> Result<(), ShellError>; // Provided methods fn reposition_popup( &mut self, popup: WindowId, anchor: LogicalRect, size: LogicalSize, ) -> Result<(), ShellError> { ... } fn set_panel( &mut self, window: WindowId, size: LogicalSize, panel: &PanelDesc<'_>, ) -> Result<(), ShellError> { ... } fn monitor(&self, id: MonitorId) -> Option<&MonitorInfo> { ... } fn wait_events(&mut self, timeout: Option<Duration>) { ... } fn waker(&mut self) -> Option<ShellWaker> { ... } fn align_event_clock(&mut self, elapsed: Duration) { ... } fn clipboard_readable(&self, window: WindowId) -> bool { ... } fn activation_token( &mut self, window: WindowId, ) -> Result<Option<String>, ShellError> { ... } fn set_text_input_area( &mut self, window: WindowId, area: Option<PhysicalRect>, ) -> Result<(), ShellError> { ... } fn set_input_region( &mut self, window: WindowId, region: Option<&[PhysicalRect]>, ) -> Result<(), ShellError> { ... } fn set_idle_inhibit( &mut self, window: WindowId, inhibit: bool, ) -> Result<(), ShellError> { ... } fn open_url( &mut self, window: WindowId, url: &WebUrl, ) -> Result<(), ShellError> { ... } fn set_text_input( &mut self, window: WindowId, field: Option<TextPurpose>, ) -> Result<(), ShellError> { ... }
}
Expand description

The window system, as the engine sees it.

Object-safe by requirement; see the crate docs for why, and backend for how an implementation is selected at runtime.

§The loop is the caller’s

There is no run that takes the program over: the caller loops on pump and wait_events itself, so ending the program is leaving that loop and dropping the shell. Each desktop backend’s Drop destroys the windows it still holds, so nothing has to be torn down by hand first. Work that arrives on another thread reaches a sleeping loop through a ShellWaker.

§Implementing this

Six obligations that are not visible in the signatures. The first three are P0.4’s; the last three are P0.5c’s, written down because a real compositor showed that leaving them unstated pushes the work onto every consumer.

  1. Stale handles fail cleanly. Every method taking a WindowId must return ShellError::InvalidWindow for a destroyed or foreign one, never act on whatever window took the slot.
  2. Timestamps are rebased. Input events carry the window system’s timestamp converted to the engine epoch — see EventTime, which documents the conversion each platform needs.
  3. Capabilities are latched. caps must return the same value for the shell’s lifetime, so a consumer that branched on it at startup is never contradicted mid-session. See caps for the Wayland-binds-a-global-late edge this rules out.
  4. Every accepted clipboard read is answered exactly once, and within a bounded time. clipboard_request that returns Ok promises exactly one ShellEvent::ClipboardData carrying that ClipboardRequestId. A backend that cannot complete the read answers ClipboardContent::Unavailable rather than dropping the request; a request with no answer is a UI stuck on “pasting…” forever, which no consumer can detect.
  5. A read that cannot be answered yet is held, never answered “empty”. If the backend does not yet know what is on the clipboard — Wayland’s focus-gated, asynchronous wl_data_device.selection is the case this exists for — it holds the request until it does, and bounds that wait. Reporting Empty for “I have not looked yet” is indistinguishable from an empty clipboard, so it makes every consumer write a retry loop with a timeout it cannot choose correctly. Where the answer is always immediately available (X11 asks the server who owns the selection; a headless shell owns it), holding costs nothing and this obligation is discharged by answering.
  6. A refusal for want of user interaction is named. Where the window system requires a recent input event to claim the clipboard — Wayland does, the browser does, X11 and Win32 do not — clipboard_offer returns ShellError::NeedsUserInteraction and not a generic backend error, so a caller can distinguish “ask the user to press the key again” from “this is broken”. A backend on a platform without the rule never returns it.

Required Methods§

Source

fn backend(&self) -> ShellBackend

Which implementation this is. Logs and bug reports only — behaviour branches on caps.

Source

fn caps(&self) -> ShellCaps

What this backend can do. Fixed for the shell’s lifetime.

Source

fn create_window( &mut self, desc: &WindowDesc<'_>, ) -> Result<WindowId, ShellError>

Creates a window. The window has no size yet.

desc.size is a request. The authoritative size arrives later, as the first ShellEvent::Resized, and until it does WindowState::size is None. This is not a headless quirk: a Wayland xdg_surface is unsized and may not be given a buffer until the compositor answers with a configure, which is a round trip away.

A swapchain needs an extent, so this is a hard ordering constraint:

let window = shell.create_window(&WindowDesc::default())?;
let size: PhysicalSize = loop {
    shell.pump(&mut |_event| { /* … */ });
    if let Some(size) = shell.window_state(window)?.size() {
        break size;
    }
};
// Only now is there an extent to create a swapchain at.
assert_eq!(size, PhysicalSize::new(1280, 720));

HeadlessShell deliberately defers the first configure by at least one pump, so a consumer that skips the loop fails in CI rather than at P0.5.

§Errors

ShellError::WindowCreation if the window system refused, or ShellError::Unsupported if this would be a second window on a backend without ShellCaps::MULTI_WINDOW.

Source

fn destroy_window(&mut self, window: WindowId) -> Result<(), ShellError>

Destroys a window. A ShellEvent::WindowDestroyed follows.

The SurfaceTarget previously returned for this window is dead afterwards; the HAL surface built from it must be destroyed first.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn window_state(&self, window: WindowId) -> Result<WindowState, ShellError>

A snapshot of the window’s current state.

For consumers that started late; not a substitute for handling events. Note that WindowState::size is None until the window system has configured the window, and that the requested mode and the effective one are separate fields — see WindowState.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn set_title(&mut self, window: WindowId, title: &str) -> Result<(), ShellError>

Sets the title bar text.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn set_visible( &mut self, window: WindowId, visible: bool, ) -> Result<(), ShellError>

Maps or unmaps the window.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn set_mode( &mut self, window: WindowId, mode: DisplayMode, ) -> Result<(), ShellError>

Asks for windowed or borderless.

A request, not a command. It updates WindowState::requested_mode immediately and nothing else; the mode that is actually in effect appears in WindowConfiguration::mode when the window system answers, which it may do with something other than what was asked (a compositor can refuse fullscreen, and a tiling window manager makes the question moot). Use WindowState::mode_request_honoured rather than reading the request back as fact.

The answer usually arrives as a ShellEvent::Resized, preceded by a ShellEvent::ScaleFactorChanged when the target monitor has a different scale. Never assume the new size — read the event.

§Errors

ShellError::InvalidWindow if the handle is stale, or ShellError::NoSuchMonitor if the named monitor is gone.

Source

fn set_constraints( &mut self, window: WindowId, constraints: SizeConstraints, ) -> Result<(), ShellError>

Asks for resize limits and an aspect lock.

Hints, not guarantees, and a request rather than a command: it updates WindowState::requested_constraints immediately, and the only observable effect is whatever size the window system reports next. No platform reports back “effective constraints”, so neither does this seam. See SizeConstraints.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn monitors(&self) -> &[MonitorInfo]

Every connected monitor.

Invalidated by ShellEvent::MonitorsChanged; call again rather than caching across it.

Source

fn pump(&mut self, sink: &mut dyn FnMut(ShellEvent))

Drains everything that has arrived from the window system into sink.

Non-blocking and finite: it delivers what is queued and returns. Call it once at the top of every frame. See the crate docs for why there is no run() and how a requestAnimationFrame-driven build uses this.

The sink is &mut dyn FnMut rather than a generic parameter because the trait must stay object-safe. That is not purely a cost: a closure passed here can borrow the caller’s state mutably, which an Iterator-returning shape could not while the shell is also borrowed.

Source

fn keep_alive(&mut self)

Lets the window system run, and delivers nothing.

Everything pump does except hand events to a sink: the platform’s queue is drained, whatever it asks of the client is answered, and what it produced is kept in this shell’s queue, in order, for the next pump. Every backend implements pump as this followed by the delivery, so the two cannot drift apart.

§What it is for: a long load on the thread that owns the window

Desktops mark a client that stops draining its queue as hung. Windows ghosts a window whose thread has not asked for a message for about five seconds — IsHungAppWindow, a greyed “Not Responding” title, clicks held — a Wayland compositor pings through xdg_wm_base and offers to kill a client that does not answer (GNOME’s “not responding” dialog), and macOS puts the spinning cursor over an application whose main thread has stopped taking events. A game that loads its assets between opening its window and its first frame calls this every so often — a few times a second is plenty — and stays responsive to all three.

Nothing is dropped, which is why this is not pump with an empty sink: a key pressed, a resize or a close request made during the load is delivered by the first frame’s pump, exactly as if the load had been one long frame. So a close request stays unanswered until then — the window stays open and the load finishes first — and a loader that must stop for one has to pump and handle events itself.

Work that can leave the loop thread should, instead: a loop that keeps running frames keeps pumping on its own, and can show progress while it does. This is for the load that cannot — work on the device the loop owns, or on anything else tied to this thread.

Non-blocking and finite, like pump, and with the same exception: on Win32 and AppKit it does not return while the user is dragging the window’s border.

Source

fn surface_target(&self, window: WindowId) -> Result<SurfaceTarget, ShellError>

The native handles a HAL backend needs to create a surface for this window.

Opaque to everyone except a HAL backend. Re-query it after a set_mode: some backends recreate the underlying surface on a mode switch, and a cached target then names a dead object. A resize is not such an event — see below.

§Available before the window is configured, and unchanged by a resize

The handle exists as soon as the window does — a wl_surface is created up front and only its size is pending — so this succeeds immediately and a HAL surface can be created from it right away. Only the swapchain has to wait, because only the swapchain needs an extent.

No variant carries a size, so the value this returns does not change when the window is resized, and a HAL surface created before the first configure stays correct forever. That is what makes “re-query after a mode change” a complete rule. SurfaceTarget::Offscreen used to carry width/height and was the one exception; P0.7 removed it, because an exception in this rule means a surface silently describing a size the window no longer has. The extent reaches a backend through WindowState::size and the swapchain descriptor, and only there — crcbl-hal’s swapchain module states that precedence as a backend obligation.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn set_pointer_mode( &mut self, window: WindowId, mode: PointerMode, ) -> Result<(), ShellError>

Frees, confines or locks the pointer.

§Errors

ShellError::Unsupported if caps lacks PointerMode::required_cap, or ShellError::InvalidWindow for a stale handle.

Source

fn set_cursor( &mut self, window: WindowId, cursor: Option<CursorIcon>, ) -> Result<(), ShellError>

Sets the cursor shape, or hides the cursor with None.

Visibility and capture are separate axes; see cursor.

Hiding works on every backend. A shape is seen only where ShellCaps::CURSOR_SHAPE is set; elsewhere it is accepted and recorded rather than refused, because a UI calls this on every hover.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn warp_pointer( &mut self, window: WindowId, position: PhysicalPoint, ) -> Result<(), ShellError>

Moves the pointer to a position in the window.

Requires ShellCaps::POINTER_WARP, which Wayland does not have and will not get. Anything reaching for this to implement a camera should use PointerMode::Locked instead; warping is for a UI that needs to reposition a cursor (a slider snapping to a value), not for input.

§Errors

ShellError::Unsupported without the capability, or ShellError::InvalidWindow for a stale handle.

Source

fn reply_close_request( &mut self, window: WindowId, reply: CloseReply, ) -> Result<(), ShellError>

Answers a ShellEvent::CloseRequested.

Until this is called the window stays open and the request stays outstanding. See CloseReply.

§Errors

ShellError::NoPendingCloseRequest if nothing was asked, or ShellError::InvalidWindow for a stale handle.

Source

fn clipboard_offer( &mut self, window: WindowId, offers: &[ClipboardOffer<'_>], ) -> Result<(), ShellError>

Publishes clipboard content in one or more formats.

Synchronous: every platform’s write is “claim the selection”, and the bytes transfer later, on demand. Offer both MimeType::TextUtf8 and MimeType::CrcblRon for engine data; see clipboard.

An empty offers slice releases the clipboard, where the platform can express that.

§Errors

ShellError::Unsupported without ShellCaps::CLIPBOARD, ShellError::InvalidWindow for a stale handle, or ShellError::NeedsUserInteraction where the window system requires a recent input event to claim the clipboard and there has not been one — implementor obligation 6 above, and not a failure a caller should retry blindly.

Source

fn clipboard_request( &mut self, window: WindowId, mime: MimeType, ) -> Result<ClipboardRequestId, ShellError>

Asks for the clipboard’s content in mime.

Asynchronous by necessity — the answer arrives as ShellEvent::ClipboardData carrying the returned id, and exactly one answer arrives for every Ok return (implementor obligations 4 and 5). clipboard has the full argument; briefly, X11 INCR transfers, Wayland file descriptors and the browser’s Promise make the synchronous shape unimplementable on three of five backends.

The answer may be several frames away, and on a backend whose clipboard is not readable at the moment of asking it is held until it is, rather than being answered Empty. A caller therefore never needs to poll or retry: ask once, handle the event. clipboard_readable is the way to find out in advance that the answer will not be immediate.

§Errors

ShellError::Unsupported without ShellCaps::CLIPBOARD, or ShellError::InvalidWindow for a stale handle. An empty clipboard is not an error — it is an answer.

Source

fn request_frame(&mut self, window: WindowId) -> Result<(), ShellError>

Asks for a ShellEvent::FrameReady when window should present its next frame.

For a loop that presents only when told to: a status bar on an output the compositor has turned off is never asked for a frame there, and a FIFO present to it can block the loop for as long as the output stays off. Ask before presenting the frame the answer paces, then present the next one once it comes. The first frame needs no answer.

let mut may_present = true;
for _ in 0..3 {
    shell.pump(&mut |event| {
        if let ShellEvent::FrameReady { window: ready } = event {
            may_present |= ready == window;
        }
    });
    if may_present {
        shell.request_frame(window)?;
        // … record and present …
        may_present = false;
    }
}
// With no compositor to hold it back, each answer came at the next pump.
assert_eq!(presented, 3);

Requests made before the answer comes share it: one FrameReady answers them all. A window destroyed with a request outstanding gets no answer.

Where ShellCaps::FRAME_CALLBACK is set — Wayland, as wl_surface.frame — the compositor answers. The request is surface state that rides the next commit, which is the one the swapchain’s present makes, and this call commits nothing itself, so a window that asks and then never presents may never be answered. Everywhere else the answer comes at the next pump, and wait_events does not sleep while one is due.

A required method, unlike its neighbours: a default could validate the handle but not keep the request, and a request nothing answers stalls the loop waiting for it.

§Errors

ShellError::InvalidWindow if the handle is stale.

Provided Methods§

Source

fn reposition_popup( &mut self, popup: WindowId, anchor: LogicalRect, size: LogicalSize, ) -> Result<(), ShellError>

Moves a live popup against a new anchor in its parent and gives it size, placed by the PopupPlacement it was created with: flipped or slid to stay on screen, as when it opened.

For a popup whose anchor moved — a bar’s clock grew and its calendar should follow — or whose content changed size. A request, like the rest of a window’s geometry: the new size arrives as the next ShellEvent::Resized, once the window system has placed it.

Requires ShellCaps::POPUP_REPOSITION. Without it, close the popup and open another.

§Errors

ShellError::Unsupported without the capability, ShellError::InvalidWindow for a stale handle, and ShellError::InvalidDescriptor for a window that is not a popup.

Source

fn set_panel( &mut self, window: WindowId, size: LogicalSize, panel: &PanelDesc<'_>, ) -> Result<(), ShellError>

Gives a live panel a new size and a new PanelDesc: its layer, anchor, reserve, margins and keyboard change in place, with the same surface and the same swapchain, where destroying it and creating another would flicker.

size means what WindowDesc::size means for a panel: ignored along an axis anchored to both opposite edges. The new descriptor is checked as create_window checks one, so a reserving panel must still name one edge. A request, like the rest of a window’s geometry: the size the panel gets arrives as the next ShellEvent::Resized, as its first one did, and none arrives when the size did not change.

The namespace and the monitor cannot change. wlr-layer-shell fixes both when the surface is made, and every backend refuses a change to either alike; close the panel and open another for that.

A visible panel changed to PanelKeyboard::Exclusive takes the keyboard at once on Wayland, where the compositor hands it over; on Win32 and AppKit it takes it when it is next shown, as a new one does.

A provided method answering ShellError::Unsupported, as create_window answers a panel on a backend without ShellCaps::PANEL, once the handle has been checked; every backend with panels implements it.

§Errors

ShellError::InvalidWindow for a stale handle, ShellError::InvalidDescriptor for a window that is not a panel or a descriptor that changes its namespace or its monitor, and whatever create_window refuses a panel with: ShellError::Unsupported for what the backend cannot build, and ShellError::NoSuchMonitor if the panel’s monitor is gone.

Source

fn monitor(&self, id: MonitorId) -> Option<&MonitorInfo>

One monitor by id, or None if it is not connected.

A provided method rather than a required one: it is a lookup over monitors, and every backend would write the same three lines.

Source

fn wait_events(&mut self, timeout: Option<Duration>)

Blocks until an event arrives or timeout elapses.

Advisory. Returning immediately is always correct, and backends without ShellCaps::EVENT_WAIT — a browser above all — do exactly that. A caller must treat this as “sleep if you can” and must not assume an event is pending afterwards; pump is still what delivers them.

timeout of None means “until something happens”, which on a backend without the capability still means “immediately”.

Source

fn waker(&mut self) -> Option<ShellWaker>

A handle that wakes wait_events from another thread — see ShellWaker.

Every call hands out a handle to the same doorbell, so a program can ask once and clone, or ask wherever it needs one. None from a backend whose wait never blocks, which is the browser: there, nothing sleeps and there is nothing to wake.

Source

fn align_event_clock(&mut self, elapsed: Duration)

Puts input timestamps on the engine’s clock.

Call this once, at startup, with the reading of the TimeSource that will drive FrameClock::update.

EventTime’s epoch is defined as “the same origin as” that source, and no backend can know it: a shell is created some time after the clock starts, and the window system’s own clock has a third origin again. Without this call, timestamps are measured from the shell’s own creation — internally consistent, so durations between two input events are already correct, but offset from a FrameClock reading by however long startup took. That offset is exactly what docs/plan/26-prediction.md’s input-to-photon latency measurement is a subtraction across.

A provided method with a no-op default, because it is only meaningful for a backend whose event clock is not already the engine’s — the browser’s event.timeStamp needs nothing done to it. The obligation it discharges is the “Timestamps are rebased” one in this trait’s implementor notes, which previously had no way to be satisfied from outside the crate.

let mut shell = HeadlessShell::new();
// The engine clock has been running for 40 ms by the time the window
// system is up; input stamped "now" should read as 40 ms, not as zero.
shell.align_event_clock(Duration::from_millis(40));
Source

fn clipboard_readable(&self, window: WindowId) -> bool

Whether a clipboard_request issued for window right now could be answered without waiting.

What a “Paste” menu item greys out on, and the visible form of a platform rule that is otherwise invisible: on Wayland a client is told what is on the clipboard only while one of its windows has keyboard focus, and only asynchronously, so a background window genuinely cannot read the clipboard. clipboard_request still works there — it holds the request — but it may take until focus arrives, and a UI that would rather not offer the action than offer a slow one asks here first.

false for a stale handle, and for a backend without ShellCaps::CLIPBOARD.

§A provided method, because most platforms have nothing to say

The default is “readable whenever the capability is present and the handle is live”, which is exactly right for X11 (any window may ConvertSelection at any time, focus is not involved), for Win32, and for HeadlessShell in its default configuration. Only a backend that really has a gate overrides it — so this is a fact a consumer can rely on, not ceremony every backend has to perform.

The staleness half goes through window_state rather than being left to each implementation. P0.6 found the earlier default — which ignored window entirely — contradicting the “false for a stale handle” sentence above: both backends that existed had overridden it and so had covered for it, and the first backend to accept the default inherited the bug. A default that cannot keep its own promise is worse than no default.

Source

fn activation_token( &mut self, window: WindowId, ) -> Result<Option<String>, ShellError>

A token another program can raise its own window with, made from window and the latest input this program received.

What a notification daemon sends as the notification spec’s ActivationToken before ActionInvoked, and what a launcher puts in a child’s XDG_ACTIVATION_TOKEN: the receiver hands it back to its window system, which raises the window if it accepts the token. Ask for it in answer to the click that warrants it, since the window system judges a token by the input it was made from.

Ok(None) where the window system has no such thing: Wayland without xdg-activation-v1, and every other backend, whose default this is. Wayland waits for the compositor’s answer, a round trip or more.

§Errors

ShellError::InvalidWindow for a stale handle, or ShellError::Backend where the window system has the mechanism but gave no token.

Source

fn set_text_input_area( &mut self, window: WindowId, area: Option<PhysicalRect>, ) -> Result<(), ShellError>

Tells the input method where text is being entered, so it can put its composition window and candidate list there instead of at a corner of the screen.

area is the text field’s caret, or the line it sits on, in the window’s client device pixels; the candidate list opens beside it without covering it. None hands placement back to the input method’s default. Call it when a field gains focus and whenever its caret moves. A hint: the input method may place its windows elsewhere, and nothing reports where they went.

§A provided method, and a no-op where there is nothing to place

Only a backend with an input method that draws its own windows has anything to do here — Win32’s IMM and TSF, a Wayland compositor’s text-input-v3 cursor rectangle, AppKit’s firstRectForCharacterRange:. The default validates the handle and does nothing else, which is exactly right for the rest. Win32, Wayland and AppKit override it. Wayland sends the area only while set_text_input names a field, since text-input-v3 has nowhere to put it otherwise; AppKit answers firstRectForCharacterRange: with it whenever asked.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn set_input_region( &mut self, window: WindowId, region: Option<&[PhysicalRect]>, ) -> Result<(), ShellError>

Limits where window takes the pointer to a union of rectangles, so the pointer anywhere else goes to whatever is behind the window.

For a transparent window that draws a shape smaller than itself: a launcher’s rounded panel, whose transparent corners should let a click through to the program beneath. A pixel of alpha zero is otherwise the window’s on every desktop backend; nothing hit-tests by alpha, because that would read the frame back from the GPU on every pointer move.

region is in the window’s client device pixels, the units of set_text_input_area. Empty rectangles are ignored, and parts outside the window take nothing; an empty slice leaves the window no pointer input at all. None gives the whole window back, which is where every window starts. Keyboard focus is not affected. The rectangles are the caller’s layout, so send them again after a ShellEvent::Resized or ShellEvent::ScaleFactorChanged that moves the shape. PhysicalRect::rounded builds them for a rounded rectangle.

Give the outer bound of what the window draws, antialiased edge included. On Win32 the region is a window region, which also clips the window’s drawing to it, so a region inside the edge’s blend cuts the edge off. Elsewhere a tighter region only loses the pointer over the edge.

A Passthrough popup stays passthrough: a region given to it is validated and has no effect.

§A provided method

The default validates the handle and does nothing else, which leaves the whole window taking the pointer. Wayland, X11 and Win32 override it, and ShellCaps::INPUT_REGION is set only where it is acted on; a caller that must have the click-through checks the bit rather than the platform.

§Errors

ShellError::InvalidWindow if the handle is stale.

Source

fn set_idle_inhibit( &mut self, window: WindowId, inhibit: bool, ) -> Result<(), ShellError>

Keeps the display on and the screensaver from starting while window asks: true to ask, false to stop.

For a game played on a gamepad, or a cutscene, which nothing the system counts as user activity interrupts. Ask while the game is being played and stop in the menus, as a video player stops at pause; asking for the whole session keeps a desktop awake that its user walked away from.

Per window, because Wayland’s zwp_idle_inhibit_manager_v1 inhibits per surface, and its compositor honours that only while the surface is shown; Win32 keeps the display on whether or not the window is. The display is kept on while any window of the shell asks. Asking twice is asking once, and stopping a window that never asked does nothing. Destroying a window ends its request, and dropping the shell ends them all.

§A provided method

The default validates the handle and answers ShellError::Unsupported, since a request nothing acts on would let the display sleep while the caller thinks otherwise. ShellCaps::IDLE_INHIBIT is set only where it is acted on, and says which backends that is.

§Errors

ShellError::InvalidWindow if the handle is stale, ShellError::Unsupported without ShellCaps::IDLE_INHIBIT, and ShellError::Backend if the system refused.

Source

fn open_url(&mut self, window: WindowId, url: &WebUrl) -> Result<(), ShellError>

Opens url in the user’s web browser, for a store page, a community link, or the end-user licence and privacy policy.

Only a WebUrl is taken: an absolute http or https URL, checked by WebUrl::parse before any backend sees it. The system’s own “open” would also run a program, open a file with whatever is associated with it, or start any registered protocol handler, and a game passing on a link a server sent it must never reach any of that; see the url module.

window owns whatever the system shows while it opens the browser, such as Win32’s message box when no browser is registered, as Wayland’s org.freedesktop.portal.OpenURI takes a parent window. Whether the browser opened the page is not reported back: success means the system accepted the request. Leave the browser’s window to come forward on its own; a fullscreen game may want to leave fullscreen first.

§A provided method

The default validates the handle and answers ShellError::Unsupported, since a link that silently does nothing leaves the player clicking it again. ShellCaps::OPEN_URL is set only where it is acted on, and says which backends that is.

§Errors

ShellError::InvalidWindow if the handle is stale, ShellError::Unsupported without ShellCaps::OPEN_URL, and ShellError::Backend if the system refused.

Source

fn set_text_input( &mut self, window: WindowId, field: Option<TextPurpose>, ) -> Result<(), ShellError>

Tells the input method what text field in window is taking typing: Some(purpose) as a field gains the keyboard focus, None as it loses it.

While a field is taking typing, a platform input method composes what is typed in the window, reporting the composition as TextPreedit and the result as TextCommit. While none is, the input method keeps out of the way and keys reach the window as keys: one in its native mode would otherwise turn a game’s WASD into a candidate list. A window starts with none.

The TextPurpose says what the field is for. A password or a PIN is sensitive: nothing typed into it may be learned, stored or suggested back.

§A provided method

The default validates the handle and does nothing else. Wayland acts on it with text-input-v3’s enable, set_content_type and disable; Win32 associates the window’s input context only for a Normal field, keeping it off for a password or a PIN as a Windows password box does. AppKit draws the same line by holding the view’s input context to the keyboard layouts for no field and for a sensitive one, and also holds secure event input while a password or PIN field’s window has the keyboard. ShellCaps::TEXT_IME is set only where this is acted on.

§Errors

ShellError::InvalidWindow if the handle is stale.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§