/* * SpaceAPI based tray applet for BSD* / Linux / UNIX etc. using GTK2 * * Author: hanez * Website: https://git.xw3.org/hanez/spacetray * * Licensed under the terms of the MIT License. */ #include #include #include #if LIBNOTIFY #include #endif #include #include #include #include #include #include #include "themes/lock_new.h" struct string { char *ptr; size_t len; }; static char *agent = "Spacetray/0.42"; static char *name = "SpaceAPI widget for UNIX"; static char *statusurl = NULL; // The delay for polling the dooris service. Adjust this before compiling static int delay = 300; // ms. aka 15 minutes bool door_open = false; bool old_door_open = false; int door_last_change = 0; char space_name[128] = "Unknown Space"; void on_quit_menu_item_activated(GtkMenuItem *item, gpointer data) { gtk_main_quit(); } void on_popup_menu(GtkStatusIcon *status_icon, guint button, guint activate_time, gpointer data) { GtkWidget *menu, *quit_item; menu = gtk_menu_new(); quit_item = gtk_menu_item_new_with_label("Quit"); g_signal_connect(G_OBJECT(quit_item), "activate", G_CALLBACK(on_quit_menu_item_activated), NULL); gtk_menu_shell_append(GTK_MENU_SHELL(menu), quit_item); gtk_widget_show_all(menu); gtk_menu_popup(GTK_MENU(menu), NULL, NULL, gtk_status_icon_position_menu, status_icon, button, activate_time); } 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); leaf_json_object = json_object_object_get(response_json_object, "space"); if (leaf_json_object != NULL) { const char *s_name = json_object_get_string(leaf_json_object); if (s_name != NULL) { strncpy(space_name, s_name, sizeof(space_name) - 1); space_name[sizeof(space_name) - 1] = '\0'; } } // 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; } } GdkPixbuf *icon_open = NULL; GdkPixbuf *icon_closed = NULL; static GdkPixbuf *pixbuf_new_from_data(const guint8 *data) { return gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, 8, 52, 48, 208, NULL, NULL); } void update_tooltip() { char door[256]; 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), "%s: Door open since %s", space_name, date_str); } else { snprintf(door, sizeof(door), "%s: Door closed since %s", space_name, date_str); } gtk_status_icon_set_tooltip(tray_icon, door); } void invoke_notification() { char door[256]; 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), "%s: Door open since %s", space_name, date_str); } else { snprintf(door, sizeof(door), "%s: Door closed since %s", space_name, date_str); } #if LIBNOTIFY NotifyNotification *n; n = notify_notification_new(name, door, NULL); if (door_open == true) { notify_notification_set_icon_from_pixbuf(n, icon_open); } else { notify_notification_set_icon_from_pixbuf(n, icon_closed); } 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"); gtk_status_icon_set_from_pixbuf(tray_icon, icon_open); } else { printf("Set to status: closed\n"); gtk_status_icon_set_from_pixbuf(tray_icon, icon_closed); } } void do_it() { static bool first_run = true; get_bouncer_data(); set_status(); update_tooltip(); 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); icon_open = pixbuf_new_from_data(icon_open_data); icon_closed = pixbuf_new_from_data(icon_closed_data); #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); g_signal_connect(G_OBJECT(tray_icon), "popup-menu", G_CALLBACK(on_popup_menu), NULL); gtk_status_icon_set_visible(tray_icon, true); do_it(); if (pthread_create(&thread_id, NULL, status_update_thread, NULL) != 0) { fprintf(stderr, "Error creating thread\n"); return 1; } gtk_main(); return 0; }