Rustfmt.
This commit is contained in:
parent
b10886508c
commit
35a8739f27
10 changed files with 511 additions and 493 deletions
162
src/alarm.rs
162
src/alarm.rs
|
@ -1,20 +1,19 @@
|
||||||
use std::io::Write;
|
|
||||||
use std::process::{Command, Stdio, Child};
|
|
||||||
use std::io::BufRead;
|
|
||||||
use termion::{color, cursor, style};
|
|
||||||
use termion::raw::RawTerminal;
|
|
||||||
use unicode_width::UnicodeWidthStr;
|
|
||||||
use crate::Config;
|
|
||||||
use crate::clock::Clock;
|
use crate::clock::Clock;
|
||||||
|
use crate::consts::{COLOR, LABEL_SIZE_LIMIT};
|
||||||
use crate::layout::{Layout, Position};
|
use crate::layout::{Layout, Position};
|
||||||
use crate::utils::*;
|
use crate::utils::*;
|
||||||
use crate::consts::{COLOR, LABEL_SIZE_LIMIT};
|
use crate::Config;
|
||||||
|
use std::io::BufRead;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::process::{Child, Command, Stdio};
|
||||||
|
use termion::raw::RawTerminal;
|
||||||
|
use termion::{color, cursor, style};
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
// Delimiter between time and label. Remember to update usage information in
|
// Delimiter between time and label. Remember to update usage information in
|
||||||
// consts.rs when changing this.
|
// consts.rs when changing this.
|
||||||
const DELIMITER: char = '/';
|
const DELIMITER: char = '/';
|
||||||
|
|
||||||
|
|
||||||
pub struct Countdown {
|
pub struct Countdown {
|
||||||
pub value: u32,
|
pub value: u32,
|
||||||
position: Option<Position>,
|
position: Option<Position>,
|
||||||
|
@ -42,17 +41,17 @@ impl Countdown {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw countdown.
|
// Draw countdown.
|
||||||
pub fn draw<W: Write>(&self, stdout: &mut RawTerminal<W>)
|
pub fn draw<W: Write>(&self, stdout: &mut RawTerminal<W>) -> Result<(), std::io::Error> {
|
||||||
-> Result<(), std::io::Error>
|
|
||||||
{
|
|
||||||
if let Some(pos) = &self.position {
|
if let Some(pos) = &self.position {
|
||||||
if self.value < 3600 {
|
if self.value < 3600 {
|
||||||
// Show minutes and seconds.
|
// Show minutes and seconds.
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}(-{:02}:{:02})",
|
"{}(-{:02}:{:02})",
|
||||||
cursor::Goto(pos.col, pos.line),
|
cursor::Goto(pos.col, pos.line),
|
||||||
(self.value / 60) % 60,
|
(self.value / 60) % 60,
|
||||||
self.value % 60)?;
|
self.value % 60
|
||||||
|
)?;
|
||||||
if self.value == 3599 {
|
if self.value == 3599 {
|
||||||
// Write three additional spaces after switching from hour display to
|
// Write three additional spaces after switching from hour display to
|
||||||
// minute display.
|
// minute display.
|
||||||
|
@ -60,29 +59,22 @@ impl Countdown {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Show hours, minutes and seconds.
|
// Show hours, minutes and seconds.
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}(-{:02}:{:02}:{:02})",
|
"{}(-{:02}:{:02}:{:02})",
|
||||||
cursor::Goto(pos.col, pos.line),
|
cursor::Goto(pos.col, pos.line),
|
||||||
self.value / 3600,
|
self.value / 3600,
|
||||||
(self.value / 60) % 60,
|
(self.value / 60) % 60,
|
||||||
self.value % 60)?;
|
self.value % 60
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn place(
|
pub fn place(&mut self, layout: &Layout, alarm: &Alarm, offset: usize, index: usize) {
|
||||||
&mut self,
|
|
||||||
layout: &Layout,
|
|
||||||
alarm: &Alarm,
|
|
||||||
offset: usize,
|
|
||||||
index: usize,
|
|
||||||
) {
|
|
||||||
// Compute position.
|
// Compute position.
|
||||||
let mut col =
|
let mut col = layout.roster.col + 3 + UnicodeWidthStr::width(alarm.label.as_str()) as u16;
|
||||||
layout.roster.col
|
|
||||||
+ 3
|
|
||||||
+ UnicodeWidthStr::width(alarm.label.as_str()) as u16;
|
|
||||||
let mut line = layout.roster.line + index as u16;
|
let mut line = layout.roster.line + index as u16;
|
||||||
|
|
||||||
// Compensate for "hidden" items in the alarm roster.
|
// Compensate for "hidden" items in the alarm roster.
|
||||||
|
@ -176,8 +168,12 @@ impl AlarmRoster {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip if time is out of boundaries.
|
// Skip if time is out of boundaries.
|
||||||
if time == 0 { return Err("Evaluates to zero.") };
|
if time == 0 {
|
||||||
if time >= 24 * 60 * 60 { return Err("Values >24h not supported.") };
|
return Err("Evaluates to zero.");
|
||||||
|
};
|
||||||
|
if time >= 24 * 60 * 60 {
|
||||||
|
return Err("Values >24h not supported.");
|
||||||
|
};
|
||||||
// Filter out double entries.
|
// Filter out double entries.
|
||||||
if self.list.iter().any(|a| a.time == time) {
|
if self.list.iter().any(|a| a.time == time) {
|
||||||
return Err("Already exists.");
|
return Err("Already exists.");
|
||||||
|
@ -208,7 +204,11 @@ impl AlarmRoster {
|
||||||
|
|
||||||
// Offset ceiling according to layout information.
|
// Offset ceiling according to layout information.
|
||||||
fn adjust_offset(&mut self, layout: &Layout) {
|
fn adjust_offset(&mut self, layout: &Layout) {
|
||||||
self.offset = self.offset.min(self.list.len().saturating_sub(layout.roster_height as usize));
|
self.offset = self.offset.min(
|
||||||
|
self.list
|
||||||
|
.len()
|
||||||
|
.saturating_sub(layout.roster_height as usize),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for active alarms.
|
// Check for active alarms.
|
||||||
|
@ -217,30 +217,38 @@ impl AlarmRoster {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_up(&mut self, layout: &Layout) {
|
pub fn scroll_up(&mut self, layout: &Layout) {
|
||||||
let excess = self.list.len().saturating_sub(layout.roster_height as usize);
|
let excess = self
|
||||||
|
.list
|
||||||
|
.len()
|
||||||
|
.saturating_sub(layout.roster_height as usize);
|
||||||
self.offset = excess.min(self.offset.saturating_sub(1));
|
self.offset = excess.min(self.offset.saturating_sub(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_down(&mut self, layout: &Layout) {
|
pub fn scroll_down(&mut self, layout: &Layout) {
|
||||||
let excess = self.list.len().saturating_sub(layout.roster_height as usize);
|
let excess = self
|
||||||
|
.list
|
||||||
|
.len()
|
||||||
|
.saturating_sub(layout.roster_height as usize);
|
||||||
self.offset = excess.min(self.offset.saturating_add(1));
|
self.offset = excess.min(self.offset.saturating_add(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and process exceeded alarms.
|
// Find and process exceeded alarms.
|
||||||
pub fn check(&mut self,
|
pub fn check(
|
||||||
|
&mut self,
|
||||||
clock: &mut Clock,
|
clock: &mut Clock,
|
||||||
layout: &Layout,
|
layout: &Layout,
|
||||||
countdown: &mut Countdown,
|
countdown: &mut Countdown,
|
||||||
force_redraw: bool,
|
force_redraw: bool,
|
||||||
) -> Option<&Alarm>
|
) -> Option<&Alarm> {
|
||||||
{
|
|
||||||
let mut ret = None;
|
let mut ret = None;
|
||||||
|
|
||||||
for (index, alarm) in self.list.iter_mut()
|
for (index, alarm) in self
|
||||||
|
.list
|
||||||
|
.iter_mut()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
// Ignore alarms marked exceeded.
|
// Ignore alarms marked exceeded.
|
||||||
.filter(|(_, a)| !a.exceeded) {
|
.filter(|(_, a)| !a.exceeded)
|
||||||
|
{
|
||||||
if alarm.time <= clock.elapsed {
|
if alarm.time <= clock.elapsed {
|
||||||
// Found alarm to raise.
|
// Found alarm to raise.
|
||||||
alarm.exceeded = true;
|
alarm.exceeded = true;
|
||||||
|
@ -267,35 +275,41 @@ impl AlarmRoster {
|
||||||
stdout: &mut RawTerminal<W>,
|
stdout: &mut RawTerminal<W>,
|
||||||
layout: &mut Layout,
|
layout: &mut Layout,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
) -> Result<(), std::io::Error>
|
) -> Result<(), std::io::Error> {
|
||||||
{
|
|
||||||
// Match offset to layout.
|
// Match offset to layout.
|
||||||
self.adjust_offset(&layout);
|
self.adjust_offset(&layout);
|
||||||
|
|
||||||
for (i, alarm) in self.list.iter()
|
for (i, alarm) in self.list.iter().skip(self.offset).enumerate() {
|
||||||
.skip(self.offset)
|
|
||||||
.enumerate()
|
|
||||||
{
|
|
||||||
// Add 1 to compensate for the line "[...]".
|
// Add 1 to compensate for the line "[...]".
|
||||||
let line = layout.roster.line + i as u16;
|
let line = layout.roster.line + i as u16;
|
||||||
|
|
||||||
if self.offset > 0 && i == 0 {
|
if self.offset > 0 && i == 0 {
|
||||||
// Indicate hidden items at top.
|
// Indicate hidden items at top.
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{}",
|
"{}{}{}{}",
|
||||||
cursor::Goto(layout.roster.col, line),
|
cursor::Goto(layout.roster.col, line),
|
||||||
style::Faint,
|
style::Faint,
|
||||||
if config.fancy { "╶╴▲╶╴" } else { "[ ^ ]" },
|
if config.fancy {
|
||||||
|
"╶╴▲╶╴"
|
||||||
|
} else {
|
||||||
|
"[ ^ ]"
|
||||||
|
},
|
||||||
style::Reset,
|
style::Reset,
|
||||||
)?;
|
)?;
|
||||||
continue;
|
continue;
|
||||||
} else if i == layout.roster_height as usize {
|
} else if i == layout.roster_height as usize {
|
||||||
// Indicate hidden items at bottom.
|
// Indicate hidden items at bottom.
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{}",
|
"{}{}{}{}",
|
||||||
cursor::Goto(layout.roster.col, line),
|
cursor::Goto(layout.roster.col, line),
|
||||||
style::Faint,
|
style::Faint,
|
||||||
if config.fancy { "╶╴▼╶╴" } else { "[ v ]" },
|
if config.fancy {
|
||||||
|
"╶╴▼╶╴"
|
||||||
|
} else {
|
||||||
|
"[ v ]"
|
||||||
|
},
|
||||||
style::Reset,
|
style::Reset,
|
||||||
)?;
|
)?;
|
||||||
break;
|
break;
|
||||||
|
@ -303,7 +317,8 @@ impl AlarmRoster {
|
||||||
|
|
||||||
match alarm.exceeded {
|
match alarm.exceeded {
|
||||||
true if config.fancy => {
|
true if config.fancy => {
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{} {} {}🭬{}{}",
|
"{}{}{}{} {} {}🭬{}{}",
|
||||||
cursor::Goto(layout.roster.col, line),
|
cursor::Goto(layout.roster.col, line),
|
||||||
color::Fg(COLOR[alarm.color_index]),
|
color::Fg(COLOR[alarm.color_index]),
|
||||||
|
@ -314,18 +329,20 @@ impl AlarmRoster {
|
||||||
color::Fg(color::Reset),
|
color::Fg(color::Reset),
|
||||||
style::Reset,
|
style::Reset,
|
||||||
)?;
|
)?;
|
||||||
},
|
}
|
||||||
false if config.fancy => {
|
false if config.fancy => {
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}█🭬{}{}",
|
"{}{}█🭬{}{}",
|
||||||
cursor::Goto(layout.roster.col, line),
|
cursor::Goto(layout.roster.col, line),
|
||||||
color::Fg(COLOR[alarm.color_index]),
|
color::Fg(COLOR[alarm.color_index]),
|
||||||
color::Fg(color::Reset),
|
color::Fg(color::Reset),
|
||||||
&alarm.label,
|
&alarm.label,
|
||||||
)?;
|
)?;
|
||||||
},
|
}
|
||||||
true => {
|
true => {
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{} {} {}{}",
|
"{}{}{}{} {} {}{}",
|
||||||
cursor::Goto(layout.roster.col, line),
|
cursor::Goto(layout.roster.col, line),
|
||||||
color::Fg(COLOR[alarm.color_index]),
|
color::Fg(COLOR[alarm.color_index]),
|
||||||
|
@ -335,16 +352,17 @@ impl AlarmRoster {
|
||||||
color::Fg(color::Reset),
|
color::Fg(color::Reset),
|
||||||
style::Reset,
|
style::Reset,
|
||||||
)?;
|
)?;
|
||||||
},
|
}
|
||||||
false => {
|
false => {
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{} {} {}",
|
"{}{} {} {}",
|
||||||
cursor::Goto(layout.roster.col, line),
|
cursor::Goto(layout.roster.col, line),
|
||||||
color::Bg(COLOR[alarm.color_index]),
|
color::Bg(COLOR[alarm.color_index]),
|
||||||
color::Bg(color::Reset),
|
color::Bg(color::Reset),
|
||||||
&alarm.label,
|
&alarm.label,
|
||||||
)?;
|
)?;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -355,10 +373,16 @@ impl AlarmRoster {
|
||||||
let mut width: u16 = 0;
|
let mut width: u16 = 0;
|
||||||
for alarm in &self.list {
|
for alarm in &self.list {
|
||||||
let length = UnicodeWidthStr::width(alarm.label.as_str()) as u16;
|
let length = UnicodeWidthStr::width(alarm.label.as_str()) as u16;
|
||||||
if length > width { width = length };
|
if length > width {
|
||||||
|
width = length
|
||||||
|
};
|
||||||
}
|
}
|
||||||
// Actual width is 4 columns wider if it's not 0.
|
// Actual width is 4 columns wider if it's not 0.
|
||||||
if width == 0 { 0 } else { width.saturating_add(4) }
|
if width == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
width.saturating_add(4)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset every alarm.
|
// Reset every alarm.
|
||||||
|
@ -383,19 +407,14 @@ impl AlarmRoster {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read alarm times from stdin.
|
// Read alarm times from stdin.
|
||||||
pub fn from_stdin(&mut self, stdin: std::io::Stdin)
|
pub fn from_stdin(&mut self, stdin: std::io::Stdin) -> Result<(), String> {
|
||||||
-> Result<(), String>
|
|
||||||
{
|
|
||||||
for line in stdin.lock().lines() {
|
for line in stdin.lock().lines() {
|
||||||
match line {
|
match line {
|
||||||
Ok(line)
|
Ok(line) if !line.starts_with('#') && !line.trim().is_empty() => {
|
||||||
if !line.starts_with('#')
|
|
||||||
&& !line.trim().is_empty()
|
|
||||||
=> {
|
|
||||||
if let Err(e) = self.add(&line) {
|
if let Err(e) = self.add(&line) {
|
||||||
return Err(format!("Value \"{}\": {}", line, e));
|
return Err(format!("Value \"{}\": {}", line, e));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Ok(_) => (), // Discard comments and empty lines.
|
Ok(_) => (), // Discard comments and empty lines.
|
||||||
Err(e) => return Err(e.to_string()),
|
Err(e) => return Err(e.to_string()),
|
||||||
}
|
}
|
||||||
|
@ -409,7 +428,12 @@ pub fn exec_command(command: &Vec<String>, elapsed: u32, label: &String) -> Opti
|
||||||
let time = if elapsed < 3600 {
|
let time = if elapsed < 3600 {
|
||||||
format!("{:02}:{:02}", elapsed / 60, elapsed % 60)
|
format!("{:02}:{:02}", elapsed / 60, elapsed % 60)
|
||||||
} else {
|
} else {
|
||||||
format!("{:02}:{:02}:{:02}", elapsed /3600, (elapsed / 60) % 60, elapsed % 60)
|
format!(
|
||||||
|
"{:02}:{:02}:{:02}",
|
||||||
|
elapsed / 3600,
|
||||||
|
(elapsed / 60) % 60,
|
||||||
|
elapsed % 60
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args: Vec<String> = Vec::new();
|
let mut args: Vec<String> = Vec::new();
|
||||||
|
@ -423,7 +447,8 @@ pub fn exec_command(command: &Vec<String>, elapsed: u32, label: &String) -> Opti
|
||||||
.args(args)
|
.args(args)
|
||||||
.stdout(Stdio::null())
|
.stdout(Stdio::null())
|
||||||
.stdin(Stdio::null())
|
.stdin(Stdio::null())
|
||||||
.spawn() {
|
.spawn()
|
||||||
|
{
|
||||||
Ok(child) => Some(child),
|
Ok(child) => Some(child),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("Error: Could not execute command. ({})", error);
|
eprintln!("Error: Could not execute command. ({})", error);
|
||||||
|
@ -431,4 +456,3 @@ pub fn exec_command(command: &Vec<String>, elapsed: u32, label: &String) -> Opti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
extern crate unicode_segmentation;
|
extern crate unicode_segmentation;
|
||||||
|
|
||||||
use std::io::Write;
|
|
||||||
use termion::{clear, cursor, color};
|
|
||||||
use termion::raw::RawTerminal;
|
|
||||||
use crate::layout::Layout;
|
use crate::layout::Layout;
|
||||||
use unicode_width::UnicodeWidthStr;
|
use std::io::Write;
|
||||||
|
use termion::raw::RawTerminal;
|
||||||
|
use termion::{clear, color, cursor};
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
use unicode_width::UnicodeWidthStr;
|
||||||
|
|
||||||
const PROMPT: &str = "Add alarm: ";
|
const PROMPT: &str = "Add alarm: ";
|
||||||
|
|
||||||
|
|
||||||
// Input buffer.
|
// Input buffer.
|
||||||
pub struct Buffer {
|
pub struct Buffer {
|
||||||
content: String,
|
content: String,
|
||||||
|
@ -50,8 +49,7 @@ impl Buffer {
|
||||||
pub fn strip_word(&mut self) {
|
pub fn strip_word(&mut self) {
|
||||||
// Reset error message.
|
// Reset error message.
|
||||||
self.message = None;
|
self.message = None;
|
||||||
let iter = UnicodeSegmentation::split_word_bound_indices(
|
let iter = UnicodeSegmentation::split_word_bound_indices(self.content.as_str().trim_end());
|
||||||
self.content.as_str().trim_end());
|
|
||||||
|
|
||||||
if let Some((index, _)) = iter.last() {
|
if let Some((index, _)) = iter.last() {
|
||||||
self.content.truncate(index);
|
self.content.truncate(index);
|
||||||
|
@ -78,54 +76,55 @@ impl Buffer {
|
||||||
&mut self,
|
&mut self,
|
||||||
stdout: &mut RawTerminal<W>,
|
stdout: &mut RawTerminal<W>,
|
||||||
layout: &mut Layout,
|
layout: &mut Layout,
|
||||||
) -> Result<(), std::io::Error>
|
) -> Result<(), std::io::Error> {
|
||||||
{
|
|
||||||
// Write error message if present and return.
|
// Write error message if present and return.
|
||||||
if let Some(msg) = self.message {
|
if let Some(msg) = self.message {
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{}{}{}{}",
|
"{}{}{}{}{}{}{}",
|
||||||
cursor::Hide,
|
cursor::Hide,
|
||||||
cursor::Goto( layout.buffer.col, layout.buffer.line),
|
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||||
clear::CurrentLine,
|
clear::CurrentLine,
|
||||||
PROMPT,
|
PROMPT,
|
||||||
color::Fg(color::LightRed),
|
color::Fg(color::LightRed),
|
||||||
&msg,
|
&msg,
|
||||||
color::Fg(color::Reset))?;
|
color::Fg(color::Reset)
|
||||||
|
)?;
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.content.is_empty() {
|
if self.content.is_empty() {
|
||||||
// Clear buffer display.
|
// Clear buffer display.
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||||
clear::CurrentLine,
|
clear::CurrentLine,
|
||||||
cursor::Hide)?;
|
cursor::Hide
|
||||||
|
)?;
|
||||||
} else {
|
} else {
|
||||||
// Check if buffer exceeds limits.
|
// Check if buffer exceeds limits.
|
||||||
while UnicodeWidthStr::width(self.content.as_str())
|
while UnicodeWidthStr::width(self.content.as_str()) + UnicodeWidthStr::width(PROMPT)
|
||||||
+ UnicodeWidthStr::width(PROMPT)
|
|
||||||
> layout.width as usize
|
> layout.width as usize
|
||||||
{
|
{
|
||||||
self.content.pop();
|
self.content.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{}{}",
|
"{}{}{}{}{}",
|
||||||
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
cursor::Goto(layout.buffer.col, layout.buffer.line),
|
||||||
clear::CurrentLine,
|
clear::CurrentLine,
|
||||||
PROMPT,
|
PROMPT,
|
||||||
cursor::Show,
|
cursor::Show,
|
||||||
&self.content)?;
|
&self.content
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw error message at input buffer position.
|
// Draw error message at input buffer position.
|
||||||
pub fn message(
|
pub fn message(&mut self, msg: &'static str) {
|
||||||
&mut self,
|
|
||||||
msg: &'static str,
|
|
||||||
) {
|
|
||||||
self.message = Some(msg);
|
self.message = Some(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
72
src/clock.rs
72
src/clock.rs
|
@ -1,12 +1,12 @@
|
||||||
pub mod font;
|
pub mod font;
|
||||||
|
|
||||||
use std::time;
|
|
||||||
use std::io::Write;
|
|
||||||
use termion::{color, cursor, style};
|
|
||||||
use termion::raw::RawTerminal;
|
|
||||||
use crate::consts::COLOR;
|
use crate::consts::COLOR;
|
||||||
use crate::Config;
|
|
||||||
use crate::layout::{Layout, Position};
|
use crate::layout::{Layout, Position};
|
||||||
|
use crate::Config;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::time;
|
||||||
|
use termion::raw::RawTerminal;
|
||||||
|
use termion::{color, cursor, style};
|
||||||
|
|
||||||
enum Pause {
|
enum Pause {
|
||||||
Instant(time::Instant),
|
Instant(time::Instant),
|
||||||
|
@ -60,10 +60,10 @@ impl Clock {
|
||||||
if let Some(start) = self.start.checked_add(delay.elapsed()) {
|
if let Some(start) = self.start.checked_add(delay.elapsed()) {
|
||||||
self.start = start;
|
self.start = start;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Pause::Time((secs, _days)) => {
|
Pause::Time((secs, _days)) => {
|
||||||
self.start = time::Instant::now() - time::Duration::from_secs(secs as u64);
|
self.start = time::Instant::now() - time::Duration::from_secs(secs as u64);
|
||||||
},
|
}
|
||||||
Pause::None => (), // O_o
|
Pause::None => (), // O_o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,8 +126,7 @@ impl Clock {
|
||||||
mut stdout: &mut RawTerminal<W>,
|
mut stdout: &mut RawTerminal<W>,
|
||||||
layout: &Layout,
|
layout: &Layout,
|
||||||
force_redraw: bool,
|
force_redraw: bool,
|
||||||
) -> Result<(), std::io::Error>
|
) -> Result<(), std::io::Error> {
|
||||||
{
|
|
||||||
// Setup style and color if appropriate.
|
// Setup style and color if appropriate.
|
||||||
if self.paused {
|
if self.paused {
|
||||||
write!(stdout, "{}", style::Faint)?;
|
write!(stdout, "{}", style::Faint)?;
|
||||||
|
@ -140,17 +139,10 @@ impl Clock {
|
||||||
if force_redraw || self.elapsed % 3600 == 0 {
|
if force_redraw || self.elapsed % 3600 == 0 {
|
||||||
// Draw hours if necessary.
|
// Draw hours if necessary.
|
||||||
if self.elapsed >= 3600 {
|
if self.elapsed >= 3600 {
|
||||||
self.draw_digit_pair(
|
self.draw_digit_pair(&mut stdout, self.elapsed / 3600, &layout.clock_hr)?;
|
||||||
&mut stdout,
|
|
||||||
self.elapsed / 3600,
|
|
||||||
&layout.clock_hr,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Draw colon.
|
// Draw colon.
|
||||||
self.draw_colon(
|
self.draw_colon(&mut stdout, &layout.clock_colon1)?;
|
||||||
&mut stdout,
|
|
||||||
&layout.clock_colon1,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw days.
|
// Draw days.
|
||||||
|
@ -161,12 +153,10 @@ impl Clock {
|
||||||
if self.days == 1 { "DAY" } else { "DAYS" },
|
if self.days == 1 { "DAY" } else { "DAYS" },
|
||||||
);
|
);
|
||||||
|
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{:>11}",
|
"{}{:>11}",
|
||||||
cursor::Goto(
|
cursor::Goto(layout.clock_days.col, layout.clock_days.line,),
|
||||||
layout.clock_days.col,
|
|
||||||
layout.clock_days.line,
|
|
||||||
),
|
|
||||||
day_count,
|
day_count,
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
@ -174,35 +164,20 @@ impl Clock {
|
||||||
|
|
||||||
// Draw minutes if necessary. Once every minute or on request.
|
// Draw minutes if necessary. Once every minute or on request.
|
||||||
if force_redraw || self.elapsed % 60 == 0 {
|
if force_redraw || self.elapsed % 60 == 0 {
|
||||||
self.draw_digit_pair(
|
self.draw_digit_pair(&mut stdout, (self.elapsed % 3600) / 60, &layout.clock_min)?;
|
||||||
&mut stdout,
|
|
||||||
(self.elapsed % 3600) / 60,
|
|
||||||
&layout.clock_min,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw colon if necessary.
|
// Draw colon if necessary.
|
||||||
if force_redraw {
|
if force_redraw {
|
||||||
self.draw_colon(
|
self.draw_colon(&mut stdout, &layout.clock_colon0)?;
|
||||||
&mut stdout,
|
|
||||||
&layout.clock_colon0,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw seconds.
|
// Draw seconds.
|
||||||
self.draw_digit_pair(
|
self.draw_digit_pair(&mut stdout, self.elapsed % 60, &layout.clock_sec)?;
|
||||||
&mut stdout,
|
|
||||||
self.elapsed % 60,
|
|
||||||
&layout.clock_sec,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Reset color and style.
|
// Reset color and style.
|
||||||
if self.paused || self.color_index != None {
|
if self.paused || self.color_index != None {
|
||||||
write!(stdout,
|
write!(stdout, "{}{}", style::NoFaint, color::Fg(color::Reset),)?;
|
||||||
"{}{}",
|
|
||||||
style::NoFaint,
|
|
||||||
color::Fg(color::Reset),
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -212,13 +187,13 @@ impl Clock {
|
||||||
stdout: &mut RawTerminal<W>,
|
stdout: &mut RawTerminal<W>,
|
||||||
value: u32,
|
value: u32,
|
||||||
pos: &Position,
|
pos: &Position,
|
||||||
) -> Result<(), std::io::Error>
|
) -> Result<(), std::io::Error> {
|
||||||
{
|
|
||||||
let left = self.font.digits[value as usize / 10].iter();
|
let left = self.font.digits[value as usize / 10].iter();
|
||||||
let right = self.font.digits[value as usize % 10].iter();
|
let right = self.font.digits[value as usize % 10].iter();
|
||||||
|
|
||||||
for (i, (left, right)) in left.zip(right).enumerate() {
|
for (i, (left, right)) in left.zip(right).enumerate() {
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{} {}",
|
"{}{} {}",
|
||||||
cursor::Goto(pos.col, pos.line + i as u16),
|
cursor::Goto(pos.col, pos.line + i as u16),
|
||||||
left,
|
left,
|
||||||
|
@ -233,9 +208,9 @@ impl Clock {
|
||||||
&self,
|
&self,
|
||||||
stdout: &mut RawTerminal<W>,
|
stdout: &mut RawTerminal<W>,
|
||||||
pos: &Position,
|
pos: &Position,
|
||||||
) -> Result<(), std::io::Error>
|
) -> Result<(), std::io::Error> {
|
||||||
{
|
write!(
|
||||||
write!(stdout,
|
stdout,
|
||||||
"{}{}{}{}",
|
"{}{}{}{}",
|
||||||
cursor::Goto(pos.col, pos.line + 1),
|
cursor::Goto(pos.col, pos.line + 1),
|
||||||
self.font.dots.0,
|
self.font.dots.0,
|
||||||
|
@ -245,4 +220,3 @@ impl Clock {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
const DIGIT_HEIGHT: u16 = 5;
|
const DIGIT_HEIGHT: u16 = 5;
|
||||||
|
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
|
@ -12,230 +11,262 @@ pub const NORMAL: Font = Font {
|
||||||
height: DIGIT_HEIGHT,
|
height: DIGIT_HEIGHT,
|
||||||
width: 5,
|
width: 5,
|
||||||
dots: ('■', '■'),
|
dots: ('■', '■'),
|
||||||
digits: [[
|
digits: [
|
||||||
|
[
|
||||||
// 0
|
// 0
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█▄▄▄█",
|
"█▄▄▄█",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 1
|
// 1
|
||||||
" ▀█ ",
|
" ▀█ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ "
|
" █ ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 2
|
// 2
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
"█▀▀▀▀",
|
"█▀▀▀▀",
|
||||||
"█ ",
|
"█ ",
|
||||||
"█▄▄▄▄"
|
"█▄▄▄▄",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 3
|
// 3
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
" ▀▀▀█",
|
" ▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
"▄▄▄▄█"
|
"▄▄▄▄█",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 4
|
// 4
|
||||||
"█ ",
|
"█ ",
|
||||||
"█ █ ",
|
"█ █ ",
|
||||||
"▀▀▀█▀",
|
"▀▀▀█▀",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ "
|
" █ ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 5
|
// 5
|
||||||
"█▀▀▀▀",
|
"█▀▀▀▀",
|
||||||
"█ ",
|
"█ ",
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
"▄▄▄▄█"
|
"▄▄▄▄█",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 6
|
// 6
|
||||||
"█ ",
|
"█ ",
|
||||||
"█ ",
|
"█ ",
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█▄▄▄█"
|
"█▄▄▄█",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 7
|
// 7
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 8
|
// 8
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█▄▄▄█"
|
"█▄▄▄█",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 9
|
// 9
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
" █"
|
" █",
|
||||||
]],
|
],
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PLAIN: Font = Font {
|
pub const PLAIN: Font = Font {
|
||||||
height: DIGIT_HEIGHT,
|
height: DIGIT_HEIGHT,
|
||||||
width: 5,
|
width: 5,
|
||||||
dots: ('█', '█'),
|
dots: ('█', '█'),
|
||||||
digits: [[
|
digits: [
|
||||||
|
[
|
||||||
// 0
|
// 0
|
||||||
"█████",
|
"█████",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█████"
|
"█████",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 1
|
// 1
|
||||||
" ██ ",
|
" ██ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ "
|
" █ ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 2
|
// 2
|
||||||
"█████",
|
"█████",
|
||||||
" █",
|
" █",
|
||||||
"█████",
|
"█████",
|
||||||
"█ ",
|
"█ ",
|
||||||
"█████"
|
"█████",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 3
|
// 3
|
||||||
"█████",
|
"█████",
|
||||||
" █",
|
" █",
|
||||||
" ████",
|
" ████",
|
||||||
" █",
|
" █",
|
||||||
"█████"
|
"█████",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 4
|
// 4
|
||||||
"█ ",
|
"█ ",
|
||||||
"█ █ ",
|
"█ █ ",
|
||||||
"█████",
|
"█████",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ "
|
" █ ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 5
|
// 5
|
||||||
"█████",
|
"█████",
|
||||||
"█ ",
|
"█ ",
|
||||||
"█████",
|
"█████",
|
||||||
" █",
|
" █",
|
||||||
"█████"
|
"█████",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 6
|
// 6
|
||||||
"█ ",
|
"█ ",
|
||||||
"█ ",
|
"█ ",
|
||||||
"█████",
|
"█████",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█████"
|
"█████",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 7
|
// 7
|
||||||
"█████",
|
"█████",
|
||||||
" █",
|
" █",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" █ "
|
" █ ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 8
|
// 8
|
||||||
"█████",
|
"█████",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█████",
|
"█████",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█████"
|
"█████",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 9
|
// 9
|
||||||
"█████",
|
"█████",
|
||||||
"█ █",
|
"█ █",
|
||||||
"█████",
|
"█████",
|
||||||
" █",
|
" █",
|
||||||
" █"
|
" █",
|
||||||
]],
|
],
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const CHROME: Font = Font {
|
pub const CHROME: Font = Font {
|
||||||
height: DIGIT_HEIGHT,
|
height: DIGIT_HEIGHT,
|
||||||
width: 5,
|
width: 5,
|
||||||
dots: ('▄', '🮏'),
|
dots: ('▄', '🮏'),
|
||||||
digits: [[
|
digits: [
|
||||||
|
[
|
||||||
// 0
|
// 0
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"▀ ▀",
|
"▀ ▀",
|
||||||
"🮐 🮐",
|
"🮐 🮐",
|
||||||
"🮐🮏🮏🮏🮐",
|
"🮐🮏🮏🮏🮐",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 1
|
// 1
|
||||||
" ▀█ ",
|
" ▀█ ",
|
||||||
" █ ",
|
" █ ",
|
||||||
" ▀ ",
|
" ▀ ",
|
||||||
" 🮐 ",
|
" 🮐 ",
|
||||||
" 🮐 "
|
" 🮐 ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 2
|
// 2
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
"▀▀▀▀▀",
|
"▀▀▀▀▀",
|
||||||
"🮐 ",
|
"🮐 ",
|
||||||
"🮐🮏🮏🮏🮏"
|
"🮐🮏🮏🮏🮏",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 3
|
// 3
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
" ▀▀▀▀",
|
" ▀▀▀▀",
|
||||||
" 🮐",
|
" 🮐",
|
||||||
"🮏🮏🮏🮏🮐"
|
"🮏🮏🮏🮏🮐",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 4
|
// 4
|
||||||
"█ ",
|
"█ ",
|
||||||
"█ █ ",
|
"█ █ ",
|
||||||
"▀▀▀▀▀",
|
"▀▀▀▀▀",
|
||||||
" 🮐 ",
|
" 🮐 ",
|
||||||
" 🮐 "
|
" 🮐 ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 5
|
// 5
|
||||||
"█▀▀▀▀",
|
"█▀▀▀▀",
|
||||||
"█ ",
|
"█ ",
|
||||||
"▀▀▀▀▀",
|
"▀▀▀▀▀",
|
||||||
" 🮐",
|
" 🮐",
|
||||||
"🮏🮏🮏🮏🮐"
|
"🮏🮏🮏🮏🮐",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 6
|
// 6
|
||||||
"█ ",
|
"█ ",
|
||||||
"█ ",
|
"█ ",
|
||||||
"▀▀▀▀▀",
|
"▀▀▀▀▀",
|
||||||
"🮐 🮐",
|
"🮐 🮐",
|
||||||
"🮐🮏🮏🮏🮐"
|
"🮐🮏🮏🮏🮐",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 7
|
// 7
|
||||||
"▀▀▀▀█",
|
"▀▀▀▀█",
|
||||||
" █",
|
" █",
|
||||||
" ▀ ",
|
" ▀ ",
|
||||||
" 🮐 ",
|
" 🮐 ",
|
||||||
" 🮐 ",
|
" 🮐 ",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 8
|
// 8
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"▀▀▀▀▀",
|
"▀▀▀▀▀",
|
||||||
"🮐 🮐",
|
"🮐 🮐",
|
||||||
"🮐🮏🮏🮏🮐"
|
"🮐🮏🮏🮏🮐",
|
||||||
], [
|
],
|
||||||
|
[
|
||||||
// 9
|
// 9
|
||||||
"█▀▀▀█",
|
"█▀▀▀█",
|
||||||
"█ █",
|
"█ █",
|
||||||
"▀▀▀▀▀",
|
"▀▀▀▀▀",
|
||||||
" 🮐",
|
" 🮐",
|
||||||
" 🮐"
|
" 🮐",
|
||||||
]],
|
],
|
||||||
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
pub const COLOR: [&dyn termion::color::Color; 6] = [
|
pub const COLOR: [&dyn termion::color::Color; 6] = [
|
||||||
&termion::color::LightGreen,
|
&termion::color::LightGreen,
|
||||||
&termion::color::LightYellow,
|
&termion::color::LightYellow,
|
||||||
|
@ -14,8 +13,10 @@ pub const LABEL_SIZE_LIMIT: usize = 32;
|
||||||
pub mod ui {
|
pub mod ui {
|
||||||
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
pub const NAME: &str = env!("CARGO_PKG_NAME");
|
||||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
pub const USAGE: &str = concat!("USAGE: ", env!("CARGO_PKG_NAME"),
|
pub const USAGE: &str = concat!(
|
||||||
" [-h|-v] [-e|--exec COMMAND] [-p] [-q] [ALARM[/LABEL]]
|
"USAGE: ",
|
||||||
|
env!("CARGO_PKG_NAME"),
|
||||||
|
" [-h|-v] [-e|--exec COMMAND] [-p] [-q] [ALARM[/LABEL]]
|
||||||
|
|
||||||
PARAMETERS:
|
PARAMETERS:
|
||||||
[ALARM TIME[/LABEL]] Any number of alarm times (HH:MM:SS) with optional
|
[ALARM TIME[/LABEL]] Any number of alarm times (HH:MM:SS) with optional
|
||||||
|
@ -32,14 +33,13 @@ OPTIONS:
|
||||||
-q, --quit Quit program after last alarm.
|
-q, --quit Quit program after last alarm.
|
||||||
|
|
||||||
SIGNALS: <SIGUSR1> Reset clock.
|
SIGNALS: <SIGUSR1> Reset clock.
|
||||||
<SIGUSR2> Pause or un-pause clock.");
|
<SIGUSR2> Pause or un-pause clock."
|
||||||
|
);
|
||||||
pub const MENUBAR: &str =
|
pub const MENUBAR: &str =
|
||||||
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
|
"[0-9] Add alarm [d] Delete alarm [SPACE] Pause [r] Reset [c] Clear color [q] Quit";
|
||||||
pub const MENUBAR_SHORT: &str =
|
pub const MENUBAR_SHORT: &str =
|
||||||
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
|
"[0-9] Add [d] Delete [SPACE] Pause [r] Reset [c] Clear [q] Quit";
|
||||||
pub const MENUBAR_INS: &str =
|
pub const MENUBAR_INS: &str =
|
||||||
"Format: HH:MM:SS/LABEL [ENTER] Accept [ESC] Cancel [CTR-C] Quit";
|
"Format: HH:MM:SS/LABEL [ENTER] Accept [ESC] Cancel [CTR-C] Quit";
|
||||||
pub const MENUBAR_PAUSED: &str =
|
pub const MENUBAR_PAUSED: &str = "[SPACE] Continue [r] Reset [UP]/[DOWN] Set clock";
|
||||||
"[SPACE] Continue [r] Reset [UP]/[DOWN] Set clock";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ pub struct Position {
|
||||||
impl Position {
|
impl Position {
|
||||||
// Terminal positions are 1-based.
|
// Terminal positions are 1-based.
|
||||||
pub fn new() -> Position {
|
pub fn new() -> Position {
|
||||||
Position {col: 1, line: 1}
|
Position { col: 1, line: 1 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ impl Layout {
|
||||||
clock_colon1: Position::new(),
|
clock_colon1: Position::new(),
|
||||||
clock_hr: Position::new(),
|
clock_hr: Position::new(),
|
||||||
clock_days: Position::new(),
|
clock_days: Position::new(),
|
||||||
roster: Position {col: 1, line: 3},
|
roster: Position { col: 1, line: 3 },
|
||||||
roster_width: 0,
|
roster_width: 0,
|
||||||
roster_height: 0,
|
roster_height: 0,
|
||||||
buffer: Position::new(),
|
buffer: Position::new(),
|
||||||
|
@ -56,9 +56,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update layout. Returns true when changes were made.
|
// Update layout. Returns true when changes were made.
|
||||||
pub fn update(&mut self, clock: &Clock, force: bool)
|
pub fn update(&mut self, clock: &Clock, force: bool) -> Result<bool, std::io::Error> {
|
||||||
-> Result<bool, std::io::Error>
|
|
||||||
{
|
|
||||||
if self.force_recalc || force {
|
if self.force_recalc || force {
|
||||||
self.force_recalc = false;
|
self.force_recalc = false;
|
||||||
let (width, height) = termion::terminal_size()?;
|
let (width, height) = termion::terminal_size()?;
|
||||||
|
@ -79,13 +77,7 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn test_update(
|
pub fn test_update(&mut self, clock: &Clock, width: u16, height: u16, roster_width: u16) {
|
||||||
&mut self,
|
|
||||||
clock: &Clock,
|
|
||||||
width: u16,
|
|
||||||
height: u16,
|
|
||||||
roster_width: u16,
|
|
||||||
) {
|
|
||||||
self.width = width;
|
self.width = width;
|
||||||
self.height = height;
|
self.height = height;
|
||||||
self.clock_width = clock.get_width();
|
self.clock_width = clock.get_width();
|
||||||
|
@ -104,7 +96,9 @@ impl Layout {
|
||||||
// terminal.
|
// terminal.
|
||||||
fn compute(&mut self, display_hours: bool) {
|
fn compute(&mut self, display_hours: bool) {
|
||||||
// Prevent integer overflow at very low screen sizes.
|
// Prevent integer overflow at very low screen sizes.
|
||||||
if self.width < self.clock_width || self.height < self.clock_height { return; }
|
if self.width < self.clock_width || self.height < self.clock_height {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let middle: u16 = self.height / 2 - 1;
|
let middle: u16 = self.height / 2 - 1;
|
||||||
|
|
||||||
|
@ -163,4 +157,3 @@ impl Layout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
177
src/lib.rs
177
src/lib.rs
|
@ -1,36 +1,34 @@
|
||||||
extern crate termion;
|
|
||||||
extern crate signal_hook;
|
extern crate signal_hook;
|
||||||
|
extern crate termion;
|
||||||
pub mod alarm;
|
pub mod alarm;
|
||||||
mod buffer;
|
mod buffer;
|
||||||
pub mod clock;
|
pub mod clock;
|
||||||
pub mod consts;
|
pub mod consts;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod utils;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
use std::{env, process, thread, time};
|
pub use alarm::AlarmRoster;
|
||||||
use std::io::Write;
|
use alarm::{exec_command, Countdown};
|
||||||
|
use buffer::Buffer;
|
||||||
|
use clock::{font, Clock};
|
||||||
|
pub use consts::ui::*;
|
||||||
|
use layout::Layout;
|
||||||
use signal_hook::consts::signal::*;
|
use signal_hook::consts::signal::*;
|
||||||
use signal_hook::iterator::Signals;
|
use signal_hook::iterator::Signals;
|
||||||
use signal_hook::low_level;
|
use signal_hook::low_level;
|
||||||
use termion::{clear, cursor, style};
|
use std::io::Write;
|
||||||
use termion::raw::{IntoRawMode, RawTerminal};
|
use std::{env, process, thread, time};
|
||||||
use termion::event::Key;
|
use termion::event::Key;
|
||||||
use termion::input::TermRead;
|
use termion::input::TermRead;
|
||||||
use buffer::Buffer;
|
use termion::raw::{IntoRawMode, RawTerminal};
|
||||||
use clock::{Clock, font};
|
use termion::{clear, cursor, style};
|
||||||
use layout::Layout;
|
|
||||||
use alarm::{Countdown, exec_command};
|
|
||||||
pub use alarm::AlarmRoster;
|
|
||||||
pub use consts::ui::*;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn run(
|
pub fn run(
|
||||||
config: Config,
|
config: Config,
|
||||||
mut alarm_roster: AlarmRoster,
|
mut alarm_roster: AlarmRoster,
|
||||||
) -> Result<Option<process::Child>, std::io::Error>
|
) -> Result<Option<process::Child>, std::io::Error> {
|
||||||
{
|
|
||||||
let mut layout = Layout::new();
|
let mut layout = Layout::new();
|
||||||
// Initialise roster_width.
|
// Initialise roster_width.
|
||||||
layout.set_roster_width(alarm_roster.width());
|
layout.set_roster_width(alarm_roster.width());
|
||||||
|
@ -47,13 +45,7 @@ pub fn run(
|
||||||
|
|
||||||
// Register signals.
|
// Register signals.
|
||||||
let mut signals = Signals::new(&[
|
let mut signals = Signals::new(&[
|
||||||
SIGTSTP,
|
SIGTSTP, SIGCONT, SIGWINCH, SIGTERM, SIGINT, SIGUSR1, SIGUSR2,
|
||||||
SIGCONT,
|
|
||||||
SIGWINCH,
|
|
||||||
SIGTERM,
|
|
||||||
SIGINT,
|
|
||||||
SIGUSR1,
|
|
||||||
SIGUSR2,
|
|
||||||
])?;
|
])?;
|
||||||
|
|
||||||
// Main loop entry.
|
// Main loop entry.
|
||||||
|
@ -67,7 +59,7 @@ pub fn run(
|
||||||
SIGCONT => {
|
SIGCONT => {
|
||||||
restore_after_suspend(&mut stdout)?;
|
restore_after_suspend(&mut stdout)?;
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
SIGWINCH => layout.schedule_recalc(),
|
SIGWINCH => layout.schedule_recalc(),
|
||||||
// Exit main loop on SIGTERM and SIGINT.
|
// Exit main loop on SIGTERM and SIGINT.
|
||||||
SIGTERM | SIGINT => break 'outer,
|
SIGTERM | SIGINT => break 'outer,
|
||||||
|
@ -77,12 +69,12 @@ pub fn run(
|
||||||
alarm_roster.reset_all();
|
alarm_roster.reset_all();
|
||||||
layout.schedule_recalc();
|
layout.schedule_recalc();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// (Un-)Pause clock on SIGUSR2.
|
// (Un-)Pause clock on SIGUSR2.
|
||||||
SIGUSR2 => {
|
SIGUSR2 => {
|
||||||
clock.toggle();
|
clock.toggle();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// We didn't register anything else.
|
// We didn't register anything else.
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -127,25 +119,25 @@ pub fn run(
|
||||||
Ok(Some(status)) if status.success() => child = None,
|
Ok(Some(status)) if status.success() => child = None,
|
||||||
// Abnormal exit.
|
// Abnormal exit.
|
||||||
Ok(Some(status)) => {
|
Ok(Some(status)) => {
|
||||||
eprintln!("Spawned process terminated with non-zero exit status. ({})", status);
|
eprintln!(
|
||||||
|
"Spawned process terminated with non-zero exit status. ({})",
|
||||||
|
status
|
||||||
|
);
|
||||||
child = None;
|
child = None;
|
||||||
},
|
}
|
||||||
// Process is still running.
|
// Process is still running.
|
||||||
Ok(None) => (),
|
Ok(None) => (),
|
||||||
// Other error.
|
// Other error.
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("Error executing command. ({})", error);
|
eprintln!("Error executing command. ({})", error);
|
||||||
child = None;
|
child = None;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for exceeded alarms.
|
// Check for exceeded alarms.
|
||||||
if let Some(alarm) = alarm_roster.check(
|
if let Some(alarm) =
|
||||||
&mut clock,
|
alarm_roster.check(&mut clock, &layout, &mut countdown, force_redraw)
|
||||||
&layout,
|
|
||||||
&mut countdown,
|
|
||||||
force_redraw)
|
|
||||||
{
|
{
|
||||||
// Do not react to exceeded alarms if the clock is paused.
|
// Do not react to exceeded alarms if the clock is paused.
|
||||||
if !clock.paused {
|
if !clock.paused {
|
||||||
|
@ -158,13 +150,17 @@ pub fn run(
|
||||||
// Run command if configured and no command is running.
|
// Run command if configured and no command is running.
|
||||||
Some(ref command) if child.is_none() => {
|
Some(ref command) if child.is_none() => {
|
||||||
child = exec_command(command, alarm.time, &alarm.label);
|
child = exec_command(command, alarm.time, &alarm.label);
|
||||||
},
|
}
|
||||||
// Last command is still running.
|
// Last command is still running.
|
||||||
Some(_) => eprintln!("Not executing command, as its predecessor is still running"),
|
Some(_) => {
|
||||||
|
eprintln!("Not executing command, as its predecessor is still running")
|
||||||
|
}
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
// Quit if configured.
|
// Quit if configured.
|
||||||
if config.quit && alarm_roster.idle() { break };
|
if config.quit && alarm_roster.idle() {
|
||||||
|
break;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,7 +168,8 @@ pub fn run(
|
||||||
// requested.
|
// requested.
|
||||||
if force_redraw {
|
if force_redraw {
|
||||||
// Write menu at the top.
|
// Write menu at the top.
|
||||||
write!(stdout,
|
write!(
|
||||||
|
stdout,
|
||||||
"{}{}{}{}{}",
|
"{}{}{}{}{}",
|
||||||
cursor::Goto(1, 1),
|
cursor::Goto(1, 1),
|
||||||
style::Faint,
|
style::Faint,
|
||||||
|
@ -189,7 +186,8 @@ pub fn run(
|
||||||
_ => " ",
|
_ => " ",
|
||||||
},
|
},
|
||||||
clear::AfterCursor,
|
clear::AfterCursor,
|
||||||
style::NoFaint)?;
|
style::NoFaint
|
||||||
|
)?;
|
||||||
|
|
||||||
// Redraw list of alarms.
|
// Redraw list of alarms.
|
||||||
alarm_roster.draw(&mut stdout, &mut layout, &config)?;
|
alarm_roster.draw(&mut stdout, &mut layout, &config)?;
|
||||||
|
@ -234,13 +232,13 @@ pub fn run(
|
||||||
buffer.visible = false;
|
buffer.visible = false;
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Escape and ^U clear input buffer.
|
// Escape and ^U clear input buffer.
|
||||||
Key::Esc | Key::Ctrl('u') => {
|
Key::Esc | Key::Ctrl('u') => {
|
||||||
buffer.reset();
|
buffer.reset();
|
||||||
buffer.visible = false;
|
buffer.visible = false;
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// ^W removes last word.
|
// ^W removes last word.
|
||||||
Key::Ctrl('w') => {
|
Key::Ctrl('w') => {
|
||||||
buffer.strip_word();
|
buffer.strip_word();
|
||||||
|
@ -248,7 +246,7 @@ pub fn run(
|
||||||
buffer.visible = false;
|
buffer.visible = false;
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Backspace.
|
// Backspace.
|
||||||
Key::Backspace => {
|
Key::Backspace => {
|
||||||
// Delete last char in buffer.
|
// Delete last char in buffer.
|
||||||
|
@ -257,7 +255,7 @@ pub fn run(
|
||||||
buffer.visible = false;
|
buffer.visible = false;
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Set clock.
|
// Set clock.
|
||||||
Key::Up if clock.paused => {
|
Key::Up if clock.paused => {
|
||||||
clock.shift(10);
|
clock.shift(10);
|
||||||
|
@ -267,43 +265,43 @@ pub fn run(
|
||||||
// here.
|
// here.
|
||||||
layout.schedule_recalc();
|
layout.schedule_recalc();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
Key::Down if clock.paused => {
|
Key::Down if clock.paused => {
|
||||||
clock.shift(-10);
|
clock.shift(-10);
|
||||||
alarm_roster.time_travel(&mut clock);
|
alarm_roster.time_travel(&mut clock);
|
||||||
layout.schedule_recalc();
|
layout.schedule_recalc();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// Scroll alarm roster.
|
// Scroll alarm roster.
|
||||||
Key::PageUp => {
|
Key::PageUp => {
|
||||||
alarm_roster.scroll_up(&layout);
|
alarm_roster.scroll_up(&layout);
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
Key::PageDown => {
|
Key::PageDown => {
|
||||||
alarm_roster.scroll_down(&layout);
|
alarm_roster.scroll_down(&layout);
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// Forward every char if in insert mode.
|
// Forward every char if in insert mode.
|
||||||
Key::Char(c) if buffer.visible => {
|
Key::Char(c) if buffer.visible => {
|
||||||
buffer.push(c);
|
buffer.push(c);
|
||||||
},
|
}
|
||||||
// Reset clock on 'r'.
|
// Reset clock on 'r'.
|
||||||
Key::Char('r') => {
|
Key::Char('r') => {
|
||||||
clock.reset();
|
clock.reset();
|
||||||
alarm_roster.reset_all();
|
alarm_roster.reset_all();
|
||||||
layout.schedule_recalc();
|
layout.schedule_recalc();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// (Un-)Pause on space.
|
// (Un-)Pause on space.
|
||||||
Key::Char(' ') => {
|
Key::Char(' ') => {
|
||||||
clock.toggle();
|
clock.toggle();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// Clear clock color on 'c'.
|
// Clear clock color on 'c'.
|
||||||
Key::Char('c') => {
|
Key::Char('c') => {
|
||||||
clock.color_index = None;
|
clock.color_index = None;
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
},
|
}
|
||||||
// Delete last alarm on 'd'.
|
// Delete last alarm on 'd'.
|
||||||
Key::Char('d') => {
|
Key::Char('d') => {
|
||||||
if alarm_roster.pop().is_some() {
|
if alarm_roster.pop().is_some() {
|
||||||
|
@ -313,7 +311,7 @@ pub fn run(
|
||||||
countdown.reset();
|
countdown.reset();
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Exit on q and ^C.
|
// Exit on q and ^C.
|
||||||
Key::Char('q') | Key::Ctrl('c') => break,
|
Key::Char('q') | Key::Ctrl('c') => break,
|
||||||
// Force redraw on ^R.
|
// Force redraw on ^R.
|
||||||
|
@ -327,7 +325,7 @@ pub fn run(
|
||||||
force_redraw = true;
|
force_redraw = true;
|
||||||
// Jump to the start of the main loop.
|
// Jump to the start of the main loop.
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
Key::Char(c) => {
|
Key::Char(c) => {
|
||||||
if c.is_ascii_digit() {
|
if c.is_ascii_digit() {
|
||||||
buffer.push(c);
|
buffer.push(c);
|
||||||
|
@ -336,7 +334,7 @@ pub fn run(
|
||||||
} else if !buffer.is_empty() && c == ':' {
|
} else if !buffer.is_empty() && c == ':' {
|
||||||
buffer.push(':');
|
buffer.push(':');
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Any other key.
|
// Any other key.
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
@ -347,11 +345,7 @@ pub fn run(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop exited. Clear screen and restore cursor.
|
// Main loop exited. Clear screen and restore cursor.
|
||||||
write!(stdout,
|
write!(stdout, "{}{}{}", clear::All, cursor::Restore, cursor::Show)?;
|
||||||
"{}{}{}",
|
|
||||||
clear::All,
|
|
||||||
cursor::Restore,
|
|
||||||
cursor::Show)?;
|
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
|
|
||||||
Ok(child)
|
Ok(child)
|
||||||
|
@ -366,9 +360,7 @@ pub struct Config {
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
// Parse command line arguments into "config".
|
// Parse command line arguments into "config".
|
||||||
pub fn new(args: env::Args, alarm_roster: &mut AlarmRoster)
|
pub fn new(args: env::Args, alarm_roster: &mut AlarmRoster) -> Result<Config, String> {
|
||||||
-> Result<Config, String>
|
|
||||||
{
|
|
||||||
let mut config = Config {
|
let mut config = Config {
|
||||||
quit: false,
|
quit: false,
|
||||||
fancy: false,
|
fancy: false,
|
||||||
|
@ -384,16 +376,16 @@ impl Config {
|
||||||
// Print usage information and exit
|
// Print usage information and exit
|
||||||
println!("{}", USAGE);
|
println!("{}", USAGE);
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
},
|
}
|
||||||
"-v" | "--version" => {
|
"-v" | "--version" => {
|
||||||
println!("{} {}", NAME, VERSION);
|
println!("{} {}", NAME, VERSION);
|
||||||
process::exit(0);
|
process::exit(0);
|
||||||
},
|
}
|
||||||
"-p" | "--plain" => config.font = &font::PLAIN,
|
"-p" | "--plain" => config.font = &font::PLAIN,
|
||||||
"-f" | "--fancy" => {
|
"-f" | "--fancy" => {
|
||||||
config.fancy = true;
|
config.fancy = true;
|
||||||
config.font = &font::CHROME;
|
config.font = &font::CHROME;
|
||||||
},
|
}
|
||||||
"-q" | "--quit" => config.quit = true,
|
"-q" | "--quit" => config.quit = true,
|
||||||
"-e" | "--exec" => {
|
"-e" | "--exec" => {
|
||||||
if let Some(e) = iter.next() {
|
if let Some(e) = iter.next() {
|
||||||
|
@ -401,19 +393,21 @@ impl Config {
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Missing parameter to \"{}\".", arg));
|
return Err(format!("Missing parameter to \"{}\".", arg));
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
any if any.starts_with('-') => {
|
any if any.starts_with('-') => {
|
||||||
// Unrecognized flag.
|
// Unrecognized flag.
|
||||||
return Err(format!("Unrecognized option: \"{}\"\nUse \"-h\" or \"--help\" for a list of valid command line options.", any));
|
return Err(format!("Unrecognized option: \"{}\"\nUse \"-h\" or \"--help\" for a list of valid command line options.", any));
|
||||||
},
|
}
|
||||||
any => {
|
any => {
|
||||||
// Alarm to add.
|
// Alarm to add.
|
||||||
if let Err(error) = alarm_roster.add(&String::from(any)) {
|
if let Err(error) = alarm_roster.add(&String::from(any)) {
|
||||||
return Err(format!("Error adding \"{}\" as alarm. ({})", any, error));
|
return Err(format!("Error adding \"{}\" as alarm. ({})", any, error));
|
||||||
}
|
}
|
||||||
},
|
|
||||||
}
|
}
|
||||||
} else { break; } // All command line parameters processed.
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
} // All command line parameters processed.
|
||||||
}
|
}
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
@ -432,23 +426,27 @@ impl Config {
|
||||||
// Next char is escaped. (If not escaped itself.)
|
// Next char is escaped. (If not escaped itself.)
|
||||||
escaped = true;
|
escaped = true;
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
// Keep spaces when escaped or quoted.
|
// Keep spaces when escaped or quoted.
|
||||||
' ' if escaped || quoted => { &segment.push(' '); },
|
' ' if escaped || quoted => {
|
||||||
|
&segment.push(' ');
|
||||||
|
}
|
||||||
// Otherwise end the current segment.
|
// Otherwise end the current segment.
|
||||||
' ' => {
|
' ' => {
|
||||||
if !&segment.is_empty() {
|
if !&segment.is_empty() {
|
||||||
command.push(segment.clone());
|
command.push(segment.clone());
|
||||||
&segment.clear();
|
&segment.clear();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
// Quotation marks toggle quote.
|
// Quotation marks toggle quote.
|
||||||
'"' | '\'' if !escaped => quoted = !quoted,
|
'"' | '\'' if !escaped => quoted = !quoted,
|
||||||
// Carry everything else. Escape if found escaped.
|
// Carry everything else. Escape if found escaped.
|
||||||
_ => {
|
_ => {
|
||||||
if escaped { &segment.push('\\'); }
|
if escaped {
|
||||||
|
&segment.push('\\');
|
||||||
|
}
|
||||||
&segment.push(c);
|
&segment.push(c);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
escaped = false;
|
escaped = false;
|
||||||
}
|
}
|
||||||
|
@ -459,18 +457,20 @@ impl Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare to suspend execution. Called on SIGTSTP.
|
// Prepare to suspend execution. Called on SIGTSTP.
|
||||||
fn suspend<W: Write>(stdout: &mut RawTerminal<W>)
|
fn suspend<W: Write>(stdout: &mut RawTerminal<W>) -> Result<(), std::io::Error> {
|
||||||
-> Result<(), std::io::Error>
|
write!(
|
||||||
{
|
stdout,
|
||||||
write!(stdout,
|
|
||||||
"{}{}{}",
|
"{}{}{}",
|
||||||
cursor::Goto(1,1),
|
cursor::Goto(1, 1),
|
||||||
clear::AfterCursor,
|
clear::AfterCursor,
|
||||||
cursor::Show)?;
|
cursor::Show
|
||||||
|
)?;
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
stdout.suspend_raw_mode()
|
stdout.suspend_raw_mode().unwrap_or_else(|error| {
|
||||||
.unwrap_or_else(|error| {
|
eprintln!(
|
||||||
eprintln!("Failed to leave raw terminal mode prior to suspend: {}", error);
|
"Failed to leave raw terminal mode prior to suspend: {}",
|
||||||
|
error
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Err(error) = low_level::emulate_default_handler(SIGTSTP as i32) {
|
if let Err(error) = low_level::emulate_default_handler(SIGTSTP as i32) {
|
||||||
|
@ -482,14 +482,13 @@ fn suspend<W: Write>(stdout: &mut RawTerminal<W>)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set up terminal after SIGTSTP or SIGSTOP.
|
// Set up terminal after SIGTSTP or SIGSTOP.
|
||||||
fn restore_after_suspend<W: Write>(stdout: &mut RawTerminal<W>)
|
fn restore_after_suspend<W: Write>(stdout: &mut RawTerminal<W>) -> Result<(), std::io::Error> {
|
||||||
-> Result<(), std::io::Error>
|
stdout.activate_raw_mode().unwrap_or_else(|error| {
|
||||||
{
|
eprintln!(
|
||||||
stdout.activate_raw_mode()
|
"Failed to re-enter raw terminal mode after suspend: {}",
|
||||||
.unwrap_or_else(|error| {
|
error
|
||||||
eprintln!("Failed to re-enter raw terminal mode after suspend: {}", error);
|
);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -1,13 +1,11 @@
|
||||||
|
use kitchentimer::{run, AlarmRoster, Config};
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
use kitchentimer::{Config, AlarmRoster, run};
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args = env::args();
|
let args = env::args();
|
||||||
let mut alarm_roster = AlarmRoster::new();
|
let mut alarm_roster = AlarmRoster::new();
|
||||||
// Parse command line arguments into config and alarm roster.
|
// Parse command line arguments into config and alarm roster.
|
||||||
let config = Config::new(args, &mut alarm_roster)
|
let config = Config::new(args, &mut alarm_roster).unwrap_or_else(|e| {
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
eprintln!("{}", e);
|
eprintln!("{}", e);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
});
|
});
|
||||||
|
@ -27,12 +25,15 @@ fn main() {
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
eprintln!("Main loop exited with error: {}", error);
|
eprintln!("Main loop exited with error: {}", error);
|
||||||
process::exit(1);
|
process::exit(1);
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wait for remaining spawned process to exit.
|
// Wait for remaining spawned process to exit.
|
||||||
if let Some(mut child) = child {
|
if let Some(mut child) = child {
|
||||||
eprint!("Waiting for spawned process (PID {}) to finish ...", child.id());
|
eprint!(
|
||||||
|
"Waiting for spawned process (PID {}) to finish ...",
|
||||||
|
child.id()
|
||||||
|
);
|
||||||
|
|
||||||
match child.wait() {
|
match child.wait() {
|
||||||
Ok(status) if status.success() => eprintln!(" ok"),
|
Ok(status) if status.success() => eprintln!(" ok"),
|
||||||
|
@ -43,5 +44,3 @@ fn main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::layout::Layout;
|
|
||||||
use crate::clock::Clock;
|
use crate::clock::Clock;
|
||||||
|
use crate::layout::Layout;
|
||||||
use crate::Config;
|
use crate::Config;
|
||||||
|
|
||||||
fn default_config() -> Config {
|
fn default_config() -> Config {
|
||||||
|
|
|
@ -6,8 +6,7 @@ pub fn grapheme_truncate(input: &mut String, limit: usize, ellipse: char) {
|
||||||
Some((i, _)) => {
|
Some((i, _)) => {
|
||||||
input.truncate(i);
|
input.truncate(i);
|
||||||
input.push(ellipse);
|
input.push(ellipse);
|
||||||
},
|
}
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue