Compare commits

..

2 Commits

Author SHA1 Message Date
itsjunetime f9df806a8f Remove accidental comment 2024-11-17 17:34:59 -07:00
itsjunetime 5e7ec97b43 Flush EventStream when receiving a scroll event to work around high scroll sensitivities 2024-11-17 17:33:43 -07:00
5 changed files with 90 additions and 127 deletions
-56
View File
@@ -1,56 +0,0 @@
name: Rust
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup sccache
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
uses: mozilla-actions/sccache-action@v0.0.6
- name: Configure sccache
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
run: |
echo "SCCACHE_GHA_ENABLED=true" >> $GITHUB_ENV
echo "RUSTC_WRAPPER=sccache" >> $GITHUB_ENV
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y cmake libjpeg-dev libfontconfig1-dev libopenjp2-7-dev libopenjpip7 libopenjp2-7 libglib2.0-dev libnss3-dev libunwind-dev libgoogle-perftools-dev libboost-dev
- name: Build newer poppler
run: |
wget https://poppler.freedesktop.org/poppler-23.10.0.tar.xz
tar xf poppler-23.10.0.tar.xz
cd poppler-23.10.0
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache \
-DENABLE_UNSTABLE_API_ABI_HEADERS=ON \
-DENABLE_GPGME=OFF \
-DENABLE_QT5=OFF \
-DENABLE_QT6=OFF \
-DENABLE_SPLASH=OFF \
-DENABLE_LIBCURL=OFF
make -j$(nproc)
sudo make install
sudo ldconfig
- uses: actions/checkout@v4
- name: Clippy
run: cargo clippy -- -D warnings
- name: Check fmt
run: cargo fmt -- --check
- name: Run tests
run: cargo test --benches -- adobe_example
- name: Build
run: cargo build
-2
View File
@@ -5,8 +5,6 @@
- Small internal changes to accomodate a few more clippy lints - Small internal changes to accomodate a few more clippy lints
- Update `ratatui` and `ratatui-image` git dependencies to latest upstream - Update `ratatui` and `ratatui-image` git dependencies to latest upstream
- Move `ratatui-image/vb64` support under `nightly` feature, enabled by default - Move `ratatui-image/vb64` support under `nightly` feature, enabled by default
- Fixed a bug where jumping to a page out of range could result in weird `esc` key behavior
- Added CI
# v0.1.0 # v0.1.0
Generated
+27 -27
View File
@@ -149,9 +149,9 @@ dependencies = [
[[package]] [[package]]
name = "axum" name = "axum"
version = "0.7.9" version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum-core", "axum-core",
@@ -253,9 +253,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]] [[package]]
name = "bytemuck" name = "bytemuck"
version = "1.20.0" version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
@@ -321,9 +321,9 @@ dependencies = [
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.1" version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
dependencies = [ dependencies = [
"jobserver", "jobserver",
"libc", "libc",
@@ -342,9 +342,9 @@ dependencies = [
[[package]] [[package]]
name = "cfg-expr" name = "cfg-expr"
version = "0.17.1" version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c360837f8f19e2e4468275138f1c0dec1647d1e17bb7c0215fe3cd7530e93c25" checksum = "d0890061c4d3223e7267f3bad2ec40b997d64faac1c2815a4a9d95018e2b9e9c"
dependencies = [ dependencies = [
"smallvec", "smallvec",
"target-lexicon", "target-lexicon",
@@ -385,18 +385,18 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.21" version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f" checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
] ]
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.21" version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec" checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"clap_lex", "clap_lex",
@@ -404,9 +404,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.3" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]] [[package]]
name = "compact_str" name = "compact_str"
@@ -681,9 +681,9 @@ dependencies = [
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.35" version = "1.0.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide",
@@ -910,9 +910,9 @@ dependencies = [
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.4.7" version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
@@ -1039,9 +1039,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.5.1" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-channel", "futures-channel",
@@ -1294,9 +1294,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.164" version = "0.2.162"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398"
[[package]] [[package]]
name = "libfuzzer-sys" name = "libfuzzer-sys"
@@ -2009,9 +2009,9 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.41" version = "0.38.40"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0"
dependencies = [ dependencies = [
"bitflags 2.6.0", "bitflags 2.6.0",
"errno", "errno",
@@ -2069,9 +2069,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.133" version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@@ -2258,7 +2258,7 @@ version = "7.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005"
dependencies = [ dependencies = [
"cfg-expr 0.17.1", "cfg-expr 0.17.0",
"heck", "heck",
"pkg-config", "pkg-config",
"toml", "toml",
+41 -4
View File
@@ -1,11 +1,14 @@
use std::{ use std::{
future::poll_fn,
io::{stdout, Read, Write}, io::{stdout, Read, Write},
num::NonZeroUsize, num::NonZeroUsize,
path::PathBuf path::PathBuf,
task::Poll
}; };
use converter::{run_conversion_loop, ConvertedPage, ConverterMsg}; use converter::{run_conversion_loop, ConvertedPage, ConverterMsg};
use crossterm::{ use crossterm::{
event::{Event, EventStream, MouseEvent, MouseEventKind},
execute, execute,
terminal::{ terminal::{
disable_raw_mode, enable_raw_mode, window_size, EndSynchronizedUpdate, disable_raw_mode, enable_raw_mode, window_size, EndSynchronizedUpdate,
@@ -14,7 +17,7 @@ use crossterm::{
}; };
use futures_util::{stream::StreamExt, FutureExt}; use futures_util::{stream::StreamExt, FutureExt};
use glib::{LogField, LogLevel, LogWriterOutput}; use glib::{LogField, LogLevel, LogWriterOutput};
use notify::{Event, EventKind, RecursiveMode, Watcher}; use notify::{EventKind, RecursiveMode, Watcher};
use ratatui::{backend::CrosstermBackend, Terminal}; use ratatui::{backend::CrosstermBackend, Terminal};
use ratatui_image::picker::Picker; use ratatui_image::picker::Picker;
use renderer::{RenderError, RenderInfo, RenderNotif}; use renderer::{RenderError, RenderInfo, RenderNotif};
@@ -132,7 +135,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
renderer::start_rendering(&file_path, render_tx, render_rx, window_size) renderer::start_rendering(&file_path, render_tx, render_rx, window_size)
}); });
let mut ev_stream = crossterm::event::EventStream::new(); let mut ev_stream = EventStream::new();
let (to_converter, from_main) = flume::unbounded(); let (to_converter, from_main) = flume::unbounded();
let (to_main, from_converter) = flume::unbounded(); let (to_main, from_converter) = flume::unbounded();
@@ -175,6 +178,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// If we can't get user input, just crash. // If we can't get user input, just crash.
let ev = ev.expect("Couldn't get any user input"); let ev = ev.expect("Couldn't get any user input");
flush_if_mouse(&ev, &mut ev_stream).await;
match tui.handle_event(&ev) { match tui.handle_event(&ev) {
None => needs_redraw = false, None => needs_redraw = false,
Some(action) => match action { Some(action) => match action {
@@ -244,7 +249,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
fn on_notify_ev( fn on_notify_ev(
to_tui_tx: flume::Sender<Result<RenderInfo, RenderError>>, to_tui_tx: flume::Sender<Result<RenderInfo, RenderError>>,
to_render_tx: flume::Sender<RenderNotif> to_render_tx: flume::Sender<RenderNotif>
) -> impl Fn(notify::Result<Event>) { ) -> impl Fn(notify::Result<notify::Event>) {
move |res| match res { move |res| match res {
// If we get an error here, and then an error sending, everything's going wrong. Just give // If we get an error here, and then an error sending, everything's going wrong. Just give
// up lol. // up lol.
@@ -267,3 +272,35 @@ fn on_notify_ev(
fn noop(_: LogLevel, _: &[LogField<'_>]) -> LogWriterOutput { fn noop(_: LogLevel, _: &[LogField<'_>]) -> LogWriterOutput {
LogWriterOutput::Handled LogWriterOutput::Handled
} }
async fn flush_if_mouse(ev: &Event, ev_stream: &mut EventStream) {
// If you have a high scroll sensitivity, on some platforms, crossterm sends 2+ mouse events per
// scroll. However, because tdf scrolls pages once per mouse event, that means that they can't
// scroll the pages of their PDF as they would expect - it jumps multiple pages per one scroll.
// So this just flushes the event queue once we detect a scroll event to make sure those extra
// scrolls are ignored.
//
// Theoretically, this introduces a race condition where events that arrive in between the
// processing of `ev` and the time we call `poll_next_unpin` can get ignored, but that's a very
// very small amount of time, so unlikely to happen. However, if that does become an issue, we
// can just flush the event queue until we see a non-scroll event, then store that event in a
// future that will return it next time we call `tokio::select!` in the main loop. That'll make
// sure we don't miss anything.
if let Event::Mouse(MouseEvent {
kind:
MouseEventKind::ScrollUp
| MouseEventKind::ScrollDown
| MouseEventKind::ScrollLeft
| MouseEventKind::ScrollRight,
..
}) = ev
{
poll_fn(|ctx| {
while ev_stream.poll_next_unpin(ctx).is_ready() {
println!("got another mouse event!");
}
Poll::Ready(())
})
.await;
}
}
+22 -38
View File
@@ -1,4 +1,4 @@
use std::{borrow::Cow, io::stdout, num::NonZeroUsize, rc::Rc}; use std::{io::stdout, num::NonZeroUsize, rc::Rc};
use crossterm::{ use crossterm::{
event::{Event, KeyCode, MouseEventKind}, event::{Event, KeyCode, MouseEventKind},
@@ -152,18 +152,18 @@ impl Tui {
let rendered_span = Span::styled(&rendered_str, Style::new().fg(Color::Cyan)); let rendered_span = Span::styled(&rendered_str, Style::new().fg(Color::Cyan));
frame.render_widget(rendered_span, bottom_layout[1]); frame.render_widget(rendered_span, bottom_layout[1]);
let (msg_str, color): (Cow<'_, str>, _) = match self.bottom_msg { let (msg_str, color) = match self.bottom_msg {
BottomMessage::Help => ( BottomMessage::Help => (
"/: Search, g: Go To Page, n: Next Search Result, N: Previous Search Result".into(), "/: Search, g: Go To Page, n: Next Search Result, N: Previous Search Result"
.to_string(),
Color::Blue Color::Blue
), ),
BottomMessage::Error(ref e) => (e.as_str().into(), Color::Red), BottomMessage::Error(ref e) => (format!("Couldn't render a page: {e}"), Color::Red),
BottomMessage::Input(ref input_state) => ( BottomMessage::Input(ref input_state) => (
match input_state { match input_state {
InputCommand::GoToPage(page) => format!("Go to: {page}"), InputCommand::GoToPage(page) => format!("Go to: {page}"),
InputCommand::Search(s) => format!("Search: {s}") InputCommand::Search(s) => format!("Search: {s}")
} },
.into(),
Color::Blue Color::Blue
), ),
BottomMessage::SearchResults(ref term) => { BottomMessage::SearchResults(ref term) => {
@@ -181,8 +181,7 @@ impl Tui {
format!( format!(
"Results for '{term}': {num_found} (searched: {}%)", "Results for '{term}': {num_found} (searched: {}%)",
num_searched / self.rendered.len() num_searched / self.rendered.len()
) ),
.into(),
Color::Blue Color::Blue
) )
} }
@@ -371,16 +370,12 @@ impl Tui {
match key.code { match key.code {
KeyCode::Char(c) => { KeyCode::Char(c) => {
// TODO: refactor back to `if let` arm guards when those are stabilized // TODO: refactor back to `if let` arm guards when those are stabilized
if let BottomMessage::Input(InputCommand::Search(ref mut term)) = if let BottomMessage::Input(InputCommand::Search(ref mut term)) = self.bottom_msg {
self.bottom_msg
{
term.push(c); term.push(c);
return Some(InputAction::Redraw); return Some(InputAction::Redraw);
} }
if let BottomMessage::Input(InputCommand::GoToPage(ref mut page)) = if let BottomMessage::Input(InputCommand::GoToPage(ref mut page)) = self.bottom_msg {
self.bottom_msg
{
return c.to_digit(10).map(|input_num| { return c.to_digit(10).map(|input_num| {
*page = (*page * 10) + input_num as usize; *page = (*page * 10) + input_num as usize;
InputAction::Redraw InputAction::Redraw
@@ -394,15 +389,13 @@ impl Tui {
'k' => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen), 'k' => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
'q' => Some(InputAction::QuitApp), 'q' => Some(InputAction::QuitApp),
'g' => { 'g' => {
self.set_bottom_msg(Some(BottomMessage::Input( self.set_bottom_msg(Some(BottomMessage::Input(InputCommand::GoToPage(0))));
InputCommand::GoToPage(0)
)));
Some(InputAction::Redraw) Some(InputAction::Redraw)
} }
'/' => { '/' => {
self.set_bottom_msg(Some(BottomMessage::Input( self.set_bottom_msg(Some(BottomMessage::Input(InputCommand::Search(
InputCommand::Search(String::new()) String::new()
))); ))));
Some(InputAction::Redraw) Some(InputAction::Redraw)
} }
'n' if self.page < self.rendered.len() - 1 => { 'n' if self.page < self.rendered.len() - 1 => {
@@ -431,29 +424,27 @@ impl Tui {
}); });
jump_to_page(&mut self.page, &mut self.last_render.rect, prev_page) jump_to_page(&mut self.page, &mut self.last_render.rect, prev_page)
} },
_ => None _ => None
} }
} },
KeyCode::Backspace => { KeyCode::Backspace => {
if let BottomMessage::Input(InputCommand::Search(ref mut term)) = if let BottomMessage::Input(InputCommand::Search(ref mut term)) = self.bottom_msg {
self.bottom_msg
{
term.pop(); term.pop();
return Some(InputAction::Redraw); return Some(InputAction::Redraw);
} }
None None
} },
KeyCode::Right => self.change_page(PageChange::Next, ChangeAmount::Single), KeyCode::Right => self.change_page(PageChange::Next, ChangeAmount::Single),
KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen), KeyCode::Down => self.change_page(PageChange::Next, ChangeAmount::WholeScreen),
KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single), KeyCode::Left => self.change_page(PageChange::Prev, ChangeAmount::Single),
KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen), KeyCode::Up => self.change_page(PageChange::Prev, ChangeAmount::WholeScreen),
KeyCode::Esc => match self.bottom_msg { KeyCode::Esc => match self.bottom_msg {
BottomMessage::Help => Some(InputAction::QuitApp), BottomMessage::Input(_) => {
_ => {
self.set_bottom_msg(None); self.set_bottom_msg(None);
Some(InputAction::Redraw) Some(InputAction::Redraw)
} }
_ => Some(InputAction::QuitApp)
}, },
KeyCode::Enter => { KeyCode::Enter => {
let BottomMessage::Input(_) = self.bottom_msg else { let BottomMessage::Input(_) = self.bottom_msg else {
@@ -472,17 +463,10 @@ impl Tui {
// Only forward the command if it's within range // Only forward the command if it's within range
InputCommand::GoToPage(page) => { InputCommand::GoToPage(page) => {
let page = *page; let page = *page;
let rendered_len = self.rendered.len(); (page < self.rendered.len()).then(|| {
if page < rendered_len {
self.set_page(page); self.set_page(page);
Some(InputAction::JumpingToPage(page)) InputAction::JumpingToPage(page)
} else { })
self.set_bottom_msg(Some(BottomMessage::Error(
format!("Cannot jump to page {page}; there are only {rendered_len} pages in the document")
)));
Some(InputAction::Redraw)
}
} }
InputCommand::Search(term) => { InputCommand::Search(term) => {
let term = term.clone(); let term = term.clone();