ltk_webkit/
lib.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
161
//! A web view for the LTK toolkit.
//!
//! Exposes [`WebView`], a page rendered off screen and drawn as a texture inside LTK's GLES context, so it composites inline with the rest of the LTK widget tree: no hole-punching, no separate surface. The API is the same on every platform; the engine behind it is WPE WebKit on Linux, a `WebKitWebView` on the headless WPEPlatform display whose DMA-BUF frames are imported as EGLImages, and CEF on Windows.
//!
//! # Quick start
//!
//! ```rust,no_run
//! use ltk::{ App, CursorShape, Element, Keysym };
//! use ltk_webkit::WebView;
//!
//! #[ derive( Clone ) ]
//! enum Msg { Tick }
//!
//! struct DemoApp { webview: WebView }
//!
//! impl App for DemoApp
//! {
//!     type Message = Msg;
//! #     fn app_id( &self ) -> &str { "net.liberux.ltk-webkit.demo" }
//! #     fn save_state( &self ) -> Option<Vec<u8>> { None }
//! #     fn restore_state( &mut self, _: Vec<u8> ) {}
//!
//!     fn view( &self ) -> Element<Msg>
//!     {
//!         ltk::column().push( self.webview.element::<Msg>() ).into()
//!     }
//!     fn update( &mut self, _: Msg ) {}
//!
//!     fn poll_external( &mut self ) -> Vec<Msg>
//!     {
//!         self.webview.tick();
//!         vec![ Msg::Tick ]
//!     }
//!     fn poll_interval( &self ) -> Option<std::time::Duration>
//!     {
//!         Some( std::time::Duration::from_millis( 16 ) )
//!     }
//!     fn invalidate_after( &self, _: &Msg ) -> ltk::InvalidationScope
//!     {
//!         if self.webview.take_redraw_request() { ltk::InvalidationScope::All }
//!         else { ltk::InvalidationScope::Only( Vec::new() ) }
//!     }
//!
//!     // Forward LTK input to WebKit.
//!     fn on_pointer_move( &mut self, x: f32, y: f32 )
//!     {
//!         self.webview.pointer_move( x as f64, y as f64 );
//!     }
//!     fn on_pointer_button( &mut self, x: f32, y: f32, pressed: bool )
//!     {
//!         if pressed { self.webview.pointer_press(   x as f64, y as f64 ); }
//!         else       { self.webview.pointer_release( x as f64, y as f64 ); }
//!     }
//!     fn on_pointer_axis( &mut self, x: f32, y: f32, dx: f32, dy: f32 )
//!     {
//!         let scale = -1.0_f32 / 3.0;
//!         self.webview.scroll( x as f64, y as f64, ( dx * scale ) as f64, ( dy * scale ) as f64 );
//!     }
//!     fn on_raw_key( &mut self, ks: Keysym, kc: u32, pressed: bool, ctrl: bool, shift: bool )
//!     {
//!         self.webview.send_key( ks.raw(), kc, pressed, ctrl, shift );
//!     }
//!     fn cursor_override( &self ) -> Option<CursorShape>
//!     {
//!         Some( self.webview.cursor_shape() )
//!     }
//!
//!     fn window_config( &self ) -> Option<( &str, &str )>
//!     {
//!         Some( ( "ltk-webkit demo", "net.liberux.ltk-webkit.demo" ) )
//!     }
//! }
//!
//! # fn _run() -> Result<(), String> {
//! let webview = WebView::new( 800.0, 600.0, "https://example.com" )?;
//! ltk::run( DemoApp { webview } );
//! # Ok(()) }
//! ```
//!
//! # What is wired up
//!
//! Forwarded **into** WebKit via the methods on [`WebView`]:
//!
//! | LTK callback                       | WebView method                     | WPE event              |
//! |------------------------------------|------------------------------------|------------------------|
//! | `App::on_pointer_move`             | [`WebView::pointer_move`]          | `POINTER_MOVE`         |
//! | `App::on_pointer_button( pressed )`| [`WebView::pointer_press`] / [`WebView::pointer_release`] | `POINTER_DOWN`/`POINTER_UP` |
//! | `App::on_pointer_axis`             | [`WebView::scroll`]                | `SCROLL`               |
//! | `App::on_raw_key`                  | [`WebView::send_key`]              | `KEYBOARD_KEY_DOWN`/`UP` |
//! | `App::on_touch_down` / `move` / `up` | [`WebView::touch_down`] / [`WebView::touch_move`] / [`WebView::touch_up`] | `TOUCH_DOWN`/`TOUCH_MOVE`/`TOUCH_UP` |
//! | layout rect change (per frame)     | (auto via [`WebView::element`])    | `WPEToplevel::resize`  |
//!
//! On a touchscreen ltk reports the primary finger through the pointer
//! callbacks, so taps reach the page as clicks. For WebKit's own touch
//! handling the host returns `true` from ltk's `App::claims_raw_touch`,
//! and WebKit's gesture recognition then turns the raw stream into
//! taps, kinetic scrolls and pinch-zoom. A host that shows the view next
//! to regular widgets returns [`WebView::contains`] from
//! `App::claims_raw_touch_at` instead, so its own chrome stays touchable.
//!
//! Forwarded **out of** WebKit (read by LTK):
//!
//! | What                            | Method                             |
//! |---------------------------------|------------------------------------|
//! | New rendered frame ready        | [`WebView::take_redraw_request`]   |
//! | Cursor shape WebKit wants       | [`WebView::cursor_shape`]          |
//! | Link under the pointer          | [`WebView::hovered_link`]          |
//!
//! # Threading
//!
//! Single-threaded usage only. WPE's `GMainLoop`, the Wayland event
//! loop and LTK's render loop must all run on the same thread (the
//! main thread, in practice). [`WebView`] exposes `Send + Sync`
//! because the [`ltk::ExternalSource::Frame`] closure requires it,
//! but the FFI pointers it holds are not actually safe to use across
//! threads — calling [`WebView::tick`] or rendering off the main thread
//! is undefined behaviour.
//!
//! # Caveats
//!
//! * Press counts (single / double / triple click) come from
//!   `wpe_view_compute_press_count`, which uses a position + time
//!   window heuristic; quirky touchpads may need calibration.
//! * Cursor changes incur a WebKit-IPC round trip; the host's
//!   `invalidate_after` should return `All` whenever
//!   [`WebView::take_redraw_request`] is `true` to avoid stale-frame lag.
//! * Cursor names WebKit emits but LTK doesn't enumerate
//!   (`col-resize`, `row-resize`, `zoom-in`, `none`, …) fall back to
//!   `Default`.
//! * `Drop` of an internal `Inner` does not destroy the bound
//!   EGLImage — minor leak, acceptable for the spike.


#![ deny( unsafe_op_in_unsafe_fn ) ]

mod page_scripts;
mod types;

#[ cfg( not( windows ) ) ]
mod wpe;

#[ cfg( windows ) ]
mod cef;

pub use types::{ Autoplay, CacheModel, DownloadEvent, DownloadId, LoadError, LoadEvent, Navigation, NavigationKind, PageTarget, Preview, PreviewPage, PrintError, PrintSettings, Snapshot, Termination, VideoCodec };

#[ cfg( not( windows ) ) ]
pub use wpe::{
	cancel_download, hardware_video_decoders, poll_downloads, prefetch_dns, remove_content_filters, set_autoplay, set_blocked_video_codecs, set_cache_model, set_content_filter, set_data_directories,
	set_download_dir, set_feature, set_memory_limit, set_modifiers, set_preferred_languages, set_third_party_cookies, set_tracking_prevention, wake_on_events, FileRequest, WebView,
};

#[ cfg( windows ) ]
pub use self::cef::{
	cancel_download, hardware_video_decoders, poll_downloads, prefetch_dns, remove_content_filters, set_autoplay, set_blocked_video_codecs, set_cache_model, set_content_filter, set_data_directories,
	set_download_dir, set_feature, set_memory_limit, set_modifiers, set_preferred_languages, set_third_party_cookies, set_tracking_prevention, wake_on_events, FileRequest, WebView,
};

#[ cfg( windows ) ]
#[ doc( hidden ) ]
pub use self::cef::helper_main;