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;