ltk/platform/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// SPDX-License-Identifier: LGPL-2.1-only
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>

//! Platform backends: what ties the runtime to a windowing system.
//!
//! The runtime (`event_loop/`, `input/`, `draw/`) holds the app, the widget tree, focus, gestures, text editing and every `App` callback, and is the same on every platform. A backend feeds it input through the entry points of [`crate::input::event`], keeps its own half of every surface in `SurfaceState::platform`, and carries out the requests of [`Platform`].
//!
//! A backend also owns the run loop: it connects to the system, builds `AppData` with `AppData::new`, installs the shared event sources with `AppData::install_sources`, and on every wake-up calls `AppData::before_draw`, `AppData::draw_due` and `AppData::after_draw` around its own work.
//!
//! The backends are `wayland`, `android`, `web` and `windows`, each behind the feature of its name; `build.rs` refuses more than one. Each backend sets the two aliases below and `run` / `try_run` under its own `cfg`, and `build.rs` sets `cfg( backend )`, which builds the runtime, when one is enabled.

#[ cfg( feature = "android" ) ]
pub( crate ) mod android;
#[ cfg( feature = "wayland" ) ]
pub( crate ) mod wayland;
#[ cfg( feature = "web" ) ]
pub( crate ) mod web;
#[ cfg( feature = "windows" ) ]
pub( crate ) mod windows;
#[ cfg( test ) ]
pub( crate ) mod mock;

#[ cfg( feature = "android" ) ]
pub( crate ) use android::run::{ run, try_run };
#[ cfg( feature = "wayland" ) ]
pub( crate ) use wayland::run::{ run, try_run };
#[ cfg( feature = "web" ) ]
pub( crate ) use web::run::{ run, try_run };
#[ cfg( feature = "windows" ) ]
pub( crate ) use windows::run::{ run, try_run };

use crate::event_loop::SurfaceFocus;
use crate::event_loop::cursor_shape::ResizeEdge;
use crate::osk::Purpose;
use crate::render::Canvas;
use crate::types::{ Color, CursorShape, Rect };

/// The backend the runtime runs on, held by `AppData::platform`.
#[ cfg( feature = "wayland" ) ]
pub( crate ) type NativePlatform<A> = wayland::WaylandPlatform<A>;
#[ cfg( feature = "android" ) ]
pub( crate ) type NativePlatform<A> = android::AndroidPlatform<A>;
#[ cfg( feature = "web" ) ]
pub( crate ) type NativePlatform<A> = web::WebPlatform<A>;
#[ cfg( feature = "windows" ) ]
pub( crate ) type NativePlatform<A> = windows::WindowsPlatform<A>;

/// The backend's half of every surface, held by `SurfaceState::platform`.
#[ cfg( feature = "wayland" ) ]
pub( crate ) type NativeSurface = wayland::WaylandSurfaceState;
#[ cfg( feature = "android" ) ]
pub( crate ) type NativeSurface = android::AndroidSurfaceState;
#[ cfg( feature = "web" ) ]
pub( crate ) type NativeSurface = web::WebSurfaceState;
#[ cfg( feature = "windows" ) ]
pub( crate ) type NativeSurface = windows::WindowsSurfaceState;

/// The user's language as `LANG` gives it (`es_ES.UTF-8`), or in a browser as the page does (`es-ES`), or on Windows as the user's locale does when `LANG` is unset.
#[ cfg( not( any( feature = "web", feature = "windows" ) ) ) ]
pub( crate ) fn language() -> String
{
	std::env::var( "LANG" ).unwrap_or_default()
}

#[ cfg( feature = "windows" ) ]
pub( crate ) fn language() -> String
{
	std::env::var( "LANG" ).ok().filter( |l| !l.is_empty() ).unwrap_or_else( windows::user_locale )
}

#[ cfg( feature = "web" ) ]
pub( crate ) fn language() -> String
{
	web_sys::window().and_then( |w| w.navigator().language() ).unwrap_or_default()
}

/// What [`Platform::present`] needs to know about a painted frame. Rects are in physical pixels, top-left origin.
pub( crate ) struct Presentation<'a>
{
	/// What changed since the last frame; empty for the whole surface.
	pub damage:        &'a [Rect],
	/// The surface is opaque where `bg` is, and elsewhere over `opaque_region`.
	pub bg:            Color,
	pub opaque_region: Option<&'a [Rect]>,
	/// Where the surface takes input; `None` everywhere, empty nowhere.
	pub input_region:  Option<&'a [Rect]>,
	/// Logical size and the physical pixels per logical one.
	pub width:         u32,
	pub height:        u32,
	pub scale:         f32,
	/// Report the next frame to the runtime as one of this surface; `None` for a surface that is not paced by frames.
	pub frame:         Option<SurfaceFocus>,
}

/// What the runtime asks of the windowing system. `AppData::platform` implements it.
///
/// Drawing a surface takes three calls: [`Self::prepare_canvas`], then the runtime paints the widget tree on the canvas, then [`Self::present`]. The canvas is the surface's own and keeps its pixels between frames, so a partial repaint only touches the damaged rects, and presenting adds no copy beyond the one into the system's buffer that a software canvas always needs.
pub( crate ) trait Platform: 'static
{
	/// The backend's half of a surface, held by `SurfaceState::platform`.
	type Surface;

	/// A display that one of `AppData::secondaries` covers.
	type Output;

	/// Whether a full repaint of `surface` should report the rects that changed rather than the whole surface.
	fn precise_damage( &self, surface: &Self::Surface ) -> bool;

	/// Make `canvas` ready to paint `surface` at `pw × ph` physical pixels, creating it on the first frame; `false` when the surface cannot be drawn now.
	fn prepare_canvas( &self, surface: &mut Self::Surface, canvas: &mut Option<Canvas>, pw: u32, ph: u32, scale: f32 ) -> bool;

	/// Show what was painted on `canvas` since [`Self::prepare_canvas`]; `false` when nothing reached the screen.
	fn present( &self, surface: &mut Self::Surface, canvas: &mut Canvas, frame: &Presentation<'_> ) -> bool;

	/// Offer `text`, which the runtime also keeps in `AppData::clipboard`, to other applications as the clipboard.
	fn publish_clipboard( &mut self, text: &str );

	/// Offer `text`, which the runtime also keeps in `AppData::primary_selection`, to other applications as the selection the middle button pastes; no-op where there is none.
	fn publish_primary_selection( &mut self, text: &str );

	/// Whether another application's primary selection is still being read, so `AppData::primary_selection` is not yet the one it set.
	fn primary_selection_pending( &self ) -> bool
	{
		false
	}

	/// Whether `main` is an application window the user can move, resize and minimise, rather than a shell or lock surface.
	fn is_toplevel( &self, main: &Self::Surface ) -> bool;

	/// Whether back with nothing left to go back to may minimise or close `main`, as [`crate::app::BackAtRoot`] says: an application's window or activity, not a shell or lock surface.
	fn back_leaves( &self, main: &Self::Surface ) -> bool;

	/// Let the system move the window with the pointer whose left button was just pressed.
	fn begin_window_move( &self, main: &Self::Surface );

	/// Let the system resize the window from `edge` with the pointer whose left button was just pressed.
	fn begin_window_resize( &self, main: &Self::Surface, edge: ResizeEdge );

	fn minimize_window( &self, main: &Self::Surface );

	/// `false` when `main` is not a window, so the runtime asks again later.
	fn set_fullscreen( &self, main: &Self::Surface, fullscreen: bool ) -> bool;

	/// Ask for a frame of `surface`, reported as one of `focus`, without presenting a new buffer; `false` while the surface does not exist yet.
	fn request_frame( &self, surface: &Self::Surface, focus: SurfaceFocus ) -> bool;

	/// `false` when the system lets the app set no cursor shape.
	fn set_cursor_shape( &mut self, shape: CursorShape ) -> bool;

	/// Whether the input method is serving a text field or the app.
	fn text_input_active( &self ) -> bool;

	/// Ask the input method for text, or renew the request so a keyboard the system folded comes back.
	fn activate_text_input( &mut self, secure: bool, purpose: Purpose );

	fn deactivate_text_input( &mut self );

	/// Send an active request again with a new purpose; no-op when none is active.
	fn reenable_text_input( &self, secure: bool, purpose: Purpose );
}