Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
330c29193f | ||
|
|
dd515787ec | ||
|
|
b11fb2d762 |
No files matched your search
Generated
+1
-1
@@ -1393,7 +1393,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ink-notes"
|
||||
version = "0.34.1"
|
||||
version = "0.34.2"
|
||||
dependencies = [
|
||||
"ab_glyph",
|
||||
"arboard",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ink-notes"
|
||||
version = "0.34.1"
|
||||
version = "0.34.2"
|
||||
edition = "2021"
|
||||
description = "Notizbuch-App für Stift, Touch und Maus unter Linux"
|
||||
repository = "https://gitea.pansi.eu/harald/ink-notes"
|
||||
|
||||
+3
-2
@@ -152,8 +152,9 @@ Die CI prüft zusätzlich `cargo fmt --check` und `cargo clippy --all-targets --
|
||||
|
||||
```text
|
||||
src/
|
||||
├── main.rs # Event-Loop (winit), Eingaben von Maus, Touch und Stift, Ablaufsteuerung
|
||||
├── lib.rs # Bibliotheks-Exporte (für Tests)
|
||||
├── main.rs # Einstieg für den Desktop, startet app::run
|
||||
├── app.rs # Event-Loop (winit), Eingaben von Maus, Touch und Stift, Ablaufsteuerung
|
||||
├── lib.rs # Bibliotheks-Exporte (App und Module, auch für Tests)
|
||||
├── canvas.rs # wgpu-Rendering: Papier, Muster, Bilder, Tinte, Nachbarseiten
|
||||
├── shader.wgsl # Vertex- und Fragment-Shader
|
||||
├── viewport.rs # Kamera (Zoom, Verschieben), Seitenstapel der durchgehenden Ansicht
|
||||
|
||||
@@ -154,8 +154,9 @@ New code comments and commit messages are written in English; older code and his
|
||||
|
||||
```text
|
||||
src/
|
||||
├── main.rs # Event loop (winit), mouse, touch and pen input, control flow
|
||||
├── lib.rs # Library exports (for tests)
|
||||
├── main.rs # Desktop entry point, starts app::run
|
||||
├── app.rs # Event loop (winit), mouse, touch and pen input, control flow
|
||||
├── lib.rs # Library exports (app and modules, also for tests)
|
||||
├── canvas.rs # wgpu rendering: paper, patterns, images, ink, neighboring pages
|
||||
├── shader.wgsl # Vertex and fragment shaders
|
||||
├── viewport.rs # Camera (zoom, pan), page stack of the continuous view, swipe momentum
|
||||
|
||||
@@ -15,7 +15,7 @@ ink-notes ist ein einzelnes Programm mit einem Event-Loop (`winit`) im Haupt-Thr
|
||||
| Haupt-Thread | Eingaben, Datenmodell, Rendering (`wgpu`), Oberfläche (`egui`) |
|
||||
| Autosave | Seiten und Manifest auf die Festplatte schreiben (`autosave.rs`) |
|
||||
| PDF-Rendering | Importierte PDF-Seiten zu Texturen rendern (`pdf_doc.rs`, `hayro`) |
|
||||
| Export | PDF/PNG/SVG-Export, damit die Oberfläche nicht hängt (`main.rs`) |
|
||||
| Export | PDF/PNG/SVG-Export, damit die Oberfläche nicht hängt (`app.rs`) |
|
||||
|
||||
Stifteingaben kommen über `octotablet` (Tablet-Protokoll von Wayland bzw. X11), Maus und Touch über `winit`. Die Oberfläche (Werkzeugleiste, Menüs, Dialoge, Textbearbeitung) ist mit `egui` gebaut und wird im selben Render-Durchgang über das Blatt gezeichnet.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ ink-notes is a single program with an event loop (`winit`) on the main thread, p
|
||||
| Main thread | Input, data model, rendering (`wgpu`), interface (`egui`) |
|
||||
| Autosave | Writes pages and manifest to disk (`autosave.rs`) |
|
||||
| PDF rendering | Renders imported PDF pages to textures (`pdf_doc.rs`, `hayro`) |
|
||||
| Export | PDF/PNG/SVG export, so the interface does not freeze (`main.rs`) |
|
||||
| Export | PDF/PNG/SVG export, so the interface does not freeze (`app.rs`) |
|
||||
|
||||
Pen input arrives via `octotablet` (the Wayland or X11 tablet protocol), mouse and touch via `winit`. The interface (toolbar, menus, dialogs, text editing) is built with `egui` and drawn over the page in the same render pass.
|
||||
|
||||
|
||||
@@ -84,6 +84,11 @@
|
||||
</branding>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="0.34.2" date="2026-10-26">
|
||||
<description>
|
||||
<p>The swipe speed for gliding is measured with the touch screen's own event times, so a swipe still glides when the app handles the lift a little late.</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.34.1" date="2026-10-25">
|
||||
<description>
|
||||
<p>A finger swipe is measured reliably again for gliding, also when the view touches the edge of the notebook. The fling diagnostics log more details.</p>
|
||||
|
||||
@@ -84,6 +84,11 @@
|
||||
</branding>
|
||||
<content_rating type="oars-1.1" />
|
||||
<releases>
|
||||
<release version="0.34.2" date="2026-10-26">
|
||||
<description>
|
||||
<p>The swipe speed for gliding is measured with the touch screen's own event times, so a swipe still glides when the app handles the lift a little late.</p>
|
||||
</description>
|
||||
</release>
|
||||
<release version="0.34.1" date="2026-10-25">
|
||||
<description>
|
||||
<p>A finger swipe is measured reliably again for gliding, also when the view touches the edge of the notebook. The fling diagnostics log more details.</p>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
Notebook app for pen, touch and mouse on Linux (tested on Zorin OS 18 / Ubuntu 24.04).
|
||||
|
||||
### Highlights:
|
||||
- **Fling timing**: The swipe speed for gliding is measured with the touch screen's own event times (Wayland), so a fast swipe glides even when the lift is handled late; the fling diagnostics also report slow event handling
|
||||
- **Fling fix**: A finger swipe is no longer lost when the view touches the edge of the notebook, so the page glides on after a fast swipe; the fling diagnostics (`INK_NOTES_DEBUG_FLING=1`) log moves, distance and timing
|
||||
- **Feedback round**: The eraser size is set in a menu like the pens (four presets and a slider); corners of snapped triangles and polygons stay pointed; slides (landscape PDFs) get flat tiles in the page overview without an empty strip
|
||||
- **Palm rejection & more feedback**: A hand resting on the screen while the pen is near is ignored (also on the title bar, no more accidental paste menu or window moving); the eraser has four sizes (button below the eraser modes, remembered); the page overview shows imported PDF pages; snapped triangles, rectangles and polygons have the same width on every side; a selection made with the pen's select button can be moved with the bare pen
|
||||
|
||||
+4352
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -156,7 +156,7 @@ mod tests {
|
||||
("file_picker.rs", include_str!("file_picker.rs")),
|
||||
("geometry.rs", include_str!("geometry.rs")),
|
||||
("library.rs", include_str!("library.rs")),
|
||||
("main.rs", include_str!("main.rs")),
|
||||
("app.rs", include_str!("app.rs")),
|
||||
("page_export.rs", include_str!("page_export.rs")),
|
||||
("pdf_doc.rs", include_str!("pdf_doc.rs")),
|
||||
("pdf_merge.rs", include_str!("pdf_merge.rs")),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! ink-notes: Latenzoptimierte Inking-App für Linux.
|
||||
|
||||
pub mod app;
|
||||
pub mod assets;
|
||||
pub mod autosave;
|
||||
pub mod background;
|
||||
|
||||
+3
-4272
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -2385,7 +2385,7 @@ mod tests {
|
||||
create_new_notebook(temp_dir.path(), "Neue Seiten").unwrap();
|
||||
let mut autosave = AutosaveController::new(dir.clone(), Duration::from_secs(60));
|
||||
|
||||
// Wie Taste N in main.rs: neue Seite anhängen, Strich darauf, speichern
|
||||
// Wie Taste N in app.rs: neue Seite anhängen, Strich darauf, speichern
|
||||
notebook.next_page();
|
||||
let idx = notebook.current_page_index;
|
||||
notebook.pages[idx] = page_with_one_stroke(notebook.pages[idx].id, PageBackground::Lined);
|
||||
|
||||
@@ -6133,7 +6133,7 @@ mod tests {
|
||||
const UI_SOURCES: [(&str, &str); 4] = [
|
||||
("ui.rs", include_str!("ui.rs")),
|
||||
("selection.rs", include_str!("selection.rs")),
|
||||
("main.rs", include_str!("main.rs")),
|
||||
("app.rs", include_str!("app.rs")),
|
||||
("file_picker.rs", include_str!("file_picker.rs")),
|
||||
];
|
||||
|
||||
|
||||
@@ -389,6 +389,14 @@ impl Fling {
|
||||
velocity
|
||||
}
|
||||
|
||||
/// The glide continues from `now` (after [`Self::release`] with the time the finger was
|
||||
/// lifted, which may lie a little in the past).
|
||||
pub fn restart_clock(&mut self, now: std::time::Instant) {
|
||||
if self.active {
|
||||
self.last = Some(now);
|
||||
}
|
||||
}
|
||||
|
||||
/// Hält an (neue Berührung, Mausklick, Mausrad).
|
||||
pub fn stop(&mut self) {
|
||||
*self = Self::default();
|
||||
@@ -430,6 +438,25 @@ mod tests {
|
||||
PageStack::new(vec![A4; count], current)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fling_measures_with_event_times_handled_late() {
|
||||
use std::time::{Duration, Instant};
|
||||
let t0 = Instant::now();
|
||||
let ms = |n: u64| t0 + Duration::from_millis(n);
|
||||
let mut fling = Fling::default();
|
||||
// The finger moved until 100 ms and was lifted at 105 ms; the app only got to the
|
||||
// lift 300 ms later, so the glide starts from there
|
||||
for i in 0..=10 {
|
||||
fling.track(Vec2::new(0.0, -30.0), ms(i * 10));
|
||||
}
|
||||
let velocity = fling.release(ms(105));
|
||||
assert!(velocity.y < -2000.0, "{velocity:?}");
|
||||
assert!(fling.is_active());
|
||||
fling.restart_clock(ms(405));
|
||||
let first = fling.step(ms(421)).unwrap();
|
||||
assert!(first.y < -20.0 && first.y > -60.0, "{first:?}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fling_glides_and_slows_down() {
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
Vendored
+4
@@ -14,6 +14,10 @@ docs of the original crate were removed.
|
||||
- `src/platform/wayland.rs`: `ignore_decoration_touches_until` lets ink-notes ignore touches on
|
||||
the title bar while the pen is near the screen, so a resting palm does not move the window
|
||||
(Issue #39).
|
||||
- `src/platform/wayland.rs` and the touch handler: `take_touch_time` returns the compositor
|
||||
timestamp of each touch event (Started, Moved, Ended), so ink-notes measures the swipe speed
|
||||
for gliding with the real event times and not with the time it got around to handling them
|
||||
(Issue #28).
|
||||
- `Cargo.toml`: `[lints.rust] warnings = "allow"`, because a path dependency is not built with
|
||||
capped lints like a crates.io dependency.
|
||||
|
||||
|
||||
Vendored
+30
@@ -28,6 +28,36 @@ pub(crate) fn decoration_touches_ignored() -> bool {
|
||||
.is_some_and(|until| std::time::Instant::now() < until)
|
||||
}
|
||||
|
||||
/// ink-notes patch: compositor timestamps (ms) of the touch events not yet taken, in event order.
|
||||
static TOUCH_TIMES: std::sync::Mutex<
|
||||
std::collections::VecDeque<(u64, crate::event::TouchPhase, u32)>,
|
||||
> = std::sync::Mutex::new(std::collections::VecDeque::new());
|
||||
|
||||
/// ink-notes patch: remembers the compositor time of a touch event that is about to be sent.
|
||||
pub(crate) fn push_touch_time(id: u64, phase: crate::event::TouchPhase, time: u32) {
|
||||
let mut times = TOUCH_TIMES.lock().unwrap_or_else(|e| e.into_inner());
|
||||
// Nobody takes them (e.g. an app that never asks): keep only the latest
|
||||
if times.len() >= 256 {
|
||||
times.pop_front();
|
||||
}
|
||||
times.push_back((id, phase, time));
|
||||
}
|
||||
|
||||
/// ink-notes patch: compositor time in milliseconds (arbitrary origin, wraps around) of the
|
||||
/// touch event with this id and phase (`Started`, `Moved` or `Ended`; `Cancelled` has none),
|
||||
/// for the event currently being handled. Call it once for every such `WindowEvent::Touch`.
|
||||
/// `None` on X11 or if the time is unknown.
|
||||
pub fn take_touch_time(id: u64, phase: crate::event::TouchPhase) -> Option<u32> {
|
||||
let mut times = TOUCH_TIMES.lock().unwrap_or_else(|e| e.into_inner());
|
||||
// Older entries that were never taken are skipped
|
||||
while let Some((i, p, time)) = times.pop_front() {
|
||||
if i == id && p == phase {
|
||||
return Some(time);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Additional methods on [`EventLoopWindowTarget`] that are specific to Wayland.
|
||||
pub trait EventLoopWindowTargetExtWayland {
|
||||
/// True if the [`EventLoopWindowTarget`] uses Wayland.
|
||||
|
||||
@@ -83,6 +83,9 @@ impl TouchHandler for WinitState {
|
||||
.touch_map
|
||||
.insert(id, TouchPoint { surface, location, frame_of: None });
|
||||
|
||||
// ink-notes patch: keep the event time for `take_touch_time`
|
||||
crate::platform::wayland::push_touch_time(id as u64, TouchPhase::Started, time);
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::Touch(Touch {
|
||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||
@@ -138,6 +141,8 @@ impl TouchHandler for WinitState {
|
||||
None => return,
|
||||
};
|
||||
|
||||
// ink-notes patch: keep the event time for `take_touch_time`
|
||||
crate::platform::wayland::push_touch_time(id as u64, TouchPhase::Ended, time);
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::Touch(Touch {
|
||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||
@@ -157,7 +162,7 @@ impl TouchHandler for WinitState {
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
touch: &WlTouch,
|
||||
_: u32,
|
||||
time: u32,
|
||||
id: i32,
|
||||
position: (f64, f64),
|
||||
) {
|
||||
@@ -192,6 +197,9 @@ impl TouchHandler for WinitState {
|
||||
|
||||
touch_point.location = LogicalPosition::<f64>::from(position);
|
||||
|
||||
// ink-notes patch: keep the event time for `take_touch_time`
|
||||
crate::platform::wayland::push_touch_time(id as u64, TouchPhase::Moved, time);
|
||||
|
||||
self.events_sink.push_window_event(
|
||||
WindowEvent::Touch(Touch {
|
||||
device_id: crate::event::DeviceId(crate::platform_impl::DeviceId::Wayland(
|
||||
|
||||
Reference in new issue
Block a user