@@ 1,47 @@
+# pomodorino
+
+pomodorino is a small pomodoro timer that consumes 0% of your CPU.
+
+## Usage
+
+pomodorino is written in C, and runs fine on POSIX systems (like Linux) and
+Windows.
+
+It has an optional dependency on libdbus, which is used to send you desktop
+notifications when it is time to take a break or get back to work. On
+Debian-based systems, it can be installed with:
+
+ apt install libdbus-1-dev
+
+You can then build the project using [Meson][1]:
+
+ meson setup build
+ meson compile -C build
+
+Alternatively, you can get basic functionality by simply compiling the program with any C
+compiler, like this:
+
+ cc -o pomodorino pomodorino.c
+
+By default, pomodorino alternates between a 25 minutes working lap and a 5
+minutes break, with a 15 minutes long break after four rounds of work. You can
+override these default values at compile time, by defining the `LAP_SECONDS`,
+`SHORT_BREAK_SECONDS` `LONG_BREAK_SECONDS` and `SESSIONS_UNTIL_LONG_BREAK`
+preprocessor defines; for example:
+
+ export CPPFLAGS='-DLAP_SECONDS=2400 -DSHORT_BREAK_SECONDS=600'
+ meson setup build
+ meson compile -C build
+
+## Why
+
+I used to use the beautiful [Solanum][2] app from the GNOME project, but its
+relatively high resource usage (especially relevant on my old laptop) convinced
+me that a command line tool was enough for my needs, and so I wrote it.
+
+By using the [`sleep(3)`][3] function, pomodorino completely suspends execution
+until the end of the current timer, so that it truly uses 0% of your CPU.
+
+[1]: https://mesonbuild.com
+[2]: https://apps.gnome.org/app/org.gnome.Solanum/
+[3]: https://man7.org/linux/man-pages/man3/sleep.3.html
@@ 1,27 @@
+project(
+ 'pomodorino',
+ 'c',
+ default_options: [
+ 'buildtype=release',
+ 'warning_level=3',
+ 'c_std=c17',
+ 'b_lto=true',
+ 'b_ndebug=if-release'
+ ]
+)
+
+deps = []
+args = []
+
+dbus_dep = dependency('dbus-1', required: false)
+if dbus_dep.found()
+ deps += dbus_dep
+ args += '-DPOMODORINO_DBUS'
+endif
+
+executable(
+ 'pomodorino',
+ 'pomodorino.c',
+ dependencies: deps,
+ c_args: args
+)
@@ 1,104 @@
+#include <stdio.h>
+
+#ifndef LAP_SECONDS
+# define LAP_SECONDS 1500U
+#endif
+
+#ifndef SHORT_BREAK_SECONDS
+# define SHORT_BREAK_SECONDS 300U
+#endif
+
+#ifndef LONG_BREAK_SECONDS
+# define LONG_BREAK_SECONDS 900U
+#endif
+
+#ifndef SESSIONS_UNTIL_LONG_BREAK
+# define SESSIONS_UNTIL_LONG_BREAK 3U
+#endif
+
+#ifndef POMODORINO_SLEEP
+# if defined(__unix__) || (defined (__APPLE__) && defined (__MACH__))
+# include <unistd.h>
+# define POMODORINO_SLEEP(seconds) sleep(seconds)
+# elif defined(_WIN32)
+# include <synchapi.h>
+# define POMODORINO_SLEEP(seconds) Sleep(seconds * 1000U)
+# else
+# error "No sleep function implemented"
+# endif
+#endif
+
+#ifdef POMODORINO_DBUS
+# include <dbus-1.0/dbus/dbus.h>
+#endif
+
+static void wait_user_input(void) {
+ int input;
+ while ((input = getchar()) != '\n' && input != EOF) {}
+}
+
+static void notify(const char* const summary, const char* const body) {
+#ifdef POMODORINO_DBUS
+ DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, 0);
+ DBusMessage* message = dbus_message_new_method_call(
+ "org.freedesktop.Notifications",
+ "/org/freedesktop/Notifications",
+ "org.freedesktop.Notifications",
+ "Notify");
+ DBusMessageIter iter[4];
+ dbus_message_iter_init_append(message, iter);
+ const char* application = "pomodorino";
+ dbus_message_iter_append_basic(iter, 's', &application);
+ unsigned id = 0;
+ dbus_message_iter_append_basic(iter, 'u', &id);
+ const char* icon = "dialog-information";
+ dbus_message_iter_append_basic(iter, 's', &icon);
+ dbus_message_iter_append_basic(iter, 's', &summary);
+ dbus_message_iter_append_basic(iter, 's', &body);
+ dbus_message_iter_open_container(iter, 'a', "s", iter + 1);
+ dbus_message_iter_close_container(iter, iter + 1);
+ dbus_message_iter_open_container(iter, 'a', "{sv}", iter + 1);
+ dbus_message_iter_open_container(iter + 1, 'e', 0, iter + 2);
+ const char* urgency = "urgency";
+ dbus_message_iter_append_basic(iter + 2, 's', &urgency);
+ dbus_message_iter_open_container(iter + 2, 'v', "y", iter + 3);
+ enum {LOW, NORMAL, CRITICAL};
+ unsigned char level = CRITICAL;
+ dbus_message_iter_append_basic(iter + 3, 'y', &level);
+ dbus_message_iter_close_container(iter + 2, iter + 3);
+ dbus_message_iter_close_container(iter + 1, iter + 2);
+ dbus_message_iter_close_container(iter, iter + 1);
+ const int timeout = 30 * 1000;
+ dbus_message_iter_append_basic(iter, 'i', &timeout);
+ dbus_connection_send(connection, message, 0);
+ dbus_connection_flush(connection);
+ dbus_message_unref(message);
+ dbus_connection_unref(connection);
+#else
+ (void)summary;
+ (void)body;
+#endif
+}
+
+int main(void) {
+ while (1) {
+ unsigned char i;
+ for (i = 1; i <= SESSIONS_UNTIL_LONG_BREAK; ++i) {
+ printf("Lap %d, press enter to start\n", i);
+ wait_user_input();
+ POMODORINO_SLEEP(LAP_SECONDS);
+ notify("Break time", "Go drink some water");
+ puts("Short break, press enter to start");
+ wait_user_input();
+ POMODORINO_SLEEP(SHORT_BREAK_SECONDS);
+ notify("Back to work", "Stay focused");
+ }
+ printf("Lap %d, press enter to start\n", i);
+ wait_user_input();
+ POMODORINO_SLEEP(LAP_SECONDS);
+ puts("Long break, press enter to start");
+ wait_user_input();
+ POMODORINO_SLEEP(LONG_BREAK_SECONDS);
+ notify("Break time", "Go drink some water");
+ }
+}