Skip to content

Commit

Permalink
Add setting: auto-power-off
Browse files Browse the repository at this point in the history
Defines the duration, in days, after which a suspended device will power off.

This behavior can be disabled by setting it to zero.
  • Loading branch information
baskerville committed Feb 22, 2020
1 parent 37fb5c7 commit f66579b
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 4 deletions.
36 changes: 33 additions & 3 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::view::intermission::{Intermission, IntermKind};
use crate::view::notification::Notification;
use crate::device::{CURRENT_DEVICE, Orientation, FrontlightKind, INTERNAL_CARD_ROOT};
use crate::font::Fonts;
use crate::rtc::Rtc;

pub const APP_NAME: &str = "Plato";
const INPUT_HISTORY_SIZE: usize = 32;
Expand All @@ -50,6 +51,7 @@ const PREPARE_SUSPEND_WAIT_DELAY: Duration = Duration::from_secs(3);

pub struct Context {
pub fb: Box<dyn Framebuffer>,
pub rtc: Option<Rtc>,
pub display: Display,
pub settings: Settings,
pub metadata: Metadata,
Expand All @@ -70,12 +72,12 @@ pub struct Context {
}

impl Context {
pub fn new(fb: Box<dyn Framebuffer>, settings: Settings, metadata: Metadata,
pub fn new(fb: Box<dyn Framebuffer>, rtc: Option<Rtc>, settings: Settings, metadata: Metadata,
filename: PathBuf, fonts: Fonts, battery: Box<dyn Battery>,
frontlight: Box<dyn Frontlight>, lightsensor: Box<dyn LightSensor>) -> Context {
let dims = fb.dims();
let rotation = CURRENT_DEVICE.transformed_rotation(fb.rotation());
Context { fb, display: Display { dims, rotation },
Context { fb, rtc, display: Display { dims, rotation },
settings, metadata, filename, fonts, dictionaries: BTreeMap::new(), keyboard_layouts: BTreeMap::new(),
input_history: HashMap::new(), battery, frontlight, lightsensor, notification_index: 0,
kb_rect: Rectangle::default(), plugged: false, covered: false, shared: false, online: false }
Expand Down Expand Up @@ -155,6 +157,9 @@ struct HistoryItem {
}

fn build_context(fb: Box<dyn Framebuffer>) -> Result<Context, Error> {
let rtc = Rtc::new("/dev/rtc0")
.map_err(|e| eprintln!("Can't open RTC device: {}.", e))
.ok();
let path = Path::new(SETTINGS_PATH);
let settings = load_toml::<Settings, _>(path);
let mut initial_run = false;
Expand Down Expand Up @@ -209,7 +214,7 @@ fn build_context(fb: Box<dyn Framebuffer>) -> Result<Context, Error> {
.context("Can't create premixed frontlight.")?) as Box<dyn Frontlight>,
};

Ok(Context::new(fb, settings, metadata, PathBuf::from(METADATA_FILENAME),
Ok(Context::new(fb, rtc, settings, metadata, PathBuf::from(METADATA_FILENAME),
fonts, battery, frontlight, lightsensor))
}

Expand Down Expand Up @@ -640,6 +645,13 @@ pub fn run() -> Result<(), Error> {
SUSPEND_WAIT_DELAY, &tx, &mut tasks);
},
Event::Suspend => {
if context.settings.auto_power_off > 0 {
context.rtc.iter().for_each(|rtc| {
rtc.set_alarm(context.settings.auto_power_off)
.map_err(|e| eprintln!("Can't set alarm: {}.", e))
.ok();
});
}
println!("{}", Local::now().format("Went to sleep on %B %-d, %Y at %H:%M."));
Command::new("scripts/suspend.sh")
.status()
Expand All @@ -649,6 +661,24 @@ pub fn run() -> Result<(), Error> {
.status()
.ok();
inactive_since = Instant::now();
if context.settings.auto_power_off > 0 {
if let Some(enabled) = context.rtc.as_ref()
.and_then(|rtc| rtc.is_alarm_enabled()
.map_err(|e| eprintln!("Can't get alarm: {}", e))
.ok()) {
if enabled {
context.rtc.iter().for_each(|rtc| {
rtc.disable_alarm()
.map_err(|e| eprintln!("Can't disable alarm: {}.", e))
.ok();
});
} else {
power_off(view.as_mut(), &mut history, &mut updating, &mut context);
exit_status = ExitStatus::PowerOff;
break;
}
}
}
},
Event::PrepareShare => {
if context.shared {
Expand Down
3 changes: 2 additions & 1 deletion src/emulator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod frontlight;
mod lightsensor;
mod symbolic_path;
mod trash;
mod rtc;
mod app;

use std::mem;
Expand Down Expand Up @@ -81,7 +82,7 @@ pub fn build_context(fb: Box<dyn Framebuffer>) -> Result<Context, Error> {
let frontlight = Box::new(LightLevels::default()) as Box<dyn Frontlight>;
let lightsensor = Box::new(0u16) as Box<dyn LightSensor>;
let fonts = Fonts::load()?;
Ok(Context::new(fb, settings, metadata, PathBuf::from(METADATA_FILENAME),
Ok(Context::new(fb, None, settings, metadata, PathBuf::from(METADATA_FILENAME),
fonts, battery, frontlight, lightsensor))
}

Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod dictionary;
mod document;
mod metadata;
mod symbolic_path;
mod rtc;
mod settings;
mod trash;
mod view;
Expand Down
85 changes: 85 additions & 0 deletions src/rtc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::mem;
use std::fs::File;
use std::path::Path;
use std::os::unix::io::AsRawFd;
use failure::Error;
use nix::{ioctl_read, ioctl_write_ptr, ioctl_none};
use chrono::{Duration, Utc, Datelike, Timelike};

ioctl_read!(rtc_read_alarm, b'p', 0x10, RtcWkalrm);
ioctl_write_ptr!(rtc_write_alarm, b'p', 0x0f, RtcWkalrm);
ioctl_none!(rtc_disable_alarm, b'p', 0x02);

#[repr(C)]
#[derive(Debug, Clone)]
pub struct RtcTime {
tm_sec: libc::c_int,
tm_min: libc::c_int,
tm_hour: libc::c_int,
tm_mday: libc::c_int,
tm_mon: libc::c_int,
tm_year: libc::c_int,
tm_wday: libc::c_int,
tm_yday: libc::c_int,
tm_isdst: libc::c_int,
}

impl Default for RtcWkalrm {
fn default() -> Self {
unsafe { mem::zeroed() }
}
}

#[repr(C)]
#[derive(Debug, Clone)]
pub struct RtcWkalrm {
enabled: libc::c_uchar,
pending: libc::c_uchar,
time: RtcTime,
}

pub struct Rtc(File);

impl Rtc {
pub fn new<P: AsRef<Path>>(path: P) -> Result<Rtc, Error> {
let file = File::open(path)?;
Ok(Rtc(file))
}

pub fn alarm(&self) -> Result<RtcWkalrm, Error> {
let mut rwa = RtcWkalrm::default();
unsafe {
rtc_read_alarm(self.0.as_raw_fd(), &mut rwa)
.map(|_| rwa)
.map_err(|e| e.into())
}
}

pub fn set_alarm(&self, days: u8) -> Result<i32, Error> {
let wt = Utc::now() + Duration::days(days as i64);
let rwa = RtcWkalrm {
enabled: 1,
pending: 0,
time: RtcTime {
tm_sec: wt.second() as libc::c_int,
tm_min: wt.minute() as libc::c_int,
tm_hour: wt.hour() as libc::c_int,
tm_mday: wt.day() as libc::c_int,
tm_mon: wt.month0() as libc::c_int,
tm_year: (wt.year() - 1900) as libc::c_int,
tm_wday: -1,
tm_yday: -1,
tm_isdst: -1,
},
};
unsafe { rtc_write_alarm(self.0.as_raw_fd(), &rwa).map_err(|e| e.into()) }
}

pub fn is_alarm_enabled(&self) -> Result<bool, Error> {
self.alarm().map(|rwa| rwa.enabled == 1)
}

pub fn disable_alarm(&self) -> Result<i32, Error> {
unsafe { rtc_disable_alarm(self.0.as_raw_fd()).map_err(|e| e.into()) }
}
}
2 changes: 2 additions & 0 deletions src/settings/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub struct Settings {
pub rotation_lock: Option<RotationLock>,
pub button_scheme: ButtonScheme,
pub auto_suspend: u8,
pub auto_power_off: u8,
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub intermission_images: HashMap<String, PathBuf>,
#[serde(skip_serializing_if = "Vec::is_empty")]
Expand Down Expand Up @@ -299,6 +300,7 @@ impl Default for Settings {
rotation_lock: None,
button_scheme: ButtonScheme::Natural,
auto_suspend: 30,
auto_power_off: 3,
intermission_images: HashMap::new(),
home: HomeSettings::default(),
reader: ReaderSettings::default(),
Expand Down

0 comments on commit f66579b

Please sign in to comment.