From e6c1cacb60010ec75e02cf72acf34278417670fd Mon Sep 17 00:00:00 2001 From: lonkaars Date: Mon, 11 Mar 2024 18:33:02 +0100 Subject: initial commit (basic functionality) --- core/config | 8 ++++++++ core/help | 37 +++++++++++++++++++++++++++++++++++++ core/lib | 18 ++++++++++++++++++ core/path | 5 +++++ core/pause | 13 +++++++++++++ core/reset | 5 +++++ core/start | 39 +++++++++++++++++++++++++++++++++++++++ core/state | 21 +++++++++++++++++++++ core/stop | 1 + core/toggle | 7 +++++++ core/update | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ dppt | 35 +++++++++++++++++++++++++++++++++++ license | 21 +++++++++++++++++++++ 13 files changed, 265 insertions(+) create mode 100644 core/config create mode 100755 core/help create mode 100644 core/lib create mode 100644 core/path create mode 100755 core/pause create mode 100755 core/reset create mode 100755 core/start create mode 100755 core/state create mode 120000 core/stop create mode 100755 core/toggle create mode 100644 core/update create mode 100755 dppt create mode 100644 license diff --git a/core/config b/core/config new file mode 100644 index 0000000..34c96b8 --- /dev/null +++ b/core/config @@ -0,0 +1,8 @@ +#!/bin/sh +# initialize default configuration values +export POMODORO_STATE_PATH="${POMODORO_STATE_PATH:-$XDG_CACHE_HOME/$progname}" # ~/.cache/dppt +export POMODORO_NORMAL_DURATION="${POMODORO_NORMAL_DURATION:-$(( 25 * 60 ))}" # 25 minutes +export POMODORO_BREAK_SHORT_DURATION="${POMODORO_BREAK_SHORT_DURATION:-$(( 5 * 60 ))}" # 5 minutes +export POMODORO_BREAK_LONG_DURATION="${POMODORO_BREAK_LONG_DURATION:-$(( 15 * 60 ))}" # 15 minutes +export POMODORO_BREAK_SHORT_INTERVAL="${POMODORO_BREAK_INTERVAL:-2}" # every other lap +export POMODORO_BREAK_LONG_INTERVAL="${POMODORO_BREAK_INTERVAL:-6}" # every 3rd break diff --git a/core/help b/core/help new file mode 100755 index 0000000..98abd1e --- /dev/null +++ b/core/help @@ -0,0 +1,37 @@ +#!/bin/sh +[ "$1" = "info" ] && echo "show this very message" && exit 2 + +cat << EOF +$progname -- daemonless posix pomodoro timer + +usage: + $progname [options] [action] [action options] + +options: + -h, --help show help + +actions: +EOF + +IFS=':' +find $path -type l,f -perm /u=x,g=x,o=x 2>/dev/null | while read -r action ; do + action_name="$(basename "$action")" + if [ -L "$action" ] ; then + printf '%s\t%s\n' "$action_name" "same as $(basename "$(readlink -f "$action")")" + else + action_info="$("$action" info)" + [ $? -eq 2 ] && printf '%s\t%s\n' "$action_name" "$action_info" + fi +done | awk ' + BEGIN { FS = "\t" } + { + if (printed[$1]) next + printf("\t%-13s %s\n", $1, $2) + printed[$1] = 1 + } +' + +cat << EOF + +if no action is provided, the \`state\` action is chosen by default +EOF diff --git a/core/lib b/core/lib new file mode 100644 index 0000000..9892663 --- /dev/null +++ b/core/lib @@ -0,0 +1,18 @@ +#!/bin/sh +# utility variables and functions +XDG_CACHE_HOME="${XDG_CACHE_HOME:-$HOME/.cache}" # XDG basedir specification + +# this variable grows automatically as long as this file is included in each +# subcommand +export subcmd="$subcmd${subcmd:+ }$(basename "$0")" + +# print an error to stdout and exit +err() { + echo "error: $*" >&2 + echo "run \`$subcmd --help\` for options" >&2 + exit 1 +} + +# debug: +# bc() { tee /dev/stderr | /usr/bin/bc ; } + diff --git a/core/path b/core/path new file mode 100644 index 0000000..23f7f5c --- /dev/null +++ b/core/path @@ -0,0 +1,5 @@ +#!/bin/sh +# initialize default search path for core + plugins +path="$core_path" +path="$XDG_DATA_HOME/$progname:$path" +export path diff --git a/core/pause b/core/pause new file mode 100755 index 0000000..a55bd30 --- /dev/null +++ b/core/pause @@ -0,0 +1,13 @@ +#!/bin/sh +[ "$1" = "info" ] && echo "pause the timer" && exit 2 + +. "$core_path/lib" +. "$core_path/update" + +[ "$state" != 'running' ] && err "timer is not running" + +time="$("$time - $now" | bc)" +state='paused' + +save_state + diff --git a/core/reset b/core/reset new file mode 100755 index 0000000..5492dce --- /dev/null +++ b/core/reset @@ -0,0 +1,5 @@ +#!/bin/sh +[ "$1" = "info" ] && echo "reset the timer and lap counter" && exit 2 + +rm -rf "$POMODORO_STATE_PATH" + diff --git a/core/start b/core/start new file mode 100755 index 0000000..ae471ad --- /dev/null +++ b/core/start @@ -0,0 +1,39 @@ +#!/bin/sh +[ "$1" = "info" ] && echo "start or resume the timer" && exit 2 + +usage() { + cat << EOF +usage: + $subcmd [options] + +options: + -h, --help display this help text + -s, --skip skip to next lap, even if the timer is currently running + +EOF +} + +. "$core_path/lib" +. "$core_path/update" + +allow_skip=0 +while [ $# -gt 0 ] ; do + case "$1" in + -h|--help) usage && exit 0 ;; + -s|--skip) shift ; allow_skip=1 ;; + --) shift ; break ;; + *) err "unknown parameter: $1" ;; + esac +done + +if [ "$state" = 'running' ] ; then + [ $allow_skip -eq 0 ] && err "timer is already running, use -s to skip lap" + time="0.0" + lap=$(( $lap + 1 )) + . "$core_path/update" +fi + +time="$(echo "$now + $time" | bc)" +state='running' + +save_state diff --git a/core/state b/core/state new file mode 100755 index 0000000..fcf327d --- /dev/null +++ b/core/state @@ -0,0 +1,21 @@ +#!/bin/sh +[ "$1" = "info" ] && echo "show timer" && exit 2 +. "$core_path/lib" + +usage() { + cat << EOF +$subcmd -- show timer + +usage: + $subcmd [options] + +options: + -h, --help show help + +actions: +EOF +} + +. "$core_path/update" +echo "[$state] $lap: ${remaining}s" + diff --git a/core/stop b/core/stop new file mode 120000 index 0000000..398b326 --- /dev/null +++ b/core/stop @@ -0,0 +1 @@ +reset \ No newline at end of file diff --git a/core/toggle b/core/toggle new file mode 100755 index 0000000..bfb96d2 --- /dev/null +++ b/core/toggle @@ -0,0 +1,7 @@ +#!/bin/sh +[ "$1" = "info" ] && echo "toggle the timer between running and paused" && exit 2 + +. "$core_path/update" + +[ "$state" = 'running' ] && exec "$prog" pause || exec "$prog" start + diff --git a/core/update b/core/update new file mode 100644 index 0000000..04fd29c --- /dev/null +++ b/core/update @@ -0,0 +1,55 @@ +#!/bin/sh +export now="$(date +%s.%N)" + +update_time=0 + +mkdir -p "$POMODORO_STATE_PATH" +load_or_init() { + property="$1" + default_value="$2" + property_path="$POMODORO_STATE_PATH/$property" + if [ -f "$property_path" ] ; then + eval $property='"$(cat "$property_path")"' + else + eval $property='$default_value' + fi +} +[ -z "$lap" ] && load_or_init lap 0 +[ -z "$state" ] && load_or_init state 'reset' +[ -z "$time" ] && load_or_init time 0.0 +[ "$time" = "0.0" ] && update_time=1 + +# calculate remaining time on timer +if [ -z "$remaining" ] ; then + remaining="$time" + [ "$state" = 'running' ] && remaining="$(echo "( $time - $now ) / 1" | bc)" +fi + +# go to next lap if this timer expired +if [ "$state" = 'running' ] && [ "${remaining#-}" != "${remaining}" ] ; then + lap=$(( $lap + 1 )) + state='paused' + update_time=1 +fi + +# update remaining time if in reset +[ "$state" = 'reset' ] && update_time=1 + +if [ $update_time -eq 1 ] ; then + time="$POMODORO_NORMAL_DURATION" + [ $(( $lap % $POMODORO_BREAK_SHORT_INTERVAL )) -eq $(( $POMODORO_BREAK_SHORT_INTERVAL - 1 )) ] && time=$POMODORO_BREAK_SHORT_DURATION + [ $(( $lap % $POMODORO_BREAK_LONG_INTERVAL )) -eq $(( $POMODORO_BREAK_LONG_INTERVAL - 1 )) ] && time=$POMODORO_BREAK_LONG_DURATION + remaining="$time" +fi + +# save state +save_state() { + mkdir -p "$POMODORO_STATE_PATH" + echo "$lap" > "$POMODORO_STATE_PATH/lap" + echo "$state" > "$POMODORO_STATE_PATH/state" + echo "$time" > "$POMODORO_STATE_PATH/time" +} + +# export state variables +export lap state time + diff --git a/dppt b/dppt new file mode 100755 index 0000000..72c3ad0 --- /dev/null +++ b/dppt @@ -0,0 +1,35 @@ +#!/bin/sh +export prog="$0" +export progname="$(basename "$0")" +export core_path="$(dirname "$(readlink -f "$0")")/core" + +. "$core_path/lib" +. "$core_path/path" +. "$core_path/config" + +# parse arguments (does not support joined arguments) +while [ $# -gt 0 ] ; do + case "$1" in + -h|--help) shift ; exec "$prog" help ;; + --) shift ; break ;; # stop parsing options + *) break ;; + esac +done + +# action is first argument after options +action="$1" +# default action is to display state +[ -z "$action" ] && exec "$prog" state +shift + +IFS=':' # make sure for loop splits $path on colon +for dir in $path ; do + # action must be in $path and be executable + ! [ -x "$dir/$action" ] && continue + # execute first action that matches above criteria (in order of path) + exec "$dir/$action" "$@" +done + +# this point is only reached if no $action was `exec`-ed +err "unknown action \"$action\"" + diff --git a/license b/license new file mode 100644 index 0000000..6faf3cf --- /dev/null +++ b/license @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 lonkaars + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -- cgit v1.2.3