-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support speaker notes #389
base: master
Are you sure you want to change the base?
Changes from 8 commits
de42655
fd6d57b
3151fbd
cbadf07
93a0dcb
22d70f7
4c7726e
df051d3
81cfbbc
4416af8
5f781ac
0fd812e
0f83b5a
dc0e039
27a6151
43b44c5
ddbaf41
3114df9
52d3f04
13e2d46
2964ac9
209cd10
64dcc88
7924524
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
Speaker Notes | ||
=== | ||
|
||
`presenterm` supports speaker notes. | ||
|
||
You can use the following HTML comment throughout your presentation markdown file: | ||
|
||
```markdown | ||
<!-- speaker_note: Your speaker note goes here. --> | ||
``` | ||
|
||
<!-- speaker_note: This is a speaker note from slide 1. --> | ||
|
||
And you can run a separate instance of `presenterm` to view them. | ||
|
||
<!-- speaker_note: You can use multiple speaker notes within each slide and interleave them with other markdown. --> | ||
|
||
<!-- end_slide --> | ||
|
||
Usage | ||
=== | ||
Run the following two commands in separate terminals. | ||
|
||
<!-- speaker_note: This is a speaker note from slide 2. --> | ||
|
||
The `--speaker-notes-mode=publisher` argument will render your actual presentation as normal, without speaker notes: | ||
|
||
``` | ||
presenterm --speaker-notes-mode=publisher examples/speaker-notes.md | ||
``` | ||
|
||
The `--speaker-notes-mode=receiver` argument will render only the speaker notes for the current slide being shown in the actual presentation: | ||
|
||
``` | ||
presenterm --speaker-notes-mode=receiver examples/speaker-notes.md | ||
``` | ||
|
||
<!-- speaker_note: Demonstrate changing slides in the actual presentation. --> | ||
|
||
As you change slides in your actual presentation, the speaker notes presentation slide will automatically navigate to the correct slide. | ||
|
||
<!-- speaker_note: Isn't that cool? --> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,9 @@ | ||
use iceoryx2::{ | ||
port::{listener::Listener, notifier::Notifier}, | ||
prelude::EventId, | ||
service::ipc::Service, | ||
}; | ||
|
||
use crate::{ | ||
custom::KeyBindingsConfig, | ||
diff::PresentationDiffer, | ||
|
@@ -37,6 +43,12 @@ pub struct PresenterOptions { | |
pub validate_overflows: bool, | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum SpeakerNoteChannel { | ||
Notifier(Notifier<Service>), | ||
Listener(Listener<Service>), | ||
} | ||
|
||
/// A slideshow presenter. | ||
/// | ||
/// This type puts everything else together. | ||
|
@@ -52,6 +64,7 @@ pub struct Presenter<'a> { | |
image_printer: Arc<ImagePrinter>, | ||
themes: Themes, | ||
options: PresenterOptions, | ||
speaker_notes_channel: Option<SpeakerNoteChannel>, | ||
} | ||
|
||
impl<'a> Presenter<'a> { | ||
|
@@ -67,6 +80,7 @@ impl<'a> Presenter<'a> { | |
themes: Themes, | ||
image_printer: Arc<ImagePrinter>, | ||
options: PresenterOptions, | ||
speaker_notes_channel: Option<SpeakerNoteChannel>, | ||
) -> Self { | ||
Self { | ||
default_theme, | ||
|
@@ -80,6 +94,7 @@ impl<'a> Presenter<'a> { | |
image_printer, | ||
themes, | ||
options, | ||
speaker_notes_channel, | ||
} | ||
} | ||
|
||
|
@@ -104,6 +119,13 @@ impl<'a> Presenter<'a> { | |
self.render(&mut drawer)?; | ||
|
||
loop { | ||
if let Some(SpeakerNoteChannel::Listener(listener)) = self.speaker_notes_channel.as_mut() { | ||
if let Some(evt) = listener.try_wait_one().unwrap() { | ||
self.apply_command(Command::GoToSlide(evt.as_value() as u32)); | ||
break; | ||
} | ||
} | ||
|
||
if self.poll_async_renders()? { | ||
self.render(&mut drawer)?; | ||
} | ||
|
@@ -136,6 +158,10 @@ impl<'a> Presenter<'a> { | |
CommandSideEffect::None => (), | ||
}; | ||
} | ||
if let Some(SpeakerNoteChannel::Notifier(notifier)) = self.speaker_notes_channel.as_mut() { | ||
let current_slide_idx = self.state.presentation().current_slide_index(); | ||
notifier.notify_with_custom_event_id(EventId::new(current_slide_idx + 1)).unwrap(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you consider using the interprocess crate instead? I need to read iceoryx2's docs more but this API feels a bit strange. You're communicating via event ids, which is enough for what we're doing but it feels a bit off. (I admit I haven't looked at the docs enough to understand what events mean here). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No hard reason to keep using the event messaging pattern and There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had come across There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fair, |
||
} | ||
} | ||
} | ||
|
||
|
@@ -194,7 +220,11 @@ impl<'a> Presenter<'a> { | |
}; | ||
// If the screen is too small, simply ignore this. Eventually the user will resize the | ||
// screen. | ||
if matches!(result, Err(RenderError::TerminalTooSmall)) { Ok(()) } else { result } | ||
if matches!(result, Err(RenderError::TerminalTooSmall)) { | ||
Ok(()) | ||
} else { | ||
result | ||
} | ||
} | ||
|
||
fn apply_command(&mut self, command: Command) -> CommandSideEffect { | ||
|
@@ -264,7 +294,11 @@ impl<'a> Presenter<'a> { | |
panic!("unreachable commands") | ||
} | ||
}; | ||
if needs_redraw { CommandSideEffect::Redraw } else { CommandSideEffect::None } | ||
if needs_redraw { | ||
CommandSideEffect::Redraw | ||
} else { | ||
CommandSideEffect::None | ||
} | ||
} | ||
|
||
fn try_reload(&mut self, path: &Path, force: bool) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requires rust 1.75 or newer, but CI runs rust 1.74.
A rust-toolchain file and Cargo.toml entry would help make this more visible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any thoughts on bumping the rust version @mfontanini?
Otherwise a different version of
iceoryx2
(if one exists for rust 1.74) or different IPC crate will need to be used.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also need to investigate some warnings and proper configuration for usage of this crate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go for it, 1.75 is almost a year old already