├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── TODO ├── arg.h ├── config.def.h ├── config.mk ├── contrib └── notify-macos ├── spt.1 └── spt.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Libraries 8 | *.lib 9 | *.a 10 | 11 | # Shared objects (inc. Windows DLLs) 12 | *.dll 13 | *.so 14 | *.so.* 15 | *.dylib 16 | 17 | # Executables 18 | *.exe 19 | *.out 20 | *.app 21 | *.i*86 22 | *.x86_64 23 | *.hex 24 | /spt 25 | 26 | # vim 27 | *.swp 28 | *~ 29 | 30 | config.h 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2020, Ivan Tham 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # spt - simple pomodoro timer 2 | # See LICENSE file for copyright and license details. 3 | 4 | include config.mk 5 | 6 | SRC = spt.c 7 | OBJ = ${SRC:.c=.o} 8 | 9 | all: options spt 10 | 11 | options: 12 | @echo spt build options: 13 | @echo "CFLAGS = ${CFLAGS}" 14 | @echo "LDFLAGS = ${LDFLAGS}" 15 | @echo "CC = ${CC}" 16 | 17 | .c.o: 18 | @echo CC $< 19 | @${CC} -c ${CFLAGS} $< 20 | 21 | ${OBJ}: config.h config.mk 22 | 23 | config.h: 24 | @echo creating $@ from config.def.h 25 | @cp config.def.h $@ 26 | 27 | spt: ${OBJ} 28 | @echo CC -o $@ 29 | @${CC} -o $@ ${OBJ} ${LDFLAGS} 30 | 31 | clean: 32 | @echo cleaning 33 | @rm -f ${SRC:.c=} ${OBJ} spt-${VERSION}.tar.xz 34 | 35 | dist: clean 36 | @echo creating dist tarball 37 | @mkdir -p spt-${VERSION} 38 | @cp -R LICENSE Makefile README config.mk config.def.h spt.info spt.1 ${SRC} spt-${VERSION} 39 | @tar -cf spt-${VERSION}.tar spt-${VERSION} 40 | @xz spt-${VERSION}.tar 41 | @rm -rf spt-${VERSION} 42 | 43 | install: all 44 | @echo installing executable file to ${DESTDIR}${PREFIX}/bin 45 | @mkdir -p ${DESTDIR}${PREFIX}/bin 46 | @cp -f spt ${DESTDIR}${PREFIX}/bin 47 | @chmod 755 ${DESTDIR}${PREFIX}/bin/spt 48 | @echo installing manual page to ${DESTDIR}${MANPREFIX}/man1 49 | @mkdir -p ${DESTDIR}${MANPREFIX}/man1 50 | @sed "s/VERSION/${VERSION}/g" < spt.1 > ${DESTDIR}${MANPREFIX}/man1/spt.1 51 | @chmod 644 ${DESTDIR}${MANPREFIX}/man1/spt.1 52 | 53 | uninstall: 54 | @echo removing executable file from ${DESTDIR}${PREFIX}/bin 55 | @rm -f ${DESTDIR}${PREFIX}/bin/spt 56 | @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 57 | @rm -f ${DESTDIR}${MANPREFIX}/man1/spt.1 58 | 59 | .PHONY: all options clean dist install uninstall 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | spt - simple pomodoro timer 2 | =========================== 3 | spt is a simple timer that uses the pomodoro technique that doubles your 4 | efficiency. 5 | 6 | Features 7 | -------- 8 | - Get the jobs done quicker than ever 9 | - Keeps you free like a dog 10 | - Able to show remaining time 11 | 12 | Installation 13 | ------------ 14 | Edit config.mk to match your local setup (spt is installed into the /usr/local 15 | namespace by default). 16 | 17 | Afterwards enter the following command to build and install spt (if necessary 18 | as root): 19 | 20 | make clean install 21 | 22 | See the man pages for additional details. 23 | 24 | Configuration 25 | ------------- 26 | The configuration of spt is done by creating a custom config.h and 27 | (re)compiling the source code. By default, the timer runs by 4 28 | pomodoro timer (25 mins) with subsequent rests in between (5 mins) 29 | followed by a long rest (15 mins) in an infinite loop. 30 | 31 | Links 32 | ----- 33 | http://pomodorotechnique.com/ 34 | 35 | 36 | The project is licensed under the MIT license. 37 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | Good luck! Nothing TODO for now. 2 | -------------------------------------------------------------------------------- /arg.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copy me if you can. 3 | * by 20h 4 | */ 5 | 6 | #ifndef __ARG_H__ 7 | #define __ARG_H__ 8 | 9 | extern char *argv0; 10 | 11 | #define USED(x) ((void)(x)) 12 | 13 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ 14 | argv[0] && argv[0][1]\ 15 | && argv[0][0] == '-';\ 16 | argc--, argv++) {\ 17 | char _argc;\ 18 | char **_argv;\ 19 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ 20 | argv++;\ 21 | argc--;\ 22 | break;\ 23 | }\ 24 | for (argv[0]++, _argv = argv; argv[0][0];\ 25 | argv[0]++) {\ 26 | if (_argv != argv)\ 27 | break;\ 28 | _argc = argv[0][0];\ 29 | switch (_argc) 30 | 31 | #define ARGEND }\ 32 | USED(_argc);\ 33 | }\ 34 | USED(argv);\ 35 | USED(argc); 36 | 37 | #define EARGF(x) ((argv[1] == NULL)? ((x), abort(), (char *)0) :\ 38 | (argc--, argv++, argv[0])) 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /config.def.h: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | 3 | /* Notification, remove DNOTIFY in config.mk if you don't want it */ 4 | static char *notifycmd = ""; /* Uses given command if not compiled by DNOTIFY */ 5 | static char *notifyext = ""; /* Notify with extra command (eg. play an alarm) */ 6 | 7 | /* 8 | * This is the array which defines all the timer that will be used. 9 | * It will be repeated after all of it is executed. 10 | */ 11 | static Timers timers[] = { 12 | /* timer(s) comment */ 13 | { 1500, "Time to start working!"}, 14 | { 300, "Time to start resting!"}, 15 | { 1500, "Time to start working!"}, 16 | { 300, "Time to start resting!"}, 17 | { 1500, "Time to start working!"}, 18 | { 300, "Time to start resting!"}, 19 | { 1500, "Time to start working!"}, 20 | { 900, "Time to take a nap!" }, 21 | }; 22 | -------------------------------------------------------------------------------- /config.mk: -------------------------------------------------------------------------------- 1 | # spt version 2 | VERSION = 0.6 3 | 4 | # Customize below to fit your system 5 | 6 | # paths 7 | PREFIX = /usr/local 8 | MANPREFIX = ${PREFIX}/share/man 9 | 10 | INCS = -I. -I/usr/include 11 | LIBS = -L/usr/lib 12 | 13 | # libnotify, comment if you don't want it 14 | DEFS = -DNOTIFY 15 | INCS+= `pkg-config --cflags libnotify` 16 | LIBS+= `pkg-config --libs libnotify` 17 | 18 | # flags 19 | CPPFLAGS += -DVERSION=\"${VERSION}\" -D_POSIX_C_SOURCE=199309 20 | CFLAGS += -g -std=c99 -pedantic -Wall -Os ${INCS} ${DEFS} ${CPPFLAGS} 21 | LDFLAGS += -g ${LIBS} 22 | 23 | # compiler and linker 24 | CC ?= cc 25 | -------------------------------------------------------------------------------- /contrib/notify-macos: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # A notification wrapper for MacOS. It will appear in Notification 3 | # Center as owned by Script Editor. 4 | # 5 | # usage: spt -n ./notify-macos 6 | osascript -e "display notification \"$2\" with title \"$1\"" 7 | -------------------------------------------------------------------------------- /spt.1: -------------------------------------------------------------------------------- 1 | .TH SPT 1 spt\-VERSION 2 | .SH NAME 3 | spt \- simple pomodoro timer 4 | .SH SYNOPSIS 5 | .B spt 6 | .RB [ \-e 7 | .IR notifyext ] 8 | .RB [ \-n 9 | .IR notifycmd ] 10 | .RB [ \-v ] 11 | .SH DESCRIPTION 12 | .B spt 13 | is a simple timer that uses pomodoro technique with desktop notification to 14 | double your efficiency. 15 | .B spt 16 | receives 2 signals: 17 | .P 18 | .RS 19 | .B SIGUSR1 20 | \- show remaining time, state 21 | .br 22 | .B SIGUSR2 23 | \- play/pause the timer 24 | .RE 25 | .SH OPTIONS 26 | .TP 27 | .BI \-e " notifyext" 28 | Execute 29 | .I notifyext 30 | when timer starts ticking. 31 | .TP 32 | .BI \-n " notifycmd" 33 | Execute 34 | .I notifycmd 35 | if not compiled with 36 | .IR "-DNOTIFY". 37 | .TP 38 | .BI \-v 39 | Prints version information to stderr, then exits. 40 | .SH TIMER 41 | 4 pomodoro timer ( 42 | .B 25 min. 43 | ) with subsequent rests in between ( 44 | .B 5 min. 45 | ) and followed by a long rest ( 46 | .B 15 min. 47 | ) in an infinite loop. 48 | .SH EXAMPLES 49 | Use system notify_send and play a music without libnotify: 50 | .IP 51 | spt -e 'aplay alarm.wav' -n 'notify-send' 52 | -------------------------------------------------------------------------------- /spt.c: -------------------------------------------------------------------------------- 1 | /* See LICENSE file for copyright and license details. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #ifdef NOTIFY 10 | #include 11 | #endif /* NOTIFY */ 12 | 13 | #include "arg.h" 14 | 15 | char *argv0; 16 | 17 | /* macros */ 18 | #define LEN(a) (sizeof(a) / sizeof(a[0])) 19 | 20 | 21 | typedef struct { 22 | unsigned int tmr; 23 | char *cmt; 24 | } Timers; 25 | 26 | #include "config.h" 27 | 28 | volatile static sig_atomic_t display, suspend; 29 | 30 | /* function declarations */ 31 | static void die(const char *errstr, ...); 32 | static void spawn(char *argv[]); 33 | static void notify_send(char *cmt); 34 | static void display_state(int remaining, int suspend); 35 | static void toggle_display(int sigint); 36 | static void toggle_suspend(int sigint); 37 | static void usage(void); 38 | 39 | /* functions implementations */ 40 | void 41 | die(const char *errstr, ...) 42 | { 43 | va_list ap; 44 | 45 | va_start(ap, errstr); 46 | vfprintf(stderr, errstr, ap); 47 | va_end(ap); 48 | exit(1); 49 | } 50 | 51 | void 52 | spawn(char *argv[]) 53 | { 54 | if (fork() == 0) { 55 | setsid(); 56 | execvp(argv[0], argv); 57 | die("spt: execvp %s\n", argv[0]); 58 | perror(" failed"); 59 | exit(0); 60 | } 61 | } 62 | 63 | void 64 | notify_send(char *cmt) 65 | { 66 | if (strcmp(notifycmd, "")) 67 | spawn((char *[]) { notifycmd, "spt", cmt, NULL }); 68 | #ifdef NOTIFY 69 | else { 70 | notify_init("spt"); 71 | NotifyNotification *n = notify_notification_new("spt", cmt, \ 72 | "dialog-information"); 73 | notify_notification_show(n, NULL); 74 | g_object_unref(G_OBJECT(n)); 75 | notify_uninit(); 76 | } 77 | #endif /* NOTIFY */ 78 | 79 | if (strcmp(notifyext, "")) /* extra commands to use */ 80 | spawn((char *[]) { "/bin/sh", "-c", notifyext, NULL }); 81 | } 82 | 83 | void 84 | display_state(int remaining, int suspend) 85 | { 86 | char buf[29]; 87 | 88 | snprintf(buf, 29, "Remaining: %02d:%02d %s", 89 | remaining / 60, 90 | remaining % 60, 91 | (suspend) ? "◼" : "▶"); 92 | 93 | notify_send(buf); 94 | display = 0; 95 | } 96 | 97 | void 98 | toggle_display(int sigint) 99 | { 100 | display = 1; 101 | } 102 | 103 | void 104 | toggle_suspend(int sigint) 105 | { 106 | suspend ^= 1; 107 | } 108 | 109 | void 110 | usage(void) 111 | { 112 | die("usage: %s [-e notifyext] [-n notifycmd] [-v]\n", argv0); 113 | } 114 | 115 | int 116 | main(int argc, char *argv[]) 117 | { 118 | struct timespec remaining; 119 | struct sigaction sa; 120 | sigset_t emptymask; 121 | int i; 122 | 123 | ARGBEGIN { 124 | case 'e': 125 | notifyext = EARGF(usage()); 126 | break; 127 | case 'n': 128 | notifycmd = EARGF(usage()); 129 | break; 130 | case 'v': 131 | die("spt " VERSION " © 2015-2020 spt engineers, " 132 | "see LICENSE for details\n"); 133 | default: 134 | usage(); 135 | } ARGEND; 136 | 137 | /* add SIGUSR1 handler: remaining_time */ 138 | sa.sa_handler = toggle_display; 139 | sigemptyset(&sa.sa_mask); 140 | sa.sa_flags = 0; 141 | 142 | if (sigaction(SIGUSR1, &sa, NULL) == -1) 143 | die("cannot associate SIGUSR1 to handler\n"); 144 | 145 | /* add SIGUSR2 handler: toggle */ 146 | sa.sa_handler = toggle_suspend; 147 | sigemptyset(&sa.sa_mask); 148 | sa.sa_flags = 0; 149 | 150 | if (sigaction(SIGUSR2, &sa, NULL) == -1) 151 | die("cannot associate SIGUSR2 to handler\n"); 152 | 153 | sigemptyset(&emptymask); 154 | 155 | for (i = 0; ; i = (i + 1) % LEN(timers)) { 156 | notify_send(timers[i].cmt); 157 | remaining.tv_sec = timers[i].tmr; 158 | remaining.tv_nsec = 0; 159 | while (remaining.tv_sec) { 160 | if (display) 161 | display_state(remaining.tv_sec, suspend); 162 | 163 | if (suspend) 164 | sigsuspend(&emptymask); 165 | else 166 | if (nanosleep(&remaining, &remaining) == 0) 167 | remaining.tv_sec = remaining.tv_nsec = 0; 168 | } 169 | } 170 | 171 | return 0; 172 | } 173 | --------------------------------------------------------------------------------