Crate ltk_webkit

Source
Expand description

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

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 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" ) )
    }
}

let webview = WebView::new( 800.0, 600.0, "https://example.com" )?;
ltk::run( DemoApp { webview } );

§What is wired up

Forwarded into WebKit via the methods on WebView:

LTK callbackWebView methodWPE event
App::on_pointer_moveWebView::pointer_movePOINTER_MOVE
App::on_pointer_button( pressed )WebView::pointer_press / WebView::pointer_releasePOINTER_DOWN/POINTER_UP
App::on_pointer_axisWebView::scrollSCROLL
App::on_raw_keyWebView::send_keyKEYBOARD_KEY_DOWN/UP
App::on_touch_down / move / upWebView::touch_down / WebView::touch_move / WebView::touch_upTOUCH_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):

WhatMethod
New rendered frame readyWebView::take_redraw_request
Cursor shape WebKit wantsWebView::cursor_shape
Link under the pointerWebView::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.

Structs§

Enums§

Functions§

  • The codecs that some video device of this machine decodes in hardware. Cameras and other devices answer with no input formats and add nothing.
  • Drain the download events produced since the previous call, including a Progress event for every live download that advanced.
  • Resolve host ahead of a load that is likely to follow, such as the address being typed.
  • For the views created from now on; an existing view keeps the policy it was built with.
  • Pages are told that the blocked codecs cannot be played, which sites such as YouTube answer by streaming another one. An empty list lifts the block. A later call replaces what an earlier one injected; pages already loaded keep what they have until their next load.
  • Creates the web context, so set_memory_limit has to come first.
  • Replace the content filter with rules_json. WebKit compiles it in the background, into store_dir, so loads that start right away may still go unfiltered; pages already loaded keep what they have until their next load.
  • Keep website data and cookies in data and caches in cache. Unset, WebKit names its directories after the program, so every WPE application without a name of its own shares them. Call it before anything else in this crate; a later call is ignored.
  • Directory new downloads are saved to. Defaults to $XDG_DOWNLOAD_DIR, then the download directory of user-dirs.dirs, then ~/Downloads.
  • Turn the feature named identifier (as webkit_feature_get_identifier gives it) on or off in the views created from now on. An identifier this WebKit does not know is reported once on stderr, with the ones that resemble it.
  • Memory a web process, or the network process, may use before WebKit starts giving it back: caches from a third of the limit, all it can from half. Unset, WebKit derives the limit from the machine, up to 3 GiB. Call it before anything else in this crate; zero is ignored.
  • The keyboard modifiers the user holds, for pages that read them on a click, a motion or the wheel, as Shift+click extends a selection and Ctrl+click adds to it. The host calls this whenever they change.
  • Accept-Language and navigator.languages, most preferred first; WebKit’s default follows the process locale.
  • Whether a page may set cookies for a site other than the one in the address bar.
  • WebKit’s Intelligent Tracking Prevention. Off by default, as in WebKit.
  • Call wake from another thread whenever the main context has events, so the host’s run loop can call crate::WebView::tick then rather than on a timer. After a call to wake the watch rests until the next tick, so a host that does not tick is woken once. Call it once, from the thread that ticks.

Type Aliases§