~tachi/pomodorino

8c63ac8ae9a954863410e1de8928cec642c32eb5 — Andrea Pappacoda 1 year, 10 months ago
Initial commit
4 files changed, 179 insertions(+), 0 deletions(-)

A .gitignore
A README.md
A meson.build
A pomodorino.c
A  => .gitignore +1 -0
@@ 1,1 @@
/.cache/clangd/

A  => README.md +47 -0
@@ 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

A  => meson.build +27 -0
@@ 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
)

A  => pomodorino.c +104 -0
@@ 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");
	}
}