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