mirror of
https://github.com/storytold/deckcraft.git
synced 2026-11-12 00:47:28 -06:00
* Show: Presenter View only with a second display With one display the presenter window opened on the same screen as the show. Under Hyprland's focus-follows-mouse, whichever window the pointer was over took full screen, and the hidden one stalled every frame: the show lagged, dropped key presses or froze. One display now shows only the slides, as in PowerPoint. The host counts displays through winit's monitor list when a show starts; hosts that can't tell keep Presenter View. * Show: count displays by MonitorHandle Wayland lists one output twice under the same wl_output id, so comparing handles counts it once, and two separate displays with the same name and geometry still count as two. * Show: docs for Presenter View with a second display slideshow-parity and hardware-parity note that on Linux one display shows only the slides, with status lines and revision history; ROADMAP progress log. display_count collects into a BTreeSet (MonitorHandle's Ord matches its Eq). * Show: docs state the one-display rule exactly Presenter View stays off when winit lists exactly one display; if it lists none, the setting is kept. The host callback covers Unix other than macOS, so the docs say Linux and the BSDs. * Apply review fixes - docs/slideshow-parity.md --------- Co-authored-by: aiongg <67543387+aiongg@users.noreply.github.com>
77 lines
3.8 KiB
Rust
77 lines
3.8 KiB
Rust
pub type ReadFn = Box<dyn Fn(&str) -> Result<Vec<u8>, String>>;
|
|
|
|
pub type WriteFn = Box<dyn FnMut(&str, &[u8]) -> Result<(), String>>;
|
|
|
|
pub type PickFn = Box<dyn FnMut(&str) -> Option<String>>;
|
|
|
|
/// `(purpose, request id)`. The host shows a picker and later pushes an [`InboxEvent`]
|
|
/// carrying that same id. Completing a cancelled or unknown id does nothing: the file is
|
|
/// not applied to whichever document happens to be active.
|
|
pub type OpenAsyncFn = Box<dyn FnMut(&str, u64)>;
|
|
|
|
pub type DownloadFn = Box<dyn FnMut(&str, &[u8])>;
|
|
|
|
/// [`Services::host_save`]: `(file name, bytes, save_as)`. An error means the page didn't get
|
|
/// the file, and the save failed.
|
|
pub type HostSaveFn = Box<dyn FnMut(&str, &[u8], bool) -> Result<(), String>>;
|
|
|
|
/// A file the host finished reading for the request `id` from [`SlideApp::begin_external_file`].
|
|
#[derive(Clone, Debug)]
|
|
pub struct InboxFile {
|
|
/// Cancellation identity. A file whose id was cancelled, or never started, is ignored.
|
|
pub id: u64,
|
|
pub name: String,
|
|
pub bytes: Vec<u8>,
|
|
}
|
|
|
|
/// What the host delivers through [`Inbox`] after an asynchronous read.
|
|
#[derive(Clone, Debug)]
|
|
pub enum InboxEvent {
|
|
File(InboxFile),
|
|
/// The picker was dismissed or the file could not be read. `id` will not be applied.
|
|
Cancelled {
|
|
id: u64,
|
|
},
|
|
}
|
|
|
|
/// Files delivered asynchronously by the host (web file picker, dropped files).
|
|
pub type Inbox = std::sync::Arc<std::sync::Mutex<Vec<InboxEvent>>>;
|
|
|
|
/// Platform services injected by the host (desktop or web).
|
|
#[derive(Default)]
|
|
pub struct Services {
|
|
/// Browser-owned presenter popup; absent on native hosts.
|
|
pub presenter: Option<Box<dyn crate::presenter::Host>>,
|
|
/// Open-file dialog for a purpose (`open`, `picture`, `audio`, `video`) → path.
|
|
pub pick_open: Option<PickFn>,
|
|
/// Save dialog with a suggested name → path.
|
|
pub pick_save: Option<PickFn>,
|
|
pub read: Option<ReadFn>,
|
|
pub write: Option<WriteFn>,
|
|
/// Asynchronous open dialog (web). The callback receives the purpose and the request id
|
|
/// from [`SlideApp::begin_external_file`]; the chosen file arrives through `inbox`.
|
|
pub open_async: Option<OpenAsyncFn>,
|
|
/// Hand bytes to the user as a named file (browser download).
|
|
pub download: Option<DownloadFn>,
|
|
/// Web, framed by a page that keeps the files (`?host=parent`): Save and Save As hand the
|
|
/// presentation to that page instead of downloading it, with whether it is a new file
|
|
/// ([`HostSaveFn`]). Save writes a presentation the page sent back under its own name and
|
|
/// format, as the desktop app writes back to the file it opened; a presentation the page
|
|
/// never sent (an untitled one, a repaired copy) goes as a new `.deckcraft` file, as Save
|
|
/// suggests on the desktop. Save As (.pptx or .deckcraft) is always a new file. Either way the
|
|
/// presentation is then that file, and the next Save goes to it. Exports and Print still go
|
|
/// through `download`, which the host also points at the page: they are copies.
|
|
pub host_save: Option<HostSaveFn>,
|
|
pub inbox: Option<Inbox>,
|
|
/// Image on the system clipboard (PNG bytes), when the host can read one.
|
|
pub clipboard_image: Option<Box<dyn FnMut() -> Option<Vec<u8>>>>,
|
|
/// HTML on the system clipboard (Pages, Keynote, and other apps that write `public.html`).
|
|
pub clipboard_html: Option<Box<dyn FnMut() -> Option<String>>>,
|
|
/// Put HTML and its plain-text alternative on the system clipboard. `true` when it landed.
|
|
pub clipboard_set_html: Option<Box<dyn FnMut(&str, &str) -> bool>>,
|
|
/// Audio output for media playback (cpal on desktop); without one media plays silently.
|
|
pub audio_out: Option<Box<dyn deckcraft_media::AudioOut>>,
|
|
/// How many displays are connected, when the host can tell: Presenter View needs a second.
|
|
pub displays: Option<Box<dyn FnMut() -> Option<usize>>>,
|
|
}
|