pomodoro

A simple pomodoro timer
git clone git://nocebo.space/pomodoro
Log | Files | Refs | LICENSE

commit fcf2d39546f1208c0d7c470b614631045d373412
Author: Naveen Narayanan zerous <zerous@nocebo.space>
Date:   Sun, 10 Dec 2017 08:50:38 +0300

Release 0.1

Diffstat:
Makefile | 33+++++++++++++++++++++++++++++++++
README.md | 25+++++++++++++++++++++++++
config.bsd.mk | 10++++++++++
config.def.h | 1+
config.h | 1+
configure | 10++++++++++
pomodoro.1 | 42++++++++++++++++++++++++++++++++++++++++++
pomodoro.c | 229+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 351 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,33 @@ +VERSION = 0.1 + +include config.mk + +CC?=gcc +INSTALL?=install +RM?=rm -f + +OBJ = pomodoro.o +BIN = pomodoro + +all: ${BIN} + +${BIN}: ${OBJ} + ${CC} ${LDFLAGS} -o $@ ${OBJ} ${LIBS} + +pomodoro.o: config.h + +config.h: + cp config.def.h config.h + +install: all + mkdir -p ${PREFIX}/bin + cp -f ${BIN} ${PREFIX}/bin + mkdir -p ${MANPREFIX}/man1 + cp -f ${BIN}.1 ${MANPREFIX}/man1 + +uninstall: + rm -f ${PREFIX}/bin/${BIN} + rm -f ${MANPREFIX}/man1/${BIN}.1 + +clean: + ${RM} ${OBJECTS} pomodoro pomodoro.core pomodoro.o diff --git a/README.md b/README.md @@ -0,0 +1,25 @@ +Pomodoro +---- + +Description: +---- + + + +Proposed Concept: +---- +1. The conventional methodology recommends a 25 minute pomodoro and a 5 minute + break. +2. Every 4 pomodoros will be rewarded with a 20-30 minute break. +3. Write a file ~/.pomodoro.log with stats like date:time; where time is the time + stamp at which the corresponding pomodoro ended. (This file might be helpful + in plotting a graph on productivity.) +4. The timer is capable of notifying the user when the pomodoro ends using + a dialog box. + +Install +---- +1. ./configure +2. make +3. make install + diff --git a/config.bsd.mk b/config.bsd.mk @@ -0,0 +1,10 @@ +PREFIX = /usr/local +MANPREFIX = ${PREFIX}/man + +CFLAGS?=-Os +CFLAGS+=-ansi -pedantic -Wall -Wextra +CFLAGS+=-Isrc -I/usr/include -I/usr/X11R6/include +CFLAGS+=-DVERSION=\"${VERSION}\" +LDFLAGS+=-L/usr/lib -L/usr/X11R6/lib + +LIBS+=-lX11 -lutil diff --git a/config.def.h b/config.def.h @@ -0,0 +1 @@ +char *log_file = "/home/zerous/.pomodoro"; /* set log file */ diff --git a/config.h b/config.h @@ -0,0 +1 @@ +const char *log_file = "/home/zerous/.pomodoro"; /* set log file */ diff --git a/configure b/configure @@ -0,0 +1,10 @@ +#!/bin/sh + +case `uname` in +OpenBSD) + ln -sf config.bsd.mk config.mk + ;; +*) + echo Unsupported system 1>&2 + ;; +esac diff --git a/pomodoro.1 b/pomodoro.1 @@ -0,0 +1,42 @@ +.Dd Dec 08, 2017 +.Dt POMODORO 1 +.Os +.Sh NAME +.Nm pomodoro +.Nd pomodoro timer +.Sh SYNOPSIS +.Nm pomodoro +.Op Fl p Ar pomodoro_period +.Op Fl b Ar break_period +.Op Fl v +.Sh DESCRIPTION +.Nm +is a simple pomodoro timer. +.Nm +has two modes of operation: interactive mode and direct mode. By default +.Nm +will run in interactive mode if no argument is provided. In interactive mode, +pomodoro period will last for 25 minutes and break period will last for 5 +minutes. Every 3 pomodoros will be rewarded with a 30-minute break. +In direct mode, user is allowed to pass in desired values for both pomodoro and +break periods. +.Sh OPTIONS +.Bl -tag -width Ds +.It Fl p Ar pomodoro_period +Set the pomodoro period in minutes. +.It Fl b Ar break_period +Set the break period in minutes. +.It Fl v +Show program version. +.El +.Sh CONFIGURATION +You can specify the location where pomodoro should place a log. +The primary use for this is to plot a productivity chart. +.Sh RATIONALE +Users who practice pomodoro technique might find +.Nm +useful. +.Sh PORTABILITY +It currently only works on OpenBSD. +.Sh Authors +.An Naveen Narayanan <zerous@nocebo.space> diff --git a/pomodoro.c b/pomodoro.c @@ -0,0 +1,229 @@ +#include <err.h> +#include <errno.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#include <X11/Xutil.h> + +#include "config.h" + +/* + * Pomodoro timer: + * Options include a one-off mode in addition to the interactive mode + * One-off mode requires two arguments: + * * pomodoro_period + * * break_period + * Usage: pomodoro b b_period p p_period + * + * Interactive mode provides a defalt pomodoro period of 25 mins & + * break period of 5 mins. + * Every 3 pomodoros get a 30-min break. + * + * Completed pomodoros are logged in ~/.pomodoro + */ + +enum {FALSE, TRUE}; + +extern char *optarg; + +void usage (void); +void take_break (unsigned int); +Bool evpredicate (void); +void record (FILE *); + +int +main (int argc, char **argv) +{ + + unsigned int break_period; + int ch; + unsigned int interactive_mode; + unsigned int pomodoro_period; + FILE *fp; + + const char *str; + + break_period = 15; + interactive_mode = TRUE; + pomodoro_period = 25; + fp = fopen(log_file, "a"); + str = "b:p:v"; + + while ((ch = getopt(argc, argv, str)) != -1) { + switch (ch) { + case 'b': + interactive_mode = FALSE; + break_period = atoi(optarg); + break; + case 'p': + interactive_mode = FALSE; + pomodoro_period = atoi(optarg); + break; + case 'v': + printf("pomodoro-%s\n", VERSION); + exit(0); + default: + usage(); + exit(1); + } + } + + if (interactive_mode) { + int c; + int p_counter; /* counts the number of pomodoros */ + + p_counter = 0; + while (1) { + fpurge(stdin); + printf("Start Pomodoro? [y/n] "); + c = getchar(); + + if ((c == 'y') || (c == 'Y')) { + /* defacto pomodoro period: 25 mins */ + /* defacto rest period: 5 mins */ + sleep(25 * 60); + p_counter++; + + if (p_counter > 3) { + p_counter = 0; + take_break(30); + record(fp); + } else { + take_break(5); + record(fp); + } + + } else { + printf("Thank you\n"); + exit(0); + } + } + } else { + sleep(pomodoro_period * 60); + take_break(break_period); + record(fp); + } + + exit(0); +} + +void +usage (void) +{ + fprintf(stderr, + "usage: pomodoro [-b break_period] [-p pomodoro_period]\n" + "-p\tspecify pomodoro period\n" + "-b\tspecify break period\n" + "-v\tshow version\n" + ); +} + +void +take_break (unsigned int bp) +{ + Display *d; + int dfd; + struct pollfd pfd[1]; + time_t curtime, prevtime; + if (!(d = XOpenDisplay(NULL))) { + err(1, "XOpenDisplay failed\n"); + return; + } + const char *n = "pomodoro"; + char *m = malloc(1024); + snprintf(m, 1024, "Take a %d min break!", bp); + int s = DefaultScreen(d), ww = 128, wh = 64; + Window w = XCreateSimpleWindow(d, RootWindow(d, s), + DisplayWidth(d, s) / 2, DisplayHeight(d, s) / 2, ww, wh, 1, + BlackPixel(d, s), 0x202020L); + Atom ad = XInternAtom(d, "WM_DELETE_WINDOW", False), + an = XInternAtom(d, "_NET_WM_NAME", False), + ai = XInternAtom(d, "_NET_WM_ICON_NAME", False), + au = XInternAtom(d, "UTF8_STRING", False); + XGCValues gcv; + GC gc = XCreateGC(d, w, 0, &gcv); + XFontStruct *f = XLoadQueryFont(d, "-*-terminus-medium-*"); + XEvent e; + XChangeProperty(d, w, an, au, 8, 0, (unsigned char *)n, strlen(n)); + XChangeProperty(d, w, ai, au, 8, 0, (unsigned char *)n, strlen(n)); + XSetTransientForHint(d, w, RootWindow(d, s)); + XSetWMProtocols(d , w, &ad, 1); + XSelectInput(d, w, ExposureMask); + if (f) + XSetFont(d, gc, f->fid); + XSetForeground(d, gc, 0xc0c0c0L); + XMapWindow(d, w); + prevtime = time(NULL); + dfd = ConnectionNumber(d); + + while (1) { + pfd[0].fd = dfd; + pfd[0].events = POLLOUT; + switch (poll(pfd, 1, 1)) { + case -1: + if (errno != EINTR) + err(1, "poll"); + break; + case 0: + fprintf(stderr, "case 0\n"); + break; + default: + curtime = time(NULL); + if (difftime(curtime,prevtime) >= bp * 60) { + free(m); + XFreeGC(d, gc); + XDestroyWindow(d, w); + XCloseDisplay(d); + return; + } + if ((pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) + errx(1, "bad fd: %d", pfd[0].fd); + while (XCheckIfEvent(d, &e, evpredicate, NULL)) { + switch (e.type) { + case Expose: + XDrawString(d, w, gc, (ww - XTextWidth(f, m, + strlen(m))) / 2, (wh + f->ascent + + f->descent) / 2, m, strlen(m)); + break; + case ClientMessage: + free(m); + XFreeGC(d, gc); + XDestroyWindow(d, w); + XCloseDisplay(d); + return; + } + break; + } + } + } +} + +Bool +evpredicate (void) +{ + return True; +} + +void +record (FILE *f) +{ + const char *cmd = "date"; + char *timestamp = malloc(128); + FILE *o; + + o = popen(cmd, "re"); + if (fgets(timestamp, 128, o) != NULL) { + fprintf(stderr, "%s", timestamp); + fprintf(f, "%s\n", timestamp); + } + + free(timestamp); + pclose(o); + return; +} +