initial commit

This commit is contained in:
Patrick Neff 2024-04-13 22:27:25 +02:00
commit ab90291f93
19 changed files with 556 additions and 0 deletions

63
.gitignore vendored Normal file
View File

@ -0,0 +1,63 @@
/compile_commands.json
/sleeptimer
# Created by https://www.toptal.com/developers/gitignore/api/c
# Edit at https://www.toptal.com/developers/gitignore?templates=c
### C ###
# Prerequisites
*.d
# Object files
*.o
*.ko
*.obj
*.elf
# Linker output
*.ilk
*.map
*.exp
# Precompiled Headers
*.gch
*.pch
# Libraries
*.lib
*.a
*.la
*.lo
# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib
# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
# Debug files
*.dSYM/
*.su
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
# End of https://www.toptal.com/developers/gitignore/api/c
/.cache

14
Makefile Normal file
View File

@ -0,0 +1,14 @@
PROGRAM = sleeptimer
LDFLAGS = $(shell pkg-config --libs dbus-1 x11 xscrnsaver)
CFLAGS = --std=gnu17 -Wall -Werror -Wextra -Wpedantic -g $(shell pkg-config --cflags dbus-1 x11 xscrnsaver)
objs = idle.o main.o dbus_wrapper.o suspend.o echo.o sleeptimer.o duration.o actions.o
sleeptimer: $(objs)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(PROGRAM) $(objs)
$(objs):
clean:
rm $(PROGRAM) $(objs)

39
actions.c Normal file
View File

@ -0,0 +1,39 @@
#include <string.h>
#include <unistd.h>
#include "echo.h"
#include "stdio.h"
#include "suspend.h"
#include "actions.h"
const char *ACTION_ECHO = "echo";
const char *ACTION_SUSPEND = "suspend";
enum Action parseAction(const char *action) {
enum Action a = ActionSuspend;
if (strcmp(action, ACTION_ECHO) == 0) {
a = ActionEcho;
} else if (strcmp(action, ACTION_SUSPEND) == 0) {
a = ActionSuspend;
} else {
a = ActionUnknown;
}
return a;
}
actionCallback getActionCallback(enum Action action) {
actionCallback callback = suspend;
switch (action) {
case ActionSuspend:
callback = suspend;
break;
case ActionEcho:
callback = echo;
break;
default:
fprintf(stderr, "Unknown method\n");
return NULL;
}
return callback;
}

11
actions.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _ACTIONS_H
#define _ACTIONS_H
#include <stdbool.h>
typedef bool (*actionCallback)(void);
enum Action { ActionUnknown, ActionSuspend, ActionEcho };
actionCallback getActionCallback(enum Action action);
enum Action parseAction(const char *action);
#endif

70
dbus_wrapper.c Normal file
View File

@ -0,0 +1,70 @@
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "dbus_wrapper.h"
DBusError dbusError;
DBusConnection *conn;
void handleDbusError(DBusError *err, const char *message) {
if (dbus_error_is_set(err)) {
fprintf(stderr, "DBus Error - %s - %s\n", message, err->message);
dbus_error_free(err);
}
}
DBusConnection *dbusConnect(void) {
dbus_error_init(&dbusError);
if (NULL == conn) {
return conn;
}
conn = dbus_bus_get(DBUS_BUS_SYSTEM, &dbusError);
if (dbus_error_is_set(&dbusError)) {
handleDbusError(&dbusError, "Could not connect");
dbus_error_free(&dbusError);
}
if (NULL == conn) {
return NULL;
}
return conn;
}
bool dbusCallMethod(DBusConnection *conn, const char *bus_name,
const char *path, const char *iface, const char *method) {
DBusMessage *msg;
DBusMessageIter args;
msg = dbus_message_new_method_call(bus_name, path, iface, method);
if (NULL == msg) {
handleDbusError(&dbusError, "Could not initialize message");
return false;
}
// append arguments
dbus_message_iter_init_append(msg, &args);
int val = 0;
if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_BOOLEAN, &val)) {
handleDbusError(&dbusError, "Could not append argument to method call");
return false;
}
if (!dbus_connection_send_with_reply_and_block(
conn, msg, -1, &dbusError)) { // -1 is default timeout
handleDbusError(&dbusError, "Could not send message");
dbus_message_unref(msg);
return false;
}
dbus_connection_flush(conn);
// free message
dbus_message_unref(msg);
dbus_connection_close(conn);
return true;
}

13
dbus_wrapper.h Normal file
View File

@ -0,0 +1,13 @@
#ifndef _DBUS_WRAPPER_H
#define _DBUS_WRAPPER_H
#include <stdbool.h>
#include <dbus/dbus.h>
void handleDbusError(DBusError *err, const char *message);
DBusConnection *dbusConnect(void);
bool dbusCallMethod(DBusConnection *conn, const char *bus_name, const char *path,
const char *iface, const char *method);
#endif // !_DBUS_WRAPPER_H

75
duration.c Normal file
View File

@ -0,0 +1,75 @@
#include "duration.h"
#include "stdlib.h"
#include "string.h"
#include <stdio.h>
enum DurationParserState {
DURATION_PARSER_STATE_INITIAL,
DURATION_PARSER_STATE_NUMBER,
DURATION_PARSER_STATE_SECOND,
DURATION_PARSER_STATE_MINUTE,
DURATION_PARSER_STATE_HOUR,
DURATION_PARSER_STATE_EOS,
};
Duration parseDuration(const char *duration) {
enum DurationParserState state = DURATION_PARSER_STATE_INITIAL;
Duration d;
d.seconds = 0;
d.hours = 0;
d.minutes = 0;
char numBuf[32] = "0";
for (const char *c = duration; *c; c++) {
if (state == DURATION_PARSER_STATE_INITIAL) {
if (*c >= '0' && *c <= '9') {
state = DURATION_PARSER_STATE_NUMBER;
} else if (*c == 's' || *c == 'S') {
state = DURATION_PARSER_STATE_SECOND;
} else if (*c == 'm' || *c == 'M') {
state = DURATION_PARSER_STATE_MINUTE;
} else if (*c == 'h' || *c == 'H') {
state = DURATION_PARSER_STATE_HOUR;
}
}
if (state == DURATION_PARSER_STATE_NUMBER) {
const char *start = c;
while (*c >= '0' && *c <= '9') {
numBuf[c - start] = *c;
c++;
}
c--;
state = DURATION_PARSER_STATE_INITIAL;
} else if (state == DURATION_PARSER_STATE_SECOND) {
d.seconds = atoi(numBuf);
strncpy(numBuf, "0", 32);
state = DURATION_PARSER_STATE_INITIAL;
} else if (state == DURATION_PARSER_STATE_MINUTE) {
d.minutes = atoi(numBuf);
strncpy(numBuf, "0", 32);
state = DURATION_PARSER_STATE_INITIAL;
} else if (state == DURATION_PARSER_STATE_HOUR) {
d.hours = atoi(numBuf);
strncpy(numBuf, "0", 32);
state = DURATION_PARSER_STATE_INITIAL;
}
}
return d;
}
unsigned long durationToSeconds(const Duration *d) {
return d->hours * 3600 + d->minutes * 60 + d->seconds;
}
Duration secondsToDuration(unsigned long seconds) {
Duration d;
d.hours = seconds / 3600;
d.minutes = (seconds % 3600) / 60;
d.seconds = seconds % 60;
return d;
}
char* formatDuration(const Duration *d) {
char buf[32];
sprintf(buf, "%02lu:%02lu:%02lu", d->hours, d->minutes, d->seconds);
return strdup(buf);
}

14
duration.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef _DURATION_H
#define _DURATION_H
typedef struct {
unsigned long seconds;
unsigned long minutes;
unsigned long hours;
} Duration;
Duration parseDuration(const char *duration);
unsigned long durationToSeconds(const Duration *duration);
Duration secondsToDuration(unsigned long seconds);
char* formatDuration(const Duration *duration);
#endif

7
echo.c Normal file
View File

@ -0,0 +1,7 @@
#include <stdbool.h>
#include <stdio.h>
bool echo(void) {
printf("Timer has ran out!\n");
return true;
}

8
echo.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef _ECHO_H
#define _ECHO_H
#include <stdbool.h>
bool echo(void);
#endif

64
flake.lock Normal file
View File

@ -0,0 +1,64 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": [
"systems"
]
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1712791164,
"narHash": "sha256-3sbWO1mbpWsLepZGbWaMovSO7ndZeFqDSdX0hZ9nVyw=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "1042fd8b148a9105f3c0aca3a6177fd1d9360ba5",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"systems": "systems"
}
},
"systems": {
"locked": {
"lastModified": 1680978846,
"narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=",
"owner": "nix-systems",
"repo": "x86_64-linux",
"rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "x86_64-linux",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

57
flake.nix Normal file
View File

@ -0,0 +1,57 @@
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
systems.url = "github:nix-systems/x86_64-linux";
flake-utils = {
url = "github:numtide/flake-utils";
inputs.systems.follows = "systems";
};
};
outputs = {
nixpkgs,
flake-utils,
...
}: let
drv = {
stdenv,
lib,
libX11,
libXScrnSaver,
dbus,
pkg-config,
...
}:
stdenv.mkDerivation {
pname = "sleeptimer";
version = "0.0.1";
src = lib.cleanSource ./.;
nativeBuildInputs = [pkg-config];
buildInputs = [libX11 libXScrnSaver dbus.dev];
installPhase = ''
mkdir -p $out/bin
cp sleeptimer $out/bin
'';
};
in
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {
inherit system;
};
llvm = pkgs.llvmPackages_latest;
package = pkgs.callPackage drv {inherit (llvm) stdenv;};
in {
devShells.default = pkgs.mkShell.override {inherit (llvm) stdenv;} {
inputsFrom = [package];
packages = with pkgs; [
bear
clang-tools
zlib
lzma
icu74
];
};
packages = {
default = package;
};
});
}

32
idle.c Normal file
View File

@ -0,0 +1,32 @@
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/extensions/scrnsaver.h>
/* Get the idle time from the X Screen Saver extionsion.
*
* Returns:
* long: Idle time in milliseconds
*/
Display *xorgDisplay;
XScreenSaverInfo *xorgScreensaverInfo;
unsigned long getIdleTime(void) {
unsigned long idle = 0;
if (NULL == xorgDisplay) {
xorgDisplay = XOpenDisplay(NULL);
}
if (!xorgDisplay) {
return idle;
}
if (NULL == xorgScreensaverInfo) {
xorgScreensaverInfo = XScreenSaverAllocInfo();
}
XScreenSaverQueryInfo(xorgDisplay, DefaultRootWindow(xorgDisplay), xorgScreensaverInfo);
idle = xorgScreensaverInfo->idle;
return idle;
}

6
idle.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef _IDLE_H
#define _IDLE_H
long getIdleTime(void);
#endif

32
main.c Normal file
View File

@ -0,0 +1,32 @@
#include <time.h>
#include <unistd.h>
#include "actions.h"
#include "duration.h"
#include "sleeptimer.h"
#include "stdio.h"
int main(int argc, char **argv) {
time_t start = time(NULL);
Duration duration = {.minutes = 30};
if (argc >= 2) {
duration = parseDuration(argv[1]);
}
enum Action action = ActionEcho;
if (argc >= 3) {
action = parseAction(argv[2]);
}
printf("Sleep timer set for \"%s\"\n", formatDuration(&duration));
unsigned long seconds = durationToSeconds(&duration);
actionCallback callback = getActionCallback(action);
if (!sleeptimer(seconds, callback)) {
return 1;
}
time_t end = time(NULL);
Duration runtime = secondsToDuration(end - start);
printf("Program ran for \"%s\"\n", formatDuration(&runtime));
}

21
sleeptimer.c Normal file
View File

@ -0,0 +1,21 @@
#include "actions.h"
#include "idle.h"
#include <stdbool.h>
#include <time.h>
#include <unistd.h>
bool sleeptimer(unsigned long seconds, actionCallback callback) {
unsigned long millis = seconds * 1000;
unsigned long idleTime = getIdleTime();
struct timespec sleepTime = {.tv_sec = 0, .tv_nsec = 100 * 1000000};
while (idleTime < millis) {
idleTime = getIdleTime();
nanosleep(&sleepTime, NULL);
}
if (!(*callback)()) {
return false;
}
return true;
}

9
sleeptimer.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __SLEEPTIMER_H
#define __SLEEPTIMER_H
#include "actions.h"
#include <stdbool.h>
bool sleeptimer(unsigned long seconds, actionCallback callback);
#endif

16
suspend.c Normal file
View File

@ -0,0 +1,16 @@
#include "dbus_wrapper.h"
bool suspend(void) {
const char *bus_name = "org.freedesktop.login1";
const char *path = "/org/freedesktop/login1";
const char *iface = "org.freedesktop.login1.Manager";
const char *method = "Suspend";
DBusConnection *conn = dbusConnect();
if (NULL == conn) {
return false;
}
dbusCallMethod(conn, bus_name, path, iface, method);
return true;
}

5
suspend.h Normal file
View File

@ -0,0 +1,5 @@
#ifndef _SUSPEND_H
#define _SUSPEND_H
#include <stdbool.h>
bool suspend(void);
#endif