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("libglib12.so", RTLD_LAZY))==NULL) &&
224 ((glib_v1_2_handle = dlopen("libglib.so", RTLD_LAZY))==NULL) )
226 ERR("unable to find glib 1.2 shared object lib!");
229 g_free_v1_2=dlsym(glib_v1_2_handle, "g_free");
232 handle = dlopen("libxmms.so", RTLD_LAZY);
234 ERR("unable to open libxmms.so");
240 handle = dlopen("libbeep.so", RTLD_LAZY);
242 ERR("unable to open libbeep.so");
247 case (PROJECT_AUDACIOUS) :
248 handle = dlopen("libaudacious.so", RTLD_LAZY);
250 ERR("unable to open libaudacious.so");
255 case (PROJECT_NONE) :
260 /* Grab the function pointers from the library */
261 xmms_remote_is_running = dlsym(handle, "xmms_remote_is_running");
264 xmms_remote_is_paused = dlsym(handle, "xmms_remote_is_paused");
267 xmms_remote_is_playing = dlsym(handle, "xmms_remote_is_playing");
270 xmms_remote_get_playlist_pos = dlsym(handle, "xmms_remote_get_playlist_pos");
273 xmms_remote_get_playlist_title = dlsym(handle, "xmms_remote_get_playlist_title");
276 xmms_remote_get_playlist_time = dlsym(handle, "xmms_remote_get_playlist_time");
279 xmms_remote_get_output_time = dlsym(handle, "xmms_remote_get_output_time");
282 xmms_remote_get_info = dlsym(handle, "xmms_remote_get_info");
284 xmms_remote_get_playlist_file = dlsym(handle, "xmms_remote_get_playlist_file");
287 xmms_remote_get_playlist_length = dlsym(handle, "xmms_remote_get_playlist_length");
290 /* Grab the runnable signal. Should be non-zero here or we do nothing. */
291 pthread_mutex_lock(&info.xmms.runnable_mutex);
292 runnable=info.xmms.runnable;
293 pthread_mutex_unlock(&info.xmms.runnable_mutex );
295 /* Loop until the main thread sets the runnable signal to 0. */
298 for (;;) { /* convenience loop so we can break below */
300 if (!(*xmms_remote_is_running)(session)) {
301 memset(&items,0,sizeof(items));
302 strcpy(items[XMMS_STATUS],"Not running");
307 if ((*xmms_remote_is_paused)(session))
308 strcpy(items[XMMS_STATUS],"Paused");
309 else if ((*xmms_remote_is_playing)(session))
310 strcpy(items[XMMS_STATUS],"Playing");
312 strcpy(items[XMMS_STATUS],"Stopped");
314 /* Current song title */
315 playpos = (*xmms_remote_get_playlist_pos)(session);
316 psong = (*xmms_remote_get_playlist_title)(session, playpos);
318 strncpy(items[XMMS_TITLE],psong,sizeof(items[XMMS_TITLE])-1);
323 /* Current song length as MM:SS */
324 frames = (*xmms_remote_get_playlist_time)(session,playpos);
325 length = frames / 1000;
326 snprintf(items[XMMS_LENGTH],sizeof(items[XMMS_LENGTH])-1,
327 "%d:%.2d", length / 60, length % 60);
329 /* Current song length in seconds */
330 snprintf(items[XMMS_LENGTH_SECONDS],sizeof(items[XMMS_LENGTH_SECONDS])-1,
333 /* Current song position as MM:SS */
334 frames = (*xmms_remote_get_output_time)(session);
335 length = frames / 1000;
336 snprintf(items[XMMS_POSITION],sizeof(items[XMMS_POSITION])-1,
337 "%d:%.2d", length / 60, length % 60);
339 /* Current song position in seconds */
340 snprintf(items[XMMS_POSITION_SECONDS],sizeof(items[XMMS_POSITION_SECONDS])-1,
343 /* Current song bitrate */
344 (*xmms_remote_get_info)(session, &rate, &freq, &chans);
345 snprintf(items[XMMS_BITRATE],sizeof(items[XMMS_BITRATE])-1, "%d", rate);
347 /* Current song frequency */
348 snprintf(items[XMMS_FREQUENCY],sizeof(items[XMMS_FREQUENCY])-1, "%d", freq);
350 /* Current song channels */
351 snprintf(items[XMMS_CHANNELS],sizeof(items[XMMS_CHANNELS])-1, "%d", chans);
353 /* Current song filename */
354 pfilename = (*xmms_remote_get_playlist_file)(session,playpos);
356 strncpy(items[XMMS_FILENAME],pfilename,sizeof(items[XMMS_FILENAME])-1);
361 /* Length of the Playlist (number of songs) */
362 length = (*xmms_remote_get_playlist_length)(session);
363 snprintf(items[XMMS_PLAYLIST_LENGTH],sizeof(items[XMMS_PLAYLIST_LENGTH])-1, "%d", length);
365 /* Playlist position (index of song) */
366 snprintf(items[XMMS_PLAYLIST_POSITION],sizeof(items[XMMS_PLAYLIST_POSITION])-1, "%d", playpos+1);
371 /* Deliver the refreshed items array to g_items. */
372 pthread_mutex_lock(&info.xmms.item_mutex);
373 memcpy(&g_items,items,sizeof(items));
374 pthread_mutex_unlock(&info.xmms.item_mutex);
376 /* Grab the runnable signal for next loop. */
377 pthread_mutex_lock(&info.xmms.runnable_mutex);
378 runnable=info.xmms.runnable;
379 pthread_mutex_unlock(&info.xmms.runnable_mutex);
386 if (glib_v1_2_handle)
387 dlclose(glib_v1_2_handle);
393 #if defined(INFOPIPE)
394 /* --------------------------------------------------
395 * Worker thread function for InfoPipe data sampling.
396 * -------------------------------------------------- */
397 void *xmms_thread_func_infopipe(void *pvoid)
399 int i,rc,fd,runnable;
402 static char buf[2048],line[128];
406 pvoid=(void*)pvoid; /* avoid warning */
408 /* Grab the runnable signal. Should be non-zero here or we do nothing. */
409 pthread_mutex_lock(&info.xmms.runnable_mutex);
410 runnable=info.xmms.runnable;
411 pthread_mutex_unlock(&info.xmms.runnable_mutex );
413 /* Loop until the main thread sets the runnable signal to 0. */
416 for (;;) { /* convenience loop so we can break below */
418 memset(buf,0,sizeof(buf));
420 if ((fd=open(INFOPIPE_NAMED_PIPE, O_RDONLY | O_NONBLOCK)) < 0) {
421 /* InfoPipe is not running */
422 memset(items,0,sizeof(items));
423 strcpy(items[XMMS_STATUS],"Not running");
430 /* On Linux, select() reduces the timer by the amount of time not slept,
431 * so we must reset the timer with each loop. */
434 rc=select(fd+1,&readset,NULL,NULL,&tm);
438 perror("infopipe select()");
441 else if (rc && FD_ISSET(fd,&readset)) { /* ready to read */
443 if (read(fd,buf,sizeof(buf)) > 0) { /* buf has data */
447 /* 14 lines of key: value pairs presented in a known order */
448 memset(line,0,sizeof(line));
449 if ( sscanf(pbuf,"%*[^:]: %[^\n]",line) == EOF )
451 while((c = *pbuf++) && (c != '\n'));
454 case INFOPIPE_PROTOCOL:
456 case INFOPIPE_VERSION:
458 case INFOPIPE_STATUS:
459 strncpy(items[XMMS_STATUS],line,sizeof(items[XMMS_STATUS])-1);
461 case INFOPIPE_PLAYLIST_TUNES:
462 strncpy(items[XMMS_PLAYLIST_LENGTH],line,sizeof(items[XMMS_PLAYLIST_LENGTH])-1);
464 case INFOPIPE_PLAYLIST_CURRTUNE:
465 strncpy(items[XMMS_PLAYLIST_POSITION],line,sizeof(items[XMMS_PLAYLIST_POSITION])-1);
467 case INFOPIPE_USEC_POSITION:
468 snprintf(items[XMMS_POSITION_SECONDS],sizeof(items[XMMS_POSITION_SECONDS])-1,
469 "%d", atoi(line) / 1000);
471 case INFOPIPE_POSITION:
472 strncpy(items[XMMS_POSITION],line,sizeof(items[XMMS_POSITION])-1);
474 case INFOPIPE_USEC_TIME:
475 snprintf(items[XMMS_LENGTH_SECONDS],sizeof(items[XMMS_LENGTH_SECONDS])-1,
476 "%d", atoi(line) / 1000);
479 strncpy(items[XMMS_LENGTH],line,sizeof(items[XMMS_LENGTH])-1);
481 case INFOPIPE_BITRATE:
482 strncpy(items[XMMS_BITRATE],line,sizeof(items[XMMS_BITRATE])-1);
484 case INFOPIPE_FREQUENCY:
485 strncpy(items[XMMS_FREQUENCY],line,sizeof(items[XMMS_FREQUENCY])-1);
487 case INFOPIPE_CHANNELS:
488 strncpy(items[XMMS_CHANNELS],line,sizeof(items[XMMS_CHANNELS])-1);
491 strncpy(items[XMMS_TITLE],line,sizeof(items[XMMS_TITLE])-1);
494 strncpy(items[XMMS_FILENAME],line,sizeof(items[XMMS_FILENAME])-1);
503 printf("%s\n",items[i]);
509 printf("no infopipe data\n");
518 /* Deliver the refreshed items array to g_items. */
519 pthread_mutex_lock(&info.xmms.item_mutex);
520 memcpy(&g_items,items,sizeof(items));
521 pthread_mutex_unlock(&info.xmms.item_mutex);
523 /* Grab the runnable signal for next loop. */
524 pthread_mutex_lock(&info.xmms.runnable_mutex);
525 runnable=info.xmms.runnable;
526 pthread_mutex_unlock(&info.xmms.runnable_mutex);