1 /**
2 * This file is a part of the Cairo-Dock project
3 *
4 * Copyright : (C) see the 'copyright' file.
5 * E-mail : see the 'copyright' file.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <stdint.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <glib.h>
24
25 #include "applet-impulse.h"
26 #include "Impulse.h"
27
28 #define IM_TAB_SIZE 256
29
30 extern CairoDockHidingEffect *g_pHidingBackend; // cairo_dock_is_hidden
31
32 ////////////////// IMPULSE //////////////////
33
_im_start(void)34 void _im_start (void)
35 {
36 cd_debug ("Impulse: start im");
37 im_start ();
38 }
39
_im_stop(void)40 void _im_stop (void)
41 {
42 cd_debug ("Impulse: stop im");
43 //im_stop(); // FIXME => if stopped, the client is not stopped and im_getSnapshot(IM_FFT) give always the same thing...
44 }
45
cd_impulse_im_setSourceIndex(gint iSourceIndex)46 void cd_impulse_im_setSourceIndex (gint iSourceIndex)
47 {
48 cd_debug ("Impulse: iSourceIndex = %d", iSourceIndex);
49 im_setSourceIndex (iSourceIndex);
50 }
51
52 ////////////////// USEFUL FUNCTIONS //////////////////
53
_get_icons_list_without_separators(CDSharedMemory * pSharedMemory)54 static void _get_icons_list_without_separators (CDSharedMemory *pSharedMemory)
55 {
56 if (pSharedMemory->pDock == NULL)
57 {
58 pSharedMemory->pIconsList = NULL;
59 return;
60 }
61
62 pSharedMemory->bIsUpdatingIconsList = TRUE;
63
64 pSharedMemory->pIconsList = NULL;
65 GList *ic;
66 Icon *pIcon;
67 for (ic = pSharedMemory->pDock->icons; ic != NULL; ic = ic->next)
68 {
69 pIcon = ic->data;
70 // cd_debug ("Impulse: icon name=%s", pIcon->cName);
71 if (! CAIRO_DOCK_ICON_TYPE_IS_SEPARATOR (pIcon))
72 pSharedMemory->pIconsList = g_list_append (pSharedMemory->pIconsList, pIcon);
73 }
74 g_list_free (ic);
75 pSharedMemory->bIsUpdatingIconsList = FALSE;
76 cd_debug ("Impulse: updated icons list: %d", g_list_length(pSharedMemory->pIconsList));
77 }
78
79 //static gboolean _animate_the_dock (CDSharedMemory *pSharedMemory)
_animate_the_dock(gpointer data)80 static gboolean _animate_the_dock (gpointer data)
81 {
82 CD_APPLET_ENTER;
83 // cd_debug ("Impulse: in");
84 if (myData.pSharedMemory->bIsUpdatingIconsList
85 || cairo_dock_is_hidden (myData.pSharedMemory->pDock)) // not needed for the animations but not for pulse.
86 CD_APPLET_LEAVE (TRUE);
87
88 if (myData.pSharedMemory->pIconsList == NULL)
89 {
90 cd_impulse_stop_animations (TRUE);
91 CD_APPLET_LEAVE (FALSE);
92 }
93
94 guint iIcons = IM_TAB_SIZE / g_list_length (myData.pSharedMemory->pIconsList); // number of icons (without separators)
95
96 double *array = im_getSnapshot();
97
98 // we check if there is a signal (most of the time, all values are > 0)
99 if (array[0] == 0.0)
100 { // is it really null?
101 int j;
102 for (j = 1; j < IM_TAB_SIZE; j++)
103 {
104 if (array[j] != 0)
105 break; // there is a signal
106 }
107
108 cd_debug ("Impulse: No Signal? %d", j);
109 if (j == IM_TAB_SIZE)
110 CD_APPLET_LEAVE (TRUE);
111 /* TODO?
112 * if null, increase the time of the loop?
113 * Maybe use: PA_SUBSCRIPTION_EVENT_CLIENT?
114 * or something else in pulse?
115 */
116 }
117
118 // Computing and animations
119 int i;
120 double l = 0.0;
121 GList *ic = myData.pSharedMemory->pIconsList;
122 Icon *pIcon;
123 gboolean bHasNotBeenAnimated = TRUE;
124 for (i = 0; ic != NULL; i++) // i < 256
125 {
126 l += array[i]; // a sum for the average
127 if (i != 0 && (i % iIcons) == 0)
128 {
129 pIcon = ic->data;
130 // cd_debug ("Impulse: Average: i=%d, l=%f ; I=%d ; l/I=%f ; %s", i, l, iIcons, l/iIcons, pIcon->cName);
131 if ((l/iIcons) > myData.pSharedMemory->fMinValueToAnim) // animation
132 {
133 //cd_debug ("Impulse: animation on this icon=%s", pIcon->cName);
134 gldi_icon_request_animation (pIcon,
135 myData.pSharedMemory->cIconAnimation,
136 myData.pSharedMemory->iNbAnimations);
137 bHasNotBeenAnimated = FALSE;
138 myData.pSharedMemory->bNeedRefreshIfNotAnimated = TRUE;
139 }
140 else if (myData.pSharedMemory->bStopAnimations)
141 gldi_icon_stop_animation (pIcon);
142 l = 0.0;
143 ic = ic->next;
144 }
145 }
146 if (bHasNotBeenAnimated && myData.pSharedMemory->bStopAnimations && myData.pSharedMemory->bNeedRefreshIfNotAnimated)
147 {
148 cd_debug ("Impulse: refresh container");
149 cairo_dock_redraw_container (CAIRO_CONTAINER (myData.pSharedMemory->pDock));
150 myData.pSharedMemory->bNeedRefreshIfNotAnimated = FALSE; // no need of refresh until the next animation and if bHasNotBeenAnimated
151 }
152
153 //cd_debug ("Impulse: out");
154 g_list_free (ic);
155 CD_APPLET_LEAVE (TRUE);
156 }
157
_impulse_check_pulse_status(void)158 static gboolean _impulse_check_pulse_status (void)
159 {
160 myData.iSidCheckStatus = 0;
161
162 cd_debug ("Impulse: checking PulseAudio Context status");
163
164 if (! myData.bHasBeenStarted && im_context_state () == IM_FAILED)
165 {
166 cd_impulse_stop_animations (FALSE); // icon will be modified here above
167 cd_debug ("Impulse: starting failed");
168 gldi_dialogs_remove_on_icon (myIcon);
169 gldi_dialog_show_temporary_with_icon (D_("There is something wrong with PulseAudio.\nCan you check its status (installed? running? version?) and report this bug (if any) to forum.glx-dock.org"),
170 myIcon,
171 myContainer,
172 5000,
173 MY_APPLET_SHARE_DATA_DIR"/"MY_APPLET_ICON_FILE);
174 CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cIconImpulseERROR, "impulse-error.svg");
175 }
176
177 return FALSE;
178 }
179
180 ////////////////// NOTIFICATIONS //////////////////
181
182 /*void cd_impulse_start_animations (void)
183 {
184 myData.iSidAnimate = g_timeout_add (myConfig.iLoopTime, (GSourceFunc) _animate_the_dock, NULL);
185 }*/
186
_remove_notifications(void)187 void _remove_notifications (void)
188 {
189 gldi_object_remove_notification (&myDockObjectMgr,
190 NOTIFICATION_ICON_MOVED,
191 (GldiNotificationFunc) cd_impulse_on_icon_changed, NULL);
192 gldi_object_remove_notification (&myDockObjectMgr,
193 NOTIFICATION_INSERT_ICON,
194 (GldiNotificationFunc) cd_impulse_on_icon_changed, NULL);
195 gldi_object_remove_notification (&myDockObjectMgr,
196 NOTIFICATION_REMOVE_ICON,
197 (GldiNotificationFunc) cd_impulse_on_icon_changed, NULL);
198 }
199
_register_notifications(void)200 void _register_notifications (void)
201 {
202 gldi_object_register_notification (&myDockObjectMgr,
203 NOTIFICATION_ICON_MOVED,
204 (GldiNotificationFunc) cd_impulse_on_icon_changed,
205 GLDI_RUN_FIRST, NULL);
206 gldi_object_register_notification (&myDockObjectMgr,
207 NOTIFICATION_INSERT_ICON,
208 (GldiNotificationFunc) cd_impulse_on_icon_changed,
209 GLDI_RUN_FIRST, NULL);
210 gldi_object_register_notification (&myDockObjectMgr,
211 NOTIFICATION_REMOVE_ICON,
212 (GldiNotificationFunc) cd_impulse_on_icon_changed,
213 GLDI_RUN_FIRST, NULL);
214 }
215
216 ////////////////// GENERAL FUNCTIONS //////////////////
217
cd_impulse_stop_animations(gboolean bChangeIcon)218 void cd_impulse_stop_animations (gboolean bChangeIcon)
219 {
220 //if (myData.pTask != NULL)
221 if (myData.iSidAnimate != 0)
222 {
223 /*gldi_task_discard (myData.pTask);
224 myData.pTask = NULL;*/
225 g_source_remove (myData.iSidAnimate);
226 myData.iSidAnimate = 0;
227 // _free_shared_memory (myData.pSharedMemory);
228 _remove_notifications ();
229 }
230 if (myData.bPulseLaunched)
231 _im_stop();
232 if (bChangeIcon)
233 cd_impulse_draw_current_state ();
234 // myData.bPulseLaunched = FALSE; //FIXME => if already started and stopped, it will crash... because not correctly stopped...
235 }
236
cd_impulse_launch_task(void)237 void cd_impulse_launch_task (void) //(GldiModuleInstance *myApplet)
238 {
239 // if a task is already launching
240 /*if (myData.pTask != NULL)
241 {
242 gldi_task_discard (myData.pTask);
243 myData.pTask = NULL;
244 }*/
245 if (myData.iSidAnimate != 0)
246 cd_impulse_stop_animations (FALSE); // icon will be modified here above
247
248 // PulseAudio Server
249 if (! myData.bPulseLaunched)
250 {
251 _im_start (); // FIXME => if already started and stopped, it will crash... because not correctly stopped...
252 myData.bPulseLaunched = TRUE;
253 }
254
255 /*myData.pTask = gldi_task_new_full (1,// (SECOND) myConfig.iLoopTime,
256 // (GldiGetDataAsyncFunc) _get_icons_list_without_separators,
257 NULL,
258 (GldiUpdateSyncFunc) _animate_the_dock,
259 (GFreeFunc) _free_shared_memory,
260 myData.pSharedMemory);
261 _get_icons_list_without_separators (myData.pSharedMemory);
262 gldi_task_launch (myData.pTask);*/
263
264 _get_icons_list_without_separators (myData.pSharedMemory);
265 _register_notifications();
266
267 myData.iSidAnimate = g_timeout_add (myConfig.iLoopTime, (GSourceFunc) _animate_the_dock, NULL); // or into a thread + time?
268 cd_debug ("Impulse: animations started (checking status: %d)", myData.iSidCheckStatus);
269 cd_impulse_draw_current_state ();
270
271 if (myData.iSidCheckStatus == 0)
272 myData.iSidCheckStatus = g_timeout_add_seconds (1, (GSourceFunc) _impulse_check_pulse_status, NULL);
273 }
274
cd_impulse_on_icon_changed(gpointer pUserData,Icon * pIcon,CairoDock * pDock)275 gboolean cd_impulse_on_icon_changed (gpointer pUserData, Icon *pIcon, CairoDock *pDock)
276 {
277 // launched and something has changed in the right dock
278 //cd_debug ("Impulse: update needed? %d | %d", pDock, myConfig.pDock);
279 if (myData.iSidAnimate != 0 && pDock == myConfig.pDock)
280 {
281 _get_icons_list_without_separators (myData.pSharedMemory);
282 }
283 return GLDI_NOTIFICATION_LET_PASS;
284 }
285
cd_impulse_draw_current_state(void)286 void cd_impulse_draw_current_state (void)
287 {
288 if (myData.iSidAnimate != 0)
289 CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cIconImpulseON, "impulse-running.svg");
290 else
291 CD_APPLET_SET_USER_IMAGE_ON_MY_ICON (myConfig.cIconImpulseOFF, "impulse-stopped.svg");
292 }
293
294 ////////////////// DELAY //////////////////
295
_impulse_restart_delayed(void)296 static gboolean _impulse_restart_delayed (void)
297 {
298 myData.iSidRestartDelayed = 0;
299
300 if (! myData.bHasBeenStarted)
301 {
302 myData.bHasBeenStarted = TRUE;
303 cd_message ("Impulse has been started");
304
305 if (myConfig.bFree) // It's maybe a hack but Cairo-Penguin does that :)
306 {
307 gldi_icon_detach (myIcon);
308 }
309 else
310 {
311 gldi_icon_insert_in_container (myIcon, myContainer, CAIRO_DOCK_ANIMATE_ICON);
312 }
313
314 cd_impulse_launch_task (); // launched the animations
315 }
316
317 return FALSE;
318 }
319
cd_impulse_start_animating_with_delay(void)320 void cd_impulse_start_animating_with_delay (void)
321 {
322 if (myData.iSidRestartDelayed != 0)
323 return ;
324
325 if (cairo_dock_is_loading ())
326 myData.iSidRestartDelayed = g_timeout_add_seconds (2, (GSourceFunc) _impulse_restart_delayed, NULL); // priority to the loading of the dock
327 else
328 myData.iSidRestartDelayed = g_timeout_add_seconds (1, (GSourceFunc) _impulse_restart_delayed, NULL); // if we have to detach the icon
329 }
330