You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
335 lines
9.4 KiB
335 lines
9.4 KiB
5 years ago
|
#include <glib.h>
|
||
|
#include <libnotify/notify.h>
|
||
|
#include <locale.h>
|
||
|
#include <stdbool.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||
|
|
||
|
static gchar *appname = "dunstify";
|
||
|
static gchar *summary = NULL;
|
||
|
static gchar *body = NULL;
|
||
|
static NotifyUrgency urgency = NOTIFY_URGENCY_NORMAL;
|
||
|
static gchar *urgency_str = NULL;
|
||
|
static gchar **hint_strs = NULL;
|
||
|
static gchar **action_strs = NULL;
|
||
|
static gint timeout = NOTIFY_EXPIRES_DEFAULT;
|
||
|
static gchar *icon = NULL;
|
||
|
static gchar *raw_icon_path = NULL;
|
||
|
static gboolean capabilities = false;
|
||
|
static gboolean serverinfo = false;
|
||
|
static gboolean printid = false;
|
||
|
static guint32 replace_id = 0;
|
||
|
static guint32 close_id = 0;
|
||
|
static gboolean block = false;
|
||
|
|
||
|
static GOptionEntry entries[] =
|
||
|
{
|
||
|
{ "appname", 'a', 0, G_OPTION_ARG_STRING, &appname, "Name of your application", "NAME" },
|
||
|
{ "urgency", 'u', 0, G_OPTION_ARG_STRING, &urgency_str, "The urgency of this notification", "URG" },
|
||
|
{ "hints", 'h', 0, G_OPTION_ARG_STRING_ARRAY, &hint_strs, "User specified hints", "HINT" },
|
||
|
{ "action", 'A', 0, G_OPTION_ARG_STRING_ARRAY, &action_strs, "Actions the user can invoke", "ACTION" },
|
||
|
{ "timeout", 't', 0, G_OPTION_ARG_INT, &timeout, "The time until the notification expires", "TIMEOUT" },
|
||
|
{ "icon", 'i', 0, G_OPTION_ARG_STRING, &icon, "An Icon that should be displayed with the notification", "ICON" },
|
||
|
{ "raw_icon", 'I', 0, G_OPTION_ARG_STRING, &raw_icon_path, "Path to the icon to be sent as raw image data", "RAW_ICON"},
|
||
|
{ "capabilities", 'c', 0, G_OPTION_ARG_NONE, &capabilities, "Print the server capabilities and exit", NULL},
|
||
|
{ "serverinfo", 's', 0, G_OPTION_ARG_NONE, &serverinfo, "Print server information and exit", NULL},
|
||
|
{ "printid", 'p', 0, G_OPTION_ARG_NONE, &printid, "Print id, which can be used to update/replace this notification", NULL},
|
||
|
{ "replace", 'r', 0, G_OPTION_ARG_INT, &replace_id, "Set id of this notification.", "ID"},
|
||
|
{ "close", 'C', 0, G_OPTION_ARG_INT, &close_id, "Set id of this notification.", "ID"},
|
||
|
{ "block", 'b', 0, G_OPTION_ARG_NONE, &block, "Block until notification is closed and print close reason", NULL},
|
||
|
{ NULL }
|
||
|
};
|
||
|
|
||
|
void die(int exit_value)
|
||
|
{
|
||
|
if (notify_is_initted())
|
||
|
notify_uninit();
|
||
|
exit(exit_value);
|
||
|
}
|
||
|
|
||
|
void print_capabilities(void)
|
||
|
{
|
||
|
GList *caps = notify_get_server_caps();
|
||
|
for (GList *iter = caps; iter; iter = iter->next) {
|
||
|
if (strlen(iter->data) > 0) {
|
||
|
g_print("%s\n", (char *)iter->data);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void print_serverinfo(void)
|
||
|
{
|
||
|
char *name;
|
||
|
char *vendor;
|
||
|
char *version;
|
||
|
char *spec_version;
|
||
|
|
||
|
if (!notify_get_server_info(&name, &vendor, &version, &spec_version)) {
|
||
|
g_printerr("Unable to get server information");
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
g_print("name:%s\nvendor:%s\nversion:%s\nspec_version:%s\n", name,
|
||
|
vendor,
|
||
|
version,
|
||
|
spec_version);
|
||
|
}
|
||
|
|
||
|
void parse_commandline(int argc, char *argv[])
|
||
|
{
|
||
|
GError *error = NULL;
|
||
|
GOptionContext *context;
|
||
|
|
||
|
context = g_option_context_new("- Dunstify");
|
||
|
g_option_context_add_main_entries(context, entries, NULL);
|
||
|
if (!g_option_context_parse(context, &argc, &argv, &error)){
|
||
|
g_printerr("Invalid commandline: %s\n", error->message);
|
||
|
exit(1);
|
||
|
}
|
||
|
|
||
|
g_option_context_free(context);
|
||
|
|
||
|
if (capabilities) {
|
||
|
print_capabilities();
|
||
|
die(0);
|
||
|
}
|
||
|
|
||
|
if (serverinfo) {
|
||
|
print_serverinfo();
|
||
|
die(0);
|
||
|
}
|
||
|
|
||
|
if (argc < 2 && close_id < 1) {
|
||
|
g_printerr("I need at least a summary\n");
|
||
|
die(1);
|
||
|
} else if (argc < 2) {
|
||
|
summary = g_strdup("These are not the summaries you are looking for");
|
||
|
} else {
|
||
|
summary = g_strdup(argv[1]);
|
||
|
}
|
||
|
|
||
|
if (argc > 2) {
|
||
|
body = g_strdup(argv[2]);
|
||
|
}
|
||
|
|
||
|
if (urgency_str) {
|
||
|
switch (urgency_str[0]) {
|
||
|
case 'l':
|
||
|
case 'L':
|
||
|
case '0':
|
||
|
urgency = NOTIFY_URGENCY_LOW;
|
||
|
break;
|
||
|
case 'n':
|
||
|
case 'N':
|
||
|
case '1':
|
||
|
urgency = NOTIFY_URGENCY_NORMAL;
|
||
|
break;
|
||
|
case 'c':
|
||
|
case 'C':
|
||
|
case '2':
|
||
|
urgency = NOTIFY_URGENCY_CRITICAL;
|
||
|
break;
|
||
|
default:
|
||
|
g_printerr("Unknown urgency: %s\n", urgency_str);
|
||
|
g_printerr("Assuming normal urgency\n");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
typedef struct _NotifyNotificationPrivate
|
||
|
{
|
||
|
guint32 id;
|
||
|
char *app_name;
|
||
|
char *summary;
|
||
|
char *body;
|
||
|
|
||
|
/* NULL to use icon data. Anything else to have server lookup icon */
|
||
|
char *icon_name;
|
||
|
|
||
|
/*
|
||
|
* -1 = use server default
|
||
|
* 0 = never timeout
|
||
|
* > 0 = Number of milliseconds before we timeout
|
||
|
*/
|
||
|
gint timeout;
|
||
|
|
||
|
GSList *actions;
|
||
|
GHashTable *action_map;
|
||
|
GHashTable *hints;
|
||
|
|
||
|
gboolean has_nondefault_actions;
|
||
|
gboolean updates_pending;
|
||
|
|
||
|
gulong proxy_signal_handler;
|
||
|
|
||
|
gint closed_reason;
|
||
|
} knickers;
|
||
|
|
||
|
int get_id(NotifyNotification *n)
|
||
|
{
|
||
|
knickers *kn = n->priv;
|
||
|
|
||
|
/* I'm sorry for taking a peek */
|
||
|
return kn->id;
|
||
|
}
|
||
|
|
||
|
void put_id(NotifyNotification *n, guint32 id)
|
||
|
{
|
||
|
knickers *kn = n->priv;
|
||
|
|
||
|
/* And know I'm putting stuff into
|
||
|
* your knickers. I'm sorry.
|
||
|
* I'm so sorry.
|
||
|
* */
|
||
|
|
||
|
kn->id = id;
|
||
|
}
|
||
|
|
||
|
void actioned(NotifyNotification *n, char *a, gpointer foo)
|
||
|
{
|
||
|
notify_notification_close(n, NULL);
|
||
|
g_print("%s\n", a);
|
||
|
die(0);
|
||
|
}
|
||
|
|
||
|
void closed(NotifyNotification *n, gpointer foo)
|
||
|
{
|
||
|
g_print("%d\n", notify_notification_get_closed_reason(n));
|
||
|
die(0);
|
||
|
}
|
||
|
|
||
|
void add_action(NotifyNotification *n, char *str)
|
||
|
{
|
||
|
char *action = str;
|
||
|
char *label = strchr(str, ',');
|
||
|
|
||
|
if (!label || *(label+1) == '\0') {
|
||
|
g_printerr("Malformed action. Excpected \"action,label\", got \"%s\"", str);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
*label = '\0';
|
||
|
label++;
|
||
|
|
||
|
notify_notification_add_action(n, action, label, actioned, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
void add_hint(NotifyNotification *n, char *str)
|
||
|
{
|
||
|
char *type = str;
|
||
|
char *name = strchr(str, ':');
|
||
|
if (!name || *(name+1) == '\0') {
|
||
|
g_printerr("Malformed hint. Expected \"type:name:value\", got \"%s\"", str);
|
||
|
return;
|
||
|
}
|
||
|
*name = '\0';
|
||
|
name++;
|
||
|
char *value = strchr(name, ':');
|
||
|
if (!value || *(value+1) == '\0') {
|
||
|
g_printerr("Malformed hint. Expected \"type:name:value\", got \"%s\"", str);
|
||
|
return;
|
||
|
}
|
||
|
*value = '\0';
|
||
|
value++;
|
||
|
|
||
|
if (strcmp(type, "int") == 0)
|
||
|
notify_notification_set_hint_int32(n, name, atoi(value));
|
||
|
else if (strcmp(type, "double") == 0)
|
||
|
notify_notification_set_hint_double(n, name, atof(value));
|
||
|
else if (strcmp(type, "string") == 0)
|
||
|
notify_notification_set_hint_string(n, name, value);
|
||
|
else if (strcmp(type, "byte") == 0) {
|
||
|
gint h_byte = g_ascii_strtoull(value, NULL, 10);
|
||
|
if (h_byte < 0 || h_byte > 0xFF)
|
||
|
g_printerr("Not a byte: \"%s\"", value);
|
||
|
else
|
||
|
notify_notification_set_hint_byte(n, name, (guchar) h_byte);
|
||
|
} else
|
||
|
g_printerr("Malformed hint. Expected a type of int, double, string or byte, got %s\n", type);
|
||
|
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
setlocale(LC_ALL, "");
|
||
|
#if !GLIB_CHECK_VERSION(2,35,0)
|
||
|
g_type_init();
|
||
|
#endif
|
||
|
parse_commandline(argc, argv);
|
||
|
|
||
|
if (!notify_init(appname)) {
|
||
|
g_printerr("Unable to initialize libnotify\n");
|
||
|
die(1);
|
||
|
}
|
||
|
|
||
|
NotifyNotification *n;
|
||
|
n = notify_notification_new(summary, body, icon);
|
||
|
notify_notification_set_timeout(n, timeout);
|
||
|
notify_notification_set_urgency(n, urgency);
|
||
|
|
||
|
GError *err = NULL;
|
||
|
|
||
|
if (raw_icon_path) {
|
||
|
GdkPixbuf *raw_icon = gdk_pixbuf_new_from_file(raw_icon_path, &err);
|
||
|
|
||
|
if(err) {
|
||
|
g_printerr("Unable to get raw icon: %s\n", err->message);
|
||
|
die(1);
|
||
|
}
|
||
|
|
||
|
notify_notification_set_image_from_pixbuf(n, raw_icon);
|
||
|
}
|
||
|
|
||
|
if (close_id > 0) {
|
||
|
put_id(n, close_id);
|
||
|
notify_notification_close(n, &err);
|
||
|
if (err) {
|
||
|
g_printerr("Unable to close notification: %s\n", err->message);
|
||
|
die(1);
|
||
|
}
|
||
|
die(0);
|
||
|
}
|
||
|
|
||
|
if (replace_id > 0) {
|
||
|
put_id(n, replace_id);
|
||
|
}
|
||
|
|
||
|
GMainLoop *l = NULL;
|
||
|
|
||
|
if (block || action_strs) {
|
||
|
l = g_main_loop_new(NULL, false);
|
||
|
g_signal_connect(n, "closed", G_CALLBACK(closed), NULL);
|
||
|
}
|
||
|
|
||
|
if (action_strs)
|
||
|
for (int i = 0; action_strs[i]; i++) {
|
||
|
add_action(n, action_strs[i]);
|
||
|
}
|
||
|
|
||
|
if (hint_strs)
|
||
|
for (int i = 0; hint_strs[i]; i++) {
|
||
|
add_hint(n, hint_strs[i]);
|
||
|
}
|
||
|
|
||
|
|
||
|
notify_notification_show(n, &err);
|
||
|
if (err) {
|
||
|
g_printerr("Unable to send notification: %s\n", err->message);
|
||
|
die(1);
|
||
|
}
|
||
|
|
||
|
if (printid)
|
||
|
g_print("%d\n", get_id(n));
|
||
|
|
||
|
if (block || action_strs)
|
||
|
g_main_loop_run(l);
|
||
|
|
||
|
g_object_unref(G_OBJECT (n));
|
||
|
|
||
|
die(0);
|
||
|
}
|
||
|
|
||
|
/* vim: set tabstop=8 shiftwidth=8 expandtab textwidth=0: */
|