Skip to content
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

appctx: New event loop usage #87

Merged
merged 5 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
84 changes: 84 additions & 0 deletions examples/basic_draw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//! This example is a very basic drawing application. Marker can draw on the
//! screen (without tilt or pressure sensitivity) and Marker Plus can use the
//! eraser end to erase.
//!
//! Drawing is done in the framebuffer without any caching, so it's not possible
//! to save the results to file, zoom or pan, etc. There are also no GUI
//! elements or interactivity other than the pen.
//!
//! The new event loop design makes this type of application very easy to make.

use libremarkable::appctx::ApplicationContext;
use libremarkable::framebuffer::{FramebufferDraw, FramebufferRefresh};
use libremarkable::framebuffer::common::{color, display_temp, dither_mode, DRAWING_QUANT_BIT, waveform_mode};
use libremarkable::framebuffer::refresh::PartialRefreshMode;
use libremarkable::input::InputEvent;
use libremarkable::input::wacom::{WacomEvent, WacomPen};

fn main() {
let mut app = ApplicationContext::new();

let mut tool_pen = false;
let mut tool_rubber = false;

app.clear(true);
app.start_event_loop(true, false, false, |ctx, event| {
if let InputEvent::WacomEvent { event } = event {
match event {
// The pen can have any number of attributes assigned to it at a
// time. For example, when drawing with the tip of the Marker,
// both the ToolPen and Touch attributes are applied. When
// drawing with the eraser end of the Marker Plus, the
// ToolRubber attribute is applied instead of ToolPen.
//
// The Tool attributes are mutually exclusive in practice, but
// the protocol technically allows them to overlap. The Touch,
// Stylus and Stylus2 events correspond to touching the display,
// pressing the first button and pressing the second button
// respectively. Markers don't have buttons but some Wacom pens
// do.
WacomEvent::InstrumentChange { pen, state } => {
eprintln!("pen {:?} state {}", pen, state);

let ptr = match pen {
WacomPen::ToolPen => Some(&mut tool_pen),
WacomPen::ToolRubber => Some(&mut tool_rubber),
_ => None
};

if let Some(ptr) = ptr { *ptr = state; }
}

WacomEvent::Draw { position, .. } => {
eprintln!("drawing at {:?}", position);

let fb = ctx.get_framebuffer_ref();

let radcolor = if tool_rubber {
// 32 is about (>=) the physical radius of the eraser
(32, color::WHITE)
} else {
(4, color::BLACK)
};

let region = fb.fill_circle((position.x.floor() as i32, position.y.floor() as i32).into(), radcolor.0, radcolor.1);

fb.partial_refresh(
&region,
PartialRefreshMode::Async,
// DU mode only supports black and white colors.
// See the documentation of the different waveform modes
// for more information
waveform_mode::WAVEFORM_MODE_DU,
display_temp::TEMP_USE_REMARKABLE_DRAW,
dither_mode::EPDC_FLAG_EXP1,
DRAWING_QUANT_BIT,
false
);
}

_ => {}
}
}
});
}
13 changes: 10 additions & 3 deletions examples/demo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use libremarkable::framebuffer::refresh::PartialRefreshMode;
use libremarkable::framebuffer::storage;
use libremarkable::framebuffer::{FramebufferDraw, FramebufferIO, FramebufferRefresh};
use libremarkable::image::GenericImage;
use libremarkable::input::{gpio, multitouch, wacom, InputDevice};
use libremarkable::input::{gpio, multitouch, wacom, InputDevice, InputEvent};
use libremarkable::ui_extensions::element::{
UIConstraintRefresh, UIElement, UIElementHandle, UIElementWrapper,
};
Expand Down Expand Up @@ -678,7 +678,7 @@ fn main() {
// Takes callback functions as arguments
// They are called with the event and the &mut framebuffer
let mut app: appctx::ApplicationContext<'_> =
appctx::ApplicationContext::new(on_button_press, on_wacom_input, on_touch_handler);
appctx::ApplicationContext::new();

// Alternatively we could have called `app.execute_lua("fb.clear()")`
app.clear(true);
Expand Down Expand Up @@ -1215,6 +1215,13 @@ fn main() {
info!("Init complete. Beginning event dispatch...");

// Blocking call to process events from digitizer + touchscreen + physical buttons
app.dispatch_events(true, true, true);
app.start_event_loop(true, true, true, |ctx, evt| {
match evt {
InputEvent::WacomEvent { event } => on_wacom_input(ctx, event),
InputEvent::MultitouchEvent { event } => on_touch_handler(ctx, event),
InputEvent::GPIO { event } => on_button_press(ctx, event),
_ => {}
}
});
clock_thread.join().unwrap();
}
60 changes: 16 additions & 44 deletions src/appctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ use crate::framebuffer::FramebufferBase;
use crate::framebuffer::FramebufferDraw;
use crate::framebuffer::FramebufferRefresh;
use crate::input::ev;
use crate::input::gpio::GPIOEvent;
use crate::input::multitouch::MultitouchEvent;
use crate::input::wacom::WacomEvent;
use crate::input::{InputDevice, InputEvent};
use crate::ui_extensions::element::{
ActiveRegionFunction, ActiveRegionHandler, UIConstraintRefresh, UIElementHandle,
Expand All @@ -42,13 +40,8 @@ pub struct ApplicationContext<'a> {
input_rx: std::sync::mpsc::Receiver<InputEvent>,

button_ctx: RwLock<Option<ev::EvDevContext>>,
on_button: fn(&mut ApplicationContext<'_>, GPIOEvent),

wacom_ctx: RwLock<Option<ev::EvDevContext>>,
on_wacom: fn(&mut ApplicationContext<'_>, WacomEvent),

touch_ctx: RwLock<Option<ev::EvDevContext>>,
on_touch: fn(&mut ApplicationContext<'_>, MultitouchEvent),

active_regions: QuadTree<ActiveRegionHandler>,
ui_elements: HashMap<String, UIElementHandle>,
Expand Down Expand Up @@ -81,11 +74,7 @@ impl<'a> ApplicationContext<'a> {
(self.yres, self.xres)
}

pub fn new(
on_button: fn(&mut ApplicationContext<'_>, GPIOEvent),
on_wacom: fn(&mut ApplicationContext<'_>, WacomEvent),
on_touch: fn(&mut ApplicationContext<'_>, MultitouchEvent),
) -> ApplicationContext<'static> {
pub fn new() -> ApplicationContext<'static> {
let framebuffer = Box::new(core::Framebuffer::from_path(
crate::device::CURRENT_DEVICE.get_framebuffer_path(),
));
Expand All @@ -104,9 +93,6 @@ impl<'a> ApplicationContext<'a> {
lua: UnsafeCell::new(Lua::new()),
input_rx,
input_tx,
on_button,
on_wacom,
on_touch,
ui_elements: HashMap::new(),
active_regions: QuadTree::default(geom::Rect::from_points(
&geom::Point { x: 0.0, y: 0.0 },
Expand Down Expand Up @@ -463,11 +449,12 @@ impl<'a> ApplicationContext<'a> {
&self.input_rx
}

pub fn dispatch_events(
pub fn start_event_loop<F: FnMut(&mut ApplicationContext<'_>, InputEvent)>(
&mut self,
activate_wacom: bool,
activate_multitouch: bool,
activate_buttons: bool,
mut callback: F
) {
let appref = self.upgrade_ref();

Expand All @@ -486,13 +473,11 @@ impl<'a> ApplicationContext<'a> {

let mut last_active_region_gesture_id: i32 = -1;
while self.running.load(Ordering::Relaxed) {
match self.input_rx.recv() {
let event = self.input_rx.recv();
match event {
Err(e) => println!("Error in input event consumer: {0}", e),
Ok(event) => match event {
InputEvent::GPIO { event } => {
(self.on_button)(appref, event);
}
InputEvent::MultitouchEvent { event } => {
Ok(event) => {
if let InputEvent::MultitouchEvent { event } = event {
// Check for and notify clickable active regions for multitouch events
if let MultitouchEvent::Press { finger }
| MultitouchEvent::Move { finger } = event
Expand All @@ -507,12 +492,9 @@ impl<'a> ApplicationContext<'a> {
last_active_region_gesture_id = gseq;
}
}
(self.on_touch)(appref, event);
}
InputEvent::WacomEvent { event } => {
(self.on_wacom)(appref, event);
}
_ => {}

callback(appref, event);
},
};
}
Expand All @@ -525,25 +507,15 @@ impl<'a> ApplicationContext<'a> {
self.running.store(true, Ordering::Relaxed);

if self.running.load(Ordering::Relaxed) {
match event {
InputEvent::GPIO { event } => {
(self.on_button)(appref, event);
}
InputEvent::MultitouchEvent { event } => {
// Check for and notify clickable active regions for multitouch events
if let MultitouchEvent::Press { finger } | MultitouchEvent::Move { finger } =
event
{
if let Some((h, _)) = self.find_active_region(finger.pos.y, finger.pos.x) {
(h.handler)(appref, h.element.clone());
}
if let InputEvent::MultitouchEvent { event } = event {
// Check for and notify clickable active regions for multitouch events
if let MultitouchEvent::Press { finger } | MultitouchEvent::Move { finger } =
event
{
if let Some((h, _)) = self.find_active_region(finger.pos.y, finger.pos.x) {
(h.handler)(appref, h.element.clone());
}
(self.on_touch)(appref, event);
}
InputEvent::WacomEvent { event } => {
(self.on_wacom)(appref, event);
}
_ => {}
}
}
}
Expand Down