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 /* make an effort to find the glib 1.2 shared lib */
222 if ( ((glib_v1_2_handle = dlopen("libglib-1.2.so.0", RTLD_LAZY))==NULL) &&
223 ((glib_v1_2_handle = dlopen("libglib-12.so.0", RTLD_LAZY))==NULL) &&
224 ((glib_v1_2_handle = dlopen("libglib-12.so", RTLD_LAZY))==NULL) &&
225 ((glib_v1_2_handle = dlopen("libglib12.so", RTLD_LAZY))==NULL) &&
226 ((glib_v1_2_handle = dlopen("libglib.so", RTLD_LAZY))==NULL) )
228 ERR("unable to find glib 1.2 shared object lib!");
231 g_free_v1_2=dlsym(glib_v1_2_handle, "g_free");
234 handle = dlopen("libxmms.so", RTLD_LAZY);
236 ERR("unable to open libxmms.so");
242 handle = dlopen("libbeep.so", RTLD_LAZY);
244 ERR("unable to open libbeep.so");
249 case (PROJECT_AUDACIOUS) :
250 handle = dlopen("libaudacious.so", RTLD_LAZY);
252 ERR("unable to open libaudacious.so");
257 case (PROJECT_NONE) :
262 /* Grab the function pointers from the library */
263 xmms_remote_is_running = dlsym(handle, "xmms_remote_is_running");
266 xmms_remote_is_paused = dlsym(handle, "xmms_remote_is_paused");
269 xmms_remote_is_playing = dlsym(handle, "xmms_remote_is_playing");
272 xmms_remote_get_playlist_pos = dlsym(handle, "xmms_remote_get_playlist_pos");
275 xmms_remote_get_playlist_title = dlsym(handle, "xmms_remote_get_playlist_title");
278 xmms_remote_get_playlist_time = dlsym(handle, "xmms_remote_get_playlist_time");
281 xmms_remote_get_output_time = dlsym(handle, "xmms_remote_get_output_time");
284 xmms_remote_get_info = dlsym(handle, "xmms_remote_get_info");
286 xmms_remote_get_playlist_file = dlsym(handle, "xmms_remote_get_playlist_file");
289 xmms_remote_get_playlist_length = dlsym(handle, "xmms_remote_get_playlist_length");
292 /* Grab the runnable signal. Should be non-zero here or we do nothing. */
293 pthread_mutex_lock(&info.xmms.runnable_mutex);
294 runnable=info.xmms.runnable;
295 pthread_mutex_unlock(&info.xmms.runnable_mutex );
297 /* Loop until the main thread sets the runnable signal to 0. */
300 for (;;) { /* convenience loop so we can break below */
302 if (!(*xmms_remote_is_running)(session)) {
303 memset(&items,0,sizeof(items));
304 strcpy(items[XMMS_STATUS],"Not running");
309 if ((*xmms_remote_is_paused)(session))
310 strcpy(items[XMMS_STATUS],"Paused");
311 else if ((*xmms_remote_is_playing)(session))
312 strcpy(items[XMMS_STATUS],"Playing");
314 strcpy(items[XMMS_STATUS],"Stopped");
316 /* Current song title */
317 playpos = (*xmms_remote_get_playlist_pos)(session);
318 psong = (*xmms_remote_get_playlist_title)(session, playpos);
320 strncpy(items[XMMS_TITLE],psong,sizeof(items[XMMS_TITLE])-1);
325 /* Current song length as MM:SS */
326 frames = (*xmms_remote_get_playlist_time)(session,playpos);
327 length = frames / 1000;
328 snprintf(items[XMMS_LENGTH],sizeof(items[XMMS_LENGTH])-1,
329 "%d:%.2d", length / 60, length % 60);
331 /* Current song length in seconds */
332 snprintf(items[XMMS_LENGTH_SECONDS],sizeof(items[XMMS_LENGTH_SECONDS])-1,
335 /* Current song position as MM:SS */
336 frames = (*xmms_remote_get_output_time)(session);
337 length = frames / 1000;
338 snprintf(items[XMMS_POSITION],sizeof(items[XMMS_POSITION])-1,
339 "%d:%.2d", length / 60, length % 60);
341 /* Current song position in seconds */
342 snprintf(items[XMMS_POSITION_SECONDS],sizeof(items[XMMS_POSITION_SECONDS])-1,
345 /* Current song bitrate */
346 (*xmms_remote_get_info)(session, &rate, &freq, &chans);
347 snprintf(items[XMMS_BITRATE],sizeof(items[XMMS_BITRATE])-1, "%d", rate);
349 /* Current song frequency */
350 snprintf(items[XMMS_FREQUENCY],sizeof(items[XMMS_FREQUENCY])-1, "%d", freq);
352 /* Current song channels */
353 snprintf(items[XMMS_CHANNELS],sizeof(items[XMMS_CHANNELS])-1, "%d", chans);
355 /* Current song filename */
356 pfilename = (*xmms_remote_get_playlist_file)(session,playpos);
358 strncpy(items[XMMS_FILENAME],pfilename,sizeof(items[XMMS_FILENAME])-1);
363 /* Length of the Playlist (number of songs) */
364 length = (*xmms_remote_get_playlist_length)(session);
365 snprintf(items[XMMS_PLAYLIST_LENGTH],sizeof(items[XMMS_PLAYLIST_LENGTH])-1, "%d", length);
367 /* Playlist position (index of song) */
368 snprintf(items[XMMS_PLAYLIST_POSITION],sizeof(items[XMMS_PLAYLIST_POSITION])-1, "%d", playpos+1);
373 /* Deliver the refreshed items array to g_items. */
374 pthread_mutex_lock(&info.xmms.item_mutex);
375 memcpy(&g_items,items,sizeof(items));
376 pthread_mutex_unlock(&info.xmms.item_mutex);
378 /* Grab the runnable signal for next loop. */
379 pthread_mutex_lock(&info.xmms.runnable_mutex);
380 runnable=info.xmms.runnable;
381 pthread_mutex_unlock(&info.xmms.runnable_mutex);
388 if (glib_v1_2_handle)
389 dlclose(glib_v1_2_handle);
395 #if defined(INFOPIPE)
396 /* --------------------------------------------------
397 * Worker thread function for InfoPipe data sampling.
398 * -------------------------------------------------- */
399 void *xmms_thread_func_infopipe(void *pvoid)
401 int i,rc,fd,runnable;
404 static char buf[2048],line[128];
408 pvoid=(void*)pvoid; /* avoid warning */
410 /* Grab the runnable signal. Should be non-zero here or we do nothing. */
411 pthread_mutex_lock(&info.xmms.runnable_mutex);
412 runnable=info.xmms.runnable;
413 pthread_mutex_unlock(&info.xmms.runnable_mutex );
415 /* Loop until the main thread sets the runnable signal to 0. */
418 for (;;) { /* convenience loop so we can break below */
420 memset(buf,0,sizeof(buf));
422 if ((fd=open(INFOPIPE_NAMED_PIPE, O_RDONLY | O_NONBLOCK)) < 0) {
423 /* InfoPipe is not running */
424 memset(items,0,sizeof(items));
425 strcpy(items[XMMS_STATUS],"Not running");
432 /* On Linux, select() reduces the timer by the amount of time not slept,
433 * so we must reset the timer with each loop. */
436 rc=select(fd+1,&readset,NULL,NULL,&tm);
440 perror("infopipe select()");
443 else if (rc && FD_ISSET(fd,&readset)) { /* ready to read */
445 if (read(fd,buf,sizeof(buf)) > 0) { /* buf has data */
449 /* 14 lines of key: value pairs presented in a known order */
450 memset(line,0,sizeof(line));
451 if ( sscanf(pbuf,"%*[^:]: %[^\n]",line) == EOF )
453 while((c = *pbuf++) && (c != '\n'));
456 case INFOPIPE_PROTOCOL:
458 case INFOPIPE_VERSION:
460 case INFOPIPE_STATUS:
461 strncpy(items[XMMS_STATUS],line,sizeof(items[XMMS_STATUS])-1);
463 case INFOPIPE_PLAYLIST_TUNES:
464 strncpy(items[XMMS_PLAYLIST_LENGTH],line,sizeof(items[XMMS_PLAYLIST_LENGTH])-1);
466 case INFOPIPE_PLAYLIST_CURRTUNE:
467 strncpy(items[XMMS_PLAYLIST_POSITION],line,sizeof(items[XMMS_PLAYLIST_POSITION])-1);
469 case INFOPIPE_USEC_POSITION:
470 snprintf(items[XMMS_POSITION_SECONDS],sizeof(items[XMMS_POSITION_SECONDS])-1,
471 "%d", atoi(line) / 1000);
473 case INFOPIPE_POSITION:
474 strncpy(items[XMMS_POSITION],line,sizeof(items[XMMS_POSITION])-1);
476 case INFOPIPE_USEC_TIME:
477 snprintf(items[XMMS_LENGTH_SECONDS],sizeof(items[XMMS_LENGTH_SECONDS])-1,
478 "%d", atoi(line) / 1000);
481 strncpy(items[XMMS_LENGTH],line,sizeof(items[XMMS_LENGTH])-1);
483 case INFOPIPE_BITRATE:
484 strncpy(items[XMMS_BITRATE],line,sizeof(items[XMMS_BITRATE])-1);
486 case INFOPIPE_FREQUENCY:
487 strncpy(items[XMMS_FREQUENCY],line,sizeof(items[XMMS_FREQUENCY])-1);
489 case INFOPIPE_CHANNELS:
490 strncpy(items[XMMS_CHANNELS],line,sizeof(items[XMMS_CHANNELS])-1);
493 strncpy(items[XMMS_TITLE],line,sizeof(items[XMMS_TITLE])-1);
496 strncpy(items[XMMS_FILENAME],line,sizeof(items[XMMS_FILENAME])-1);
505 printf("%s\n",items[i]);
511 printf("no infopipe data\n");
520 /* Deliver the refreshed items array to g_items. */
521 pthread_mutex_lock(&info.xmms.item_mutex);
522 memcpy(&g_items,items,sizeof(items));
523 pthread_mutex_unlock(&info.xmms.item_mutex);
525 /* Grab the runnable signal for next loop. */
526 pthread_mutex_lock(&info.xmms.runnable_mutex);
527 runnable=info.xmms.runnable;
528 pthread_mutex_unlock(&info.xmms.runnable_mutex);