/* * SpaceAPI based tray applet for BSD* / Linux / UNIX etc. using GTK2 * * Author: hanez * Website: https://hanez.org/project/spacetray/ * * License (MIT License): * * TODO: * - Add door open/close date to notification and status * - Fix: not show notification twice at start when status "open" */ #include #define GLIB_DISABLE_DEPRECATION_WARNINGS #define GDK_PIXBUF_ENABLE_BACKWARDS_COMPATIBILITY #include #include #include #ifdef G_GNUC_BEGIN_IGNORE_DEPRECATIONS #define IGNORE_DEPRECATIONS_START G_GNUC_BEGIN_IGNORE_DEPRECATIONS #define IGNORE_DEPRECATIONS_END G_GNUC_END_IGNORE_DEPRECATIONS #else #define IGNORE_DEPRECATIONS_START #define IGNORE_DEPRECATIONS_END #endif #if LIBNOTIFY #include #endif #include #include #include #include #include #include #include "themes/lock.h" struct string { char *ptr; size_t len; }; static char *name = "SpaceAPI widget for UNIX"; static char *statusurl = NULL; static char *agent = "Spacetray/0.42"; // The delay for polling the dooris service. Adjust this before compiling static int delay = 1000; // ms. aka 15 minutes bool door_open = false; bool old_door_open = false; int door_last_change = 0; void do_it(); void invoke_notification(); gboolean do_it_wrapper(gpointer data) { do_it(); return FALSE; } void *status_update_thread(void *arg) { while (1) { sleep(delay); g_idle_add(do_it_wrapper, NULL); } return NULL; } GtkStatusIcon *tray_icon; void init_string(struct string *s) { s->len = 0; s->ptr = malloc(s->len+1); if (s->ptr == NULL) { fprintf(stderr, "malloc() failed\n"); exit(EXIT_FAILURE); } s->ptr[0] = '\0'; } size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s) { size_t new_len = s->len + size*nmemb; s->ptr = realloc(s->ptr, new_len+1); if (s->ptr == NULL) { fprintf(stderr, "realloc() failed\n"); exit(EXIT_FAILURE); } memcpy(s->ptr+s->len, ptr, size*nmemb); s->ptr[new_len] = '\0'; s->len = new_len; return size*nmemb; } void get_bouncer_data() { CURL *curl; CURLcode res; struct json_object *response_json_object; struct json_object *door_json_object; struct json_object *leaf_json_object; bool door_status; struct string s; printf("Called get_bouncer_data()\n"); old_door_open = door_open; init_string(&s); curl = curl_easy_init(); if(!curl) { fprintf(stderr, "curl_easy_init() failed.\n"); return; } curl_easy_setopt(curl, CURLOPT_URL, statusurl); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); curl_easy_setopt(curl, CURLOPT_USERAGENT, agent); res = curl_easy_perform(curl); curl_easy_cleanup(curl); if(res != CURLE_OK) { fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); return; } response_json_object = json_tokener_parse(s.ptr); free(s.ptr); // door objects door_json_object = json_object_object_get(response_json_object, "state"); leaf_json_object = json_object_object_get(door_json_object, "open"); door_status = json_object_get_int(leaf_json_object); //door_status = true; leaf_json_object = json_object_object_get(door_json_object, "lastchange"); door_last_change = json_object_get_int(leaf_json_object); // free response object json_object_put(response_json_object); printf("Door status: = %d\n", door_status); printf("Door last change: = %d\n", door_last_change); if (door_status == true) { door_open = true; } else { door_open = false; } } void invoke_notification() { char door[128]; char date_str[64]; time_t last_change_time = (time_t)door_last_change; struct tm *tm_info = localtime(&last_change_time); strftime(date_str, sizeof(date_str), "%Y-%m-%d %H:%M:%S", tm_info); if (door_open == true) { snprintf(door, sizeof(door), "Door open since %s", date_str); } else { snprintf(door, sizeof(door), "Door closed since %s", date_str); } gtk_status_icon_set_tooltip(tray_icon, door); #if LIBNOTIFY NotifyNotification *n; n = notify_notification_new(name, door, NULL); //notify_notification_set_timeout(n, 10000); if (door_open == true) { IGNORE_DEPRECATIONS_START notify_notification_set_icon_from_pixbuf(n, gdk_pixbuf_from_pixdata( &icon_open_pixbuf, true, NULL)); IGNORE_DEPRECATIONS_END } else { IGNORE_DEPRECATIONS_START notify_notification_set_icon_from_pixbuf(n, gdk_pixbuf_from_pixdata( &icon_closed_pixbuf, true, NULL)); IGNORE_DEPRECATIONS_END } notify_notification_show(n, NULL); g_object_unref(G_OBJECT(n)); #endif } void set_status() { if (door_open == true) { printf("Set to status: open\n"); IGNORE_DEPRECATIONS_START gtk_status_icon_set_from_pixbuf(tray_icon, gdk_pixbuf_from_pixdata(&icon_open_pixbuf, true, NULL)); IGNORE_DEPRECATIONS_END } else { printf("Set to status: closed\n"); IGNORE_DEPRECATIONS_START gtk_status_icon_set_from_pixbuf(tray_icon, gdk_pixbuf_from_pixdata(&icon_closed_pixbuf, true, NULL)); IGNORE_DEPRECATIONS_END } } void do_it() { static bool first_run = true; get_bouncer_data(); set_status(); if (!first_run && door_open != old_door_open) { invoke_notification(); } first_run = false; } int main(int argc, char **argv) { pthread_t thread_id; if (argc != 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } statusurl = argv[1]; gtk_init(&argc, &argv); #if LIBNOTIFY notify_init(name); #endif tray_icon = gtk_status_icon_new(); g_signal_connect(G_OBJECT(tray_icon), "activate", G_CALLBACK(do_it), NULL); gtk_status_icon_set_visible(tray_icon, true); do_it(); // DEBUG STUFF //door_open = true; //set_status(); // END DEBUG STUFF //invoke_notification(); //gtk_timeout_add(delay, (GtkFunction)do_it, (gpointer)NULL); // guint xxx = g_timeout_add_seconds(1000, (GSourceFunc)do_it, (gpointer)NULL); if (pthread_create(&thread_id, NULL, status_update_thread, NULL) != 0) { fprintf(stderr, "Error creating thread\n"); return 1; } gtk_main(); return 0; }