commit fcf2d39546f1208c0d7c470b614631045d373412
Author: Naveen Narayanan zerous <zerous@nocebo.space>
Date: Sun, 10 Dec 2017 08:50:38 +0300
Release 0.1
Diffstat:
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;
+}
+