1 /* -------------------------------------------------------------------------
2 * xmms.c: conky support for XMMS-related projects
4 * Copyright (C) 2005 Philip Kovacs kovacsp3@comcast.net
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 * --------------------------------------------------------------------------- */
32 #if defined(XMMS) || defined(BMP) || defined(AUDACIOUS)
38 #include <sys/select.h>
40 #include <sys/types.h>
43 #define INFOPIPE_NAMED_PIPE "/tmp/xmms-info"
45 /* 14 keys comprise the output of the infopipe plugin. */
50 INFOPIPE_PLAYLIST_TUNES,
51 INFOPIPE_PLAYLIST_CURRTUNE,
52 INFOPIPE_USEC_POSITION,
64 static char *xmms_project_name[] = {
73 /* access to this item array is synchronized with mutexes */
74 static xmms_t g_items;
76 /* ------------------------------------
77 * Conky update function for XMMS data.
78 * ------------------------------------ */
79 void update_xmms(void)
82 The worker thread is updating the g_items array asynchronously to the main
83 conky thread. We merely copy the g_items array into the main thread's info
84 structure when the main thread's update cycle fires. Note that using the
85 mutexes here makes it easier since we won't have to do any sync in conky.c.
87 pthread_mutex_lock(&info.xmms.item_mutex);
88 memcpy(&info.xmms.items,g_items,sizeof(g_items));
89 pthread_mutex_unlock(&info.xmms.item_mutex);
93 /* ------------------------------------------------------------
94 * Create a worker thread for xmms-related media player status.
96 * Returns 0 on success, -1 on error.
97 * ------------------------------------------------------------*/
98 int create_xmms_thread(void)
100 /* Was an an available project requested? */
101 if (!TEST_XMMS_PROJECT_AVAILABLE(info.xmms.project_mask, info.xmms.current_project)) {
102 ERR("xmms_player '%s' not configured", xmms_project_name[info.xmms.current_project]);
106 /* The project should not be PROJECT_NONE */
107 if (info.xmms.current_project==PROJECT_NONE)
110 /* Is a worker is thread already running? */
111 if (info.xmms.thread)
114 /* Joinable thread for xmms activity */
115 pthread_attr_init(&info.xmms.thread_attr);
116 pthread_attr_setdetachstate(&info.xmms.thread_attr, PTHREAD_CREATE_JOINABLE);
118 pthread_mutex_init(&info.xmms.item_mutex, NULL);
119 pthread_mutex_init(&info.xmms.runnable_mutex, NULL);
120 /* Init runnable condition for worker thread */
121 pthread_mutex_lock(&info.xmms.runnable_mutex);
122 info.xmms.runnable=1;
123 pthread_mutex_unlock(&info.xmms.runnable_mutex);
124 #if defined(XMMS) || defined(BMP) || defined(AUDACIOUS)
125 if (info.xmms.current_project==PROJECT_XMMS ||
126 info.xmms.current_project==PROJECT_BMP ||
127 info.xmms.current_project==PROJECT_AUDACIOUS) {
128 if (pthread_create(&info.xmms.thread, &info.xmms.thread_attr, xmms_thread_func_dynamic, NULL))
132 #if defined(INFOPIPE)
133 if (info.xmms.current_project==PROJECT_INFOPIPE) {
134 if (pthread_create(&info.xmms.thread, &info.xmms.thread_attr, xmms_thread_func_infopipe, NULL))
142 /* ------------------------------------------------
143 * Destroy xmms-related media player status thread.
145 * Returns 0 on success, -1 on error.
146 * ------------------------------------------------ */
147 int destroy_xmms_thread(void)
149 /* Is a worker is thread running? If not, no error. */
150 if (!info.xmms.thread)
153 /* Signal xmms worker thread to terminate */
154 pthread_mutex_lock(&info.xmms.runnable_mutex);
155 info.xmms.runnable=0;
156 pthread_mutex_unlock(&info.xmms.runnable_mutex);
157 /* Destroy thread attribute and wait for thread */
158 pthread_attr_destroy(&info.xmms.thread_attr);
159 if (pthread_join(info.xmms.thread, NULL))
161 /* Destroy mutexes */
162 pthread_mutex_destroy(&info.xmms.item_mutex);
163 pthread_mutex_destroy(&info.xmms.runnable_mutex);
165 info.xmms.thread=(pthread_t)0;
169 #if defined(XMMS) || defined(BMP) || defined(AUDACIOUS)
170 void check_dlerror(void)
172 static const char *error;
174 if ((error = dlerror()) != NULL) {
175 ERR("error grabbing function symbol");
180 /* ------------------------------------------------------------
181 * Worker thread function for XMMS/BMP/Audacious data sampling.
182 * ------------------------------------------------------------ */
183 void *xmms_thread_func_dynamic(void *pvoid)
185 void *handle,*glib_v1_2_handle;
188 gint session,playpos,frames,length;
189 gint rate,freq,chans;
190 gchar *psong,*pfilename;
192 /* Function pointers for the functions we load dynamically */
193 void (*g_free_v1_2)(gpointer mem);
194 gboolean (*xmms_remote_is_running)(gint session);
195 gboolean (*xmms_remote_is_paused)(gint session);
196 gboolean (*xmms_remote_is_playing)(gint session);
197 gint (*xmms_remote_get_playlist_pos)(gint session);
198 gchar *(*xmms_remote_get_playlist_title)(gint session, gint pos);
199 gint (*xmms_remote_get_playlist_time)(gint session, gint pos);
200 gint (*xmms_remote_get_output_time)(gint session);
201 void (*xmms_remote_get_info)(gint session, gint *rate, gint *freq, gint *chans);
202 gchar *(*xmms_remote_get_playlist_file)(gint session, gint pos);
203 gint (*xmms_remote_get_playlist_length)(gint session);
205 pvoid=(void *)pvoid; /* avoid warning */
210 glib_v1_2_handle=NULL;
213 /* Conky will likely be linked to libglib-2.0.so and not libglib-1.2.so.0. If conky is receiving
214 * gchar * data from xmms, these strings need to be freed using g_free() from libglib-1.2.so.0.
215 * This macro selects the g_free() from the correct library. */
216 #define G_FREE(mem) (info.xmms.current_project==PROJECT_XMMS ? (*g_free_v1_2)(mem) : g_free(mem))
218 switch(info.xmms.current_project) {
220 case (PROJECT_XMMS) :
221 glib_v1_2_handle = dlopen("libglib-1.2.so.0", RTLD_LAZY);
222 if (!glib_v1_2_handle) {
223 ERR("unable to open libglib-1.2.so");
226 g_free_v1_2=dlsym(glib_v1_2_handle, "g_free");
229 handle = dlopen("libxmms.so", RTLD_LAZY);
231 ERR("unable to open libxmms.so");
237 handle = dlopen("libbeep.so", RTLD_LAZY);
239 ERR("unable to open libbeep.so");
244 case (PROJECT_AUDACIOUS) :
245 handle = dlopen("libaudacious.so", RTLD_LAZY);
247 ERR("unable to open libaudacious.so");
252 case (PROJECT_NONE) :
257 /* Grab the function pointers from the library */
258 xmms_remote_is_running = dlsym(handle, "xmms_remote_is_running");
261 xmms_remote_is_paused = dlsym(handle, "xmms_remote_is_paused");
264 xmms_remote_is_playing = dlsym(handle, "xmms_remote_is_playing");
267 xmms_remote_get_playlist_pos = dlsym(handle, "xmms_remote_get_playlist_pos");
270 xmms_remote_get_playlist_title = dlsym(handle, "xmms_remote_get_playlist_title");
273 xmms_remote_get_playlist_time = dlsym(handle, "xmms_remote_get_playlist_time");
276 xmms_remote_get_output_time = dlsym(handle, "xmms_remote_get_output_time");
279 xmms_remote_get_info = dlsym(handle, "xmms_remote_get_info");
281 xmms_remote_get_playlist_file = dlsym(handle, "xmms_remote_get_playlist_file");
284 xmms_remote_get_playlist_length = dlsym(handle, "xmms_remote_get_playlist_length");
287 /* Grab the runnable signal. Should be non-zero here or we do nothing. */
288 pthread_mutex_lock(&info.xmms.runnable_mutex);
289 runnable=info.xmms.runnable;
290 pthread_mutex_unlock(&info.xmms.runnable_mutex );
292 /* Loop until the main thread sets the runnable signal to 0. */
295 for (;;) { /* convenience loop so we can break below */
297 if (!(*xmms_remote_is_running)(session)) {
298 memset(&items,0,sizeof(items));
299 strcpy(items[XMMS_STATUS],"Not running");
304 if ((*xmms_remote_is_paused)(session))
305 strcpy(items[XMMS_STATUS],"Paused");
306 else if ((*xmms_remote_is_playing)(session))
307 strcpy(items[XMMS_STATUS],"Playing");
309 strcpy(items[XMMS_STATUS],"Stopped");
311 /* Current song title */
312 playpos = (*xmms_remote_get_playlist_pos)(session);
313 psong = (*xmms_remote_get_playlist_title)(session, playpos);
315 strncpy(items[XMMS_TITLE],psong,sizeof(items[XMMS_TITLE])-1);
320 /* Current song length as MM:SS */
321 frames = (*xmms_remote_get_playlist_time)(session,playpos);
322 length = frames / 1000;
323 snprintf(items[XMMS_LENGTH],sizeof(items[XMMS_LENGTH])-1,
324 "%d:%.2d", length / 60, length % 60);
326 /* Current song length in seconds */
327 snprintf(items[XMMS_LENGTH_SECONDS],sizeof(items[XMMS_LENGTH_SECONDS])-1,
330 /* Current song position as MM:SS */
331 frames = (*xmms_remote_get_output_time)(session);
332 length = frames / 1000;
333 snprintf(items[XMMS_POSITION],sizeof(items[XMMS_POSITION])-1,
334 "%d:%.2d", length / 60, length % 60);
336 /* Current song position in seconds */
337 snprintf(items[XMMS_POSITION_SECONDS],sizeof(items[XMMS_POSITION_SECONDS])-1,
340 /* Current song bitrate */
341 (*xmms_remote_get_info)(session, &rate, &freq, &chans);
342 snprintf(items[XMMS_BITRATE],sizeof(items[XMMS_BITRATE])-1, "%d", rate);
344 /* Current song frequency */
345 snprintf(items[XMMS_FREQUENCY],sizeof(items[XMMS_FREQUENCY])-1, "%d", freq);
347 /* Current song channels */
348 snprintf(items[XMMS_CHANNELS],sizeof(items[XMMS_CHANNELS])-1, "%d", chans);
350 /* Current song filename */
351 pfilename = (*xmms_remote_get_playlist_file)(session,playpos);
353 strncpy(items[XMMS_FILENAME],pfilename,sizeof(items[XMMS_FILENAME])-1);
358 /* Length of the Playlist (number of songs) */
359 length = (*xmms_remote_get_playlist_length)(session);
360 snprintf(items[XMMS_PLAYLIST_LENGTH],sizeof(items[XMMS_PLAYLIST_LENGTH])-1, "%d", length);
362 /* Playlist position (index of song) */
363 snprintf(items[XMMS_PLAYLIST_POSITION],sizeof(items[XMMS_PLAYLIST_POSITION])-1, "%d", playpos+1);
368 /* Deliver the refreshed items array to g_items. */
369 pthread_mutex_lock(&info.xmms.item_mutex);
370 memcpy(&g_items,items,sizeof(items));
371 pthread_mutex_unlock(&info.xmms.item_mutex);
373 /* Grab the runnable signal for next loop. */
374 pthread_mutex_lock(&info.xmms.runnable_mutex);
375 runnable=info.xmms.runnable;
376 pthread_mutex_unlock(&info.xmms.runnable_mutex);
383 if (glib_v1_2_handle)
384 dlclose(glib_v1_2_handle);
390 #if defined(INFOPIPE)
391 /* --------------------------------------------------
392 * Worker thread function for InfoPipe data sampling.
393 * -------------------------------------------------- */
394 void *xmms_thread_func_infopipe(void *pvoid)
396 int i,rc,fd,runnable;
399 static char buf[2048],line[128];
403 pvoid=(void*)pvoid; /* avoid warning */
405 /* Grab the runnable signal. Should be non-zero here or we do nothing. */
406 pthread_mutex_lock(&info.xmms.runnable_mutex);
407 runnable=info.xmms.runnable;
408 pthread_mutex_unlock(&info.xmms.runnable_mutex );
410 /* Loop until the main thread sets the runnable signal to 0. */
413 for (;;) { /* convenience loop so we can break below */
415 memset(buf,0,sizeof(buf));
417 if ((fd=open(INFOPIPE_NAMED_PIPE, O_RDONLY | O_NONBLOCK)) < 0) {
418 /* InfoPipe is not running */
419 memset(items,0,sizeof(items));
420 strcpy(items[XMMS_STATUS],"Not running");
427 /* On Linux, select() reduces the timer by the amount of time not slept,
428 * so we must reset the timer with each loop. */
431 rc=select(fd+1,&readset,NULL,NULL,&tm);
435 perror("infopipe select()");
438 else if (rc && FD_ISSET(fd,&readset)) { /* ready to read */
440 if (read(fd,buf,sizeof(buf)) > 0) { /* buf has data */
444 /* 14 lines of key: value pairs presented in a known order */
445 memset(line,0,sizeof(line));
446 if ( sscanf(pbuf,"%*[^:]: %[^\n]",line) == EOF )
448 while((c = *pbuf++) && (c != '\n'));
451 case INFOPIPE_PROTOCOL:
453 case INFOPIPE_VERSION:
455 case INFOPIPE_STATUS:
456 strncpy(items[XMMS_STATUS],line,sizeof(items[XMMS_STATUS])-1);
458 case INFOPIPE_PLAYLIST_TUNES:
459 strncpy(items[XMMS_PLAYLIST_LENGTH],line,sizeof(items[XMMS_PLAYLIST_LENGTH])-1);
461 case INFOPIPE_PLAYLIST_CURRTUNE:
462 strncpy(items[XMMS_PLAYLIST_POSITION],line,sizeof(items[XMMS_PLAYLIST_POSITION])-1);
464 case INFOPIPE_USEC_POSITION:
465 snprintf(items[XMMS_POSITION_SECONDS],sizeof(items[XMMS_POSITION_SECONDS])-1,
466 "%d", atoi(line) / 1000);
468 case INFOPIPE_POSITION:
469 strncpy(items[XMMS_POSITION],line,sizeof(items[XMMS_POSITION])-1);
471 case INFOPIPE_USEC_TIME:
472 snprintf(items[XMMS_LENGTH_SECONDS],sizeof(items[XMMS_LENGTH_SECONDS])-1,
473 "%d", atoi(line) / 1000);
476 strncpy(items[XMMS_LENGTH],line,sizeof(items[XMMS_LENGTH])-1);
478 case INFOPIPE_BITRATE:
479 strncpy(items[XMMS_BITRATE],line,sizeof(items[XMMS_BITRATE])-1);
481 case INFOPIPE_FREQUENCY:
482 strncpy(items[XMMS_FREQUENCY],line,sizeof(items[XMMS_FREQUENCY])-1);
484 case INFOPIPE_CHANNELS:
485 strncpy(items[XMMS_CHANNELS],line,sizeof(items[XMMS_CHANNELS])-1);
488 strncpy(items[XMMS_TITLE],line,sizeof(items[XMMS_TITLE])-1);
491 strncpy(items[XMMS_FILENAME],line,sizeof(items[XMMS_FILENAME])-1);
500 printf("%s\n",items[i]);
506 printf("no infopipe data\n");
515 /* Deliver the refreshed items array to g_items. */
516 pthread_mutex_lock(&info.xmms.item_mutex);
517 memcpy(&g_items,items,sizeof(items));
518 pthread_mutex_unlock(&info.xmms.item_mutex);
520 /* Grab the runnable signal for next loop. */
521 pthread_mutex_lock(&info.xmms.runnable_mutex);
522 runnable=info.xmms.runnable;
523 pthread_mutex_unlock(&info.xmms.runnable_mutex);