1 /*
2  * Copyright (C) 2020 Purism SPC
3  * SPDX-License-Identifier: GPL-3.0+
4  * Author: Guido Günther <agx@sigxcpu.org>
5  */
6 #define LIBFEEDBACK_USE_UNSTABLE_API
7 #include "libfeedback.h"
8 
9 #include <glib.h>
10 #include <gio/gio.h>
11 #include <glib-unix.h>
12 
13 #include <locale.h>
14 
15 #define DEFAULT_EVENT "phone-incoming-call"
16 
17 static GMainLoop *loop;
18 
19 static gboolean
on_shutdown_signal(gpointer unused)20 on_shutdown_signal (gpointer unused)
21 {
22   /* End right away, lfb_uninit will end running feedback */
23   g_main_loop_quit (loop);
24 
25   return FALSE;
26 }
27 
28 static gboolean
on_watch_expired(gpointer unused)29 on_watch_expired (gpointer unused)
30 {
31   g_warning ("Watch expired waiting for all feedbacks to finish");
32   g_main_loop_quit (loop);
33 
34   return G_SOURCE_REMOVE;
35 }
36 
37 static void
on_feedback_ended(LfbEvent * event,int * data)38 on_feedback_ended (LfbEvent *event, int* data)
39 {
40   g_return_if_fail (LFB_IS_EVENT (event));
41 
42   g_debug  ("Feedback ended for event");
43   *data = TRUE;
44   g_main_loop_quit (loop);
45 }
46 
47 static gboolean
on_user_input(GIOChannel * channel,GIOCondition cond,LfbEvent * event)48 on_user_input (GIOChannel *channel, GIOCondition cond, LfbEvent *event)
49 {
50   g_autoptr(GError) err = NULL;
51 
52   if (cond == G_IO_IN) {
53     g_print ("Ending feedback\n");
54     if (!lfb_event_end_feedback (event, &err))
55       g_warning ("Failed to end feedback: %s", err->message);
56   }
57   return FALSE;
58 }
59 
60 static gboolean
trigger_event(const char * name,const gchar * profile,gint timeout)61 trigger_event (const char *name, const gchar *profile, gint timeout)
62 {
63   g_autoptr(GError) err = NULL;
64   g_autoptr(LfbEvent) event = NULL;
65   g_autoptr(GIOChannel) input = NULL;
66   int success = FALSE;
67 
68   g_unix_signal_add (SIGTERM, on_shutdown_signal, NULL);
69   g_unix_signal_add (SIGINT, on_shutdown_signal, NULL);
70 
71   g_print ("Triggering feedback for event '%s'\n", name);
72   event = lfb_event_new (name);
73   lfb_event_set_timeout (event, timeout);
74   if (profile)
75     lfb_event_set_feedback_profile (event, profile);
76 
77   g_signal_connect (event, "feedback-ended", (GCallback)on_feedback_ended, &success);
78   if (!lfb_event_trigger_feedback (event, &err)) {
79     g_print ("Failed to report event: %s\n", err->message);
80     return FALSE;
81   }
82 
83   input = g_io_channel_unix_new (STDIN_FILENO);
84   g_io_add_watch (input, G_IO_IN, (GIOFunc)on_user_input, event);
85   g_print ("Press <RETURN> to end feedback right away.\n");
86 
87   loop = g_main_loop_new (NULL, FALSE);
88   g_main_loop_run (loop);
89   g_main_loop_unref (loop);
90 
91   return success;
92 }
93 
94 static void
on_profile_changed(LfbGdbusFeedback * proxy,GParamSpec * psepc,gpointer unused)95 on_profile_changed (LfbGdbusFeedback *proxy, GParamSpec *psepc, gpointer unused)
96 {
97   g_print ("Set feedback profile to: '%s'\n",
98            lfb_get_feedback_profile ());
99   g_main_loop_quit (loop);
100 }
101 
102 static gboolean
set_profile(const gchar * profile)103 set_profile (const gchar *profile)
104 {
105   LfbGdbusFeedback *proxy;
106   const gchar *current;
107 
108   current = lfb_get_feedback_profile ();
109   g_debug ("Current profile is %s", current);
110   if (!g_strcmp0 (current, profile)) {
111     g_print ("Profile is already set to %s\n", profile);
112     return TRUE;
113   }
114 
115   g_debug ("Setting profile to %s", profile);
116   proxy = lfb_get_proxy ();
117 
118   /* Set profile and wait until we got notified about the profile change */
119   loop = g_main_loop_new (NULL, FALSE);
120   lfb_set_feedback_profile (profile);
121   g_signal_connect (proxy, "notify::profile", (GCallback)on_profile_changed, NULL);
122   g_main_loop_run (loop);
123   g_print ("Current feedback profile is: '%s'\n",
124            lfb_get_feedback_profile ());
125   return TRUE;
126 }
127 
128 int
main(int argc,char * argv[0])129 main (int argc, char *argv[0])
130 {
131   g_autoptr(GOptionContext) opt_context = NULL;
132   g_autoptr(GError) err = NULL;
133   g_autofree gchar *profile = NULL;
134   const char *name = NULL;
135   gboolean success;
136   int watch = 30;
137   int timeout = -1;
138   const GOptionEntry options [] = {
139     {"event", 'E', 0, G_OPTION_ARG_STRING, &name,
140      "Event name. (default: " DEFAULT_EVENT ").", NULL},
141     {"timeout", 't', 0, G_OPTION_ARG_INT, &timeout,
142      "Run feedback for timeout seconds", NULL},
143     {"profile", 'P', 0, G_OPTION_ARG_STRING, &profile,
144      "Profile name to set", NULL},
145     {"watch", 'w', 0, G_OPTION_ARG_INT, &watch,
146      "How long to watch for feedback longest", NULL},
147     { NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, NULL }
148   };
149 
150   setlocale (LC_ALL, "");
151   opt_context = g_option_context_new ("- A cli for feedbackd");
152   g_option_context_add_main_entries (opt_context, options, NULL);
153   if (!g_option_context_parse (opt_context, &argc, &argv, &err)) {
154     g_warning ("%s", err->message);
155     return 1;
156   }
157 
158   if (!lfb_init ("org.sigxcpu.fbcli", &err)) {
159     g_print ("Failed to init libfeedback: %s\n", err->message);
160     return 1;
161   }
162 
163   if (profile && !name)
164     success = set_profile (profile);
165   else {
166     if (!name)
167       name = g_strdup (DEFAULT_EVENT);
168 
169     g_timeout_add_seconds (watch, (GSourceFunc)on_watch_expired, NULL);
170     success = trigger_event (name, profile, timeout);
171   }
172 
173   lfb_uninit ();
174   return !success;
175 }
176