From 260690bc6b904d1ca0025e539b9663ddb9fd2424 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Thu, 12 Jun 2014 12:30:32 +0100 Subject: Imported Upstream version 3.5 --- src/Makefile | 13 +- src/audacious/Makefile | 62 +- src/audacious/adder.c | 146 ++- src/audacious/api-alias-begin.h | 2 +- src/audacious/api-alias-end.h | 2 +- src/audacious/api-declare-begin.h | 2 +- src/audacious/api-declare-end.h | 2 +- src/audacious/api-define-begin.h | 2 +- src/audacious/api-define-end.h | 2 +- src/audacious/api-local-begin.h | 2 +- src/audacious/api-local-end.h | 2 +- src/audacious/api.h | 19 +- src/audacious/art.c | 66 +- src/audacious/chardet.c | 194 +--- src/audacious/config.c | 411 +++++--- src/audacious/dbus-server.c | 779 +++++++++++++++ src/audacious/dbus-service.h | 237 ----- src/audacious/dbus.c | 1049 --------------------- src/audacious/dbus.h | 48 - src/audacious/debug.h | 2 +- src/audacious/drct-api.h | 24 +- src/audacious/drct.c | 21 +- src/audacious/effect.c | 13 +- src/audacious/effect.h | 2 +- src/audacious/equalizer.c | 12 +- src/audacious/equalizer_preset.c | 272 +++--- src/audacious/fft.c | 6 - src/audacious/history.c | 14 +- src/audacious/images/appearance.png | Bin 0 -> 2721 bytes src/audacious/images/audacious.png | Bin 1788 -> 0 bytes src/audacious/images/menu_playlist.png | Bin 688 -> 0 bytes src/audacious/images/menu_plugin.png | Bin 588 -> 0 bytes src/audacious/images/menu_queue_toggle.png | Bin 686 -> 0 bytes src/audacious/input-api.h | 60 ++ src/audacious/input.h | 69 ++ src/audacious/interface.c | 72 +- src/audacious/main.c | 555 ++++++----- src/audacious/main.h | 21 +- src/audacious/misc-api.h | 65 +- src/audacious/misc.h | 12 +- src/audacious/mpris-signals.c | 64 -- src/audacious/mpris_player.xml | 77 -- src/audacious/mpris_root.xml | 31 - src/audacious/mpris_tracklist.xml | 52 - src/audacious/objects.xml | 515 ---------- src/audacious/output.c | 75 +- src/audacious/output.h | 4 +- src/audacious/playback.c | 422 ++++----- src/audacious/playback.h | 2 +- src/audacious/playlist-api.h | 7 +- src/audacious/playlist-files.c | 181 ++-- src/audacious/playlist-new.c | 101 +- src/audacious/playlist-utils.c | 136 +-- src/audacious/plugin-init.c | 8 +- src/audacious/plugin-preferences.c | 27 +- src/audacious/plugin-registry.c | 151 +-- src/audacious/plugin-view.c | 44 +- src/audacious/plugin.h | 181 +--- src/audacious/pluginenum.c | 57 +- src/audacious/plugins-api.h | 9 +- src/audacious/plugins.h | 8 +- src/audacious/preferences.c | 641 +++++++++++++ src/audacious/preferences.h | 6 +- src/audacious/probe-buffer.c | 28 +- src/audacious/probe.c | 49 +- src/audacious/scanner.c | 12 +- src/audacious/scanner.h | 2 +- src/audacious/signals.c | 18 +- src/audacious/types.h | 8 +- src/audacious/ui_albumart.c | 239 +++-- src/audacious/ui_plugin_menu.c | 38 +- src/audacious/ui_preferences.c | 1405 +++++++++------------------- src/audacious/ui_preferences.h | 4 +- src/audacious/util.c | 184 ++-- src/audacious/util.h | 14 +- src/audacious/vis_runner.c | 141 ++- src/audtool/Makefile | 17 +- src/audtool/audtool.h | 215 ++--- src/audtool/handlers_equalizer.c | 142 ++- src/audtool/handlers_general.c | 225 ++--- src/audtool/handlers_playback.c | 127 +-- src/audtool/handlers_playlist.c | 451 +++++---- src/audtool/handlers_playqueue.c | 147 ++- src/audtool/handlers_vitals.c | 176 ++-- src/audtool/main.c | 314 ++++--- src/audtool/report.c | 111 +-- src/audtool/wrappers.c | 194 ++++ src/audtool/wrappers.h | 43 + src/dbus/Makefile | 18 + src/dbus/aud-dbus.xml | 409 ++++++++ src/libaudclient/Makefile | 23 - src/libaudclient/audctrl.c | 993 -------------------- src/libaudclient/audctrl.h | 138 --- src/libaudcore/Makefile | 17 +- src/libaudcore/audio.c | 7 +- src/libaudcore/audio.h.in | 2 +- src/libaudcore/audstrings.c | 599 +++++++----- src/libaudcore/audstrings.h | 101 +- src/libaudcore/charset.c | 180 ++++ src/libaudcore/core.h | 27 +- src/libaudcore/eventqueue.c | 31 +- src/libaudcore/hook.c | 2 +- src/libaudcore/hook.h | 3 + src/libaudcore/index.c | 142 ++- src/libaudcore/index.h | 66 +- src/libaudcore/inifile.c | 134 +++ src/libaudcore/inifile.h | 33 + src/libaudcore/multihash.c | 153 +++ src/libaudcore/multihash.h | 95 ++ src/libaudcore/strpool.c | 259 +++-- src/libaudcore/tinylock.c | 65 ++ src/libaudcore/tinylock.h.in | 56 ++ src/libaudcore/tuple.c | 286 ++---- src/libaudcore/tuple.h | 49 +- src/libaudcore/tuple_compiler.c | 182 ++-- src/libaudcore/tuple_compiler.h | 5 +- src/libaudcore/tuple_formatter.c | 74 +- src/libaudcore/tuple_formatter.h | 28 - src/libaudcore/vfs.c | 103 +- src/libaudcore/vfs.h | 34 +- src/libaudcore/vfs_async.c | 2 +- src/libaudcore/vfs_common.c | 280 +----- src/libaudcore/vfs_local.c | 231 +++++ src/libaudcore/vfs_local.h | 27 + src/libaudgui/Makefile | 17 +- src/libaudgui/about.c | 37 +- src/libaudgui/confirm.c | 84 +- src/libaudgui/effects-menu.c | 94 -- src/libaudgui/equalizer.c | 134 ++- src/libaudgui/icons-stock.c | 60 -- src/libaudgui/iface-menu.c | 55 -- src/libaudgui/infopopup.c | 62 +- src/libaudgui/infowin.c | 402 +++----- src/libaudgui/init.c | 111 ++- src/libaudgui/init.h | 42 +- src/libaudgui/jump-to-time.c | 62 +- src/libaudgui/libaudgui-gtk.h | 36 +- src/libaudgui/libaudgui.h | 18 +- src/libaudgui/list.c | 2 +- src/libaudgui/menu.c | 134 +++ src/libaudgui/menu.h | 62 ++ src/libaudgui/pixbufs.c | 141 +++ src/libaudgui/playlists.c | 182 ++-- src/libaudgui/queue-manager.c | 67 +- src/libaudgui/scaled-image.c | 114 +++ src/libaudgui/ui_fileopener.c | 180 ++-- src/libaudgui/ui_jumptotrack.c | 96 +- src/libaudgui/ui_jumptotrack_cache.c | 105 +-- src/libaudgui/ui_playlist_manager.c | 148 +-- src/libaudgui/ui_regex.h | 39 - src/libaudgui/urilist.c | 9 +- src/libaudgui/url-opener.c | 77 +- src/libaudgui/util.c | 267 ++---- src/libaudtag/ape/ape.c | 137 ++- src/libaudtag/audtag.c | 62 +- src/libaudtag/audtag.h | 3 + src/libaudtag/id3/id3-common.c | 287 ++++-- src/libaudtag/id3/id3-common.h | 33 +- src/libaudtag/id3/id3v1.c | 200 ++-- src/libaudtag/id3/id3v22.c | 315 +------ src/libaudtag/id3/id3v24.c | 447 +++------ src/libaudtag/tag_module.c | 5 +- src/libaudtag/util.c | 255 ++--- src/libaudtag/util.h | 44 +- 164 files changed, 9756 insertions(+), 11086 deletions(-) create mode 100644 src/audacious/dbus-server.c delete mode 100644 src/audacious/dbus-service.h delete mode 100644 src/audacious/dbus.c delete mode 100644 src/audacious/dbus.h create mode 100644 src/audacious/images/appearance.png delete mode 100644 src/audacious/images/audacious.png delete mode 100644 src/audacious/images/menu_playlist.png delete mode 100644 src/audacious/images/menu_plugin.png delete mode 100644 src/audacious/images/menu_queue_toggle.png create mode 100644 src/audacious/input-api.h create mode 100644 src/audacious/input.h delete mode 100644 src/audacious/mpris-signals.c delete mode 100644 src/audacious/mpris_player.xml delete mode 100644 src/audacious/mpris_root.xml delete mode 100644 src/audacious/mpris_tracklist.xml delete mode 100644 src/audacious/objects.xml create mode 100644 src/audacious/preferences.c create mode 100644 src/audtool/wrappers.c create mode 100644 src/audtool/wrappers.h create mode 100644 src/dbus/Makefile create mode 100644 src/dbus/aud-dbus.xml delete mode 100644 src/libaudclient/Makefile delete mode 100644 src/libaudclient/audctrl.c delete mode 100644 src/libaudclient/audctrl.h create mode 100644 src/libaudcore/charset.c create mode 100644 src/libaudcore/inifile.c create mode 100644 src/libaudcore/inifile.h create mode 100644 src/libaudcore/multihash.c create mode 100644 src/libaudcore/multihash.h create mode 100644 src/libaudcore/tinylock.c create mode 100644 src/libaudcore/tinylock.h.in delete mode 100644 src/libaudcore/tuple_formatter.h create mode 100644 src/libaudcore/vfs_local.c create mode 100644 src/libaudcore/vfs_local.h delete mode 100644 src/libaudgui/effects-menu.c delete mode 100644 src/libaudgui/icons-stock.c delete mode 100644 src/libaudgui/iface-menu.c create mode 100644 src/libaudgui/menu.c create mode 100644 src/libaudgui/menu.h create mode 100644 src/libaudgui/pixbufs.c create mode 100644 src/libaudgui/scaled-image.c delete mode 100644 src/libaudgui/ui_regex.h (limited to 'src') diff --git a/src/Makefile b/src/Makefile index 3b0ed74..482f7c0 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,9 +1,16 @@ -SUBDIRS = libaudcore libaudgui libaudtag audacious - include ../extra.mk +SUBDIRS := audacious libaudcore libaudgui libaudtag + ifeq ($(USE_DBUS),yes) -SUBDIRS := libaudclient audtool ${SUBDIRS} +SUBDIRS := dbus audtool ${SUBDIRS} endif include ../buildsys.mk + +audacious libaudgui libaudtag: libaudcore +audacious: libaudgui libaudtag + +ifeq ($(USE_DBUS),yes) +audacious audtool: dbus +endif diff --git a/src/audacious/Makefile b/src/audacious/Makefile index c87881e..bccf836 100644 --- a/src/audacious/Makefile +++ b/src/audacious/Makefile @@ -14,7 +14,6 @@ SRCS = adder.c \ history.c \ interface.c \ main.c \ - mpris-signals.c \ output.c \ playback.c \ playlist-files.c \ @@ -25,6 +24,7 @@ SRCS = adder.c \ plugin-registry.c \ plugin-init.c \ plugin-view.c \ + preferences.c \ probe.c \ probe-buffer.c \ scanner.c \ @@ -36,10 +36,6 @@ SRCS = adder.c \ visualization.c \ ui_albumart.c -ifeq ($(USE_DBUS),yes) -SRCS += dbus.c -endif - ifeq ($(HAVE_MSWINDOWS),yes) SRCS += audacious.rc endif @@ -49,12 +45,12 @@ INCLUDES = api.h \ api-alias-end.h \ api-define-begin.h \ api-define-end.h \ - dbus.h \ - dbus-service.h \ debug.h \ drct.h \ drct-api.h \ i18n.h \ + input.h \ + input-api.h \ misc.h \ misc-api.h \ playlist.h \ @@ -67,39 +63,43 @@ INCLUDES = api.h \ DATA = images/about-logo.png \ images/album.png \ - images/audacious.png \ + images/appearance.png \ images/audio.png \ images/connectivity.png \ images/info.png \ - images/menu_playlist.png \ - images/menu_plugin.png \ - images/menu_queue_toggle.png \ images/playlist.png \ images/plugins.png -CLEAN = build_stamp.c dbus-client-bindings.h dbus-server-bindings.h +CLEAN = build_stamp.c + +ifeq ($(USE_DBUS),yes) +SRCS += dbus-server.c +EXT_DEPS += ../dbus/aud-dbus.a +endif include ../../buildsys.mk ifeq ($(USE_DBUS),yes) -DBUS_BINDINGS = dbus-server-bindings.h dbus-client-bindings.h -LIBS := -L../libaudclient -laudclient ${LIBS} +CPPFLAGS := -I../dbus ${CPPFLAGS} ${GIO_CFLAGS} +LIBS := ../dbus/aud-dbus.a ${LIBS} ${GIO_LIBS} endif -pre-depend: ${DBUS_BINDINGS} - CPPFLAGS := -I.. -I../.. \ ${CPPFLAGS} \ ${GLIB_CFLAGS} \ ${GMODULE_CFLAGS} \ - ${GTHREAD_CFLAGS} \ ${GTK_CFLAGS} \ - ${AUDACIOUS_DEFINES} \ - ${DBUS_CFLAGS} \ - ${REGEX_CFLAGS} \ - -D_AUDACIOUS_CORE \ ${LIBGUESS_CFLAGS} +CPPFLAGS := ${CPPFLAGS} \ + -D_AUDACIOUS_CORE \ + -DHARDCODE_BINDIR=\"${bindir}\" \ + -DHARDCODE_DATADIR=\"${datadir}/audacious\" \ + -DHARDCODE_PLUGINDIR=\"${plugindir}\" \ + -DHARDCODE_LOCALEDIR=\"${localedir}\" \ + -DHARDCODE_DESKTOPFILE=\"${datarootdir}/applications/audacious.desktop\" \ + -DHARDCODE_ICONFILE=\"${datarootdir}/pixmaps/audacious.png\" + LIBS := -L../libaudcore -laudcore \ -L../libaudgui -laudgui \ -L../libaudtag -laudtag \ @@ -107,25 +107,7 @@ LIBS := -L../libaudcore -laudcore \ ${LIBINTL} \ ${GLIB_LIBS} \ ${GMODULE_LIBS} \ - ${GTHREAD_LIBS} \ - ${GTK_LIBS} \ - ${DBUS_LIBS} \ - ${REGEX_LIBS} \ - ${LIBGUESS_LIBS} - -DBUS_BINDINGS_SOURCES = objects.xml \ - mpris_root.xml \ - mpris_tracklist.xml \ - mpris_player.xml + ${GTK_LIBS} desktop_DATA = audacious.desktop desktopdir = ${datarootdir}/applications - -dbus-server-bindings.h: ${DBUS_BINDINGS_SOURCES} - ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=audacious_rc objects.xml > $@ - ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=mpris_root mpris_root.xml >> $@ - ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=mpris_tracklist mpris_tracklist.xml >> $@ - ${DBUS_BINDING_TOOL} --mode=glib-server --prefix=mpris_player mpris_player.xml >> $@ - -dbus-client-bindings.h: ${DBUS_BINDINGS_SOURCES} - ${DBUS_BINDING_TOOL} --mode=glib-client --prefix=audacious_rc objects.xml > $@ diff --git a/src/audacious/adder.c b/src/audacious/adder.c index 5d3a138..509f363 100644 --- a/src/audacious/adder.c +++ b/src/audacious/adder.c @@ -1,6 +1,6 @@ /* * adder.c - * Copyright 2011 John Lindgren + * Copyright 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,11 +17,11 @@ * the use of this software. */ -#include #include #include #include +#include #include #include @@ -33,6 +33,7 @@ #include "plugins.h" #include "main.h" #include "misc.h" +#include "util.h" typedef struct { int playlist_id, at; @@ -45,6 +46,7 @@ typedef struct { typedef struct { int playlist_id, at; bool_t play; + char * title; Index * filenames, * tuples, * decoders; } AddResult; @@ -66,7 +68,7 @@ static GtkWidget * status_window = NULL, * status_path_label, static bool_t status_cb (void * unused) { - if (! headless && ! status_window) + if (! headless_mode () && ! status_window) { status_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_type_hint ((GtkWindow *) status_window, @@ -102,7 +104,7 @@ static bool_t status_cb (void * unused) snprintf (scratch, sizeof scratch, dngettext (PACKAGE, "%d file found", "%d files found", status_count), status_count); - if (headless) + if (headless_mode ()) { printf ("Searching, %s ...\r", scratch); fflush (stdout); @@ -138,34 +140,12 @@ static void status_done_locked (void) status_source = 0; } - if (headless) + if (headless_mode ()) printf ("\n"); else if (status_window) gtk_widget_destroy (status_window); } -static void index_free_filenames (Index * filenames) -{ - int count = index_count (filenames); - for (int i = 0; i < count; i ++) - str_unref (index_get (filenames, i)); - - index_free (filenames); -} - -static void index_free_tuples (Index * tuples) -{ - int count = index_count (tuples); - for (int i = 0; i < count; i ++) - { - Tuple * tuple = index_get (tuples, i); - if (tuple) - tuple_unref (tuple); - } - - index_free (tuples); -} - static AddTask * add_task_new (int playlist_id, int at, bool_t play, Index * filenames, Index * tuples, PlaylistFilterFunc filter, void * user) @@ -184,9 +164,9 @@ static AddTask * add_task_new (int playlist_id, int at, bool_t play, static void add_task_free (AddTask * task) { if (task->filenames) - index_free_filenames (task->filenames); + index_free_full (task->filenames, (IndexFreeFunc) str_unref); if (task->tuples) - index_free_tuples (task->tuples); + index_free_full (task->tuples, (IndexFreeFunc) tuple_unref); g_slice_free (AddTask, task); } @@ -197,6 +177,7 @@ static AddResult * add_result_new (int playlist_id, int at, bool_t play) result->playlist_id = playlist_id; result->at = at; result->play = play; + result->title = NULL; result->filenames = index_new (); result->tuples = index_new (); result->decoders = index_new (); @@ -205,10 +186,12 @@ static AddResult * add_result_new (int playlist_id, int at, bool_t play) static void add_result_free (AddResult * result) { + str_unref (result->title); + if (result->filenames) - index_free_filenames (result->filenames); + index_free_full (result->filenames, (IndexFreeFunc) str_unref); if (result->tuples) - index_free_tuples (result->tuples); + index_free_full (result->tuples, (IndexFreeFunc) tuple_unref); if (result->decoders) index_free (result->decoders); @@ -257,86 +240,79 @@ static void add_file (char * filename, Tuple * tuple, PluginHandle * decoder, return; } - index_append (result->filenames, filename); - index_append (result->tuples, tuple); - index_append (result->decoders, decoder); + index_insert (result->filenames, -1, filename); + index_insert (result->tuples, -1, tuple); + index_insert (result->decoders, -1, decoder); } static void add_folder (char * filename, PlaylistFilterFunc filter, - void * user, AddResult * result) + void * user, AddResult * result, bool_t is_single) { + char * path = NULL; + g_return_if_fail (filename); + if (filter && ! filter (filename, user)) - { - str_unref (filename); - return; - } + goto DONE; status_update (filename, index_count (result->filenames)); - char * unix_name = uri_to_filename (filename); - if (! unix_name) - { - str_unref (filename); - return; - } + if (! (path = uri_to_filename (filename))) + goto DONE; GList * files = NULL; - DIR * folder = opendir (unix_name); + GDir * folder = g_dir_open (path, 0, NULL); if (! folder) - goto FREE; + goto DONE; + + const char * name; + while ((name = g_dir_read_name (folder))) + { + char * filepath = filename_build (path, name); + files = g_list_prepend (files, filepath); + } + + g_dir_close (folder); - struct dirent * entry; - while ((entry = readdir (folder))) + if (files && is_single) { - if (entry->d_name[0] != '.') - files = g_list_prepend (files, g_build_filename (unix_name, entry->d_name, NULL)); + char * last = last_path_element (path); + result->title = str_get (last ? last : path); } - closedir (folder); - files = g_list_sort (files, (GCompareFunc) string_compare); + files = g_list_sort (files, (GCompareFunc) str_compare); while (files) { - struct stat info; -#ifdef S_ISLNK - if (lstat (files->data, & info) < 0) -#else - if (stat (files->data, & info) < 0) -#endif + GStatBuf info; + if (g_lstat (files->data, & info) < 0) goto NEXT; if (S_ISREG (info.st_mode)) { char * item_name = filename_to_uri (files->data); if (item_name) - { - add_file (str_get (item_name), NULL, NULL, filter, user, result, TRUE); - g_free (item_name); - } + add_file (item_name, NULL, NULL, filter, user, result, TRUE); } else if (S_ISDIR (info.st_mode)) { char * item_name = filename_to_uri (files->data); if (item_name) - { - add_folder (str_get (item_name), filter, user, result); - g_free (item_name); - } + add_folder (item_name, filter, user, result, FALSE); } NEXT: - g_free (files->data); + str_unref (files->data); files = g_list_delete_link (files, files); } -FREE: +DONE: str_unref (filename); - g_free (unix_name); + str_unref (path); } static void add_playlist (char * filename, PlaylistFilterFunc filter, - void * user, AddResult * result) + void * user, AddResult * result, bool_t is_single) { g_return_if_fail (filename); if (filter && ! filter (filename, user)) @@ -355,29 +331,33 @@ static void add_playlist (char * filename, PlaylistFilterFunc filter, return; } + if (is_single) + result->title = title; + else + str_unref (title); + int count = index_count (filenames); for (int i = 0; i < count; i ++) add_file (index_get (filenames, i), tuples ? index_get (tuples, i) : NULL, NULL, filter, user, result, FALSE); str_unref (filename); - str_unref (title); index_free (filenames); if (tuples) index_free (tuples); } static void add_generic (char * filename, Tuple * tuple, - PlaylistFilterFunc filter, void * user, AddResult * result) + PlaylistFilterFunc filter, void * user, AddResult * result, bool_t is_single) { g_return_if_fail (filename); if (tuple) add_file (filename, tuple, NULL, filter, user, result, FALSE); else if (vfs_file_test (filename, G_FILE_TEST_IS_DIR)) - add_folder (filename, filter, user, result); + add_folder (filename, filter, user, result, is_single); else if (filename_is_playlist (filename)) - add_playlist (filename, filter, user, result); + add_playlist (filename, filter, user, result, is_single); else add_file (filename, NULL, NULL, filter, user, result, FALSE); } @@ -399,6 +379,16 @@ static bool_t add_finish (void * unused) if (result->at < 0 || result->at > count) result->at = count; + if (result->title && ! count) + { + char * old_title = playlist_get_title (playlist); + + if (! strcmp (old_title, N_("New Playlist"))) + playlist_set_title (playlist, result->title); + + str_unref (old_title); + } + playlist_entry_insert_batch_raw (playlist, result->at, result->filenames, result->tuples, result->decoders); result->filenames = NULL; @@ -461,7 +451,7 @@ static void * add_worker (void * unused) { add_generic (index_get (task->filenames, i), task->tuples ? index_get (task->tuples, i) : NULL, task->filter, task->user, - result); + result, (count == 1)); index_set (task->filenames, i, NULL); if (task->tuples) @@ -518,8 +508,8 @@ void playlist_entry_insert (int playlist, int at, const char * filename, { Index * filenames = index_new (); Index * tuples = index_new (); - index_append (filenames, str_get (filename)); - index_append (tuples, tuple); + index_insert (filenames, -1, str_get (filename)); + index_insert (tuples, -1, tuple); playlist_entry_insert_batch (playlist, at, filenames, tuples, play); } diff --git a/src/audacious/api-alias-begin.h b/src/audacious/api-alias-begin.h index 3a947fa..457d29d 100644 --- a/src/audacious/api-alias-begin.h +++ b/src/audacious/api-alias-begin.h @@ -1,6 +1,6 @@ /* * api-alias-begin.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-alias-end.h b/src/audacious/api-alias-end.h index ac0a2bd..a003cde 100644 --- a/src/audacious/api-alias-end.h +++ b/src/audacious/api-alias-end.h @@ -1,6 +1,6 @@ /* * api-alias-end.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-declare-begin.h b/src/audacious/api-declare-begin.h index d6c3d34..defc557 100644 --- a/src/audacious/api-declare-begin.h +++ b/src/audacious/api-declare-begin.h @@ -1,6 +1,6 @@ /* * api-declare-begin.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-declare-end.h b/src/audacious/api-declare-end.h index 8ac6135..fa73092 100644 --- a/src/audacious/api-declare-end.h +++ b/src/audacious/api-declare-end.h @@ -1,6 +1,6 @@ /* * api-declare-end.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-define-begin.h b/src/audacious/api-define-begin.h index 9904761..5fe32cd 100644 --- a/src/audacious/api-define-begin.h +++ b/src/audacious/api-define-begin.h @@ -1,6 +1,6 @@ /* * api-define-begin.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-define-end.h b/src/audacious/api-define-end.h index 25d874f..91bd85f 100644 --- a/src/audacious/api-define-end.h +++ b/src/audacious/api-define-end.h @@ -1,6 +1,6 @@ /* * api-define-end.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-local-begin.h b/src/audacious/api-local-begin.h index 88b780d..b32eeb1 100644 --- a/src/audacious/api-local-begin.h +++ b/src/audacious/api-local-begin.h @@ -1,6 +1,6 @@ /* * api-local-begin.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api-local-end.h b/src/audacious/api-local-end.h index 1aff447..81ec43d 100644 --- a/src/audacious/api-local-end.h +++ b/src/audacious/api-local-end.h @@ -1,6 +1,6 @@ /* * api-local-end.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/api.h b/src/audacious/api.h index 523c2b9..f68987a 100644 --- a/src/audacious/api.h +++ b/src/audacious/api.h @@ -1,6 +1,6 @@ /* * api.h - * Copyright 2010 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -20,9 +20,26 @@ #ifndef AUDACIOUS_API_H #define AUDACIOUS_API_H +/* API version. Plugins are marked with this number at compile time. + * + * _AUD_PLUGIN_VERSION is the current version; _AUD_PLUGIN_VERSION_MIN is + * the oldest one we are backward compatible with. Plugins marked older than + * _AUD_PLUGIN_VERSION_MIN or newer than _AUD_PLUGIN_VERSION are not loaded. + * + * Before releases that add new pointers to the end of the API tables, increment + * _AUD_PLUGIN_VERSION but leave _AUD_PLUGIN_VERSION_MIN the same. + * + * Before releases that break backward compatibility (e.g. remove pointers from + * the API tables), increment _AUD_PLUGIN_VERSION *and* set + * _AUD_PLUGIN_VERSION_MIN to the same value. */ + +#define _AUD_PLUGIN_VERSION_MIN 45 /* 3.5-devel */ +#define _AUD_PLUGIN_VERSION 45 /* 3.5-devel */ + typedef const struct { const struct ConfigDBAPI * configdb_api; const struct DRCTAPI * drct_api; + const struct InputAPI * input_api; const struct MiscAPI * misc_api; const struct PlaylistAPI * playlist_api; const struct PluginsAPI * plugins_api; diff --git a/src/audacious/art.c b/src/audacious/art.c index f09fcc5..24658a0 100644 --- a/src/audacious/art.c +++ b/src/audacious/art.c @@ -19,13 +19,14 @@ #include #include -#include #include -#include #include #include #include +#include +#include + #include #include @@ -47,12 +48,11 @@ typedef struct { int64_t len; /* album art as (possibly a temporary) file */ - char * art_file; + char * art_file; /* pooled */ bool_t is_temp; } ArtItem; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t cond = PTHREAD_COND_INITIALIZER; static GHashTable * art_items; /* of ArtItem */ static char * current_ref; /* pooled */ @@ -66,13 +66,13 @@ static void art_item_free (ArtItem * item) char * unixname = uri_to_filename (item->art_file); if (unixname) { - unlink (unixname); - free (unixname); + g_unlink (unixname); + str_unref (unixname); } } - free (item->data); - free (item->art_file); + g_free (item->data); + str_unref (item->art_file); g_slice_free (ArtItem, item); } @@ -140,17 +140,16 @@ static void request_callback (ScanRequest * request) assert (item != NULL && ! item->flag); scan_request_get_image_data (request, & item->data, & item->len); - item->art_file = scan_request_get_image_file (request); + item->art_file = str_get (scan_request_get_image_file (request)); item->flag = FLAG_DONE; if (! send_source) send_source = g_idle_add (send_requests, NULL); - pthread_cond_broadcast (& cond); pthread_mutex_unlock (& mutex); } -static ArtItem * art_item_get (const char * file, bool_t blocking) +static ArtItem * art_item_get (const char * file) { ArtItem * item = g_hash_table_lookup (art_items, file); @@ -169,15 +168,7 @@ static ArtItem * art_item_get (const char * file, bool_t blocking) scan_request (file, SCAN_IMAGE, NULL, request_callback); } - if (! blocking) - return NULL; - - item->refcount ++; - - while (! item->flag) - pthread_cond_wait (& cond, & mutex); - - return item; + return NULL; } static void art_item_unref (const char * file, ArtItem * item) @@ -222,15 +213,14 @@ void art_cleanup (void) art_items = NULL; } -void art_get_data_real (const char * file, const void * * data, int64_t * len, - bool_t blocking) +void art_request_data (const char * file, const void * * data, int64_t * len) { * data = NULL; * len = 0; pthread_mutex_lock (& mutex); - ArtItem * item = art_item_get (file, blocking); + ArtItem * item = art_item_get (file); if (! item) goto UNLOCK; @@ -250,12 +240,12 @@ UNLOCK: pthread_mutex_unlock (& mutex); } -const char * art_get_file_real (const char * file, bool_t blocking) +const char * art_request_file (const char * file) { const char * art_file = NULL; pthread_mutex_lock (& mutex); - ArtItem * item = art_item_get (file, blocking); + ArtItem * item = art_item_get (file); if (! item) goto UNLOCK; @@ -267,7 +257,7 @@ const char * art_get_file_real (const char * file, bool_t blocking) { item->art_file = filename_to_uri (unixname); item->is_temp = TRUE; - free (unixname); + str_unref (unixname); } } @@ -281,30 +271,6 @@ UNLOCK: return art_file; } -void art_request_data (const char * file, const void * * data, int64_t * len) -{ - return art_get_data_real (file, data, len, FALSE); -} - -const char * art_request_file (const char * file) -{ - return art_get_file_real (file, FALSE); -} - -void art_get_data (const char * file, const void * * data, int64_t * len) -{ - fprintf (stderr, "aud_art_get_data() is deprecated. Use " - "aud_art_request_data() instead.\n"); - return art_get_data_real (file, data, len, TRUE); -} - -const char * art_get_file (const char * file) -{ - fprintf (stderr, "aud_art_get_file() is deprecated. Use " - "aud_art_request_file() instead.\n"); - return art_get_file_real (file, TRUE); -} - void art_unref (const char * file) { pthread_mutex_lock (& mutex); diff --git a/src/audacious/chardet.c b/src/audacious/chardet.c index 145ec3f..08fe97e 100644 --- a/src/audacious/chardet.c +++ b/src/audacious/chardet.c @@ -1,6 +1,6 @@ /* * chardet.c - * Copyright 2006-2010 Yoshiki Yazawa, Matti Hämäläinen, and John Lindgren + * Copyright 2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,196 +17,36 @@ * the use of this software. */ -#include -#include #include +#include -#include "debug.h" -#include "i18n.h" #include "main.h" #include "misc.h" -#ifdef USE_CHARDET -# include -#endif - -static char * cd_chardet_to_utf8 (const char * str, int len, - int * arg_bytes_read, int * arg_bytes_written); - -static char * str_to_utf8_fallback (const char * str) +static void chardet_update (void) { - char * out = g_strconcat (str, _(" (invalid UTF-8)"), NULL); + char * region = get_str (NULL, "chardet_detector"); + char * fallbacks = get_str (NULL, "chardet_fallback"); - for (char * c = out; * c; c ++) - { - if (* c & 0x80) - * c = '?'; - } + Index * list = str_list_to_index (fallbacks, ", "); + str_set_charsets (region[0] ? region : NULL, list); - return out; + str_unref (region); + str_unref (fallbacks); } -static char * cd_str_to_utf8 (const char * str) +void chardet_init (void) { - char *out_str; - - if (str == NULL) - return NULL; - - /* Note: Currently, playlist calls this function repeatedly, even - * if the string is already converted into utf-8. - * chardet_to_utf8() would convert a valid utf-8 string into a - * different utf-8 string, if fallback encodings were supplied and - * the given string could be treated as a string in one of - * fallback encodings. To avoid this, g_utf8_validate() had been - * used at the top of evaluation. - */ - - /* Note 2: g_utf8_validate() has so called encapsulated utf-8 - * problem, thus chardet_to_utf8() took the place of that. - */ - - /* Note 3: As introducing madplug, the problem of conversion from - * ISO-8859-1 to UTF-8 arose. This may be coped with g_convert() - * located near the end of chardet_to_utf8(), but it requires utf8 - * validation guard where g_utf8_validate() was. New - * dfa_validate_utf8() employs libguess' DFA engine to validate - * utf-8 and can properly distinguish examples of encapsulated - * utf-8. It is considered to be safe to use as a guard. - */ - - /* Already UTF-8? */ -#ifdef USE_CHARDET - if (libguess_validate_utf8(str, strlen(str))) - return g_strdup(str); -#else - if (g_utf8_validate(str, strlen(str), NULL)) - return g_strdup(str); -#endif + chardet_update (); - /* chardet encoding detector */ - if ((out_str = cd_chardet_to_utf8 (str, strlen (str), NULL, NULL))) - return out_str; - - /* all else fails, we mask off character codes >= 128, replace with '?' */ - return str_to_utf8_fallback(str); + hook_associate ("set chardet_detector", (HookFunction) chardet_update, NULL); + hook_associate ("set chardet_fallback", (HookFunction) chardet_update, NULL); } -static char * cd_chardet_to_utf8 (const char * str, int len, - int * arg_bytes_read, int * arg_bytes_write) +void chardet_cleanup (void) { - char *ret = NULL; - int * bytes_read, * bytes_write; - int my_bytes_read, my_bytes_write; - - bytes_read = arg_bytes_read != NULL ? arg_bytes_read : &my_bytes_read; - bytes_write = arg_bytes_write != NULL ? arg_bytes_write : &my_bytes_write; - - g_return_val_if_fail(str != NULL, NULL); - -#ifdef USE_CHARDET - if (libguess_validate_utf8(str, len)) -#else - if (g_utf8_validate(str, len, NULL)) -#endif - { - if (len < 0) - len = strlen (str); - - ret = g_malloc (len + 1); - memcpy (ret, str, len); - ret[len] = 0; - - if (arg_bytes_read != NULL) - * arg_bytes_read = len; - if (arg_bytes_write != NULL) - * arg_bytes_write = len; - - return ret; - } + hook_dissociate ("set chardet_detector", (HookFunction) chardet_update); + hook_dissociate ("set chardet_fallback", (HookFunction) chardet_update); -#ifdef USE_CHARDET - char * det = get_string (NULL, "chardet_detector"); - - if (det[0]) - { - AUDDBG("guess encoding (%s) %s\n", det, str); - const char * encoding = libguess_determine_encoding (str, len, det); - AUDDBG("encoding = %s\n", encoding); - if (encoding) - { - gsize read_gsize = 0, written_gsize = 0; - ret = g_convert (str, len, "UTF-8", encoding, & read_gsize, & written_gsize, NULL); - * bytes_read = read_gsize; - * bytes_write = written_gsize; - } - } - - g_free (det); -#endif - - /* If detection failed or was not enabled, try fallbacks (if there are any) */ - if (! ret) - { - char * fallbacks = get_string (NULL, "chardet_fallback"); - char * * split = g_strsplit_set (fallbacks, " ,:;|/", -1); - - for (char * * enc = split; * enc; enc ++) - { - gsize read_gsize = 0, written_gsize = 0; - ret = g_convert (str, len, "UTF-8", * enc, & read_gsize, & written_gsize, NULL); - * bytes_read = read_gsize; - * bytes_write = written_gsize; - - if (len == *bytes_read) - break; - else { - g_free(ret); - ret = NULL; - } - } - - g_strfreev (split); - g_free (fallbacks); - } - - /* First fallback: locale (duh!) */ - if (ret == NULL) - { - gsize read_gsize = 0, written_gsize = 0; - ret = g_locale_to_utf8 (str, len, & read_gsize, & written_gsize, NULL); - * bytes_read = read_gsize; - * bytes_write = written_gsize; - } - - /* The final fallback is ISO-8859-1, if no other is specified or conversions fail */ - if (ret == NULL) - { - gsize read_gsize = 0, written_gsize = 0; - ret = g_convert (str, len, "UTF-8", "ISO-8859-1", & read_gsize, & written_gsize, NULL); - * bytes_read = read_gsize; - * bytes_write = written_gsize; - } - - if (ret != NULL) - { - if (g_utf8_validate(ret, -1, NULL)) - return ret; - else - { - g_warning("g_utf8_validate() failed for converted string in cd_chardet_to_utf8: '%s'", ret); - g_free(ret); - return NULL; - } - } - - return NULL; /* If we have no idea, return NULL. */ -} - -void chardet_init (void) -{ -#ifdef USE_CHARDET - libguess_determine_encoding(NULL, -1, ""); -#endif - str_set_utf8_impl (cd_str_to_utf8, cd_chardet_to_utf8); + str_set_charsets (NULL, NULL); } diff --git a/src/audacious/config.c b/src/audacious/config.c index c40de94..0bc4966 100644 --- a/src/audacious/config.c +++ b/src/audacious/config.c @@ -1,6 +1,6 @@ /* * config.c - * Copyright 2011 John Lindgren + * Copyright 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -18,12 +18,12 @@ */ #include -#include -#include #include #include #include +#include +#include #include "main.h" #include "misc.h" @@ -81,6 +81,7 @@ static const char * const core_defaults[] = { "stop_after_current_song", "FALSE", /* playlist */ + "chardet_fallback", "ISO-8859-1", #ifdef _WIN32 "convert_backslash", "TRUE", #else @@ -93,111 +94,270 @@ static const char * const core_defaults[] = { NULL}; -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static GHashTable * defaults; -static GKeyFile * keyfile; -static bool_t modified; +typedef enum { + OP_IS_DEFAULT, + OP_GET, + OP_SET, + OP_SET_NO_FLAG, + OP_CLEAR, + OP_CLEAR_NO_FLAG +} OpType; + +typedef struct { + const char * section; + const char * key; + const char * value; +} ConfigItem; + +typedef struct { + MultihashNode node; + ConfigItem item; +} ConfigNode; + +typedef struct { + OpType type; + ConfigItem item; + unsigned hash; + bool_t result; +} ConfigOp; + +typedef struct { + char * section; +} LoadState; + +typedef struct { + GArray * list; +} SaveState; + +static unsigned item_hash (const ConfigItem * item) +{ + return g_str_hash (item->section) + g_str_hash (item->key); +} -void config_load (void) +/* assumes pooled strings */ +static int item_compare (const ConfigItem * a, const ConfigItem * b) { - g_return_if_fail (! defaults && ! keyfile); - pthread_mutex_lock (& mutex); + if (str_equal (a->section, b->section)) + return strcmp (a->key, b->key); + else + return strcmp (a->section, b->section); +} - defaults = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, - (GDestroyNotify) g_hash_table_destroy); - keyfile = g_key_file_new (); +/* assumes pooled strings */ +static void item_clear (ConfigItem * item) +{ + str_unref ((char *) item->section); + str_unref ((char *) item->key); + str_unref ((char *) item->value); +} - char * path = g_strdup_printf ("%s/config", get_path (AUD_PATH_USER_DIR)); - if (g_file_test (path, G_FILE_TEST_EXISTS)) - { - GError * error = NULL; - if (! g_key_file_load_from_file (keyfile, path, 0, & error)) - { - fprintf (stderr, "Error loading config: %s\n", error->message); - g_error_free (error); - } - } - g_free (path); +static unsigned config_node_hash (const MultihashNode * node0) +{ + const ConfigNode * node = (const ConfigNode *) node0; - modified = FALSE; - pthread_mutex_unlock (& mutex); + return item_hash (& node->item); +} - config_set_defaults (NULL, core_defaults); +static bool_t config_node_match (const MultihashNode * node0, const void * data, unsigned hash) +{ + const ConfigNode * node = (const ConfigNode *) node0; + const ConfigItem * item = data; + + return ! strcmp (node->item.section, item->section) && ! strcmp (node->item.key, item->key); } -void config_save (void) +static MultihashTable defaults = { + .hash_func = config_node_hash, + .match_func = config_node_match +}; + +static MultihashTable config = { + .hash_func = config_node_hash, + .match_func = config_node_match +}; + +static volatile bool_t modified; + +static MultihashNode * add_cb (const void * data, unsigned hash, void * state) { - g_return_if_fail (defaults && keyfile); - pthread_mutex_lock (& mutex); + ConfigOp * op = state; - if (! modified) + switch (op->type) { - pthread_mutex_unlock (& mutex); - return; + case OP_IS_DEFAULT: + op->result = ! op->item.value[0]; /* empty string is default */ + return NULL; + + case OP_SET: + op->result = TRUE; + modified = TRUE; + + case OP_SET_NO_FLAG:; + ConfigNode * node = g_slice_new (ConfigNode); + node->item.section = str_get (op->item.section); + node->item.key = str_get (op->item.key); + node->item.value = str_get (op->item.value); + return (MultihashNode *) node; + + default: + return NULL; } +} - char * path = g_strdup_printf ("%s/config", get_path (AUD_PATH_USER_DIR)); - char * data = g_key_file_to_data (keyfile, NULL, NULL); +static bool_t action_cb (MultihashNode * node0, void * state) +{ + ConfigNode * node = (ConfigNode *) node0; + ConfigOp * op = state; - GError * error = NULL; - if (! g_file_set_contents (path, data, -1, & error)) + switch (op->type) { - fprintf (stderr, "Error saving config: %s\n", error->message); - g_error_free (error); - } + case OP_IS_DEFAULT: + op->result = ! strcmp (node->item.value, op->item.value); + return FALSE; + + case OP_GET: + op->item.value = str_ref (node->item.value); + return FALSE; + + case OP_SET: + op->result = !! strcmp (node->item.value, op->item.value); + if (op->result) + modified = TRUE; + + case OP_SET_NO_FLAG: + str_unref ((char *) node->item.value); + node->item.value = str_get (op->item.value); + return FALSE; + + case OP_CLEAR: + op->result = TRUE; + modified = TRUE; - g_free (data); - g_free (path); + case OP_CLEAR_NO_FLAG: + item_clear (& node->item); + g_slice_free (ConfigNode, node); + return TRUE; - modified = FALSE; - pthread_mutex_unlock (& mutex); + default: + return FALSE; + } } -void config_cleanup (void) +static bool_t config_op_run (ConfigOp * op, OpType type, MultihashTable * table) { - g_return_if_fail (defaults && keyfile); - pthread_mutex_lock (& mutex); + if (! op->hash) + op->hash = item_hash (& op->item); + + op->type = type; + op->result = FALSE; + multihash_lookup (table, & op->item, op->hash, add_cb, action_cb, op); + return op->result; +} - g_key_file_free (keyfile); - keyfile = NULL; - g_hash_table_destroy (defaults); - defaults = NULL; +static void load_heading (const char * section, void * data) +{ + LoadState * state = data; - pthread_mutex_unlock (& mutex); + str_unref (state->section); + state->section = str_get (section); } -void config_clear_section (const char * section) +static void load_entry (const char * key, const char * value, void * data) { - g_return_if_fail (defaults && keyfile); - pthread_mutex_lock (& mutex); + LoadState * state = data; + g_return_if_fail (state->section); - if (! section) - section = DEFAULT_SECTION; + ConfigOp op = {.item = {state->section, key, value}}; + config_op_run (& op, OP_SET_NO_FLAG, & config); +} + +void config_load (void) +{ + char * folder = filename_to_uri (get_path (AUD_PATH_USER_DIR)); + SCONCAT2 (path, folder, "/config"); + str_unref (folder); - if (g_key_file_has_group (keyfile, section)) + if (vfs_file_test (path, VFS_EXISTS)) { - g_key_file_remove_group (keyfile, section, NULL); - modified = TRUE; + VFSFile * file = vfs_fopen (path, "r"); + + if (file) + { + LoadState state = {0}; + + inifile_parse (file, load_heading, load_entry, & state); + + str_unref (state.section); + vfs_fclose (file); + } } - pthread_mutex_unlock (& mutex); + config_set_defaults (NULL, core_defaults); } -void config_set_defaults (const char * section, const char * const * entries) +static bool_t add_to_save_list (MultihashNode * node0, void * state0) { - g_return_if_fail (defaults && keyfile); - pthread_mutex_lock (& mutex); + ConfigNode * node = (ConfigNode *) node0; + SaveState * state = state0; - if (! section) - section = DEFAULT_SECTION; + int pos = state->list->len; + g_array_set_size (state->list, pos + 1); + + ConfigItem * copy = & g_array_index (state->list, ConfigItem, pos); + copy->section = str_ref (node->item.section); + copy->key = str_ref (node->item.key); + copy->value = str_ref (node->item.value); + + modified = FALSE; + + return FALSE; +} + +void config_save (void) +{ + if (! modified) + return; + + SaveState state = {.list = g_array_new (FALSE, FALSE, sizeof (ConfigItem))}; + + multihash_iterate (& config, add_to_save_list, & state); + g_array_sort (state.list, (GCompareFunc) item_compare); - GHashTable * table = g_hash_table_lookup (defaults, section); - if (! table) + char * folder = filename_to_uri (get_path (AUD_PATH_USER_DIR)); + SCONCAT2 (path, folder, "/config"); + str_unref (folder); + + VFSFile * file = vfs_fopen (path, "w"); + + if (file) { - table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) str_unref); - g_hash_table_replace (defaults, g_strdup (section), table); + const char * current_heading = NULL; + + for (int i = 0; i < state.list->len; i ++) + { + ConfigItem * item = & g_array_index (state.list, ConfigItem, i); + + if (! str_equal (item->section, current_heading)) + { + inifile_write_heading (file, item->section); + current_heading = item->section; + } + + inifile_write_entry (file, item->key, item->value); + } + + vfs_fclose (file); } + g_array_set_clear_func (state.list, (GDestroyNotify) item_clear); + g_array_free (state.list, TRUE); +} + +void config_set_defaults (const char * section, const char * const * entries) +{ + if (! section) + section = DEFAULT_SECTION; + while (1) { const char * name = * entries ++; @@ -205,130 +365,89 @@ void config_set_defaults (const char * section, const char * const * entries) if (! name || ! value) break; - g_hash_table_replace (table, g_strdup (name), str_get (value)); + ConfigOp op = {.item = {section, name, value}}; + config_op_run (& op, OP_SET_NO_FLAG, & defaults); } - - pthread_mutex_unlock (& mutex); } -static const char * get_default (const char * section, const char * name) +void config_cleanup (void) { - GHashTable * table = g_hash_table_lookup (defaults, section); - const char * def = table ? g_hash_table_lookup (table, name) : NULL; - return def ? def : ""; + ConfigOp op = {.type = OP_CLEAR_NO_FLAG}; + multihash_iterate (& config, action_cb, & op); + multihash_iterate (& defaults, action_cb, & op); } -void set_string (const char * section, const char * name, const char * value) +void set_str (const char * section, const char * name, const char * value) { - g_return_if_fail (defaults && keyfile); g_return_if_fail (name && value); - pthread_mutex_lock (& mutex); - - if (! section) - section = DEFAULT_SECTION; - - const char * def = get_default (section, name); - bool_t changed = FALSE; - - if (! strcmp (value, def)) - { - if (g_key_file_has_key (keyfile, section, name, NULL)) - { - g_key_file_remove_key (keyfile, section, name, NULL); - changed = TRUE; - } - } - else - { - char * old = g_key_file_has_key (keyfile, section, name, NULL) ? - g_key_file_get_value (keyfile, section, name, NULL) : NULL; - if (! old || strcmp (value, old)) - { - g_key_file_set_value (keyfile, section, name, value); - changed = TRUE; - } + ConfigOp op = {.item = {section ? section : DEFAULT_SECTION, name, value}}; - g_free (old); - } + bool_t is_default = config_op_run (& op, OP_IS_DEFAULT, & defaults); + bool_t changed = config_op_run (& op, is_default ? OP_CLEAR : OP_SET, & config); - if (changed) + if (changed && ! section) { - modified = TRUE; - - if (! strcmp (section, DEFAULT_SECTION)) - { - char * event = g_strdup_printf ("set %s", name); - event_queue (event, NULL); - g_free (event); - } + SCONCAT2 (event, "set ", name); + event_queue (event, NULL); } - - pthread_mutex_unlock (& mutex); } -char * get_string (const char * section, const char * name) +char * get_str (const char * section, const char * name) { - g_return_val_if_fail (defaults && keyfile, g_strdup ("")); - g_return_val_if_fail (name, g_strdup ("")); - pthread_mutex_lock (& mutex); + g_return_val_if_fail (name, NULL); - if (! section) - section = DEFAULT_SECTION; + ConfigOp op = {.item = {section ? section : DEFAULT_SECTION, name, NULL}}; - char * value = g_key_file_has_key (keyfile, section, name, NULL) ? - g_key_file_get_value (keyfile, section, name, NULL) : NULL; + config_op_run (& op, OP_GET, & config); - if (! value) - value = g_strdup (get_default (section, name)); + if (! op.item.value) + config_op_run (& op, OP_GET, & defaults); - pthread_mutex_unlock (& mutex); - return value; + return op.item.value ? (char *) op.item.value : str_get (""); } void set_bool (const char * section, const char * name, bool_t value) { - set_string (section, name, value ? "TRUE" : "FALSE"); + set_str (section, name, value ? "TRUE" : "FALSE"); } bool_t get_bool (const char * section, const char * name) { - char * string = get_string (section, name); + char * string = get_str (section, name); bool_t value = ! strcmp (string, "TRUE"); - g_free (string); + str_unref (string); return value; } void set_int (const char * section, const char * name, int value) { - char * string = int_to_string (value); + char * string = int_to_str (value); g_return_if_fail (string); - set_string (section, name, string); - g_free (string); + set_str (section, name, string); + str_unref (string); } int get_int (const char * section, const char * name) { - int value = 0; - char * string = get_string (section, name); - string_to_int (string, & value); - g_free (string); + char * string = get_str (section, name); + int value = str_to_int (string); + str_unref (string); return value; } void set_double (const char * section, const char * name, double value) { - char * string = double_to_string (value); + char * string = double_to_str (value); g_return_if_fail (string); - set_string (section, name, string); - g_free (string); + set_str (section, name, string); + str_unref (string); } double get_double (const char * section, const char * name) { - double value = 0; - char * string = get_string (section, name); - string_to_double (string, & value); - g_free (string); + char * string = get_str (section, name); + double value = str_to_double (string); + str_unref (string); return value; } diff --git a/src/audacious/dbus-server.c b/src/audacious/dbus-server.c new file mode 100644 index 0000000..af55f20 --- /dev/null +++ b/src/audacious/dbus-server.c @@ -0,0 +1,779 @@ +/* + * dbus-server.c + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include + +#include + +#include "aud-dbus.h" +#include "drct.h" +#include "main.h" +#include "misc.h" +#include "playlist.h" +#include "ui_preferences.h" + +typedef ObjAudacious Obj; +typedef GDBusMethodInvocation Invoc; + +#define FINISH(name) \ + obj_audacious_complete_##name (obj, invoc) + +#define FINISH2(name, ...) \ + obj_audacious_complete_##name (obj, invoc, __VA_ARGS__) + +static Index * strv_to_index (const char * const * strv) +{ + Index * index = index_new (); + while (* strv) + index_insert (index, -1, str_get (* strv ++)); + + return index; +} + +static bool_t do_add (Obj * obj, Invoc * invoc, const char * file) +{ + playlist_entry_insert (playlist_get_active (), -1, file, NULL, FALSE); + FINISH (add); + return TRUE; +} + +static bool_t do_add_list (Obj * obj, Invoc * invoc, const char * const * filenames) +{ + playlist_entry_insert_batch (playlist_get_active (), -1, + strv_to_index (filenames), NULL, FALSE); + FINISH (add_list); + return TRUE; +} + +static bool_t do_add_url (Obj * obj, Invoc * invoc, const char * url) +{ + playlist_entry_insert (playlist_get_active (), -1, url, NULL, FALSE); + FINISH (add_url); + return TRUE; +} + +static bool_t do_advance (Obj * obj, Invoc * invoc) +{ + drct_pl_next (); + FINISH (advance); + return TRUE; +} + +static bool_t do_auto_advance (Obj * obj, Invoc * invoc) +{ + FINISH2 (auto_advance, ! get_bool (NULL, "no_playlist_advance")); + return TRUE; +} + +static bool_t do_balance (Obj * obj, Invoc * invoc) +{ + int balance; + drct_get_volume_balance (& balance); + FINISH2 (balance, balance); + return TRUE; +} + +static bool_t do_clear (Obj * obj, Invoc * invoc) +{ + int playlist = playlist_get_active (); + playlist_entry_delete (playlist, 0, playlist_entry_count (playlist)); + FINISH (clear); + return TRUE; +} + +static bool_t do_delete (Obj * obj, Invoc * invoc, unsigned pos) +{ + playlist_entry_delete (playlist_get_active (), pos, 1); + FINISH (delete); + return TRUE; +} + +static bool_t do_delete_active_playlist (Obj * obj, Invoc * invoc) +{ + playlist_delete (playlist_get_active ()); + FINISH (delete_active_playlist); + return TRUE; +} + +static bool_t do_eject (Obj * obj, Invoc * invoc) +{ + if (! headless_mode ()) + audgui_run_filebrowser (TRUE); + + FINISH (eject); + return TRUE; +} + +static bool_t do_equalizer_activate (Obj * obj, Invoc * invoc, bool_t active) +{ + set_bool (NULL, "equalizer_active", active); + FINISH (equalizer_activate); + return TRUE; +} + +static bool_t do_get_active_playlist (Obj * obj, Invoc * invoc) +{ + FINISH2 (get_active_playlist, playlist_get_active ()); + return TRUE; +} + +static bool_t do_get_active_playlist_name (Obj * obj, Invoc * invoc) +{ + char * title = playlist_get_title (playlist_get_active ()); + FINISH2 (get_active_playlist_name, title ? title : ""); + str_unref (title); + return TRUE; +} + +static bool_t do_get_eq (Obj * obj, Invoc * invoc) +{ + double preamp = get_double (NULL, "equalizer_preamp"); + double bands[AUD_EQUALIZER_NBANDS]; + eq_get_bands (bands); + + GVariant * var = g_variant_new_fixed_array (G_VARIANT_TYPE_DOUBLE, bands, + AUD_EQUALIZER_NBANDS, sizeof (double)); + FINISH2 (get_eq, preamp, var); + return TRUE; +} + +static bool_t do_get_eq_band (Obj * obj, Invoc * invoc, int band) +{ + FINISH2 (get_eq_band, eq_get_band (band)); + return TRUE; +} + +static bool_t do_get_eq_preamp (Obj * obj, Invoc * invoc) +{ + FINISH2 (get_eq_preamp, get_double (NULL, "equalizer_preamp")); + return TRUE; +} + +static bool_t do_get_info (Obj * obj, Invoc * invoc) +{ + int bitrate, samplerate, channels; + drct_get_info (& bitrate, & samplerate, & channels); + FINISH2 (get_info, bitrate, samplerate, channels); + return TRUE; +} + +static bool_t do_get_playqueue_length (Obj * obj, Invoc * invoc) +{ + FINISH2 (get_playqueue_length, playlist_queue_count (playlist_get_active ())); + return TRUE; +} + +static bool_t do_get_tuple_fields (Obj * obj, Invoc * invoc) +{ + const char * fields[TUPLE_FIELDS + 1]; + + for (int i = 0; i < TUPLE_FIELDS; i ++) + fields[i] = tuple_field_get_name (i); + + fields[TUPLE_FIELDS] = NULL; + + FINISH2 (get_tuple_fields, fields); + return TRUE; +} + +static bool_t do_info (Obj * obj, Invoc * invoc) +{ + int bitrate, samplerate, channels; + drct_get_info (& bitrate, & samplerate, & channels); + FINISH2 (info, bitrate, samplerate, channels); + return TRUE; +} + +static bool_t do_jump (Obj * obj, Invoc * invoc, unsigned pos) +{ + playlist_set_position (playlist_get_active (), pos); + FINISH (jump); + return TRUE; +} + +static bool_t do_length (Obj * obj, Invoc * invoc) +{ + FINISH2 (length, playlist_entry_count (playlist_get_active ())); + return TRUE; +} + +static bool_t do_main_win_visible (Obj * obj, Invoc * invoc) +{ + FINISH2 (main_win_visible, ! headless_mode () && interface_is_shown ()); + return TRUE; +} + +static bool_t do_new_playlist (Obj * obj, Invoc * invoc) +{ + playlist_insert (-1); + playlist_set_active (playlist_count () - 1); + FINISH (new_playlist); + return TRUE; +} + +static bool_t do_number_of_playlists (Obj * obj, Invoc * invoc) +{ + FINISH2 (number_of_playlists, playlist_count ()); + return TRUE; +} + +static bool_t do_open_list (Obj * obj, Invoc * invoc, const char * const * filenames) +{ + drct_pl_open_list (strv_to_index (filenames)); + FINISH (open_list); + return TRUE; +} + +static bool_t do_open_list_to_temp (Obj * obj, Invoc * invoc, const char * const * filenames) +{ + drct_pl_open_temp_list (strv_to_index (filenames)); + FINISH (open_list_to_temp); + return TRUE; +} + +static bool_t do_pause (Obj * obj, Invoc * invoc) +{ + drct_pause (); + FINISH (pause); + return TRUE; +} + +static bool_t do_paused (Obj * obj, Invoc * invoc) +{ + FINISH2 (paused, drct_get_paused ()); + return TRUE; +} + +static bool_t do_play (Obj * obj, Invoc * invoc) +{ + drct_play (); + FINISH (play); + return TRUE; +} + +static bool_t do_play_active_playlist (Obj * obj, Invoc * invoc) +{ + drct_play_playlist (playlist_get_active ()); + FINISH (play_active_playlist); + return TRUE; +} + +static bool_t do_play_pause (Obj * obj, Invoc * invoc) +{ + drct_play_pause (); + FINISH (play_pause); + return TRUE; +} + +static bool_t do_playing (Obj * obj, Invoc * invoc) +{ + FINISH2 (playing, drct_get_playing ()); + return TRUE; +} + +static bool_t do_playlist_add (Obj * obj, Invoc * invoc, const char * list) +{ + playlist_entry_insert (playlist_get_active (), -1, list, NULL, FALSE); + FINISH (playlist_add); + return TRUE; +} + +static bool_t do_playlist_enqueue_to_temp (Obj * obj, Invoc * invoc, const char * url) +{ + drct_pl_open_temp (url); + FINISH (playlist_enqueue_to_temp); + return TRUE; +} + +static bool_t do_playlist_ins_url_string (Obj * obj, Invoc * invoc, const char * url, int pos) +{ + playlist_entry_insert (playlist_get_active (), pos, url, NULL, FALSE); + FINISH (playlist_ins_url_string); + return TRUE; +} + +static bool_t do_playqueue_add (Obj * obj, Invoc * invoc, int pos) +{ + playlist_queue_insert (playlist_get_active (), -1, pos); + FINISH (playqueue_add); + return TRUE; +} + +static bool_t do_playqueue_clear (Obj * obj, Invoc * invoc) +{ + int playlist = playlist_get_active (); + playlist_queue_delete (playlist, 0, playlist_queue_count (playlist)); + FINISH (playqueue_clear); + return TRUE; +} + +static bool_t do_playqueue_is_queued (Obj * obj, Invoc * invoc, int pos) +{ + bool_t queued = (playlist_queue_find_entry (playlist_get_active (), pos) >= 0); + FINISH2 (playqueue_is_queued, queued); + return TRUE; +} + +static bool_t do_playqueue_remove (Obj * obj, Invoc * invoc, int pos) +{ + int playlist = playlist_get_active (); + int qpos = playlist_queue_find_entry (playlist, pos); + + if (qpos >= 0) + playlist_queue_delete (playlist, qpos, 1); + + FINISH (playqueue_remove); + return TRUE; +} + +static bool_t do_position (Obj * obj, Invoc * invoc) +{ + FINISH2 (position, playlist_get_position (playlist_get_active ())); + return TRUE; +} + +static bool_t do_queue_get_list_pos (Obj * obj, Invoc * invoc, unsigned qpos) +{ + FINISH2 (queue_get_list_pos, playlist_queue_get_entry (playlist_get_active (), qpos)); + return TRUE; +} + +static bool_t do_queue_get_queue_pos (Obj * obj, Invoc * invoc, unsigned pos) +{ + FINISH2 (queue_get_queue_pos, playlist_queue_find_entry (playlist_get_active (), pos)); + return TRUE; +} + +static bool_t do_quit (Obj * obj, Invoc * invoc) +{ + drct_quit (); + FINISH (quit); + return TRUE; +} + +static bool_t do_repeat (Obj * obj, Invoc * invoc) +{ + FINISH2 (repeat, get_bool (NULL, "repeat")); + return TRUE; +} + +static bool_t do_reverse (Obj * obj, Invoc * invoc) +{ + drct_pl_prev (); + FINISH (reverse); + return TRUE; +} + +static bool_t do_seek (Obj * obj, Invoc * invoc, unsigned pos) +{ + drct_seek (pos); + FINISH (seek); + return TRUE; +} + +static bool_t do_set_active_playlist (Obj * obj, Invoc * invoc, int playlist) +{ + playlist_set_active (playlist); + FINISH (set_active_playlist); + return TRUE; +} + +static bool_t do_set_active_playlist_name (Obj * obj, Invoc * invoc, const char * title) +{ + playlist_set_title (playlist_get_active (), title); + FINISH (set_active_playlist_name); + return TRUE; +} + +static bool_t do_set_eq (Obj * obj, Invoc * invoc, double preamp, GVariant * var) +{ + if (! g_variant_is_of_type (var, G_VARIANT_TYPE ("ad"))) + return FALSE; + + size_t nbands = 0; + const double * bands = g_variant_get_fixed_array (var, & nbands, sizeof (double)); + + if (nbands != AUD_EQUALIZER_NBANDS) + return FALSE; + + set_double (NULL, "equalizer_preamp", preamp); + eq_set_bands (bands); + FINISH (set_eq); + return TRUE; +} + +static bool_t do_set_eq_band (Obj * obj, Invoc * invoc, int band, double value) +{ + eq_set_band (band, value); + FINISH (set_eq_band); + return TRUE; +} + +static bool_t do_set_eq_preamp (Obj * obj, Invoc * invoc, double preamp) +{ + set_double (NULL, "equalizer_preamp", preamp); + FINISH (set_eq_preamp); + return TRUE; +} + +static bool_t do_set_volume (Obj * obj, Invoc * invoc, int vl, int vr) +{ + drct_set_volume (vl, vr); + FINISH (set_volume); + return TRUE; +} + +static bool_t do_show_about_box (Obj * obj, Invoc * invoc, bool_t show) +{ + if (! headless_mode ()) + { + if (show) + audgui_show_about_window (); + else + audgui_hide_about_window (); + } + + FINISH (show_about_box); + return TRUE; +} + +static bool_t do_show_filebrowser (Obj * obj, Invoc * invoc, bool_t show) +{ + if (! headless_mode ()) + { + if (show) + audgui_run_filebrowser (FALSE); + else + audgui_hide_filebrowser (); + } + + FINISH (show_filebrowser); + return TRUE; +} + +static bool_t do_show_jtf_box (Obj * obj, Invoc * invoc, bool_t show) +{ + if (! headless_mode ()) + { + if (show) + audgui_jump_to_track (); + else + audgui_jump_to_track_hide (); + } + + FINISH (show_jtf_box); + return TRUE; +} + +static bool_t do_show_main_win (Obj * obj, Invoc * invoc, bool_t show) +{ + if (! headless_mode ()) + interface_show (show); + + FINISH (show_main_win); + return TRUE; +} + +static bool_t do_show_prefs_box (Obj * obj, Invoc * invoc, bool_t show) +{ + if (! headless_mode ()) + { + if (show) + show_prefs_window (); + else + hide_prefs_window (); + } + + FINISH (show_prefs_box); + return TRUE; +} + +static bool_t do_shuffle (Obj * obj, Invoc * invoc) +{ + FINISH2 (shuffle, get_bool (NULL, "shuffle")); + return TRUE; +} + +static bool_t do_song_filename (Obj * obj, Invoc * invoc, unsigned pos) +{ + char * filename = playlist_entry_get_filename (playlist_get_active (), pos); + FINISH2 (song_filename, filename ? filename : ""); + str_unref (filename); + return TRUE; +} + +static bool_t do_song_frames (Obj * obj, Invoc * invoc, unsigned pos) +{ + FINISH2 (song_frames, playlist_entry_get_length (playlist_get_active (), pos, FALSE)); + return TRUE; +} + +static bool_t do_song_length (Obj * obj, Invoc * invoc, unsigned pos) +{ + int length = playlist_entry_get_length (playlist_get_active (), pos, FALSE); + FINISH2 (song_length, length >= 0 ? length / 1000 : -1); + return TRUE; +} + +static bool_t do_song_title (Obj * obj, Invoc * invoc, unsigned pos) +{ + char * title = playlist_entry_get_title (playlist_get_active (), pos, FALSE); + FINISH2 (song_title, title ? title : ""); + str_unref (title); + return TRUE; +} + +static bool_t do_song_tuple (Obj * obj, Invoc * invoc, unsigned pos, const char * key) +{ + int field = tuple_field_by_name (key); + Tuple * tuple = NULL; + GVariant * var = NULL; + + if (field >= 0) + tuple = playlist_entry_get_tuple (playlist_get_active (), pos, FALSE); + + if (tuple) + { + char * str; + + switch (tuple_get_value_type (tuple, field)) + { + case TUPLE_STRING: + str = tuple_get_str (tuple, field); + var = g_variant_new_string (str); + str_unref (str); + break; + + case TUPLE_INT: + var = g_variant_new_int32 (tuple_get_int (tuple, field)); + break; + + default: + break; + } + + tuple_unref (tuple); + } + + if (! var) + var = g_variant_new_string (""); + + FINISH2 (song_tuple, g_variant_new_variant (var)); + return TRUE; +} + +static bool_t do_status (Obj * obj, Invoc * invoc) +{ + const char * status = "stopped"; + if (drct_get_playing ()) + status = drct_get_paused () ? "paused" : "playing"; + + FINISH2 (status, status); + return TRUE; +} + +static bool_t do_stop (Obj * obj, Invoc * invoc) +{ + drct_stop (); + FINISH (stop); + return TRUE; +} + +static bool_t do_stop_after (Obj * obj, Invoc * invoc) +{ + FINISH2 (stop_after, get_bool (NULL, "stop_after_current_song")); + return TRUE; +} + +static bool_t do_stopped (Obj * obj, Invoc * invoc) +{ + FINISH2 (stopped, ! drct_get_playing ()); + return TRUE; +} + +static bool_t do_time (Obj * obj, Invoc * invoc) +{ + FINISH2 (time, drct_get_time ()); + return TRUE; +} + +static bool_t do_toggle_auto_advance (Obj * obj, Invoc * invoc) +{ + set_bool (NULL, "no_playlist_advance", ! get_bool (NULL, "no_playlist_advance")); + FINISH (toggle_auto_advance); + return TRUE; +} + +static bool_t do_toggle_repeat (Obj * obj, Invoc * invoc) +{ + set_bool (NULL, "repeat", ! get_bool (NULL, "repeat")); + FINISH (toggle_repeat); + return TRUE; +} + +static bool_t do_toggle_shuffle (Obj * obj, Invoc * invoc) +{ + set_bool (NULL, "shuffle", ! get_bool (NULL, "shuffle")); + FINISH (toggle_shuffle); + return TRUE; +} + +static bool_t do_toggle_stop_after (Obj * obj, Invoc * invoc) +{ + set_bool (NULL, "stop_after_current_song", ! get_bool (NULL, "stop_after_current_song")); + FINISH (toggle_stop_after); + return TRUE; +} + +static bool_t do_version (Obj * obj, Invoc * invoc) +{ + FINISH2 (version, VERSION); + return TRUE; +} + +static bool_t do_volume (Obj * obj, Invoc * invoc) +{ + int left, right; + drct_get_volume (& left, & right); + FINISH2 (volume, left, right); + return TRUE; +} + +static const struct +{ + const char * signal; + GCallback callback; +} +handlers[] = +{ + {"handle-add", (GCallback) do_add}, + {"handle-add-list", (GCallback) do_add_list}, + {"handle-add-url", (GCallback) do_add_url}, + {"handle-advance", (GCallback) do_advance}, + {"handle-auto-advance", (GCallback) do_auto_advance}, + {"handle-balance", (GCallback) do_balance}, + {"handle-clear", (GCallback) do_clear}, + {"handle-delete", (GCallback) do_delete}, + {"handle-delete-active-playlist", (GCallback) do_delete_active_playlist}, + {"handle-eject", (GCallback) do_eject}, + {"handle-equalizer-activate", (GCallback) do_equalizer_activate}, + {"handle-get-active-playlist", (GCallback) do_get_active_playlist}, + {"handle-get-active-playlist-name", (GCallback) do_get_active_playlist_name}, + {"handle-get-eq", (GCallback) do_get_eq}, + {"handle-get-eq-band", (GCallback) do_get_eq_band}, + {"handle-get-eq-preamp", (GCallback) do_get_eq_preamp}, + {"handle-get-info", (GCallback) do_get_info}, + {"handle-get-playqueue-length", (GCallback) do_get_playqueue_length}, + {"handle-get-tuple-fields", (GCallback) do_get_tuple_fields}, + {"handle-info", (GCallback) do_info}, + {"handle-jump", (GCallback) do_jump}, + {"handle-length", (GCallback) do_length}, + {"handle-main-win-visible", (GCallback) do_main_win_visible}, + {"handle-new-playlist", (GCallback) do_new_playlist}, + {"handle-number-of-playlists", (GCallback) do_number_of_playlists}, + {"handle-open-list", (GCallback) do_open_list}, + {"handle-open-list-to-temp", (GCallback) do_open_list_to_temp}, + {"handle-pause", (GCallback) do_pause}, + {"handle-paused", (GCallback) do_paused}, + {"handle-play", (GCallback) do_play}, + {"handle-play-active-playlist", (GCallback) do_play_active_playlist}, + {"handle-play-pause", (GCallback) do_play_pause}, + {"handle-playing", (GCallback) do_playing}, + {"handle-playlist-add", (GCallback) do_playlist_add}, + {"handle-playlist-enqueue-to-temp", (GCallback) do_playlist_enqueue_to_temp}, + {"handle-playlist-ins-url-string", (GCallback) do_playlist_ins_url_string}, + {"handle-playqueue-add", (GCallback) do_playqueue_add}, + {"handle-playqueue-clear", (GCallback) do_playqueue_clear}, + {"handle-playqueue-is-queued", (GCallback) do_playqueue_is_queued}, + {"handle-playqueue-remove", (GCallback) do_playqueue_remove}, + {"handle-position", (GCallback) do_position}, + {"handle-queue-get-list-pos", (GCallback) do_queue_get_list_pos}, + {"handle-queue-get-queue-pos", (GCallback) do_queue_get_queue_pos}, + {"handle-quit", (GCallback) do_quit}, + {"handle-repeat", (GCallback) do_repeat}, + {"handle-reverse", (GCallback) do_reverse}, + {"handle-seek", (GCallback) do_seek}, + {"handle-set-active-playlist", (GCallback) do_set_active_playlist}, + {"handle-set-active-playlist-name", (GCallback) do_set_active_playlist_name}, + {"handle-set-eq", (GCallback) do_set_eq}, + {"handle-set-eq-band", (GCallback) do_set_eq_band}, + {"handle-set-eq-preamp", (GCallback) do_set_eq_preamp}, + {"handle-set-volume", (GCallback) do_set_volume}, + {"handle-show-about-box", (GCallback) do_show_about_box}, + {"handle-show-filebrowser", (GCallback) do_show_filebrowser}, + {"handle-show-jtf-box", (GCallback) do_show_jtf_box}, + {"handle-show-main-win", (GCallback) do_show_main_win}, + {"handle-show-prefs-box", (GCallback) do_show_prefs_box}, + {"handle-shuffle", (GCallback) do_shuffle}, + {"handle-song-filename", (GCallback) do_song_filename}, + {"handle-song-frames", (GCallback) do_song_frames}, + {"handle-song-length", (GCallback) do_song_length}, + {"handle-song-title", (GCallback) do_song_title}, + {"handle-song-tuple", (GCallback) do_song_tuple}, + {"handle-status", (GCallback) do_status}, + {"handle-stop", (GCallback) do_stop}, + {"handle-stop-after", (GCallback) do_stop_after}, + {"handle-stopped", (GCallback) do_stopped}, + {"handle-time", (GCallback) do_time}, + {"handle-toggle-auto-advance", (GCallback) do_toggle_auto_advance}, + {"handle-toggle-repeat", (GCallback) do_toggle_repeat}, + {"handle-toggle-shuffle", (GCallback) do_toggle_shuffle}, + {"handle-toggle-stop-after", (GCallback) do_toggle_stop_after}, + {"handle-version", (GCallback) do_version}, + {"handle-volume", (GCallback) do_volume} +}; + +static GDBusInterfaceSkeleton * skeleton = NULL; + +void dbus_server_init (void) +{ + GError * error = NULL; + GDBusConnection * bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, & error); + + if (! bus) + goto ERROR; + + g_bus_own_name_on_connection (bus, "org.atheme.audacious", 0, NULL, NULL, NULL, NULL); + + skeleton = (GDBusInterfaceSkeleton *) obj_audacious_skeleton_new (); + + for (int i = 0; i < ARRAY_LEN (handlers); i ++) + g_signal_connect (skeleton, handlers[i].signal, handlers[i].callback, NULL); + + if (! g_dbus_interface_skeleton_export (skeleton, bus, "/org/atheme/audacious", & error)) + goto ERROR; + + return; + +ERROR: + if (error) + { + fprintf (stderr, "D-Bus error: %s\n", error->message); + g_error_free (error); + } +} + +void dbus_server_cleanup (void) +{ + if (skeleton) + { + g_object_unref (skeleton); + skeleton = NULL; + } +} diff --git a/src/audacious/dbus-service.h b/src/audacious/dbus-service.h deleted file mode 100644 index 8d773fa..0000000 --- a/src/audacious/dbus-service.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * dbus-service.h - * Copyright 2007-2011 Ben Tucker, Yoshiki Yazawa, Matti Hämäläinen, - * John Lindgren, and William Pitcock - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef AUDACIOUS_DBUS_SERVICE_H -#define AUDACIOUS_DBUS_SERVICE_H - -#include -#include - -#define DBUS_API_SUBJECT_TO_CHANGE -#include - -typedef struct { - GObject parent; - DBusGProxy *proxy; -} RemoteObject, MprisRoot, MprisPlayer, MprisTrackList; - -typedef struct { - GObjectClass parent_class; -} RemoteObjectClass, MprisRootClass, MprisPlayerClass, MprisTrackListClass; - -/* MPRIS API */ -// Capabilities -enum { - MPRIS_CAPS_NONE = 0, - MPRIS_CAPS_CAN_GO_NEXT = 1 << 0, - MPRIS_CAPS_CAN_GO_PREV = 1 << 1, - MPRIS_CAPS_CAN_PAUSE = 1 << 2, - MPRIS_CAPS_CAN_PLAY = 1 << 3, - MPRIS_CAPS_CAN_SEEK = 1 << 4, - MPRIS_CAPS_CAN_PROVIDE_METADATA = 1 << 5, - MPRIS_CAPS_PROVIDES_TIMING = 1 << 6, -}; - -// Status -typedef enum { - MPRIS_STATUS_INVALID = -1, - MPRIS_STATUS_PLAY = 0, - MPRIS_STATUS_PAUSE = 1, - MPRIS_STATUS_STOP = 2, -} PlaybackStatus; - -extern MprisPlayer * mpris; - -// MPRIS / -bool_t mpris_root_identity(MprisRoot *obj, char **identity, - GError **error); -bool_t mpris_root_quit(MprisPlayer *obj, GError **error); - -// MPRIS /Player -bool_t mpris_player_next(MprisPlayer *obj, GError **error); -bool_t mpris_player_prev(MprisPlayer *obj, GError **error); -bool_t mpris_player_pause(MprisPlayer *obj, GError **error); -bool_t mpris_player_stop(MprisPlayer *obj, GError **error); -bool_t mpris_player_play(MprisPlayer *obj, GError **error); -bool_t mpris_player_repeat(MprisPlayer *obj, bool_t rpt, GError **error); -bool_t mpris_player_get_status(MprisPlayer *obj, GValueArray **status, - GError **error); -bool_t mpris_player_get_metadata(MprisPlayer *obj, GHashTable **metadata, - GError **error); -bool_t mpris_player_get_caps(MprisPlayer *obj, int *capabilities, - GError **error); -bool_t mpris_player_volume_set(MprisPlayer *obj, int vol, GError **error); -bool_t mpris_player_volume_get(MprisPlayer *obj, int *vol, - GError **error); -bool_t mpris_player_position_set(MprisPlayer *obj, int pos, GError **error); -bool_t mpris_player_position_get(MprisPlayer *obj, int *pos, - GError **error); -enum { - TRACK_CHANGE_SIG, - STATUS_CHANGE_SIG, - CAPS_CHANGE_SIG, - LAST_SIG -}; - -enum { - TRACKLIST_CHANGE_SIG, - LAST_TRACKLIST_SIG -}; - -bool_t mpris_emit_track_change(MprisPlayer *obj); -bool_t mpris_emit_status_change(MprisPlayer *obj, PlaybackStatus status); -bool_t mpris_emit_caps_change(MprisPlayer *obj); -bool_t mpris_emit_tracklist_change(MprisTrackList *obj, int playlist); - -// MPRIS /TrackList -bool_t mpris_tracklist_get_metadata(MprisTrackList *obj, int pos, - GHashTable **metadata, GError **error); -bool_t mpris_tracklist_get_current_track(MprisTrackList *obj, int *pos, - GError **error); -bool_t mpris_tracklist_get_length(MprisTrackList *obj, int *length, - GError **error); -bool_t mpris_tracklist_add_track(MprisTrackList *obj, char *uri, - bool_t play, GError **error); -bool_t mpris_tracklist_del_track(MprisTrackList *obj, int pos, - GError **error); -bool_t mpris_tracklist_loop(MprisTrackList *obj, bool_t loop, - GError **error); -bool_t mpris_tracklist_random(MprisTrackList *obj, bool_t random, - GError **error); - -/* Legacy API */ -// Audacious General Information -bool_t audacious_rc_version(RemoteObject *obj, char **version, GError **error); -bool_t audacious_rc_quit(RemoteObject *obj, GError **error); -bool_t audacious_rc_eject(RemoteObject *obj, GError **error); -bool_t audacious_rc_main_win_visible(RemoteObject *obj, - bool_t *is_main_win, GError **error); -bool_t audacious_rc_show_main_win(RemoteObject *obj, bool_t show, - GError **error); -bool_t audacious_rc_equalizer_visible(RemoteObject *obj, bool_t *is_eq_win, - GError **error); -bool_t audacious_rc_show_equalizer(RemoteObject *obj, bool_t show, - GError **error); -bool_t audacious_rc_playlist_visible(RemoteObject *obj, - bool_t *is_pl_win, - GError **error); -bool_t audacious_rc_show_playlist(RemoteObject *obj, bool_t show, - GError **error); -bool_t audacious_rc_get_tuple_fields(RemoteObject *obj, char ***fields, - GError **error); - -// Playback Information/Manipulation -bool_t audacious_rc_play(RemoteObject *obj, GError **error); -bool_t audacious_rc_pause(RemoteObject *obj, GError **error); -bool_t audacious_rc_stop(RemoteObject *obj, GError **error); -bool_t audacious_rc_playing(RemoteObject *obj, bool_t *is_playing, - GError **error); -bool_t audacious_rc_paused(RemoteObject *obj, bool_t *is_paused, - GError **error); -bool_t audacious_rc_stopped(RemoteObject *obj, bool_t *is_stopped, - GError **error); -bool_t audacious_rc_status(RemoteObject *obj, char **status, - GError **error); -bool_t audacious_rc_info(RemoteObject *obj, int *rate, int *freq, - int *nch, GError **error); -bool_t audacious_rc_time(RemoteObject *obj, int *time, GError **error); -bool_t audacious_rc_seek(RemoteObject *obj, unsigned int pos, GError **error); -bool_t audacious_rc_volume(RemoteObject *obj, int *vl, int *vr, - GError **error); -bool_t audacious_rc_set_volume(RemoteObject *obj, int vl, int vr, - GError **error); -bool_t audacious_rc_balance(RemoteObject *obj, int *balance, - GError **error); - -// Playlist Information/Manipulation -bool_t audacious_rc_position(RemoteObject *obj, int *pos, GError **error); -bool_t audacious_rc_advance(RemoteObject *obj, GError **error); -bool_t audacious_rc_reverse(RemoteObject *obj, GError **error); -bool_t audacious_rc_length(RemoteObject *obj, int *length, - GError **error); -bool_t audacious_rc_song_title(RemoteObject *obj, unsigned int pos, - char **title, GError **error); -bool_t audacious_rc_song_filename(RemoteObject *obj, unsigned int pos, - char **filename, GError **error); -bool_t audacious_rc_song_length(RemoteObject *obj, unsigned int pos, int *length, - GError **error); -bool_t audacious_rc_song_frames(RemoteObject *obj, unsigned int pos, int *length, - GError **error); -bool_t audacious_rc_song_tuple(RemoteObject *obj, unsigned int pos, char *tuple, - GValue *value, GError **error); -bool_t audacious_rc_jump(RemoteObject *obj, unsigned int pos, GError **error); -bool_t audacious_rc_add(RemoteObject *obj, char *file, GError **error); -bool_t audacious_rc_add_url(RemoteObject *obj, char *url, - GError **error); -bool_t audacious_rc_add_list (RemoteObject * obj, char * * filenames, - GError * * error); -bool_t audacious_rc_open_list (RemoteObject * obj, char * * filenames, - GError * * error); -bool_t audacious_rc_open_list_to_temp (RemoteObject * obj, char * * - filenames, GError * * error); -bool_t audacious_rc_delete(RemoteObject *obj, unsigned int pos, GError **error); -bool_t audacious_rc_clear(RemoteObject *obj, GError **error); -bool_t audacious_rc_auto_advance(RemoteObject *obj, bool_t *is_advance, - GError **error); -bool_t audacious_rc_toggle_auto_advance(RemoteObject *obj, GError **error); -bool_t audacious_rc_repeat(RemoteObject *obj, bool_t *is_repeat, - GError **error); -bool_t audacious_rc_toggle_repeat(RemoteObject *obj, GError **error); -bool_t audacious_rc_shuffle(RemoteObject *obj, bool_t *is_shuffle, - GError **error); -bool_t audacious_rc_toggle_shuffle(RemoteObject *obj, GError **error); - -/* new */ -bool_t audacious_rc_show_prefs_box(RemoteObject *obj, bool_t show, GError **error); -bool_t audacious_rc_show_about_box(RemoteObject *obj, bool_t show, GError **error); -bool_t audacious_rc_show_jtf_box(RemoteObject *obj, bool_t show, GError **error); -bool_t audacious_rc_show_filebrowser(RemoteObject *obj, bool_t show, GError **error); //new Nov 8 -bool_t audacious_rc_play_pause(RemoteObject *obj, GError **error); -bool_t audacious_rc_activate(RemoteObject *obj, GError **error); -bool_t audacious_rc_queue_get_list_pos(RemoteObject *obj, int qpos, int *pos, GError **error); -bool_t audacious_rc_queue_get_queue_pos(RemoteObject *obj, int pos, int *qpos, GError **error); -bool_t audacious_rc_get_info(RemoteObject *obj, int *rate, int *freq, int *nch, GError **error); -bool_t audacious_rc_toggle_aot(RemoteObject *obj, bool_t ontop, GError **error); -bool_t audacious_rc_get_playqueue_length(RemoteObject *obj, int *length, GError **error); -bool_t audacious_rc_playqueue_add(RemoteObject *obj, int pos, GError **error); -bool_t audacious_rc_playqueue_remove(RemoteObject *obj, int pos, GError **error); -bool_t audacious_rc_playqueue_clear(RemoteObject *obj, GError **error); -bool_t audacious_rc_playqueue_is_queued(RemoteObject *obj, int pos, bool_t *is_queued, GError **error); -bool_t audacious_rc_playlist_ins_url_string(RemoteObject *obj, char *url, int pos, GError **error); -bool_t audacious_rc_playlist_enqueue_to_temp(RemoteObject *obj, char *url, GError **error); -bool_t audacious_rc_playlist_add(RemoteObject *obj, void * list, GError **error); - -/* new on nov 7 */ -bool_t audacious_rc_get_eq(RemoteObject *obj, double *preamp, GArray **bands, GError **error); -bool_t audacious_rc_get_eq_preamp(RemoteObject *obj, double *preamp, GError **error); -bool_t audacious_rc_get_eq_band(RemoteObject *obj, int band, double *value, GError **error); -bool_t audacious_rc_set_eq(RemoteObject *obj, double preamp, GArray *bands, GError **error); -bool_t audacious_rc_set_eq_preamp(RemoteObject *obj, double preamp, GError **error); -bool_t audacious_rc_set_eq_band(RemoteObject *obj, int band, double value, GError **error); -bool_t audacious_rc_equalizer_activate(RemoteObject *obj, bool_t active, GError **error); - -/* new in 2.4 */ -bool_t audacious_rc_get_active_playlist_name(RemoteObject *obj, char **title, GError **error); - -/* new in 3.1 */ -bool_t audacious_rc_stop_after (RemoteObject * obj, bool_t * is_stopping, GError * * error); -bool_t audacious_rc_toggle_stop_after (RemoteObject * obj, GError * * error); - -#endif /* AUDACIOUS_DBUS_SERVICE_H */ diff --git a/src/audacious/dbus.c b/src/audacious/dbus.c deleted file mode 100644 index 950c4cc..0000000 --- a/src/audacious/dbus.c +++ /dev/null @@ -1,1049 +0,0 @@ -/* - * dbus.c - * Copyright 2007-2011 Ben Tucker, Yoshiki Yazawa, William Pitcock, - * Matti Hämäläinen, Andrew Shadoura, John Lindgren, and - * Tomasz Moń - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include -#include - -#include -#include -#include -#include -#include "dbus.h" -#include "dbus-service.h" -#include "dbus-server-bindings.h" - -#include - -#include - -#include "debug.h" -#include "drct.h" -#include "playlist.h" -#include "interface.h" -#include "misc.h" - -static DBusGConnection *dbus_conn = NULL; -static unsigned int signals[LAST_SIG] = { 0 }; -static unsigned int tracklist_signals[LAST_TRACKLIST_SIG] = { 0 }; - -MprisPlayer * mpris = NULL; -MprisTrackList * mpris_tracklist = NULL; - -G_DEFINE_TYPE (RemoteObject, audacious_rc, G_TYPE_OBJECT) -G_DEFINE_TYPE (MprisRoot, mpris_root, G_TYPE_OBJECT) -G_DEFINE_TYPE (MprisPlayer, mpris_player, G_TYPE_OBJECT) -G_DEFINE_TYPE (MprisTrackList, mpris_tracklist, G_TYPE_OBJECT) - -#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) - -static void mpris_playlist_update_hook(void * unused, MprisTrackList *obj); - -void audacious_rc_class_init(RemoteObjectClass * klass) -{ -} - -void mpris_root_class_init(MprisRootClass * klass) -{ -} - -void mpris_player_class_init(MprisPlayerClass * klass) -{ - signals[CAPS_CHANGE_SIG] = g_signal_new("caps_change", G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); - signals[TRACK_CHANGE_SIG] = - g_signal_new("track_change", - G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, DBUS_TYPE_G_STRING_VALUE_HASHTABLE); - - GType status_type = dbus_g_type_get_struct ("GValueArray", G_TYPE_INT, - G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID); - signals[STATUS_CHANGE_SIG] = - g_signal_new ("status_change", G_OBJECT_CLASS_TYPE (klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, - g_cclosure_marshal_VOID__BOXED, G_TYPE_NONE, 1, status_type); -} - -void mpris_tracklist_class_init(MprisTrackListClass * klass) -{ - tracklist_signals[TRACKLIST_CHANGE_SIG] = g_signal_new("track_list_change", G_OBJECT_CLASS_TYPE(klass), - G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); -} - -void audacious_rc_init(RemoteObject * object) -{ - GError *error = NULL; - DBusGProxy *driver_proxy; - unsigned int request_ret; - - AUDDBG ("Registering remote D-Bus interfaces.\n"); - - dbus_g_object_type_install_info(audacious_rc_get_type(), &dbus_glib_audacious_rc_object_info); - - // Register DBUS path - dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH, G_OBJECT(object)); - - // Register the service name, the constants here are defined in - // dbus-glib-bindings.h - driver_proxy = dbus_g_proxy_new_for_name(dbus_conn, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); - - if (!org_freedesktop_DBus_request_name(driver_proxy, AUDACIOUS_DBUS_SERVICE, 0, &request_ret, &error)) - { - g_warning("Unable to register service: %s", error->message); - g_error_free(error); - } - - if (!org_freedesktop_DBus_request_name(driver_proxy, AUDACIOUS_DBUS_SERVICE_MPRIS, 0, &request_ret, &error)) - { - g_warning("Unable to register service: %s", error->message); - g_error_free(error); - } - - g_object_unref(driver_proxy); -} - -void mpris_root_init(MprisRoot * object) -{ - dbus_g_object_type_install_info(mpris_root_get_type(), &dbus_glib_mpris_root_object_info); - - // Register DBUS path - dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH_MPRIS_ROOT, G_OBJECT(object)); -} - -void mpris_player_init(MprisPlayer * object) -{ - dbus_g_object_type_install_info(mpris_player_get_type(), &dbus_glib_mpris_player_object_info); - - // Register DBUS path - dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH_MPRIS_PLAYER, G_OBJECT(object)); - - // Add signals - DBusGProxy *proxy = object->proxy; - if (proxy != NULL) - { - dbus_g_proxy_add_signal (proxy, "StatusChange", dbus_g_type_get_struct - ("GValueArray", G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, - G_TYPE_INVALID), G_TYPE_INVALID); - dbus_g_proxy_add_signal (proxy, "CapsChange", G_TYPE_INT, G_TYPE_INVALID); - dbus_g_proxy_add_signal(proxy, "TrackChange", DBUS_TYPE_G_STRING_VALUE_HASHTABLE, G_TYPE_INVALID); - } - else - { - /* XXX / FIXME: Why does this happen? -- ccr */ - AUDDBG ("object->proxy == NULL; not adding some signals.\n"); - } -} - -void mpris_tracklist_init(MprisTrackList * object) -{ - dbus_g_object_type_install_info(mpris_tracklist_get_type(), &dbus_glib_mpris_tracklist_object_info); - - // Register DBUS path - dbus_g_connection_register_g_object(dbus_conn, AUDACIOUS_DBUS_PATH_MPRIS_TRACKLIST, G_OBJECT(object)); - - // Add signals - DBusGProxy *proxy = object->proxy; - if (proxy != NULL) - { - dbus_g_proxy_add_signal(proxy, "TrackListChange", G_TYPE_INT, G_TYPE_INVALID); - } - else - { - /* XXX / FIXME: Why does this happen? -- ccr */ - AUDDBG ("object->proxy == NULL, not adding some signals.\n"); - } -} - -void init_dbus() -{ - GError *error = NULL; - DBusConnection *local_conn; - - AUDDBG ("Trying to initialize D-Bus.\n"); - dbus_conn = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - if (dbus_conn == NULL) - { - g_warning("Unable to connect to dbus: %s", error->message); - g_error_free(error); - return; - } - - g_object_new(audacious_rc_get_type(), NULL); - g_object_new(mpris_root_get_type(), NULL); - mpris = g_object_new(mpris_player_get_type(), NULL); - mpris_tracklist = g_object_new(mpris_tracklist_get_type(), NULL); - - local_conn = dbus_g_connection_get_connection(dbus_conn); - dbus_connection_set_exit_on_disconnect(local_conn, FALSE); - - hook_associate ("playlist update", - (HookFunction) mpris_playlist_update_hook, mpris_tracklist); -} - -void cleanup_dbus (void) -{ - hook_dissociate ("playlist update", (HookFunction) mpris_playlist_update_hook); -} - -static GValue *tuple_value_to_gvalue(const Tuple * tuple, const char * key) -{ - GValue *val; - TupleValueType type = tuple_get_value_type (tuple, -1, key); - - if (type == TUPLE_STRING) - { - val = g_new0(GValue, 1); - g_value_init(val, G_TYPE_STRING); - char * str = tuple_get_str (tuple, -1, key); - g_value_set_string (val, str); - str_unref (str); - return val; - } - else if (type == TUPLE_INT) - { - val = g_new0(GValue, 1); - g_value_init(val, G_TYPE_INT); - int x = tuple_get_int (tuple, -1, key); - g_value_set_int (val, x); - return val; - } - return NULL; -} - -/** - * Retrieves value named tuple_key and inserts it inside hash table. - * - * @param[in,out] md GHashTable to insert into - * @param[in] tuple Tuple to read data from - * @param[in] tuple_key Tuple field key - * @param[in] key key used for inserting into hash table. - */ -static void tuple_insert_to_hash_full(GHashTable * md, const Tuple * tuple, - const char * tuple_key, const char *key) -{ - GValue *value = tuple_value_to_gvalue(tuple, tuple_key); - if (value != NULL) - g_hash_table_insert (md, (void *) key, value); -} - -static void tuple_insert_to_hash(GHashTable * md, const Tuple * tuple, - const char *key) -{ - tuple_insert_to_hash_full(md, tuple, key, key); -} - -static void remove_metadata_value(void * value) -{ - g_value_unset((GValue *) value); - g_free((GValue *) value); -} - -static GHashTable *make_mpris_metadata(const char * filename, const Tuple * tuple) -{ - GHashTable *md = NULL; - void * value; - - md = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, remove_metadata_value); - - value = g_malloc(sizeof(GValue)); - memset(value, 0, sizeof(GValue)); - g_value_init(value, G_TYPE_STRING); - g_value_take_string(value, g_strdup(filename)); - g_hash_table_insert(md, "location", value); - - if (tuple != NULL) - { - tuple_insert_to_hash_full(md, tuple, "length", "mtime"); - tuple_insert_to_hash(md, tuple, "title"); - tuple_insert_to_hash(md, tuple, "artist"); - tuple_insert_to_hash(md, tuple, "album"); - tuple_insert_to_hash(md, tuple, "comment"); - tuple_insert_to_hash(md, tuple, "genre"); - tuple_insert_to_hash(md, tuple, "year"); - tuple_insert_to_hash(md, tuple, "codec"); - tuple_insert_to_hash(md, tuple, "quality"); - tuple_insert_to_hash_full(md, tuple, "track-number", "tracknumber"); - tuple_insert_to_hash_full(md, tuple, "bitrate", "audio-bitrate"); - } - - return md; -} - -static GValue * get_field (int playlist, int entry, const char * field) -{ - Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE); - GValue * value = tuple ? tuple_value_to_gvalue (tuple, field) : NULL; - - if (tuple) - tuple_unref (tuple); - - return value; -} - -static GHashTable * get_mpris_metadata (int playlist, int entry) -{ - char * filename = playlist_entry_get_filename (playlist, entry); - Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE); - - GHashTable * metadata = NULL; - if (filename && tuple) - metadata = make_mpris_metadata (filename, tuple); - - str_unref (filename); - if (tuple) - tuple_unref (tuple); - - return metadata; -} - -/* MPRIS API */ -// MPRIS / -bool_t mpris_root_identity(MprisRoot * obj, char ** identity, GError ** error) -{ - *identity = g_strdup_printf("Audacious %s", VERSION); - return TRUE; -} - -bool_t mpris_root_quit(MprisPlayer * obj, GError ** error) -{ - event_queue("quit", NULL); - return TRUE; -} - -// MPRIS /Player - -bool_t mpris_player_next (MprisPlayer * obj, GError * * error) -{ - drct_pl_next (); - return TRUE; -} - -bool_t mpris_player_prev (MprisPlayer * obj, GError * * error) -{ - drct_pl_prev (); - return TRUE; -} - -bool_t mpris_player_pause (MprisPlayer * obj, GError * * error) -{ - drct_pause (); - return TRUE; -} - -bool_t mpris_player_stop (MprisPlayer * obj, GError * * error) -{ - drct_stop (); - return TRUE; -} - -bool_t mpris_player_play (MprisPlayer * obj, GError * * error) -{ - drct_play (); - return TRUE; -} - -bool_t mpris_player_repeat(MprisPlayer * obj, bool_t rpt, GError ** error) -{ - set_bool (NULL, "repeat", rpt); - return TRUE; -} - -static void append_int_value(GValueArray * ar, int tmp) -{ - GValue value; - memset(&value, 0, sizeof(value)); - g_value_init(&value, G_TYPE_INT); - g_value_set_int(&value, tmp); - g_value_array_append(ar, &value); -} - -static int get_playback_status (void) -{ - if (! drct_get_playing ()) - return MPRIS_STATUS_STOP; - - return drct_get_paused () ? MPRIS_STATUS_PAUSE : MPRIS_STATUS_PLAY; -} - -bool_t mpris_player_get_status(MprisPlayer * obj, GValueArray * *status, GError * *error) -{ - *status = g_value_array_new(4); - - append_int_value(*status, (int) get_playback_status()); - append_int_value (* status, get_bool (NULL, "shuffle")); - append_int_value (* status, get_bool (NULL, "no_playlist_advance")); - append_int_value (* status, get_bool (NULL, "repeat")); - return TRUE; -} - -bool_t mpris_player_get_metadata (MprisPlayer * obj, GHashTable * * metadata, - GError * * error) -{ - int playlist = playlist_get_playing (); - int entry = (playlist >= 0) ? playlist_get_position (playlist) : -1; - - * metadata = (entry >= 0) ? get_mpris_metadata (playlist, entry) : NULL; - if (! * metadata) - * metadata = g_hash_table_new (g_str_hash, g_str_equal); - - return TRUE; -} - -bool_t mpris_player_get_caps(MprisPlayer * obj, int * capabilities, GError ** error) -{ - *capabilities = MPRIS_CAPS_CAN_GO_NEXT | MPRIS_CAPS_CAN_GO_PREV | MPRIS_CAPS_CAN_PAUSE | MPRIS_CAPS_CAN_PLAY | MPRIS_CAPS_CAN_SEEK | MPRIS_CAPS_CAN_PROVIDE_METADATA | MPRIS_CAPS_PROVIDES_TIMING; - return TRUE; -} - -bool_t mpris_player_volume_set(MprisPlayer * obj, int vol, GError ** error) -{ - drct_set_volume_main (vol); - return TRUE; -} - -bool_t mpris_player_volume_get(MprisPlayer * obj, int * vol, GError ** error) -{ - drct_get_volume_main (vol); - return TRUE; -} - -bool_t mpris_player_position_set (MprisPlayer * obj, int pos, GError * * error) -{ - drct_seek (pos); - return TRUE; -} - -bool_t mpris_player_position_get (MprisPlayer * obj, int * pos, GError * * error) -{ - * pos = drct_get_time (); - return TRUE; -} - -// MPRIS /Player signals -bool_t mpris_emit_caps_change(MprisPlayer * obj) -{ - g_signal_emit(obj, signals[CAPS_CHANGE_SIG], 0, 0); - return TRUE; -} - -bool_t mpris_emit_track_change(MprisPlayer * obj) -{ - int playlist, entry; - GHashTable *metadata; - - playlist = playlist_get_playing(); - entry = playlist_get_position(playlist); - char * filename = playlist_entry_get_filename (playlist, entry); - Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE); - - if (filename && tuple) - { - metadata = make_mpris_metadata (filename, tuple); - g_signal_emit (obj, signals[TRACK_CHANGE_SIG], 0, metadata); - g_hash_table_destroy (metadata); - } - - str_unref (filename); - if (tuple) - tuple_unref (tuple); - - return (filename && tuple); -} - -bool_t mpris_emit_status_change(MprisPlayer * obj, PlaybackStatus status) -{ - GValueArray *ar = g_value_array_new(4); - - if (status == MPRIS_STATUS_INVALID) - status = get_playback_status (); - - append_int_value(ar, (int) status); - append_int_value (ar, get_bool (NULL, "shuffle")); - append_int_value (ar, get_bool (NULL, "no_playlist_advance")); - append_int_value (ar, get_bool (NULL, "repeat")); - - g_signal_emit(obj, signals[STATUS_CHANGE_SIG], 0, ar); - g_value_array_free(ar); - return TRUE; -} - -// MPRIS /TrackList -bool_t mpris_emit_tracklist_change(MprisTrackList * obj, int playlist) -{ - g_signal_emit(obj, tracklist_signals[TRACKLIST_CHANGE_SIG], 0, playlist_entry_count(playlist)); - return TRUE; -} - -static void mpris_playlist_update_hook(void * unused, MprisTrackList * obj) -{ - int playlist = playlist_get_active(); - - mpris_emit_tracklist_change(obj, playlist); -} - -bool_t mpris_tracklist_get_metadata (MprisTrackList * obj, int pos, - GHashTable * * metadata, GError * * error) -{ - * metadata = get_mpris_metadata (playlist_get_active (), pos); - if (! * metadata) - * metadata = g_hash_table_new (g_str_hash, g_str_equal); - - return TRUE; -} - -bool_t mpris_tracklist_get_current_track (MprisTrackList * obj, int * pos, - GError * * error) -{ - * pos = playlist_get_position (playlist_get_active ()); - return TRUE; -} - -bool_t mpris_tracklist_get_length (MprisTrackList * obj, int * length, GError * * error) -{ - * length = playlist_entry_count (playlist_get_active ()); - return TRUE; -} - -bool_t mpris_tracklist_add_track (MprisTrackList * obj, char * uri, bool_t play, - GError * * error) -{ - playlist_entry_insert (playlist_get_active (), -1, uri, NULL, play); - return TRUE; -} - -bool_t mpris_tracklist_del_track (MprisTrackList * obj, int pos, GError * * error) -{ - playlist_entry_delete (playlist_get_active (), pos, 1); - return TRUE; -} - -bool_t mpris_tracklist_loop (MprisTrackList * obj, bool_t loop, GError * * - error) -{ - set_bool (NULL, "repeat", loop); - return TRUE; -} - -bool_t mpris_tracklist_random (MprisTrackList * obj, bool_t random, - GError * * error) -{ - set_bool (NULL, "shuffle", random); - return TRUE; -} - -// Audacious General Information -bool_t audacious_rc_version(RemoteObject * obj, char ** version, GError ** error) -{ - *version = g_strdup(VERSION); - return TRUE; -} - -bool_t audacious_rc_quit(RemoteObject * obj, GError * *error) -{ - event_queue("quit", NULL); - return TRUE; -} - -bool_t audacious_rc_eject(RemoteObject * obj, GError ** error) -{ - interface_show_filebrowser (TRUE); - return TRUE; -} - -bool_t audacious_rc_main_win_visible (RemoteObject * obj, - bool_t * visible, GError ** error) -{ - * visible = interface_is_shown (); - return TRUE; -} - -bool_t audacious_rc_show_main_win (RemoteObject * obj, bool_t show, - GError * * error) -{ - interface_show (show); - return TRUE; -} - -bool_t audacious_rc_get_tuple_fields(RemoteObject * obj, char *** fields, GError ** error) -{ - * fields = g_new (char *, TUPLE_FIELDS); - - for (int i = 0; i < TUPLE_FIELDS; i ++) - (* fields)[i] = g_strdup (tuple_field_get_name (i)); - - (* fields)[TUPLE_FIELDS] = NULL; - return TRUE; -} - - -// Playback Information/Manipulation - -bool_t audacious_rc_play (RemoteObject * obj, GError * * error) -{ - drct_play (); - return TRUE; -} - -bool_t audacious_rc_pause (RemoteObject * obj, GError * * error) -{ - drct_pause (); - return TRUE; -} - -bool_t audacious_rc_stop (RemoteObject * obj, GError * * error) -{ - drct_stop (); - return TRUE; -} - -bool_t audacious_rc_playing (RemoteObject * obj, bool_t * is_playing, GError * * error) -{ - * is_playing = drct_get_playing (); - return TRUE; -} - -bool_t audacious_rc_paused (RemoteObject * obj, bool_t * is_paused, GError * * error) -{ - * is_paused = drct_get_paused (); - return TRUE; -} - -bool_t audacious_rc_stopped (RemoteObject * obj, bool_t * is_stopped, GError * * error) -{ - * is_stopped = ! drct_get_playing (); - return TRUE; -} - -bool_t audacious_rc_status (RemoteObject * obj, char * * status, GError * * error) -{ - if (drct_get_playing ()) - * status = strdup (drct_get_paused () ? "paused" : "playing"); - else - * status = strdup ("stopped"); - - return TRUE; -} - -bool_t audacious_rc_info (RemoteObject * obj, int * rate, int * freq, int * nch, - GError * * error) -{ - drct_get_info (rate, freq, nch); - return TRUE; -} - -bool_t audacious_rc_time (RemoteObject * obj, int * time, GError * * error) -{ - * time = drct_get_time (); - return TRUE; -} - -bool_t audacious_rc_seek (RemoteObject * obj, unsigned int pos, GError * * error) -{ - drct_seek (pos); - return TRUE; -} - -bool_t audacious_rc_volume(RemoteObject * obj, int * vl, int * vr, GError ** error) -{ - drct_get_volume (vl, vr); - return TRUE; -} - -bool_t audacious_rc_set_volume(RemoteObject * obj, int vl, int vr, GError ** error) -{ - drct_set_volume (vl, vr); - return TRUE; -} - -bool_t audacious_rc_balance(RemoteObject * obj, int * balance, GError ** error) -{ - drct_get_volume_balance (balance); - return TRUE; -} - -// Playlist Information/Manipulation - -bool_t audacious_rc_position (RemoteObject * obj, int * pos, GError * * error) -{ - * pos = playlist_get_position (playlist_get_active ()); - return TRUE; -} - -bool_t audacious_rc_advance(RemoteObject * obj, GError * *error) -{ - drct_pl_next (); - return TRUE; -} - -bool_t audacious_rc_reverse (RemoteObject * obj, GError * * error) -{ - drct_pl_prev (); - return TRUE; -} - -bool_t audacious_rc_length (RemoteObject * obj, int * length, GError * * error) -{ - * length = playlist_entry_count (playlist_get_active ()); - return TRUE; -} - -bool_t audacious_rc_song_title (RemoteObject * obj, unsigned int pos, char * * - title, GError * * error) -{ - char * title2 = playlist_entry_get_title (playlist_get_active (), pos, FALSE); - if (! title2) - return FALSE; - - * title = strdup (title2); - str_unref (title2); - return TRUE; -} - -bool_t audacious_rc_song_filename (RemoteObject * obj, unsigned int pos, - char * * filename, GError * * error) -{ - char * filename2 = playlist_entry_get_filename (playlist_get_active (), pos); - if (! filename2) - return FALSE; - - * filename = strdup (filename2); - str_unref (filename2); - return TRUE; -} - -bool_t audacious_rc_song_length(RemoteObject * obj, unsigned int pos, int * length, GError * *error) -{ - audacious_rc_song_frames(obj, pos, length, error); - *length /= 1000; - return TRUE; -} - -bool_t audacious_rc_song_frames (RemoteObject * obj, unsigned int pos, int * - length, GError * * error) -{ - * length = playlist_entry_get_length (playlist_get_active (), pos, FALSE); - return TRUE; -} - -bool_t audacious_rc_song_tuple (RemoteObject * obj, unsigned int pos, char * - field, GValue * value, GError * * error) -{ - GValue * value2 = get_field (playlist_get_active (), pos, field); - if (! value2) - return FALSE; - - memset (value, 0, sizeof (GValue)); - g_value_init (value, G_VALUE_TYPE (value2)); - g_value_copy (value2, value); - g_value_unset (value2); - g_free (value2); - return TRUE; -} - -bool_t audacious_rc_jump (RemoteObject * obj, unsigned int pos, GError * * error) -{ - playlist_set_position (playlist_get_active (), pos); - return TRUE; -} - -bool_t audacious_rc_add(RemoteObject * obj, char * file, GError * *error) -{ - return audacious_rc_playlist_ins_url_string(obj, file, -1, error); -} - -bool_t audacious_rc_add_url(RemoteObject * obj, char * file, GError * *error) -{ - return audacious_rc_playlist_ins_url_string(obj, file, -1, error); -} - -static Index * strings_to_index (char * * strings) -{ - Index * index = index_new (); - - while (* strings) - index_append (index, str_get (* strings ++)); - - return index; -} - -bool_t audacious_rc_add_list (RemoteObject * obj, char * * filenames, - GError * * error) -{ - drct_pl_add_list (strings_to_index (filenames), -1); - return TRUE; -} - -bool_t audacious_rc_open_list (RemoteObject * obj, char * * filenames, - GError * * error) -{ - drct_pl_open_list (strings_to_index (filenames)); - return TRUE; -} - -bool_t audacious_rc_open_list_to_temp (RemoteObject * obj, char * * - filenames, GError * * error) -{ - drct_pl_open_temp_list (strings_to_index (filenames)); - return TRUE; -} - -bool_t audacious_rc_delete (RemoteObject * obj, unsigned int pos, GError * * error) -{ - playlist_entry_delete (playlist_get_active (), pos, 1); - return TRUE; -} - -bool_t audacious_rc_clear (RemoteObject * obj, GError * * error) -{ - int playlist = playlist_get_active (); - playlist_entry_delete (playlist, 0, playlist_entry_count (playlist)); - return TRUE; -} - -bool_t audacious_rc_auto_advance(RemoteObject * obj, bool_t * is_advance, GError ** error) -{ - * is_advance = ! get_bool (NULL, "no_playlist_advance"); - return TRUE; -} - -bool_t audacious_rc_toggle_auto_advance(RemoteObject * obj, GError ** error) -{ - set_bool (NULL, "no_playlist_advance", ! get_bool (NULL, "no_playlist_advance")); - return TRUE; -} - -bool_t audacious_rc_repeat(RemoteObject * obj, bool_t * is_repeating, GError ** error) -{ - *is_repeating = get_bool (NULL, "repeat"); - return TRUE; -} - -bool_t audacious_rc_toggle_repeat (RemoteObject * obj, GError * * error) -{ - set_bool (NULL, "repeat", ! get_bool (NULL, "repeat")); - return TRUE; -} - -bool_t audacious_rc_shuffle(RemoteObject * obj, bool_t * is_shuffling, GError ** error) -{ - *is_shuffling = get_bool (NULL, "shuffle"); - return TRUE; -} - -bool_t audacious_rc_toggle_shuffle (RemoteObject * obj, GError * * error) -{ - set_bool (NULL, "shuffle", ! get_bool (NULL, "shuffle")); - return TRUE; -} - -bool_t audacious_rc_stop_after (RemoteObject * obj, bool_t * is_stopping, GError * * error) -{ - * is_stopping = get_bool (NULL, "stop_after_current_song"); - return TRUE; -} - -bool_t audacious_rc_toggle_stop_after (RemoteObject * obj, GError * * error) -{ - set_bool (NULL, "stop_after_current_song", ! get_bool (NULL, "stop_after_current_song")); - return TRUE; -} - -/* New on Oct 5 */ -bool_t audacious_rc_show_prefs_box(RemoteObject * obj, bool_t show, GError ** error) -{ - event_queue("prefswin show", GINT_TO_POINTER(show)); - return TRUE; -} - -bool_t audacious_rc_show_about_box(RemoteObject * obj, bool_t show, GError ** error) -{ - event_queue("aboutwin show", GINT_TO_POINTER(show)); - return TRUE; -} - -bool_t audacious_rc_show_jtf_box(RemoteObject * obj, bool_t show, GError ** error) -{ - if (show) - interface_show_jump_to_track (); - - return TRUE; -} - -bool_t audacious_rc_show_filebrowser(RemoteObject * obj, bool_t show, GError ** error) -{ - if (show) - interface_show_filebrowser (FALSE); - - return TRUE; -} - -bool_t audacious_rc_play_pause (RemoteObject * obj, GError * * error) -{ - drct_play_pause (); - return TRUE; -} - -bool_t audacious_rc_get_info (RemoteObject * obj, int * rate, int * freq, - int * nch, GError * * error) -{ - drct_get_info (rate, freq, nch); - return TRUE; -} - -bool_t audacious_rc_toggle_aot(RemoteObject * obj, bool_t ontop, GError ** error) -{ - hook_call("mainwin set always on top", &ontop); - return TRUE; -} - -bool_t audacious_rc_playqueue_add (RemoteObject * obj, int pos, GError * * error) -{ - playlist_queue_insert (playlist_get_active (), -1, pos); - return TRUE; -} - -bool_t audacious_rc_playqueue_remove (RemoteObject * obj, int pos, GError * * error) -{ - int playlist = playlist_get_active (); - int at = playlist_queue_find_entry (playlist, pos); - - if (at >= 0) - playlist_queue_delete (playlist, at, 1); - - return TRUE; -} - -bool_t audacious_rc_playqueue_clear (RemoteObject * obj, GError * * error) -{ - int playlist = playlist_get_active (); - playlist_queue_delete (playlist, 0, playlist_queue_count (playlist)); - return TRUE; -} - -bool_t audacious_rc_get_playqueue_length (RemoteObject * obj, int * length, - GError * * error) -{ - * length = playlist_queue_count (playlist_get_active ()); - return TRUE; -} - -bool_t audacious_rc_queue_get_list_pos (RemoteObject * obj, int qpos, int * pos, - GError * * error) -{ - * pos = playlist_queue_get_entry (playlist_get_active (), qpos); - return TRUE; -} - -bool_t audacious_rc_queue_get_queue_pos (RemoteObject * obj, int pos, int * - qpos, GError * * error) -{ - * qpos = playlist_queue_find_entry (playlist_get_active (), pos); - return TRUE; -} - -bool_t audacious_rc_playqueue_is_queued (RemoteObject * obj, int pos, bool_t * - is_queued, GError * * error) -{ - * is_queued = (playlist_queue_find_entry (playlist_get_active (), pos) >= 0); - return TRUE; -} - -bool_t audacious_rc_playlist_ins_url_string (RemoteObject * obj, char * url, int - pos, GError * * error) -{ - playlist_entry_insert (playlist_get_active (), pos, url, NULL, FALSE); - return TRUE; -} - -bool_t audacious_rc_playlist_add(RemoteObject * obj, void *list, GError * *error) -{ - return audacious_rc_playlist_ins_url_string(obj, list, -1, error); -} - -bool_t audacious_rc_playlist_enqueue_to_temp (RemoteObject * obj, char * url, - GError * * error) -{ - drct_pl_open_temp (url); - return TRUE; -} - -/* New on Nov 7: Equalizer */ -bool_t audacious_rc_get_eq(RemoteObject * obj, double * preamp, GArray ** bands, GError ** error) -{ - * preamp = get_double (NULL, "equalizer_preamp"); - * bands = g_array_new (FALSE, FALSE, sizeof (double)); - g_array_set_size (* bands, AUD_EQUALIZER_NBANDS); - eq_get_bands ((double *) (* bands)->data); - - return TRUE; -} - -bool_t audacious_rc_get_eq_preamp(RemoteObject * obj, double * preamp, GError ** error) -{ - * preamp = get_double (NULL, "equalizer_preamp"); - return TRUE; -} - -bool_t audacious_rc_get_eq_band(RemoteObject * obj, int band, double * value, GError ** error) -{ - * value = eq_get_band (band); - return TRUE; -} - -bool_t audacious_rc_set_eq(RemoteObject * obj, double preamp, GArray * bands, GError ** error) -{ - set_double (NULL, "equalizer_preamp", preamp); - eq_set_bands ((double *) bands->data); - return TRUE; -} - -bool_t audacious_rc_set_eq_preamp(RemoteObject * obj, double preamp, GError ** error) -{ - set_double (NULL, "equalizer_preamp", preamp); - return TRUE; -} - -bool_t audacious_rc_set_eq_band(RemoteObject * obj, int band, double value, GError ** error) -{ - eq_set_band (band, value); - return TRUE; -} - -bool_t audacious_rc_equalizer_activate(RemoteObject * obj, bool_t active, GError ** error) -{ - set_bool (NULL, "equalizer_active", active); - return TRUE; -} - -bool_t audacious_rc_get_active_playlist_name (RemoteObject * obj, char * * - title, GError * * error) -{ - char * title2 = playlist_get_title (playlist_get_active ()); - * title = strdup (title2); - str_unref (title2); - return TRUE; -} - -DBusGProxy *audacious_get_dbus_proxy(void) -{ - DBusGConnection *connection = NULL; - GError *error = NULL; - connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); - g_clear_error(&error); - return dbus_g_proxy_new_for_name(connection, AUDACIOUS_DBUS_SERVICE, AUDACIOUS_DBUS_PATH, AUDACIOUS_DBUS_INTERFACE); -} diff --git a/src/audacious/dbus.h b/src/audacious/dbus.h deleted file mode 100644 index 3a3300d..0000000 --- a/src/audacious/dbus.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * dbus.h - * Copyright 2007 Ben Tucker - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef AUDACIOUS_DBUS_H -#define AUDACIOUS_DBUS_H - -#include - -#define AUDACIOUS_DBUS_SERVICE "org.atheme.audacious" -#define AUDACIOUS_DBUS_PATH "/org/atheme/audacious" -#define AUDACIOUS_DBUS_INTERFACE "org.atheme.audacious" -#define AUDACIOUS_DBUS_SERVICE_MPRIS "org.mpris.audacious" -#define AUDACIOUS_DBUS_INTERFACE_MPRIS "org.freedesktop.MediaPlayer" -#define AUDACIOUS_DBUS_PATH_MPRIS_ROOT "/" -#define AUDACIOUS_DBUS_PATH_MPRIS_PLAYER "/Player" -#define AUDACIOUS_DBUS_PATH_MPRIS_TRACKLIST "/TrackList" - -#define NONE = 0 -#define CAN_GO_NEXT = 1 << 0 -#define CAN_GO_PREV = 1 << 1 -#define CAN_PAUSE = 1 << 2 -#define CAN_PLAY = 1 << 3 -#define CAN_SEEK = 1 << 4 -#define CAN_RESTORE_CONTEXT = 1 << 5 -#define CAN_PROVIDE_METADATA = 1 << 6 -#define PROVIDES_TIMING = 1 << 7 - -void init_dbus (void); -void cleanup_dbus (void); -DBusGProxy * audacious_get_dbus_proxy (void); - -#endif /* AUDACIOUS_DBUS_H */ diff --git a/src/audacious/debug.h b/src/audacious/debug.h index b10fd6c..2b372ad 100644 --- a/src/audacious/debug.h +++ b/src/audacious/debug.h @@ -1,6 +1,6 @@ /* * debug.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/drct-api.h b/src/audacious/drct-api.h index 1a259f0..cffaf4b 100644 --- a/src/audacious/drct-api.h +++ b/src/audacious/drct-api.h @@ -1,6 +1,6 @@ /* * drct-api.h - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -31,6 +31,7 @@ AUD_VFUNC0 (drct_quit) * and must be freed with str_unref(). */ AUD_VFUNC0 (drct_play) +AUD_VFUNC0 (drct_play_pause) AUD_VFUNC1 (drct_play_playlist, int, playlist) AUD_VFUNC0 (drct_pause) AUD_VFUNC0 (drct_stop) @@ -44,6 +45,13 @@ AUD_FUNC0 (int, drct_get_time) AUD_FUNC0 (int, drct_get_length) AUD_VFUNC1 (drct_seek, int, time) +/* "A-B repeat": when playback reaches point B, it returns to point A (where A + * and B are in milliseconds). The value -1 is interpreted as the beginning of + * the song (for A) or the end of the song (for B). A-B repeat is disabled + * entirely by setting both A and B to -1. */ +AUD_VFUNC2 (drct_set_ab_repeat, int, a, int, b) +AUD_VFUNC2 (drct_get_ab_repeat, int *, a, int *, b) + /* --- VOLUME CONTROL --- */ AUD_VFUNC2 (drct_get_volume, int *, left, int *, right) @@ -68,17 +76,3 @@ AUD_VFUNC1 (drct_pl_open, const char *, filename) AUD_VFUNC1 (drct_pl_open_list, Index *, filenames) AUD_VFUNC1 (drct_pl_open_temp, const char *, filename) AUD_VFUNC1 (drct_pl_open_temp_list, Index *, filenames) - -/* deprecated; use playlist_delete_selected() */ -AUD_VFUNC1 (drct_pl_delete_selected, int, playlist) - -/* added in Audacious 3.4 */ - -/* "A-B repeat": when playback reaches point B, it returns to point A (where A - * and B are in milliseconds). The value -1 is interpreted as the beginning of - * the song (for A) or the end of the song (for B). A-B repeat is disabled - * entirely by setting both A and B to -1. */ -AUD_VFUNC2 (drct_set_ab_repeat, int, a, int, b) -AUD_VFUNC2 (drct_get_ab_repeat, int *, a, int *, b) - -AUD_VFUNC0 (drct_play_pause) diff --git a/src/audacious/drct.c b/src/audacious/drct.c index 40e3151..5fbef8f 100644 --- a/src/audacious/drct.c +++ b/src/audacious/drct.c @@ -1,6 +1,6 @@ /* * drct.c - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,7 +17,6 @@ * the use of this software. */ -#include #include #include @@ -26,13 +25,6 @@ #include "misc.h" #include "playlist.h" -/* --- PROGRAM CONTROL --- */ - -void drct_quit (void) -{ - hook_call ("quit", NULL); -} - /* --- PLAYBACK CONTROL --- */ void drct_play (void) @@ -169,7 +161,7 @@ static void add_list (Index * filenames, int at, bool_t to_temp, bool_t play) void drct_pl_add (const char * filename, int at) { Index * filenames = index_new (); - index_append (filenames, str_get (filename)); + index_insert (filenames, -1, str_get (filename)); add_list (filenames, at, FALSE, FALSE); } @@ -181,7 +173,7 @@ void drct_pl_add_list (Index * filenames, int at) void drct_pl_open (const char * filename) { Index * filenames = index_new (); - index_append (filenames, str_get (filename)); + index_insert (filenames, -1, str_get (filename)); add_list (filenames, -1, get_bool (NULL, "open_to_temporary"), TRUE); } @@ -193,7 +185,7 @@ void drct_pl_open_list (Index * filenames) void drct_pl_open_temp (const char * filename) { Index * filenames = index_new (); - index_append (filenames, str_get (filename)); + index_insert (filenames, -1, str_get (filename)); add_list (filenames, -1, TRUE, TRUE); } @@ -201,8 +193,3 @@ void drct_pl_open_temp_list (Index * filenames) { add_list (filenames, -1, TRUE, TRUE); } - -void drct_pl_delete_selected (int list) -{ - playlist_delete_selected (list); -} diff --git a/src/audacious/effect.c b/src/audacious/effect.c index 23f12ea..c344e06 100644 --- a/src/audacious/effect.c +++ b/src/audacious/effect.c @@ -1,6 +1,6 @@ /* * effect.c - * Copyright 2010 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -50,7 +50,7 @@ static bool_t effect_start_cb (PluginHandle * plugin, EffectStartState * state) g_return_val_if_fail (header != NULL, TRUE); header->start (state->channels, state->rate); - RunningEffect * effect = g_malloc (sizeof (RunningEffect)); + RunningEffect * effect = g_slice_new (RunningEffect); effect->plugin = plugin; effect->header = header; effect->channels_returned = * state->channels; @@ -66,7 +66,10 @@ void effect_start (int * channels, int * rate) pthread_mutex_lock (& mutex); AUDDBG ("Starting effects.\n"); - g_list_foreach (running_effects, (GFunc) g_free, NULL); + + for (GList * node = running_effects; node; node = node->next) + g_slice_free (RunningEffect, node->data); + g_list_free (running_effects); running_effects = NULL; @@ -96,7 +99,7 @@ static void effect_process_cb (RunningEffect * effect, EffectProcessState * effect->header->finish (state->data, state->samples); running_effects = g_list_remove (running_effects, effect); - g_free (effect); + g_slice_free (RunningEffect, effect); } else effect->header->process (state->data, state->samples); @@ -166,7 +169,7 @@ static void effect_insert (PluginHandle * plugin, EffectPlugin * header) return; AUDDBG ("Adding %s without reset.\n", plugin_get_name (plugin)); - RunningEffect * effect = g_malloc (sizeof (RunningEffect)); + RunningEffect * effect = g_slice_new (RunningEffect); effect->plugin = plugin; effect->header = header; effect->remove_flag = FALSE; diff --git a/src/audacious/effect.h b/src/audacious/effect.h index c663eb7..2a0add9 100644 --- a/src/audacious/effect.h +++ b/src/audacious/effect.h @@ -1,6 +1,6 @@ /* * effect.h - * Copyright 2010 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/equalizer.c b/src/audacious/equalizer.c index ef67b89..1b74f31 100644 --- a/src/audacious/equalizer.c +++ b/src/audacious/equalizer.c @@ -182,18 +182,18 @@ void eq_cleanup (void) void eq_set_bands (const double *values) { - char *string = double_array_to_string (values, EQ_BANDS); + char *string = double_array_to_str (values, EQ_BANDS); g_return_if_fail (string); - set_string (NULL, "equalizer_bands", string); - g_free (string); + set_str (NULL, "equalizer_bands", string); + str_unref (string); } void eq_get_bands (double *values) { memset (values, 0, sizeof (double) * EQ_BANDS); - char *string = get_string (NULL, "equalizer_bands"); - string_to_double_array (string, values, EQ_BANDS); - g_free (string); + char *string = get_str (NULL, "equalizer_bands"); + str_to_double_array (string, values, EQ_BANDS); + str_unref (string); } void eq_set_band (int band, double value) diff --git a/src/audacious/equalizer_preset.c b/src/audacious/equalizer_preset.c index dce62f9..6c70eb8 100644 --- a/src/audacious/equalizer_preset.c +++ b/src/audacious/equalizer_preset.c @@ -1,6 +1,7 @@ /* * equalizer_preset.c - * Copyright 2003-2011 Eugene Zagidullin, William Pitcock, and John Lindgren + * Copyright 2003-2013 Eugene Zagidullin, William Pitcock, John Lindgren, and + * Thomas Lange * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -18,235 +19,224 @@ */ #include +#include #include +#include + #include "debug.h" #include "i18n.h" -#include "interface.h" #include "misc.h" -static EqualizerPreset * equalizer_preset_new (const char * name) +EqualizerPreset * equalizer_preset_new (const char * name) { - EqualizerPreset *preset = g_new0(EqualizerPreset, 1); - preset->name = g_strdup(name); + EqualizerPreset * preset = g_slice_new0 (EqualizerPreset); + preset->name = str_get (name); return preset; } +void equalizer_preset_free (EqualizerPreset * preset) +{ + str_unref (preset->name); + g_slice_free (EqualizerPreset, preset); +} + Index * equalizer_read_presets (const char * basename) { - char *filename, *name; - GKeyFile *rcfile; - int i, p = 0; - EqualizerPreset *preset; + GKeyFile * rcfile = g_key_file_new (); - filename = g_build_filename (get_path (AUD_PATH_USER_DIR), basename, NULL); + char * filename = filename_build (get_path (AUD_PATH_USER_DIR), basename); - rcfile = g_key_file_new(); - if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, NULL)) + if (! g_key_file_load_from_file (rcfile, filename, G_KEY_FILE_NONE, NULL)) { - g_free(filename); - filename = g_build_filename (get_path (AUD_PATH_DATA_DIR), basename, - NULL); + str_unref (filename); + filename = filename_build (get_path (AUD_PATH_DATA_DIR), basename); - if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, NULL)) + if (! g_key_file_load_from_file (rcfile, filename, G_KEY_FILE_NONE, NULL)) { - g_free(filename); - return NULL; + str_unref (filename); + g_key_file_free (rcfile); + return NULL; } } - g_free(filename); + str_unref (filename); Index * list = index_new (); - for (;;) + for (int p = 0;; p ++) { - char section[32]; + SPRINTF (section, "Preset%d", p); - g_snprintf(section, sizeof(section), "Preset%d", p++); + char * name = g_key_file_get_string (rcfile, "Presets", section, NULL); + if (! name) + break; - if ((name = g_key_file_get_string(rcfile, "Presets", section, NULL)) != NULL) - { - preset = g_new0(EqualizerPreset, 1); - preset->name = name; - preset->preamp = g_key_file_get_double(rcfile, name, "Preamp", NULL); + EqualizerPreset * preset = equalizer_preset_new (name); + preset->preamp = g_key_file_get_double (rcfile, name, "Preamp", NULL); - for (i = 0; i < AUD_EQUALIZER_NBANDS; i++) - { - char band[16]; - g_snprintf(band, sizeof(band), "Band%d", i); + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i++) + { + SPRINTF (band, "Band%d", i); + preset->bands[i] = g_key_file_get_double (rcfile, name, band, NULL); + } - preset->bands[i] = g_key_file_get_double(rcfile, name, band, NULL); - } + index_insert (list, -1, preset); - index_append (list, preset); - } - else - break; + g_free (name); } - g_key_file_free(rcfile); + g_key_file_free (rcfile); return list; } -bool_t equalizer_write_preset_file (Index * list, const char * basename) +bool_t equalizer_write_presets (Index * list, const char * basename) { - char *filename; - int i; - GKeyFile *rcfile; - char *data; - gsize len; - - rcfile = g_key_file_new(); + GKeyFile * rcfile = g_key_file_new (); for (int p = 0; p < index_count (list); p ++) { EqualizerPreset * preset = index_get (list, p); - char * tmp = g_strdup_printf ("Preset%d", p); - g_key_file_set_string(rcfile, "Presets", tmp, preset->name); - g_free(tmp); - - g_key_file_set_double(rcfile, preset->name, "Preamp", preset->preamp); + SPRINTF (tmp, "Preset%d", p); + g_key_file_set_string (rcfile, "Presets", tmp, preset->name); + g_key_file_set_double (rcfile, preset->name, "Preamp", preset->preamp); - for (i = 0; i < 10; i++) + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) { - tmp = g_strdup_printf("Band%d", i); - g_key_file_set_double(rcfile, preset->name, tmp, - preset->bands[i]); - g_free(tmp); + SPRINTF (tmp, "Band%d", i); + g_key_file_set_double (rcfile, preset->name, tmp, preset->bands[i]); } } - filename = g_build_filename (get_path (AUD_PATH_USER_DIR), basename, NULL); + size_t len; + char * data = g_key_file_to_data (rcfile, & len, NULL); - data = g_key_file_to_data(rcfile, &len, NULL); + char * filename = filename_build (get_path (AUD_PATH_USER_DIR), basename); bool_t success = g_file_set_contents (filename, data, len, NULL); - g_free(data); + str_unref (filename); + + g_key_file_free (rcfile); + g_free (data); - g_key_file_free(rcfile); - g_free(filename); return success; } -Index * import_winamp_eqf (VFSFile * file) +/* Note: Winamp 2.x had a +/- 20 dB range. + * Winamp 5.x had a +/- 12 dB range, which we use here. */ +#define FROM_WINAMP_VAL(x) ((31.5 - (x)) * (12.0 / 31.5)) +#define TO_WINAMP_VAL(x) (round (31.5 - (x) * (31.5 / 12.0))) + +Index * import_winamp_presets (VFSFile * file) { char header[31]; char bands[11]; - int i = 0; - EqualizerPreset *preset = NULL; - char *markup; - char preset_name[0xb4]; + char preset_name[181]; - if (vfs_fread (header, 1, sizeof header, file) != sizeof header || strncmp - (header, "Winamp EQ library file v1.1", 27)) - goto error; + if (vfs_fread (header, 1, sizeof header, file) != sizeof header || + strncmp (header, "Winamp EQ library file v1.1", 27)) + return NULL; - AUDDBG("The EQF header is OK\n"); + Index * list = index_new (); - if (vfs_fseek(file, 0x1f, SEEK_SET) == -1) goto error; + while (vfs_fread (preset_name, 1, 180, file) == 180) + { + preset_name[180] = 0; /* protect against buffer overflow */ - Index * list = index_new (); + if (vfs_fseek (file, 77, SEEK_CUR)) /* unknown crap --asphyx */ + break; - while (vfs_fread(preset_name, 1, 0xb4, file) == 0xb4) { - AUDDBG("The preset name is '%s'\n", preset_name); - if (vfs_fseek (file, 0x4d, SEEK_CUR)) /* unknown crap --asphyx */ + if (vfs_fread (bands, 1, 11, file) != 11) break; - if (vfs_fread(bands, 1, 11, file) != 11) break; - preset = equalizer_preset_new(preset_name); - /*this was divided by 63, but shouldn't it be 64? --majeru*/ - preset->preamp = EQUALIZER_MAX_GAIN - ((bands[10] * EQUALIZER_MAX_GAIN * 2) / 64.0); + EqualizerPreset * preset = equalizer_preset_new (preset_name); + preset->preamp = FROM_WINAMP_VAL (bands[10]); - for (i = 0; i < 10; i++) - preset->bands[i] = EQUALIZER_MAX_GAIN - ((bands[i] * EQUALIZER_MAX_GAIN * 2) / 64.0); + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) + preset->bands[i] = FROM_WINAMP_VAL (bands[i]); - index_append (list, preset); + index_insert (list, -1, preset); } return list; +} + +bool_t export_winamp_preset (EqualizerPreset * preset, VFSFile * file) +{ + char name[257]; + char bands[11]; -error: - markup = g_strdup_printf (_("Error importing Winamp EQF file '%s'"), - vfs_get_filename (file)); - interface_show_error(markup); + if (vfs_fwrite ("Winamp EQ library file v1.1\x1a!--", 1, 31, file) != 31) + return FALSE; - g_free(markup); - return NULL; + strncpy (name, preset->name, 257); + + if (vfs_fwrite (name, 1, 257, file) != 257) + return FALSE; + + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) + bands[i] = TO_WINAMP_VAL (preset->bands[i]); + + bands[10] = TO_WINAMP_VAL (preset->preamp); + + if (vfs_fwrite (bands, 1, 11, file) != 11) + return FALSE; + + return TRUE; } bool_t save_preset_file (EqualizerPreset * preset, const char * filename) { - GKeyFile *rcfile; - int i; - char *data; - gsize len; - - rcfile = g_key_file_new(); - g_key_file_set_double(rcfile, "Equalizer preset", "Preamp", preset->preamp); - - for (i = 0; i < 10; i++) { - char tmp[7]; - g_snprintf(tmp, sizeof(tmp), "Band%d", i); - g_key_file_set_double(rcfile, "Equalizer preset", tmp, - preset->bands[i]); + GKeyFile * rcfile = g_key_file_new (); + + g_key_file_set_double (rcfile, "Equalizer preset", "Preamp", preset->preamp); + + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) + { + SPRINTF (tmp, "Band%d", i); + g_key_file_set_double (rcfile, "Equalizer preset", tmp, preset->bands[i]); } - data = g_key_file_to_data(rcfile, &len, NULL); + size_t len; + char * data = g_key_file_to_data (rcfile, & len, NULL); + VFSFile * file = vfs_fopen (filename, "w"); bool_t success = FALSE; - VFSFile * file = vfs_fopen (filename, "w"); - if (file == NULL) - goto DONE; - if (vfs_fwrite (data, 1, strlen (data), file) == strlen (data)) - success = TRUE; - vfs_fclose (file); - -DONE: - g_free(data); - g_key_file_free(rcfile); + if (file) + { + success = (vfs_fwrite (data, 1, len, file) == len); + vfs_fclose (file); + } + + g_key_file_free (rcfile); + g_free (data); + return success; } -static EqualizerPreset * equalizer_read_aud_preset (const char * filename) +EqualizerPreset * load_preset_file (const char * filename) { - int i; - EqualizerPreset *preset; - GKeyFile *rcfile; - - preset = g_new0(EqualizerPreset, 1); - preset->name = g_strdup(""); + GKeyFile * rcfile = g_key_file_new (); - rcfile = g_key_file_new(); - if (!g_key_file_load_from_file(rcfile, filename, G_KEY_FILE_NONE, NULL)) + if (! g_key_file_load_from_file (rcfile, filename, G_KEY_FILE_NONE, NULL)) { - g_key_file_free(rcfile); - g_free(preset->name); - g_free(preset); + g_key_file_free (rcfile); return NULL; } - preset->preamp = g_key_file_get_double(rcfile, "Equalizer preset", "Preamp", NULL); - for (i = 0; i < 10; i++) - { - char tmp[7]; - g_snprintf(tmp, sizeof(tmp), "Band%d", i); + EqualizerPreset * preset = equalizer_preset_new (""); + + preset->preamp = g_key_file_get_double (rcfile, "Equalizer preset", "Preamp", NULL); - preset->bands[i] = g_key_file_get_double(rcfile, "Equalizer preset", tmp, NULL); + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) + { + SPRINTF (tmp, "Band%d", i); + preset->bands[i] = g_key_file_get_double (rcfile, "Equalizer preset", tmp, NULL); } - g_key_file_free(rcfile); - return preset; -} + g_key_file_free (rcfile); -EqualizerPreset * -load_preset_file(const char *filename) -{ - if (filename) { - EqualizerPreset *preset = equalizer_read_aud_preset(filename); - return preset; - } - return NULL; + return preset; } diff --git a/src/audacious/fft.c b/src/audacious/fft.c index d967396..09a6602 100644 --- a/src/audacious/fft.c +++ b/src/audacious/fft.c @@ -22,12 +22,6 @@ #include "fft.h" -#ifndef HAVE_CEXPF -/* e^(a+bi) = (e^a)(cos(b)+sin(b)i) */ -#define cexpf(x) (expf(crealf(x))*(cosf(cimagf(x))+sinf(cimagf(x))*I)) -#warning Your C library does not have cexpf(). Please update it. -#endif - #define N 512 /* size of the DFT */ #define LOGN 9 /* log N (base 2) */ diff --git a/src/audacious/history.c b/src/audacious/history.c index b09903f..4dde368 100644 --- a/src/audacious/history.c +++ b/src/audacious/history.c @@ -36,8 +36,6 @@ static void history_save (void) if (! modified) return; - config_clear_section ("history"); - GList * node = history.head; for (int i = 0; i < MAX_ENTRIES; i ++) { @@ -46,7 +44,7 @@ static void history_save (void) char name[32]; snprintf (name, sizeof name, "entry%d", i); - set_string ("history", name, node->data); + set_str ("history", name, node->data); node = node->next; } @@ -63,11 +61,11 @@ static void history_load (void) { char name[32]; snprintf (name, sizeof name, "entry%d", i); - char * path = get_string ("history", name); + char * path = get_str ("history", name); if (! path[0]) { - g_free (path); + str_unref (path); break; } @@ -85,7 +83,7 @@ void history_cleanup (void) hook_dissociate ("config save", (HookFunction) history_save); - g_queue_foreach (& history, (GFunc) g_free, NULL); + g_queue_foreach (& history, (GFunc) str_unref, NULL); g_queue_clear (& history); loaded = FALSE; @@ -108,11 +106,11 @@ void history_add (const char * path) next = node->next; if (! strcmp (node->data, path)) { - g_free (node->data); + str_unref (node->data); g_queue_delete_link (& history, node); } } - g_queue_push_head (& history, g_strdup (path)); + g_queue_push_head (& history, str_get (path)); modified = TRUE; } diff --git a/src/audacious/images/appearance.png b/src/audacious/images/appearance.png new file mode 100644 index 0000000..f73239a Binary files /dev/null and b/src/audacious/images/appearance.png differ diff --git a/src/audacious/images/audacious.png b/src/audacious/images/audacious.png deleted file mode 100644 index 5de1b7f..0000000 Binary files a/src/audacious/images/audacious.png and /dev/null differ diff --git a/src/audacious/images/menu_playlist.png b/src/audacious/images/menu_playlist.png deleted file mode 100644 index 2bd5af9..0000000 Binary files a/src/audacious/images/menu_playlist.png and /dev/null differ diff --git a/src/audacious/images/menu_plugin.png b/src/audacious/images/menu_plugin.png deleted file mode 100644 index d90ab66..0000000 Binary files a/src/audacious/images/menu_plugin.png and /dev/null differ diff --git a/src/audacious/images/menu_queue_toggle.png b/src/audacious/images/menu_queue_toggle.png deleted file mode 100644 index 6cf6443..0000000 Binary files a/src/audacious/images/menu_queue_toggle.png and /dev/null differ diff --git a/src/audacious/input-api.h b/src/audacious/input-api.h new file mode 100644 index 0000000..7c49b0e --- /dev/null +++ b/src/audacious/input-api.h @@ -0,0 +1,60 @@ +/* + * input-api.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +/* Do not include this file directly; use input.h instead. */ + +/* These functions are to be used only from the play() function of an input plugin. */ + +/* Prepares the output system for playback in the specified format. Returns + * TRUE on success, FALSE if the selected format is not supported. */ +AUD_FUNC3 (bool_t, input_open_audio, int, format, int, rate, int, channels) + +/* Informs the output system of replay gain values for the current song so + * that volume levels can be adjusted accordingly, if the user so desires. + * This may be called at any time during playback should the values change. */ +AUD_VFUNC1 (input_set_gain, const ReplayGainInfo *, info) + +/* Passes audio data to the output system for playback. The data must be in + * the format passed to open_audio, and the length (in bytes) must be an + * integral number of frames. This function blocks until all the data has + * been written (though it may not yet be heard by the user). */ +AUD_VFUNC2 (input_write_audio, void *, data, int, length) + +/* Returns the time counter. Note that this represents the amount of audio + * data passed to the output system, not the amount actually heard by the + * user. */ +AUD_FUNC0 (int, input_written_time) + +/* Returns a reference to the current tuple for the stream. */ +AUD_FUNC0 (Tuple *, input_get_tuple) + +/* Updates the tuple for the stream. The caller gives up ownership of one + * reference to the tuple. */ +AUD_VFUNC1 (input_set_tuple, Tuple *, tuple) + +/* Updates the displayed bitrate, in bits per second. */ +AUD_VFUNC1 (input_set_bitrate, int, bitrate) + +/* Checks whether playback is to be stopped. The play() function should poll + * check_stop() periodically and return as soon as check_stop() returns TRUE. */ +AUD_FUNC0 (bool_t, input_check_stop) + +/* Checks whether a seek has been requested. If so, discards any buffered audio + * and returns the position to seek to, in milliseconds. Otherwise, returns -1. */ +AUD_FUNC0 (int, input_check_seek) diff --git a/src/audacious/input.h b/src/audacious/input.h new file mode 100644 index 0000000..4935483 --- /dev/null +++ b/src/audacious/input.h @@ -0,0 +1,69 @@ +/* + * input.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef AUDACIOUS_INPUT_H +#define AUDACIOUS_INPUT_H + +#include +#include +#include + +#define AUD_API_NAME InputAPI +#define AUD_API_SYMBOL input_api + +#ifdef _AUDACIOUS_CORE + +#include "api-local-begin.h" +#include "input-api.h" +#include "api-local-end.h" + +#else + +#include +#include +#include + +#include +#include +#include + +#endif + +#undef AUD_API_NAME +#undef AUD_API_SYMBOL + +#endif + +#ifdef AUD_API_DECLARE + +#define AUD_API_NAME InputAPI +#define AUD_API_SYMBOL input_api + +#include "api-define-begin.h" +#include "input-api.h" +#include "api-define-end.h" + +#include "api-declare-begin.h" +#include "input-api.h" +#include "api-declare-end.h" + +#undef AUD_API_NAME +#undef AUD_API_SYMBOL + +#endif diff --git a/src/audacious/interface.c b/src/audacious/interface.c index 066b0ef..5c8045f 100644 --- a/src/audacious/interface.c +++ b/src/audacious/interface.c @@ -1,6 +1,6 @@ /* * interface.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -21,21 +21,23 @@ #include #include +#include #include "debug.h" #include "general.h" +#include "i18n.h" #include "interface.h" -#include "main.h" #include "misc.h" #include "plugin.h" #include "plugins.h" #include "visualization.h" -static IfacePlugin *current_interface = NULL; +static IfacePlugin * current_interface = NULL; static pthread_mutex_t error_mutex = PTHREAD_MUTEX_INITIALIZER; static GQueue error_queue = G_QUEUE_INIT; static int error_source; +static GtkWidget * error_win; bool_t interface_load (PluginHandle * plugin) { @@ -73,18 +75,7 @@ bool_t interface_is_shown (void) { g_return_val_if_fail (current_interface, FALSE); - if (PLUGIN_HAS_FUNC (current_interface, is_shown)) - return current_interface->is_shown (); - return TRUE; -} - -bool_t interface_is_focused (void) -{ - g_return_val_if_fail (current_interface, FALSE); - - if (PLUGIN_HAS_FUNC (current_interface, is_focused)) - return current_interface->is_focused (); - return TRUE; + return get_bool (NULL, "show_interface"); } static bool_t error_idle_func (void * unused) @@ -96,12 +87,12 @@ static bool_t error_idle_func (void * unused) { pthread_mutex_unlock (& error_mutex); - if (current_interface && PLUGIN_HAS_FUNC (current_interface, show_error)) - current_interface->show_error (message); - else + if (headless_mode ()) fprintf (stderr, "ERROR: %s\n", message); + else + audgui_simple_message (& error_win, GTK_MESSAGE_ERROR, _("Error"), message); - g_free (message); + str_unref (message); pthread_mutex_lock (& error_mutex); } @@ -116,7 +107,7 @@ void interface_show_error (const char * message) { pthread_mutex_lock (& error_mutex); - g_queue_push_tail (& error_queue, g_strdup (message)); + g_queue_push_tail (& error_queue, str_get (message)); if (! error_source) error_source = g_idle_add (error_idle_func, NULL); @@ -124,27 +115,6 @@ void interface_show_error (const char * message) pthread_mutex_unlock (& error_mutex); } -/* - * bool_t play_button - * TRUE - open files - * FALSE - add files - */ -void interface_show_filebrowser (bool_t play_button) -{ - g_return_if_fail (current_interface); - - if (PLUGIN_HAS_FUNC (current_interface, show_filebrowser)) - current_interface->show_filebrowser (play_button); -} - -void interface_show_jump_to_track (void) -{ - g_return_if_fail (current_interface); - - if (PLUGIN_HAS_FUNC (current_interface, show_jump_to_track)) - current_interface->show_jump_to_track (); -} - static bool_t delete_cb (GtkWidget * window, GdkEvent * event, PluginHandle * plugin) { @@ -180,26 +150,6 @@ void interface_remove_plugin_widget (PluginHandle * plugin, GtkWidget * widget) gtk_widget_destroy (gtk_widget_get_parent (widget)); } -void interface_install_toolbar (void * widget) -{ - g_return_if_fail (current_interface); - - if (PLUGIN_HAS_FUNC (current_interface, install_toolbar)) - current_interface->install_toolbar (widget); - else - g_object_ref (widget); -} - -void interface_uninstall_toolbar (void * widget) -{ - g_return_if_fail (current_interface); - - if (PLUGIN_HAS_FUNC (current_interface, uninstall_toolbar)) - current_interface->uninstall_toolbar (widget); - else - g_object_unref (widget); -} - static bool_t probe_cb (PluginHandle * p, PluginHandle * * pp) { * pp = p; diff --git a/src/audacious/main.c b/src/audacious/main.c index 1e245fb..8440969 100644 --- a/src/audacious/main.c +++ b/src/audacious/main.c @@ -1,6 +1,6 @@ /* * main.c - * Copyright 2007-2011 William Pitcock and John Lindgren + * Copyright 2007-2013 William Pitcock and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -29,11 +29,11 @@ #include #include +#include #include #ifdef USE_DBUS -#include "../libaudclient/audctrl.h" -#include "dbus.h" +#include "aud-dbus.h" #endif #include "debug.h" @@ -50,20 +50,40 @@ #define AUTOSAVE_INTERVAL 300 /* seconds */ -bool_t headless; - static struct { - char **filenames; - int session; - bool_t play, stop, pause, fwd, rew, play_pause, show_jump_box; - bool_t enqueue, mainwin, remote; - bool_t enqueue_to_temp; - bool_t quit_after_play; - bool_t version; + bool_t help, version; + bool_t play, pause, play_pause, stop, fwd, rew; + bool_t enqueue, enqueue_to_temp; + bool_t mainwin, show_jump_box; + bool_t headless, quit_after_play; bool_t verbose; - char *previous_session_id; } options; +static Index * filenames; + +static const struct { + const char * long_arg; + char short_arg; + bool_t * value; + const char * desc; +} arg_map[] = { + {"help", 'h', & options.help, N_("Show command-line help")}, + {"version", 'v', & options.version, N_("Show version")}, + {"play", 'p', & options.play, N_("Start playback")}, + {"pause", 'u', & options.pause, N_("Pause playback")}, + {"play-pause", 't', & options.play_pause, N_("Pause if playing, play otherwise")}, + {"stop", 's', & options.stop, N_("Stop playback")}, + {"rew", 'r', & options.rew, N_("Skip to previous song")}, + {"fwd", 'f', & options.fwd, N_("Skip to next song")}, + {"enqueue", 'e', & options.enqueue, N_("Add files to the playlist")}, + {"enqueue-to-temp", 'E', & options.enqueue_to_temp, N_("Add files to a temporary playlist")}, + {"show-main-window", 'm', & options.mainwin, N_("Display the main window")}, + {"show-jump-box", 'j', & options.show_jump_box, N_("Display the jump-to-song window")}, + {"headless", 'H', & options.headless, N_("Start without a graphical interface")}, + {"quit-after-play", 'q', & options.quit_after_play, N_("Quit on playback stop")}, + {"verbose", 'V', & options.verbose, N_("Print debugging messages")}, +}; + static char * aud_paths[AUD_PATH_COUNT]; static void make_dirs(void) @@ -78,41 +98,8 @@ static void make_dirs(void) make_directory(aud_paths[AUD_PATH_PLAYLISTS_DIR], mode755); } -static void normalize_path (char * path) -{ -#ifdef _WIN32 - string_replace_char (path, '/', '\\'); -#endif - int len = strlen (path); -#ifdef _WIN32 - if (len > 3 && path[len - 1] == '\\') /* leave "C:\" */ -#else - if (len > 1 && path[len - 1] == '/') /* leave leading "/" */ -#endif - path[len - 1] = 0; -} - -static char * last_path_element (char * path) -{ - char * slash = strrchr (path, G_DIR_SEPARATOR); - return (slash && slash[1]) ? slash + 1 : NULL; -} - -static void strip_path_element (char * path, char * elem) -{ -#ifdef _WIN32 - if (elem > path + 3) -#else - if (elem > path + 1) -#endif - elem[-1] = 0; /* overwrite slash */ - else - elem[0] = 0; /* leave [drive letter and] leading slash */ -} - -static void relocate_path (char * * pathp, const char * old, const char * new) +static char * relocate_path (const char * path, const char * old, const char * new) { - char * path = * pathp; int oldlen = strlen (old); int newlen = strlen (new); @@ -122,79 +109,87 @@ static void relocate_path (char * * pathp, const char * old, const char * new) newlen --; #ifdef _WIN32 - if (strncasecmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR)) + if (g_ascii_strncasecmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR)) #else if (strncmp (path, old, oldlen) || (path[oldlen] && path[oldlen] != G_DIR_SEPARATOR)) #endif { fprintf (stderr, "Failed to relocate a data path. Falling back to " "compile-time path: %s\n", path); - return; + return str_get (path); } - * pathp = g_strdup_printf ("%.*s%s", newlen, new, path + oldlen); - g_free (path); + return str_printf ("%.*s%s", newlen, new, path + oldlen); } static void relocate_paths (void) { - /* Start with the paths hard coded at compile time. */ - aud_paths[AUD_PATH_BIN_DIR] = g_strdup (HARDCODE_BINDIR); - aud_paths[AUD_PATH_DATA_DIR] = g_strdup (HARDCODE_DATADIR); - aud_paths[AUD_PATH_PLUGIN_DIR] = g_strdup (HARDCODE_PLUGINDIR); - aud_paths[AUD_PATH_LOCALE_DIR] = g_strdup (HARDCODE_LOCALEDIR); - aud_paths[AUD_PATH_DESKTOP_FILE] = g_strdup (HARDCODE_DESKTOPFILE); - aud_paths[AUD_PATH_ICON_FILE] = g_strdup (HARDCODE_ICONFILE); - normalize_path (aud_paths[AUD_PATH_BIN_DIR]); - normalize_path (aud_paths[AUD_PATH_DATA_DIR]); - normalize_path (aud_paths[AUD_PATH_PLUGIN_DIR]); - normalize_path (aud_paths[AUD_PATH_LOCALE_DIR]); - normalize_path (aud_paths[AUD_PATH_DESKTOP_FILE]); - normalize_path (aud_paths[AUD_PATH_ICON_FILE]); + char bindir[] = HARDCODE_BINDIR; + char datadir[] = HARDCODE_DATADIR; + char plugindir[] = HARDCODE_PLUGINDIR; + char localedir[] = HARDCODE_LOCALEDIR; + char desktopfile[] = HARDCODE_DESKTOPFILE; + char iconfile[] = HARDCODE_ICONFILE; + + filename_normalize (bindir); + filename_normalize (datadir); + filename_normalize (plugindir); + filename_normalize (localedir); + filename_normalize (desktopfile); + filename_normalize (iconfile); /* Compare the compile-time path to the executable and the actual path to * see if we have been moved. */ - char * old = g_strdup (aud_paths[AUD_PATH_BIN_DIR]); - char * new = get_path_to_self (); - if (! new) + char * self = get_path_to_self (); + if (! self) { -ERR: - g_free (old); - g_free (new); +FALLBACK: + /* Fall back to compile-time paths. */ + aud_paths[AUD_PATH_BIN_DIR] = str_get (bindir); + aud_paths[AUD_PATH_DATA_DIR] = str_get (datadir); + aud_paths[AUD_PATH_PLUGIN_DIR] = str_get (plugindir); + aud_paths[AUD_PATH_LOCALE_DIR] = str_get (localedir); + aud_paths[AUD_PATH_DESKTOP_FILE] = str_get (desktopfile); + aud_paths[AUD_PATH_ICON_FILE] = str_get (iconfile); + return; } - normalize_path (new); + + SCOPY (old, bindir); + SCOPY (new, self); + + str_unref (self); + + filename_normalize (new); /* Strip the name of the executable file, leaving the path. */ char * base = last_path_element (new); if (! base) - goto ERR; - strip_path_element (new, base); + goto FALLBACK; + + cut_path_element (new, base); /* Strip innermost folder names from both paths as long as they match. This * leaves a compile-time prefix and a run-time one to replace it with. */ char * a, * b; while ((a = last_path_element (old)) && (b = last_path_element (new)) && #ifdef _WIN32 - ! strcasecmp (a, b)) + ! g_ascii_strcasecmp (a, b)) #else ! strcmp (a, b)) #endif { - strip_path_element (old, a); - strip_path_element (new, b); + cut_path_element (old, a); + cut_path_element (new, b); } /* Do the replacements. */ - relocate_path (& aud_paths[AUD_PATH_BIN_DIR], old, new); - relocate_path (& aud_paths[AUD_PATH_DATA_DIR], old, new); - relocate_path (& aud_paths[AUD_PATH_PLUGIN_DIR], old, new); - relocate_path (& aud_paths[AUD_PATH_LOCALE_DIR], old, new); - relocate_path (& aud_paths[AUD_PATH_DESKTOP_FILE], old, new); - relocate_path (& aud_paths[AUD_PATH_ICON_FILE], old, new); - - g_free (old); - g_free (new); + aud_paths[AUD_PATH_BIN_DIR] = relocate_path (bindir, old, new); + aud_paths[AUD_PATH_DATA_DIR] = relocate_path (datadir, old, new); + aud_paths[AUD_PATH_PLUGIN_DIR] = relocate_path (plugindir, old, new); + aud_paths[AUD_PATH_LOCALE_DIR] = relocate_path (localedir, old, new); + aud_paths[AUD_PATH_DESKTOP_FILE] = relocate_path (desktopfile, old, new); + aud_paths[AUD_PATH_ICON_FILE] = relocate_path (iconfile, old, new); } static void init_paths (void) @@ -202,24 +197,18 @@ static void init_paths (void) relocate_paths (); const char * xdg_config_home = g_get_user_config_dir (); - const char * xdg_data_home = g_get_user_data_dir (); + + aud_paths[AUD_PATH_USER_DIR] = filename_build (xdg_config_home, "audacious"); + aud_paths[AUD_PATH_PLAYLISTS_DIR] = filename_build (aud_paths[AUD_PATH_USER_DIR], "playlists"); #ifdef _WIN32 /* Some libraries (libmcs) and plugins (filewriter) use these variables, * which are generally not set on Windows. */ g_setenv ("HOME", g_get_home_dir (), TRUE); g_setenv ("XDG_CONFIG_HOME", xdg_config_home, TRUE); - g_setenv ("XDG_DATA_HOME", xdg_data_home, TRUE); + g_setenv ("XDG_DATA_HOME", g_get_user_data_dir (), TRUE); g_setenv ("XDG_CACHE_HOME", g_get_user_cache_dir (), TRUE); #endif - - aud_paths[AUD_PATH_USER_DIR] = g_build_filename(xdg_config_home, "audacious", NULL); - aud_paths[AUD_PATH_USER_PLUGIN_DIR] = g_build_filename(xdg_data_home, "audacious", "Plugins", NULL); - aud_paths[AUD_PATH_PLAYLISTS_DIR] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "playlists", NULL); - aud_paths[AUD_PATH_GTKRC_FILE] = g_build_filename(aud_paths[AUD_PATH_USER_DIR], "gtkrc", NULL); - - for (int i = 0; i < AUD_PATH_COUNT; i ++) - AUDDBG ("Data path: %s\n", aud_paths[i]); } const char * get_path (int id) @@ -228,181 +217,202 @@ const char * get_path (int id) return aud_paths[id]; } -static GOptionEntry cmd_entries[] = { - {"rew", 'r', 0, G_OPTION_ARG_NONE, &options.rew, N_("Skip backwards in playlist"), NULL}, - {"play", 'p', 0, G_OPTION_ARG_NONE, &options.play, N_("Start playing current playlist"), NULL}, - {"pause", 'u', 0, G_OPTION_ARG_NONE, &options.pause, N_("Pause current song"), NULL}, - {"stop", 's', 0, G_OPTION_ARG_NONE, &options.stop, N_("Stop current song"), NULL}, - {"play-pause", 't', 0, G_OPTION_ARG_NONE, &options.play_pause, N_("Pause if playing, play otherwise"), NULL}, - {"fwd", 'f', 0, G_OPTION_ARG_NONE, &options.fwd, N_("Skip forward in playlist"), NULL}, - {"show-jump-box", 'j', 0, G_OPTION_ARG_NONE, &options.show_jump_box, N_("Display Jump to File dialog"), NULL}, - {"enqueue", 'e', 0, G_OPTION_ARG_NONE, &options.enqueue, N_("Add files to the playlist"), NULL}, - {"enqueue-to-temp", 'E', 0, G_OPTION_ARG_NONE, &options.enqueue_to_temp, N_("Add new files to a temporary playlist"), NULL}, - {"show-main-window", 'm', 0, G_OPTION_ARG_NONE, &options.mainwin, N_("Display the main window"), NULL}, - {"headless", 'h', 0, G_OPTION_ARG_NONE, & headless, N_("Headless mode"), NULL}, - {"quit-after-play", 'q', 0, G_OPTION_ARG_NONE, &options.quit_after_play, N_("Quit on playback stop"), NULL}, - {"version", 'v', 0, G_OPTION_ARG_NONE, &options.version, N_("Show version"), NULL}, - {"verbose", 'V', 0, G_OPTION_ARG_NONE, &options.verbose, N_("Print debugging messages"), NULL}, - {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &options.filenames, N_("FILE..."), NULL}, - {NULL}, -}; - -static void parse_options (int * argc, char *** argv) +static bool_t parse_options (int argc, char * * argv) { - GOptionContext *context; - GError *error = NULL; - - memset (& options, 0, sizeof options); - options.session = -1; + char * cur = g_get_current_dir (); + bool_t success = TRUE; - context = g_option_context_new(_("- play multimedia files")); - g_option_context_add_main_entries(context, cmd_entries, PACKAGE); - g_option_context_add_group(context, gtk_get_option_group(FALSE)); +#ifdef _WIN32 + get_argv_utf8 (& argc, & argv); +#endif - if (!g_option_context_parse(context, argc, argv, &error)) + for (int n = 1; n < argc; n ++) { - fprintf (stderr, - _("%s: %s\nTry `%s --help' for more information.\n"), (* argv)[0], - error->message, (* argv)[0]); - g_error_free (error); - exit (EXIT_FAILURE); - } + if (argv[n][0] != '-') /* filename */ + { + char * uri = NULL; - g_option_context_free (context); + if (strstr (argv[n], "://")) + uri = str_get (argv[n]); + else if (g_path_is_absolute (argv[n])) + uri = filename_to_uri (argv[n]); + else + { + char * tmp = filename_build (cur, argv[n]); + uri = filename_to_uri (tmp); + str_unref (tmp); + } + + if (uri) + { + if (! filenames) + filenames = index_new (); + + index_insert (filenames, -1, uri); + } + } + else if (argv[n][1] == '-') /* long option */ + { + int i; + + for (i = 0; i < ARRAY_LEN (arg_map); i ++) + { + if (! strcmp (argv[n] + 2, arg_map[i].long_arg)) + { + * arg_map[i].value = TRUE; + break; + } + } + + if (i == ARRAY_LEN (arg_map)) + { + fprintf (stderr, _("Unknown option: %s\n"), argv[n]); + success = FALSE; + goto OUT; + } + } + else /* short form */ + { + for (int c = 1; argv[n][c]; c ++) + { + int i; + + for (i = 0; i < ARRAY_LEN (arg_map); i ++) + { + if (argv[n][c] == arg_map[i].short_arg) + { + * arg_map[i].value = TRUE; + break; + } + } + + if (i == ARRAY_LEN (arg_map)) + { + fprintf (stderr, _("Unknown option: -%c\n"), argv[n][c]); + success = FALSE; + goto OUT; + } + } + } + } verbose = options.verbose; + +OUT: +#ifdef _WIN32 + free_argv_utf8 (& argc, & argv); +#endif + + g_free (cur); + return success; } -static bool_t get_lock (void) +static void print_help (void) { - char * path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "lock", aud_paths[AUD_PATH_USER_DIR]); - int handle = open (path, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); + static const char pad[20] = " "; - if (handle < 0) - { - if (errno != EEXIST) - fprintf (stderr, "Cannot create %s: %s.\n", path, strerror (errno)); + fprintf (stderr, _("Usage: audacious [OPTION] ... [FILE] ...\n\n")); - g_free (path); - return FALSE; - } + for (int i = 0; i < ARRAY_LEN (arg_map); i ++) + fprintf (stderr, " -%c, --%s%.*s%s\n", arg_map[i].short_arg, + arg_map[i].long_arg, (int) (20 - strlen (arg_map[i].long_arg)), pad, + _(arg_map[i].desc)); - close (handle); - g_free (path); - return TRUE; + fprintf (stderr, "\n"); } -static void release_lock (void) +bool_t headless_mode (void) { - char * path = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "lock", aud_paths[AUD_PATH_USER_DIR]); - unlink (path); - g_free (path); + return options.headless; } -static Index * convert_filenames (void) +#ifdef USE_DBUS +static void do_remote (void) { - if (! options.filenames) - return NULL; + GDBusConnection * bus = NULL; + ObjAudacious * obj = NULL; + GError * error = NULL; - Index * filenames = index_new (); - char * * f = options.filenames; - char * cur = g_get_current_dir (); + if (! (bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, & error))) + goto ERR; - for (int i = 0; f[i]; i ++) - { - char * uri = NULL; + if (! (obj = obj_audacious_proxy_new_sync (bus, 0, "org.atheme.audacious", + "/org/atheme/audacious", NULL, & error))) + goto ERR; - if (strstr (f[i], "://")) - uri = str_get (f[i]); - else if (g_path_is_absolute (f[i])) - { - char * tmp = filename_to_uri (f[i]); - uri = str_get (tmp); - free (tmp); - } - else - { - char * tmp = g_build_filename (cur, f[i], NULL); - char * tmp2 = filename_to_uri (tmp); - uri = str_get (tmp2); - free (tmp); - free (tmp2); - } + /* check whether remote is running */ + char * version = NULL; + obj_audacious_call_version_sync (obj, & version, NULL, NULL); - if (uri) - index_append (filenames, uri); - } + if (! version) + goto DONE; - g_free (cur); - return filenames; -} + AUDDBG ("Connected to remote version %s.\n", version); -static void do_remote (void) -{ -#ifdef USE_DBUS - DBusGProxy * session = audacious_get_dbus_proxy (); + /* if no command line options, then present running instance */ + if (! (filenames || options.play || options.pause || options.play_pause || + options.stop || options.rew || options.fwd || options.show_jump_box || + options.mainwin)) + options.mainwin = TRUE; - if (session && audacious_remote_is_running (session)) + if (filenames) { - Index * filenames = convert_filenames (); + int n_filenames = index_count (filenames); + const char * * list = g_new (const char *, n_filenames + 1); - /* if no command line options, then present running instance */ - if (! (filenames || options.play || options.pause || options.play_pause || - options.stop || options.rew || options.fwd || options.show_jump_box || - options.mainwin)) - options.mainwin = TRUE; + for (int i = 0; i < n_filenames; i ++) + list[i] = index_get (filenames, i); - if (filenames) - { - GList * list = NULL; + list[n_filenames] = NULL; + + if (options.enqueue_to_temp) + obj_audacious_call_open_list_to_temp_sync (obj, list, NULL, NULL); + else if (options.enqueue) + obj_audacious_call_add_list_sync (obj, list, NULL, NULL); + else + obj_audacious_call_open_list_sync (obj, list, NULL, NULL); - for (int f = index_count (filenames); f --; ) - list = g_list_prepend (list, index_get (filenames, f)); + g_free (list); + } - if (options.enqueue_to_temp) - audacious_remote_playlist_open_list_to_temp (session, list); - else if (options.enqueue) - audacious_remote_playlist_add (session, list); - else - audacious_remote_playlist_open_list (session, list); + if (options.play) + obj_audacious_call_play_sync (obj, NULL, NULL); + if (options.pause) + obj_audacious_call_pause_sync (obj, NULL, NULL); + if (options.play_pause) + obj_audacious_call_play_pause_sync (obj, NULL, NULL); + if (options.stop) + obj_audacious_call_stop_sync (obj, NULL, NULL); + if (options.rew) + obj_audacious_call_reverse_sync (obj, NULL, NULL); + if (options.fwd) + obj_audacious_call_advance_sync (obj, NULL, NULL); + if (options.show_jump_box) + obj_audacious_call_show_jtf_box_sync (obj, TRUE, NULL, NULL); + if (options.mainwin) + obj_audacious_call_show_main_win_sync (obj, TRUE, NULL, NULL); - g_list_free (list); + g_free (version); + g_object_unref (obj); - for (int f = 0; f < index_count (filenames); f ++) - str_unref (index_get (filenames, f)); + exit (EXIT_SUCCESS); - index_free (filenames); - } +ERR: + fprintf (stderr, "D-Bus error: %s\n", error->message); + g_error_free (error); - if (options.play) - audacious_remote_play (session); - if (options.pause) - audacious_remote_pause (session); - if (options.play_pause) - audacious_remote_play_pause (session); - if (options.stop) - audacious_remote_stop (session); - if (options.rew) - audacious_remote_playlist_prev (session); - if (options.fwd) - audacious_remote_playlist_next (session); - if (options.show_jump_box) - audacious_remote_show_jtf_box (session); - if (options.mainwin) - audacious_remote_main_win_toggle (session, TRUE); - - exit (EXIT_SUCCESS); - } -#endif +DONE: + if (obj) + g_object_unref (obj); - fprintf (stderr, "WARNING: Audacious seems to be already running but is not responding.\n"); + return; } +#endif static void do_commands (void) { bool_t resume = get_bool (NULL, "resume_playback_on_startup"); - Index * filenames = convert_filenames (); if (filenames) { if (options.enqueue_to_temp) @@ -417,6 +427,8 @@ static void do_commands (void) drct_pl_open_list (filenames); resume = FALSE; } + + filenames = NULL; } if (resume) @@ -430,14 +442,31 @@ static void do_commands (void) drct_pause (); } - if (options.show_jump_box) - interface_show_jump_to_track (); - if (options.mainwin) + if (options.show_jump_box && ! options.headless) + audgui_jump_to_track (); + if (options.mainwin && ! options.headless) interface_show (TRUE); } +static void main_cleanup (void) +{ + for (int i = 0; i < AUD_PATH_COUNT; i ++) + str_unref (aud_paths[i]); + + if (filenames) + index_free_full (filenames, (IndexFreeFunc) str_unref); + + strpool_shutdown (); +} + static void init_one (void) { + atexit (main_cleanup); + +#ifdef HAVE_SIGWAIT + signals_init_one (); +#endif + init_paths (); make_dirs (); @@ -447,17 +476,20 @@ static void init_one (void) bindtextdomain (PACKAGE "-plugins", aud_paths[AUD_PATH_LOCALE_DIR]); bind_textdomain_codeset (PACKAGE "-plugins", "UTF-8"); textdomain (PACKAGE); + +#if ! GLIB_CHECK_VERSION (2, 36, 0) + g_type_init (); +#endif } -static void init_two (int * p_argc, char * * * p_argv) +static void init_two (void) { - if (! headless) - { -#if ! GLIB_CHECK_VERSION (2, 32, 0) - g_thread_init (NULL); + if (! options.headless) + gtk_init (NULL, NULL); + +#ifdef HAVE_SIGWAIT + signals_init_two (); #endif - gtk_init (p_argc, p_argv); - } AUDDBG ("Loading configuration.\n"); config_load (); @@ -468,10 +500,6 @@ static void init_two (int * p_argc, char * * * p_argv) eq_init (); playlist_init (); -#ifdef HAVE_SIGWAIT - signals_init (); -#endif - tag_set_verbose (verbose); vfs_set_verbose (verbose); @@ -491,16 +519,12 @@ static void init_two (int * p_argc, char * * * p_argv) start_plugins_two (); #ifdef USE_DBUS - init_dbus (); + dbus_server_init (); #endif - - mpris_signals_init (); } static void shut_down (void) { - mpris_signals_cleanup (); - AUDDBG ("Saving playlist state.\n"); save_playlists (TRUE); @@ -508,7 +532,7 @@ static void shut_down (void) stop_plugins_two (); #ifdef USE_DBUS - cleanup_dbus (); + dbus_server_cleanup (); #endif AUDDBG ("Stopping playback.\n"); @@ -522,17 +546,18 @@ static void shut_down (void) AUDDBG ("Unloading lowlevel plugins.\n"); stop_plugins_one (); + event_queue_cancel_all (); + AUDDBG ("Saving configuration.\n"); config_save (); config_cleanup (); AUDDBG ("Cleaning up.\n"); art_cleanup (); + chardet_cleanup (); eq_cleanup (); history_cleanup (); playlist_end (); - - strpool_shutdown (); } bool_t do_autosave (void) @@ -555,10 +580,21 @@ static void maybe_quit (void) gtk_main_quit (); } -int main(int argc, char ** argv) +int main (int argc, char * * argv) { init_one (); - parse_options (& argc, & argv); + + if (! parse_options (argc, argv)) + { + print_help (); + return EXIT_FAILURE; + } + + if (options.help) + { + print_help (); + return EXIT_SUCCESS; + } if (options.version) { @@ -566,11 +602,12 @@ int main(int argc, char ** argv) return EXIT_SUCCESS; } - if (! get_lock ()) - do_remote (); /* may exit */ +#if USE_DBUS + do_remote (); /* may exit */ +#endif AUDDBG ("No remote session; starting up.\n"); - init_two (& argc, & argv); + init_two (); AUDDBG ("Startup complete.\n"); g_timeout_add_seconds (AUTOSAVE_INTERVAL, (GSourceFunc) do_autosave, NULL); @@ -580,16 +617,18 @@ int main(int argc, char ** argv) hook_associate ("playback stop", (HookFunction) maybe_quit, NULL); hook_associate ("playlist add complete", (HookFunction) maybe_quit, NULL); - hook_associate ("quit", (HookFunction) gtk_main_quit, NULL); gtk_main (); hook_dissociate ("playback stop", (HookFunction) maybe_quit); hook_dissociate ("playlist add complete", (HookFunction) maybe_quit); - hook_dissociate ("quit", (HookFunction) gtk_main_quit); QUIT: shut_down (); - release_lock (); return EXIT_SUCCESS; } + +void drct_quit (void) +{ + gtk_main_quit (); +} diff --git a/src/audacious/main.h b/src/audacious/main.h index 81f9a14..0fa3432 100644 --- a/src/audacious/main.h +++ b/src/audacious/main.h @@ -1,6 +1,6 @@ /* * main.h - * Copyright 2011 John Lindgren + * Copyright 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -32,8 +32,15 @@ void adder_cleanup (void); void art_init (void); void art_cleanup (void); +/* dbus-server.c */ +#ifdef USE_DBUS +void dbus_server_init (void); +void dbus_server_cleanup (void); +#endif + /* chardet.c */ void chardet_init (void); +void chardet_cleanup (void); /* config.c */ void config_load (void); @@ -44,17 +51,15 @@ void config_cleanup (void); void history_cleanup (void); /* main.c */ -extern bool_t headless; bool_t do_autosave (void); -/* mpris-signals.c */ -void mpris_signals_init (void); -void mpris_signals_cleanup (void); - /* signals.c */ -void signals_init (void); +#ifdef HAVE_SIGWAIT +void signals_init_one (void); +void signals_init_two (void); +#endif /* ui_albumart.c */ -char * get_associated_image_file (const char * filename); +char * get_associated_image_file (const char * filename); /* pooled */ #endif diff --git a/src/audacious/misc-api.h b/src/audacious/misc-api.h index 5ebff3d..b9ea2a5 100644 --- a/src/audacious/misc-api.h +++ b/src/audacious/misc-api.h @@ -1,6 +1,6 @@ /* * misc-api.h - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -19,22 +19,30 @@ /* Do not include this file directly; use misc.h instead. */ +/* all (char *) return values must be freed with str_unref() */ + /* art.c (thread-safe) */ -/* deprecated; use the non-blocking art_request_* functions instead */ -AUD_VFUNC3 (art_get_data, const char *, file, const void * *, data, int64_t *, len) -AUD_FUNC1 (const char *, art_get_file, const char *, file) +/* Gets album art for (the URI of a song file) as JPEG or PNG data. If + * the album art is not yet loaded, sets to NULL and begins to load the + * album art in the background. On completion, the "art ready" hook is called, + * with as a parameter. The "current art ready" hook is also called if + * is the currently playing song. */ +AUD_VFUNC3 (art_request_data, const char *, file, const void * *, data, int64_t *, len) + +/* Similar to art_request_data() but returns the URI of an image file. + * (A temporary file will be created if necessary.) */ +AUD_FUNC1 (const char *, art_request_file, const char *, file) /* Releases album art returned by art_request_data() or art_request_file(). */ AUD_VFUNC1 (art_unref, const char *, file) -/* config.c */ +/* config.c (thread-safe) */ -AUD_VFUNC1 (config_clear_section, const char *, section) AUD_VFUNC2 (config_set_defaults, const char *, section, const char * const *, entries) -AUD_VFUNC3 (set_string, const char *, section, const char *, name, const char *, value) -AUD_FUNC2 (char *, get_string, const char *, section, const char *, name) +AUD_VFUNC3 (set_str, const char *, section, const char *, name, const char *, value) +AUD_FUNC2 (char *, get_str, const char *, section, const char *, name) AUD_VFUNC3 (set_bool, const char *, section, const char *, name, bool_t, value) AUD_FUNC2 (bool_t, get_bool, const char *, section, const char *, name) AUD_VFUNC3 (set_int, const char *, section, const char *, name, int, value) @@ -49,11 +57,17 @@ AUD_VFUNC2 (eq_set_band, int, band, double, value) AUD_FUNC1 (double, eq_get_band, int, band) /* equalizer_preset.c */ +AUD_FUNC1 (EqualizerPreset *, equalizer_preset_new, const char *, name) +AUD_VFUNC1 (equalizer_preset_free, EqualizerPreset *, preset) AUD_FUNC1 (Index *, equalizer_read_presets, const char *, basename) -AUD_FUNC2 (bool_t, equalizer_write_preset_file, Index *, list, const char *, basename) +AUD_FUNC2 (bool_t, equalizer_write_presets, Index *, list, const char *, basename) + +/* note: legacy code! these are local filenames, not URIs */ AUD_FUNC1 (EqualizerPreset *, load_preset_file, const char *, filename) AUD_FUNC2 (bool_t, save_preset_file, EqualizerPreset *, preset, const char *, filename) -AUD_FUNC1 (Index *, import_winamp_eqf, VFSFile *, file) + +AUD_FUNC1 (Index *, import_winamp_presets, VFSFile *, file) +AUD_FUNC2 (bool_t, export_winamp_preset, EqualizerPreset *, preset, VFSFile *, file) /* history.c */ AUD_FUNC1 (const char *, history_get, int, entry) @@ -62,19 +76,13 @@ AUD_VFUNC1 (history_add, const char *, path) /* interface.c */ AUD_VFUNC1 (interface_show, bool_t, show) AUD_FUNC0 (bool_t, interface_is_shown) -AUD_FUNC0 (bool_t, interface_is_focused) /* interface_show_error() is safe to call from any thread */ AUD_VFUNC1 (interface_show_error, const char *, message) -AUD_VFUNC1 (interface_show_filebrowser, bool_t, play) -AUD_VFUNC0 (interface_show_jump_to_track) - -AUD_VFUNC1 (interface_install_toolbar, void *, button) -AUD_VFUNC1 (interface_uninstall_toolbar, void *, button) - /* main.c */ AUD_FUNC1 (const char *, get_path, int, path) +AUD_FUNC0 (bool_t, headless_mode) /* output.c */ AUD_VFUNC1 (output_reset, int, type) @@ -103,23 +111,18 @@ AUD_VFUNC2 (plugin_menu_remove, int, id, MenuFunc, func) AUD_VFUNC4 (create_widgets_with_domain, /* GtkWidget * */ void *, box, const PreferencesWidget *, widgets, int, n_widgets, const char *, domain) AUD_VFUNC0 (show_prefs_window) +AUD_VFUNC1 (show_prefs_for_plugin_type, int, type) /* util.c */ -AUD_FUNC2 (char *, construct_uri, const char *, base, const char *, reference) + +/* Constructs a full URI given: + * 1. path: one of the following: + * a. a full URI (returned unchanged) + * b. an absolute filename (in the system locale) + * c. a relative path (character set detected according to user settings) + * 2. reference: the full URI of the playlist containing */ +AUD_FUNC2 (char *, construct_uri, const char *, path, const char *, reference) /* visualization.c */ AUD_VFUNC2 (vis_func_add, int, type, VisFunc, func) AUD_VFUNC1 (vis_func_remove, VisFunc, func) - -/* added in Audacious 3.4 */ - -/* Gets album art for (the URI of a song file) as JPEG or PNG data. If - * the album art is not yet loaded, sets to NULL and begins to load the - * album art in the background. On completion, the "art ready" hook is called, - * with as a parameter. The "current art ready" hook is also called if - * is the currently playing song. */ -AUD_VFUNC3 (art_request_data, const char *, file, const void * *, data, int64_t *, len) - -/* Similar to art_request_data() but returns the URI of an image file. - * (A temporary file will be created if necessary.) */ -AUD_FUNC1 (const char *, art_request_file, const char *, file) diff --git a/src/audacious/misc.h b/src/audacious/misc.h index c214eb6..3d25517 100644 --- a/src/audacious/misc.h +++ b/src/audacious/misc.h @@ -1,6 +1,6 @@ /* * misc.h - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -34,27 +34,17 @@ enum { AUD_PATH_DESKTOP_FILE, AUD_PATH_ICON_FILE, AUD_PATH_USER_DIR, - AUD_PATH_USER_PLUGIN_DIR, AUD_PATH_PLAYLISTS_DIR, - AUD_PATH_GTKRC_FILE, /* deprecated */ AUD_PATH_COUNT }; -typedef struct { - char * name; - float preamp, bands[10]; -} EqualizerPreset; - enum {OUTPUT_RESET_EFFECTS_ONLY, OUTPUT_RESET_SOFT, OUTPUT_RESET_HARD}; enum { AUD_MENU_MAIN, AUD_MENU_PLAYLIST, - AUD_MENU_PLAYLIST_RCLICK, AUD_MENU_PLAYLIST_ADD, AUD_MENU_PLAYLIST_REMOVE, - AUD_MENU_PLAYLIST_SELECT, - AUD_MENU_PLAYLIST_MISC, AUD_MENU_COUNT}; typedef void (* MenuFunc) (void); diff --git a/src/audacious/mpris-signals.c b/src/audacious/mpris-signals.c deleted file mode 100644 index a5d55fb..0000000 --- a/src/audacious/mpris-signals.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * mpris-signals.c - * Copyright 2011 John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifdef USE_DBUS - -#include - -#include "dbus-service.h" -#include "main.h" - -static void mpris_status_cb (void * hook_data, void * user_data) -{ - mpris_emit_status_change (mpris, GPOINTER_TO_INT (user_data)); -} -#endif - -void mpris_signals_init (void) -{ -#ifdef USE_DBUS - hook_associate ("playback begin", mpris_status_cb, GINT_TO_POINTER - (MPRIS_STATUS_PLAY)); - hook_associate ("playback pause", mpris_status_cb, GINT_TO_POINTER - (MPRIS_STATUS_PAUSE)); - hook_associate ("playback unpause", mpris_status_cb, GINT_TO_POINTER - (MPRIS_STATUS_PLAY)); - hook_associate ("playback stop", mpris_status_cb, GINT_TO_POINTER - (MPRIS_STATUS_STOP)); - - hook_associate ("set shuffle", mpris_status_cb, GINT_TO_POINTER (MPRIS_STATUS_INVALID)); - hook_associate ("set repeat", mpris_status_cb, GINT_TO_POINTER (MPRIS_STATUS_INVALID)); - hook_associate ("set no_playlist_advance", mpris_status_cb, GINT_TO_POINTER - (MPRIS_STATUS_INVALID)); -#endif -} - -void mpris_signals_cleanup (void) -{ -#ifdef USE_DBUS - hook_dissociate ("playback begin", mpris_status_cb); - hook_dissociate ("playback pause", mpris_status_cb); - hook_dissociate ("playback unpause", mpris_status_cb); - hook_dissociate ("playback stop", mpris_status_cb); - - hook_dissociate ("set shuffle", mpris_status_cb); - hook_dissociate ("set repeat", mpris_status_cb); - hook_dissociate ("set no_playlist_advance", mpris_status_cb); -#endif -} diff --git a/src/audacious/mpris_player.xml b/src/audacious/mpris_player.xml deleted file mode 100644 index c954f8b..0000000 --- a/src/audacious/mpris_player.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/audacious/mpris_root.xml b/src/audacious/mpris_root.xml deleted file mode 100644 index 7d84ded..0000000 --- a/src/audacious/mpris_root.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/audacious/mpris_tracklist.xml b/src/audacious/mpris_tracklist.xml deleted file mode 100644 index 46859a2..0000000 --- a/src/audacious/mpris_tracklist.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/audacious/objects.xml b/src/audacious/objects.xml deleted file mode 100644 index 4b3317f..0000000 --- a/src/audacious/objects.xml +++ /dev/null @@ -1,515 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/audacious/output.c b/src/audacious/output.c index dda7300..b6f862a 100644 --- a/src/audacious/output.c +++ b/src/audacious/output.c @@ -1,6 +1,6 @@ /* * output.c - * Copyright 2009-2012 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,8 +24,6 @@ #include /* for g_usleep */ -#include - #include "debug.h" #include "effect.h" #include "equalizer.h" @@ -68,8 +66,13 @@ static ReplayGainInfo gain_info; static bool_t change_op; static OutputPlugin * new_op; +static void * buffer1, * buffer2; +static int buffer1_size, buffer2_size; + static inline int FR2MS (int64_t f, int r) { return (f > 0) ? (f * 1000 + r / 2) / r : (f * 1000 - r / 2) / r; } +static inline int MS2FR (int64_t ms, int r) + { return (ms > 0) ? (ms * r + 500) / 1000 : (ms * r - 500) / 1000; } static inline int get_format (void) { @@ -82,6 +85,16 @@ static inline int get_format (void) } } +static void ensure_buffer (void * * buffer, int * size, int newsize) +{ + if (newsize > * size) + { + g_free (* buffer); + * buffer = g_malloc (newsize); + * size = newsize; + } +} + /* assumes LOCK_ALL, s_output */ static void cleanup_output (void) { @@ -94,6 +107,13 @@ static void cleanup_output (void) s_output = FALSE; + g_free (buffer1); + g_free (buffer2); + buffer1 = NULL; + buffer2 = NULL; + buffer1_size = 0; + buffer2_size = 0; + if (PLUGIN_HAS_FUNC (cop, close_audio)) cop->close_audio (); @@ -217,8 +237,6 @@ static void apply_software_volume (float * data, int channels, int samples) /* assumes LOCK_ALL, s_output */ static void write_output_raw (void * data, int samples) { - void * buffer = NULL; - vis_runner_pass_audio (FR2MS (out_frames, out_rate), data, samples, out_channels, out_rate); out_frames += samples / out_channels; @@ -231,9 +249,9 @@ static void write_output_raw (void * data, int samples) if (out_format != FMT_FLOAT) { - buffer = malloc (FMT_SIZEOF (out_format) * samples); - audio_to_int (data, buffer, out_format, samples); - data = buffer; + ensure_buffer (& buffer2, & buffer2_size, FMT_SIZEOF (out_format) * samples); + audio_to_int (data, buffer2, out_format, samples); + data = buffer2; } while (! (s_aborted || s_resetting)) @@ -270,26 +288,39 @@ static void write_output_raw (void * data, int samples) LOCK_MINOR; } - - free (buffer); } /* assumes LOCK_ALL, s_input, s_output */ -static void write_output (void * data, int size) +static bool_t write_output (void * data, int size, int stop_time) { - void * buffer = NULL; + bool_t stopped = FALSE; + int64_t cur_frame = in_frames; int samples = size / FMT_SIZEOF (in_format); + + /* always update in_frames, whether we use all the decoded frames or not */ in_frames += samples / in_channels; + if (stop_time != -1) + { + int64_t frames_left = MS2FR (stop_time - seek_time, in_rate) - cur_frame; + int64_t samples_left = in_channels * MAX (0, frames_left); + + if (samples >= samples_left) + { + samples = samples_left; + stopped = TRUE; + } + } + if (s_aborted) - return; + return ! stopped; if (in_format != FMT_FLOAT) { - buffer = malloc (sizeof (float) * samples); - audio_from_int (data, in_format, buffer, samples); - data = buffer; + ensure_buffer (& buffer1, & buffer1_size, sizeof (float) * samples); + audio_from_int (data, in_format, buffer1, samples); + data = buffer1; } float * fdata = data; @@ -297,7 +328,7 @@ static void write_output (void * data, int size) effect_process (& fdata, & samples); write_output_raw (fdata, samples); - free (buffer); + return ! stopped; } /* assumes LOCK_ALL, s_output */ @@ -359,9 +390,11 @@ void output_set_replaygain_info (const ReplayGainInfo * info) UNLOCK_ALL; } -void output_write_audio (void * data, int size) +/* returns FALSE if stop_time is reached */ +bool_t output_write_audio (void * data, int size, int stop_time) { LOCK_ALL; + bool_t good = FALSE; if (s_input) { @@ -372,10 +405,11 @@ void output_write_audio (void * data, int size) LOCK_ALL; } - write_output (data, size); + good = write_output (data, size, stop_time); } UNLOCK_ALL; + return good; } void output_abort_write (void) @@ -432,9 +466,6 @@ void output_set_time (int time) } UNLOCK_ALL; - - /* See comment in playback_seek(). */ - event_queue ("playback seek", NULL); } bool_t output_is_open (void) diff --git a/src/audacious/output.h b/src/audacious/output.h index 8d8ffe1..ac9ee48 100644 --- a/src/audacious/output.h +++ b/src/audacious/output.h @@ -1,6 +1,6 @@ /* * output.h - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,7 +25,7 @@ bool_t output_open_audio (int format, int rate, int channels); void output_set_replaygain_info (const ReplayGainInfo * info); -void output_write_audio (void * data, int length); +bool_t output_write_audio (void * data, int size, int stop_time); void output_abort_write (void); void output_pause (bool_t pause); int output_written_time (void); diff --git a/src/audacious/playback.c b/src/audacious/playback.c index 2f1a1b3..7d9c56f 100644 --- a/src/audacious/playback.c +++ b/src/audacious/playback.c @@ -1,6 +1,6 @@ /* * playback.c - * Copyright 2009-2012 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -23,9 +23,11 @@ #include #include +#include #include "drct.h" #include "i18n.h" +#include "input.h" #include "interface.h" #include "misc.h" #include "output.h" @@ -33,38 +35,31 @@ #include "playlist.h" #include "plugin.h" -static void playback_restart (int seek_time, bool_t pause); - -static const struct OutputAPI output_api = { - .open_audio = output_open_audio, - .set_replaygain_info = output_set_replaygain_info, - .write_audio = output_write_audio, - .abort_write = output_abort_write, - .pause = output_pause, - .written_time = output_written_time, - .flush = output_set_time}; - -static InputPlayback playback_api; - static pthread_t playback_thread_handle; static int end_source = 0; static pthread_mutex_t ready_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t ready_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t control_mutex = PTHREAD_MUTEX_INITIALIZER; + /* level 1 data (persists to end of song) */ static bool_t playing = FALSE; -static bool_t restart_flag = FALSE; -static int time_offset = 0, initial_seek = 0; +static int time_offset = 0; +static int stop_time = -1; static bool_t paused = FALSE; static bool_t ready_flag = FALSE; static bool_t playback_error = FALSE; static bool_t song_finished = FALSE; -static void * current_data = NULL; +static int seek_request = -1; /* under control_mutex */ +static int repeat_a = -1; /* under control_mutex */ + +static volatile int repeat_b = -1; /* atomic */ +static volatile int stop_flag = FALSE; /* atomic */ + static int current_bitrate = -1, current_samplerate = -1, current_channels = -1; -/* level 2 data (persists when restarting same song) */ static int current_entry = -1; static char * current_filename = NULL; /* pooled */ static char * current_title = NULL; /* pooled */ @@ -72,39 +67,37 @@ static int current_length = -1; static InputPlugin * current_decoder = NULL; static VFSFile * current_file = NULL; -static ReplayGainInfo gain_from_playlist; - -static int repeat_a = -1, repeat_b = -1; +static ReplayGainInfo current_gain; -/* level 3 data (persists to end of playlist) */ +/* level 2 data (persists to end of playlist) */ static bool_t stopped = TRUE; static int failed_entries = 0; /* clears gain info if tuple == NULL */ static void read_gain_from_tuple (const Tuple * tuple) { - memset (& gain_from_playlist, 0, sizeof gain_from_playlist); + memset (& current_gain, 0, sizeof current_gain); if (tuple == NULL) return; - int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL); - int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL); - int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL); - int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL); - int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL); - int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL); + int album_gain = tuple_get_int (tuple, FIELD_GAIN_ALBUM_GAIN); + int album_peak = tuple_get_int (tuple, FIELD_GAIN_ALBUM_PEAK); + int track_gain = tuple_get_int (tuple, FIELD_GAIN_TRACK_GAIN); + int track_peak = tuple_get_int (tuple, FIELD_GAIN_TRACK_PEAK); + int gain_unit = tuple_get_int (tuple, FIELD_GAIN_GAIN_UNIT); + int peak_unit = tuple_get_int (tuple, FIELD_GAIN_PEAK_UNIT); if (gain_unit) { - gain_from_playlist.album_gain = album_gain / (float) gain_unit; - gain_from_playlist.track_gain = track_gain / (float) gain_unit; + current_gain.album_gain = album_gain / (float) gain_unit; + current_gain.track_gain = track_gain / (float) gain_unit; } if (peak_unit) { - gain_from_playlist.album_peak = album_peak / (float) peak_unit; - gain_from_playlist.track_peak = track_peak / (float) peak_unit; + current_gain.album_peak = album_peak / (float) peak_unit; + current_gain.track_peak = track_peak / (float) peak_unit; } } @@ -114,8 +107,7 @@ static bool_t update_from_playlist (void) char * title = playback_entry_get_title (); int length = playback_entry_get_length (); - /* pointer comparison works for pooled strings */ - if (entry == current_entry && title == current_title && length == current_length) + if (entry == current_entry && str_equal (title, current_title) && length == current_length) { str_unref (title); return FALSE; @@ -134,26 +126,19 @@ bool_t drct_get_ready (void) return FALSE; pthread_mutex_lock (& ready_mutex); - - /* on restart, always report ready */ - bool_t ready = ready_flag || restart_flag; - + bool_t ready = ready_flag; pthread_mutex_unlock (& ready_mutex); return ready; } -static void set_pb_ready (InputPlayback * p) +static void set_ready (void) { g_return_if_fail (playing); - pthread_mutex_lock (& ready_mutex); - /* on restart, don't update or send "playback ready" */ - if (! restart_flag) - { - update_from_playlist (); - event_queue ("playback ready", NULL); - } + pthread_mutex_lock (& ready_mutex); + update_from_playlist (); + event_queue ("playback ready", NULL); ready_flag = TRUE; pthread_cond_signal (& ready_cond); @@ -190,15 +175,7 @@ int drct_get_time (void) wait_until_ready (); - int time = -1; - - if (current_decoder && current_decoder->get_time) - time = current_decoder->get_time (& playback_api); - - if (time < 0) - time = output_get_time (); - - return time - time_offset; + return output_get_time () - time_offset; } void drct_pause (void) @@ -208,11 +185,9 @@ void drct_pause (void) wait_until_ready (); - if (! current_decoder || ! current_decoder->pause) - return; - paused = ! paused; - current_decoder->pause (& playback_api, paused); + + output_pause (paused); if (paused) hook_call ("playback pause", NULL); @@ -220,21 +195,27 @@ void drct_pause (void) hook_call ("playback unpause", NULL); } -static void playback_finish (void) +static void playback_cleanup (void) { g_return_if_fail (playing); wait_until_ready (); - /* calling stop() is unnecessary if the song finished on its own; - * also, it might flush the output buffer, breaking gapless playback */ - if (current_decoder && ! song_finished) - current_decoder->stop (& playback_api); + if (! song_finished) + { + g_atomic_int_set (& stop_flag, TRUE); + output_abort_write (); + } pthread_join (playback_thread_handle, NULL); output_close_audio (); hook_dissociate ("playlist update", update_cb); + event_queue_cancel ("playback ready", NULL); + event_queue_cancel ("playback seek", NULL); + event_queue_cancel ("info change", NULL); + event_queue_cancel ("title change", NULL); + if (end_source) { g_source_remove (end_source); @@ -243,30 +224,21 @@ static void playback_finish (void) /* level 1 data cleanup */ playing = FALSE; - restart_flag = FALSE; - time_offset = initial_seek = 0; + time_offset = 0; + stop_time = -1; paused = FALSE; ready_flag = FALSE; playback_error = FALSE; song_finished = FALSE; - current_data = NULL; - current_bitrate = current_samplerate = current_channels = -1; -} - -static void playback_cleanup (void) -{ - g_return_if_fail (current_filename); - playback_finish (); + seek_request = -1; + repeat_a = -1; - event_queue_cancel ("playback ready", NULL); - event_queue_cancel ("playback seek", NULL); - event_queue_cancel ("info change", NULL); - event_queue_cancel ("title change", NULL); + g_atomic_int_set (& repeat_b, -1); + g_atomic_int_set (& stop_flag, FALSE); - set_bool (NULL, "stop_after_current_song", FALSE); + current_bitrate = current_samplerate = current_channels = -1; - /* level 2 data cleanup */ current_entry = -1; str_unref (current_filename); current_filename = NULL; @@ -284,7 +256,7 @@ static void playback_cleanup (void) read_gain_from_tuple (NULL); - repeat_a = repeat_b = -1; + set_bool (NULL, "stop_after_current_song", FALSE); } void playback_stop (void) @@ -292,12 +264,12 @@ void playback_stop (void) if (stopped) return; - if (current_filename) + if (playing) playback_cleanup (); output_drain (); - /* level 3 data cleanup */ + /* level 2 data cleanup */ stopped = TRUE; failed_entries = 0; @@ -342,17 +314,10 @@ static bool_t end_cb (void * unused) if (! get_bool (NULL, "no_playlist_advance")) do_next (playlist); } - else if (repeat_a >= 0 || repeat_b >= 0) - { - if (! failed_entries) - playback_restart (MAX (repeat_a, 0), FALSE); - else - do_stop (playlist); - } else if (get_bool (NULL, "no_playlist_advance")) { if (get_bool (NULL, "repeat") && ! failed_entries) - playback_restart (0, FALSE); + playback_play (0, FALSE); else do_stop (playlist); } @@ -367,6 +332,16 @@ static bool_t end_cb (void * unused) return FALSE; } +static bool_t open_file (void) +{ + /* no need to open a handle for custom URI schemes */ + if (current_decoder->schemes && current_decoder->schemes[0]) + return TRUE; + + current_file = vfs_fopen (current_filename, "r"); + return (current_file != NULL); +} + static void * playback_thread (void * unused) { if (! current_decoder) @@ -384,87 +359,66 @@ static void * playback_thread (void * unused) } Tuple * tuple = playback_entry_get_tuple (); - read_gain_from_tuple (tuple); + int length = playback_entry_get_length (); - int start_time = time_offset = 0; - int end_time = -1; + if (length < 1) + seek_request = -1; - if (tuple && playback_entry_get_length () > 0) + if (tuple && length > 0) { - if (tuple_get_value_type (tuple, FIELD_SEGMENT_START, NULL) == TUPLE_INT) - time_offset = tuple_get_int (tuple, FIELD_SEGMENT_START, NULL); - - start_time = time_offset + MAX (initial_seek, 0); + if (tuple_get_value_type (tuple, FIELD_SEGMENT_START) == TUPLE_INT) + { + time_offset = tuple_get_int (tuple, FIELD_SEGMENT_START); + if (time_offset) + seek_request = time_offset + MAX (seek_request, 0); + } - if (repeat_b >= 0) - end_time = time_offset + repeat_b; - else if (tuple_get_value_type (tuple, FIELD_SEGMENT_END, NULL) == TUPLE_INT) - end_time = tuple_get_int (tuple, FIELD_SEGMENT_END, NULL); + if (tuple_get_value_type (tuple, FIELD_SEGMENT_END) == TUPLE_INT) + stop_time = tuple_get_int (tuple, FIELD_SEGMENT_END); } + read_gain_from_tuple (tuple); + if (tuple) tuple_unref (tuple); - if (! current_decoder->schemes || ! current_decoder->schemes[0]) + if (! open_file ()) { - if (current_file) - vfs_rewind (current_file); - else - current_file = vfs_fopen (current_filename, "r"); - - if (! current_file) - { - SPRINTF (error, _("%s could not be opened."), current_filename); - interface_show_error (error); - playback_error = TRUE; - goto DONE; - } + SPRINTF (error, _("%s could not be opened."), current_filename); + interface_show_error (error); + playback_error = TRUE; + goto DONE; } - playback_error = ! current_decoder->play (& playback_api, current_filename, - current_file, start_time, end_time, paused); + playback_error = ! current_decoder->play (current_filename, current_file); DONE: if (! ready_flag) - set_pb_ready (& playback_api); + set_ready (); end_source = g_timeout_add (0, end_cb, NULL); return NULL; } -static void playback_start (int seek_time, bool_t pause) +void playback_play (int seek_time, bool_t pause) { - playing = TRUE; - initial_seek = seek_time; - paused = pause; - stopped = FALSE; + char * new_filename = playback_entry_get_filename (); + g_return_if_fail (new_filename); - hook_associate ("playlist update", update_cb, NULL); - pthread_create (& playback_thread_handle, NULL, playback_thread, NULL); -} - -static void playback_restart (int seek_time, bool_t pause) -{ if (playing) - playback_finish (); - - restart_flag = TRUE; + playback_cleanup (); - playback_start (seek_time, pause); + current_filename = new_filename; - /* on restart, send "playback seek" instead of "playback begin" */ - hook_call ("playback seek", NULL); -} + playing = TRUE; + paused = pause; -void playback_play (int seek_time, bool_t pause) -{ - if (current_filename) - playback_cleanup (); + seek_request = (seek_time > 0) ? seek_time : -1; - current_filename = playback_entry_get_filename (); - g_return_if_fail (current_filename); + stopped = FALSE; - playback_start (seek_time, pause); + hook_associate ("playlist update", update_cb, NULL); + pthread_create (& playback_thread_handle, NULL, playback_thread, NULL); hook_call ("playback begin", NULL); } @@ -486,67 +440,121 @@ void drct_seek (int time) wait_until_ready (); - if (! current_decoder || ! current_decoder->mseek || current_length < 1) + if (current_length < 1) return; - current_decoder->mseek (& playback_api, time_offset + CLAMP (time, 0, - current_length)); + pthread_mutex_lock (& control_mutex); + + seek_request = time_offset + CLAMP (time, 0, current_length); + output_abort_write (); - /* If the plugin is using our output system, don't call "playback seek" - * immediately but wait for output_set_time() to be called. This ensures - * that a "playback seek" handler can call playback_get_time() and get the - * new time. */ - if (! output_is_open ()) - hook_call ("playback seek", NULL); + pthread_mutex_unlock (& control_mutex); } -static void set_data (InputPlayback * p, void * data) +bool_t input_open_audio (int format, int rate, int channels) { - g_return_if_fail (playing); - current_data = data; + g_return_val_if_fail (playing, FALSE); + + if (! output_open_audio (format, rate, channels)) + return FALSE; + + output_set_replaygain_info (& current_gain); + + if (paused) + output_pause (TRUE); + + current_samplerate = rate; + current_channels = channels; + + if (ready_flag) + event_queue ("info change", NULL); + + return TRUE; } -static void * get_data (InputPlayback * p) +void input_set_gain (const ReplayGainInfo * info) { - g_return_val_if_fail (playing, NULL); - return current_data; + g_return_if_fail (playing); + memcpy (& current_gain, info, sizeof current_gain); + output_set_replaygain_info (& current_gain); } -static void set_params (InputPlayback * p, int bitrate, int samplerate, - int channels) +void input_write_audio (void * data, int length) { g_return_if_fail (playing); - current_bitrate = bitrate; - current_samplerate = samplerate; - current_channels = channels; + if (! ready_flag) + set_ready (); - if (drct_get_ready ()) - event_queue ("info change", NULL); + int b = g_atomic_int_get (& repeat_b); + + if (b >= 0) + { + if (! output_write_audio (data, length, b)) + { + pthread_mutex_lock (& control_mutex); + seek_request = MAX (repeat_a, time_offset); + pthread_mutex_unlock (& control_mutex); + } + } + else + { + if (! output_write_audio (data, length, stop_time)) + g_atomic_int_set (& stop_flag, TRUE); + } +} + +int input_written_time (void) +{ + g_return_val_if_fail (playing, -1); + return output_written_time (); } -static void set_tuple (InputPlayback * p, Tuple * tuple) +Tuple * input_get_tuple (void) +{ + g_return_val_if_fail (playing, NULL); + return playback_entry_get_tuple (); +} + +void input_set_tuple (Tuple * tuple) { g_return_if_fail (playing); - read_gain_from_tuple (tuple); playback_entry_set_tuple (tuple); } -static void set_gain_from_playlist (InputPlayback * p) +void input_set_bitrate (int bitrate) { g_return_if_fail (playing); - p->output->set_replaygain_info (& gain_from_playlist); + current_bitrate = bitrate; + + if (ready_flag) + event_queue ("info change", NULL); } -static InputPlayback playback_api = { - .output = & output_api, - .set_data = set_data, - .get_data = get_data, - .set_pb_ready = set_pb_ready, - .set_params = set_params, - .set_tuple = set_tuple, - .set_gain_from_playlist = set_gain_from_playlist, -}; +bool_t input_check_stop (void) +{ + g_return_val_if_fail (playing, TRUE); + return g_atomic_int_get (& stop_flag); +} + +int input_check_seek (void) +{ + g_return_val_if_fail (playing, -1); + + pthread_mutex_lock (& control_mutex); + int seek = seek_request; + + if (seek != -1) + { + output_set_time (seek); + seek_request = -1; + + event_queue ("playback seek", NULL); + } + + pthread_mutex_unlock (& control_mutex); + return seek; +} char * drct_get_filename (void) { @@ -567,14 +575,9 @@ char * drct_get_title (void) if (current_length > 0) { - int len = current_length / 1000; - - if (len < 3600) - snprintf (s, sizeof s, get_bool (NULL, "leading_zero") ? - " (%02d:%02d)" : " (%d:%02d)", len / 60, len % 60); - else - snprintf (s, sizeof s, " (%d:%02d:%02d)", len / 3600, (len / 60) % - 60, len % 60); + char t[16]; + audgui_format_time (t, sizeof t, current_length); + snprintf (s, sizeof s, " (%s)", t); } else s[0] = 0; @@ -605,23 +608,12 @@ void drct_get_info (int * bitrate, int * samplerate, int * channels) void drct_get_volume (int * l, int * r) { - if (playing && drct_get_ready () && current_decoder && - current_decoder->get_volume && current_decoder->get_volume (l, r)) - return; - output_get_volume (l, r); } void drct_set_volume (int l, int r) { - l = CLAMP (l, 0, 100); - r = CLAMP (r, 0, 100); - - if (playing && drct_get_ready () && current_decoder && - current_decoder->set_volume && current_decoder->set_volume (l, r)) - return; - - output_set_volume (l, r); + output_set_volume (CLAMP (l, 0, 100), CLAMP (r, 0, 100)); } void drct_set_ab_repeat (int a, int b) @@ -634,27 +626,27 @@ void drct_set_ab_repeat (int a, int b) if (current_length < 1) return; - repeat_a = a; + if (a >= 0) + a += time_offset; + if (b >= 0) + b += time_offset; - if (repeat_b != b) - { - repeat_b = b; + pthread_mutex_lock (& control_mutex); - /* Restart playback so the new setting takes effect. We could add - * something like InputPlugin::set_stop_time(), but this is the only - * place it would be used. */ - int seek_time = drct_get_time (); - bool_t was_paused = paused; - - if (repeat_b >= 0 && seek_time >= repeat_b) - seek_time = MAX (repeat_a, 0); + repeat_a = a; + g_atomic_int_set (& repeat_b, b); - playback_restart (seek_time, was_paused); + if (b != -1 && output_get_time () >= b) + { + seek_request = MAX (a, time_offset); + output_abort_write (); } + + pthread_mutex_unlock (& control_mutex); } void drct_get_ab_repeat (int * a, int * b) { - * a = playing ? repeat_a : -1; - * b = playing ? repeat_b : -1; + * a = (playing && repeat_a != -1) ? repeat_a - time_offset : -1; + * b = (playing && repeat_b != -1) ? repeat_b - time_offset : -1; } diff --git a/src/audacious/playback.h b/src/audacious/playback.h index fb84b01..2937791 100644 --- a/src/audacious/playback.h +++ b/src/audacious/playback.h @@ -1,6 +1,6 @@ /* * playback.h - * Copyright 2006-2011 William Pitcock and John Lindgren + * Copyright 2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/audacious/playlist-api.h b/src/audacious/playlist-api.h index 08d71a8..634fd05 100644 --- a/src/audacious/playlist-api.h +++ b/src/audacious/playlist-api.h @@ -1,6 +1,6 @@ /* * playlist-api.h - * Copyright 2010-2012 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -124,8 +124,9 @@ AUD_VFUNC5 (playlist_entry_insert_batch, int, playlist, int, at, /* Similar to playlist_entry_insert_batch, but allows the caller to prevent some * items from being added by returning false from the callback. Useful - * for searching a folder and adding only new files to the playlist. is - * an untyped pointer passed to the callback. */ + * for searching a folder and adding only new files to the playlist. Filenames + * passed to the callback can be used with str_ref(), str_equal(), etc. + * is an additional, untyped pointer passed to the callback. */ AUD_VFUNC7 (playlist_entry_insert_filtered, int, playlist, int, at, Index *, filenames, Index *, tuples, PlaylistFilterFunc, filter, void *, user, bool_t, play) diff --git a/src/audacious/playlist-files.c b/src/audacious/playlist-files.c index 795042e..8ebe521 100644 --- a/src/audacious/playlist-files.c +++ b/src/audacious/playlist-files.c @@ -1,6 +1,6 @@ /* * playlist-files.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -27,80 +27,98 @@ #include "plugin.h" #include "plugins.h" -static PluginHandle * get_plugin_silent (const char * filename) +typedef struct { - char buf[32]; - if (! uri_get_extension (filename, buf, sizeof buf)) - return NULL; - - return playlist_plugin_for_extension (buf); + const char * filename; + char * title; + Index * filenames; + Index * tuples; + bool_t plugin_found; + bool_t success; } +PlaylistData; -bool_t filename_is_playlist (const char * filename) +static void plugin_for_filename (const char * filename, PluginForEachFunc func, void * data) { - return (get_plugin_silent (filename) != NULL); + char ext[32]; + if (uri_get_extension (filename, ext, sizeof ext)) + playlist_plugin_for_ext (ext, func, data); } -static PluginHandle * get_plugin (const char * filename, bool_t saving) +static bool_t plugin_found_cb (PluginHandle * plugin, void * data) { - PluginHandle * plugin = get_plugin_silent (filename); - - if (! plugin) - { - SPRINTF (error, _("Cannot %s %s: unsupported file extension."), - saving ? _("save") : _("load"), filename); - interface_show_error (error); - return NULL; - } - - return plugin; + * (PluginHandle * *) data = plugin; + return FALSE; /* stop when first plugin is found */ } -bool_t playlist_load (const char * filename, char * * title, - Index * * filenames_p, Index * * tuples_p) +bool_t filename_is_playlist (const char * filename) { - AUDDBG ("Loading playlist %s.\n", filename); + PluginHandle * plugin = NULL; + plugin_for_filename (filename, plugin_found_cb, & plugin); + return (plugin != NULL); +} - PluginHandle * plugin = get_plugin (filename, FALSE); - if (! plugin) - return FALSE; +static bool_t playlist_load_cb (PluginHandle * plugin, void * data_) +{ + PlaylistData * data = (PlaylistData *) data_; PlaylistPlugin * pp = plugin_get_header (plugin); - g_return_val_if_fail (pp && PLUGIN_HAS_FUNC (pp, load), FALSE); + if (! pp || ! PLUGIN_HAS_FUNC (pp, load)) + return TRUE; /* try another plugin */ + + data->plugin_found = TRUE; - VFSFile * file = vfs_fopen (filename, "r"); + VFSFile * file = vfs_fopen (data->filename, "r"); if (! file) - return FALSE; + return FALSE; /* stop if we can't open file */ - Index * filenames = index_new (); - Index * tuples = index_new (); - bool_t success = pp->load (filename, file, title, filenames, tuples); + data->success = pp->load (data->filename, file, & data->title, data->filenames, data->tuples); vfs_fclose (file); + return ! data->success; /* stop when playlist is loaded */ +} + +bool_t playlist_load (const char * filename, char * * title, Index * * filenames, Index * * tuples) +{ + PlaylistData data = + { + .filename = filename, + .filenames = index_new (), + .tuples = index_new () + }; + + AUDDBG ("Loading playlist %s.\n", filename); + plugin_for_filename (filename, playlist_load_cb, & data); + + if (! data.plugin_found) + { + SPRINTF (error, _("Cannot load %s: unsupported file extension."), filename); + interface_show_error (error); + } - if (! success) + if (! data.success) { - index_free (filenames); - index_free (tuples); + str_unref (data.title); + index_free_full (data.filenames, (IndexFreeFunc) str_unref); + index_free_full (data.tuples, (IndexFreeFunc) tuple_unref); return FALSE; } - if (index_count (tuples)) - g_return_val_if_fail (index_count (tuples) == index_count (filenames), - FALSE); + if (index_count (data.tuples)) + g_return_val_if_fail (index_count (data.tuples) == index_count (data.filenames), FALSE); else { - index_free (tuples); - tuples = NULL; + index_free (data.tuples); + data.tuples = NULL; } - * filenames_p = filenames; - * tuples_p = tuples; + * title = data.title; + * filenames = data.filenames; + * tuples = data.tuples; return TRUE; } -bool_t playlist_insert_playlist_raw (int list, int at, - const char * filename) +bool_t playlist_insert_playlist_raw (int list, int at, const char * filename) { char * title = NULL; Index * filenames, * tuples; @@ -117,59 +135,60 @@ bool_t playlist_insert_playlist_raw (int list, int at, return TRUE; } -bool_t playlist_save (int list, const char * filename) +static bool_t playlist_save_cb (PluginHandle * plugin, void * data_) { - AUDDBG ("Saving playlist %s.\n", filename); - - PluginHandle * plugin = get_plugin (filename, TRUE); - if (! plugin) - return FALSE; + PlaylistData * data = data_; PlaylistPlugin * pp = plugin_get_header (plugin); - g_return_val_if_fail (pp, FALSE); + if (! pp || ! PLUGIN_HAS_FUNC (pp, save)) + return TRUE; /* try another plugin */ - if (! PLUGIN_HAS_FUNC (pp, save)) - { - SPRINTF (error, _("Cannot save %s: plugin does not support saving."), filename); - interface_show_error (error); - return FALSE; - } + data->plugin_found = TRUE; - bool_t fast = get_bool (NULL, "metadata_on_play"); - - VFSFile * file = vfs_fopen (filename, "w"); + VFSFile * file = vfs_fopen (data->filename, "w"); if (! file) - return FALSE; + return FALSE; /* stop if we can't open file */ - char * title = playlist_get_title (list); + data->success = pp->save (data->filename, file, data->title, data->filenames, data->tuples); + + vfs_fclose (file); + return FALSE; /* stop after first attempt (successful or not) */ +} + +bool_t playlist_save (int list, const char * filename) +{ + PlaylistData data = + { + .filename = filename, + .title = playlist_get_title (list), + .filenames = index_new (), + .tuples = index_new () + }; int entries = playlist_entry_count (list); - Index * filenames = index_new (); - index_allocate (filenames, entries); - Index * tuples = index_new (); - index_allocate (tuples, entries); + bool_t fast = get_bool (NULL, "metadata_on_play"); + + index_allocate (data.filenames, entries); + index_allocate (data.tuples, entries); for (int i = 0; i < entries; i ++) { - index_append (filenames, playlist_entry_get_filename (list, i)); - index_append (tuples, playlist_entry_get_tuple (list, i, fast)); + index_insert (data.filenames, -1, playlist_entry_get_filename (list, i)); + index_insert (data.tuples, -1, playlist_entry_get_tuple (list, i, fast)); } - bool_t success = pp->save (filename, file, title, filenames, tuples); - - vfs_fclose (file); - str_unref (title); + AUDDBG ("Saving playlist %s.\n", filename); + plugin_for_filename (filename, playlist_save_cb, & data); - for (int i = 0; i < entries; i ++) + if (! data.plugin_found) { - str_unref (index_get (filenames, i)); - Tuple * tuple = index_get (tuples, i); - if (tuple) - tuple_unref (tuple); + SPRINTF (error, _("Cannot save %s: unsupported file extension."), filename); + interface_show_error (error); } - index_free (filenames); - index_free (tuples); + str_unref (data.title); + index_free_full (data.filenames, (IndexFreeFunc) str_unref); + index_free_full (data.tuples, (IndexFreeFunc) tuple_unref); - return success; + return data.success; } diff --git a/src/audacious/playlist-new.c b/src/audacious/playlist-new.c index 29aab8f..23696b0 100644 --- a/src/audacious/playlist-new.c +++ b/src/audacious/playlist-new.c @@ -1,6 +1,6 @@ /* * playlist-new.c - * Copyright 2009-2012 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -126,22 +127,13 @@ static void scan_restart (void); static bool_t next_song_locked (Playlist * playlist, bool_t repeat, int hint); -static char * title_format; - -static char * title_from_tuple (Tuple * tuple) -{ - if (! title_format) - title_format = get_string (NULL, "generic_title_format"); - - return tuple_format_title (tuple, title_format); -} +static TupleFormatter * title_formatter; static void entry_set_tuple_real (Entry * entry, Tuple * tuple) { /* Hack: We cannot refresh segmented entries (since their info is read from * the cue sheet when it is first loaded), so leave them alone. -jlindgren */ - if (entry->tuple && tuple_get_value_type (entry->tuple, FIELD_SEGMENT_START, - NULL) == TUPLE_INT) + if (entry->tuple && tuple_get_value_type (entry->tuple, FIELD_SEGMENT_START) == TUPLE_INT) { if (tuple) tuple_unref (tuple); @@ -168,8 +160,8 @@ static void entry_set_tuple_real (Entry * entry, Tuple * tuple) } else { - entry->formatted = title_from_tuple (tuple); - entry->length = tuple_get_int (tuple, FIELD_LENGTH, NULL); + entry->formatted = tuple_format_title (title_formatter, tuple); + entry->length = tuple_get_int (tuple, FIELD_LENGTH); if (entry->length < 0) entry->length = 0; } @@ -286,11 +278,7 @@ static void playlist_free (Playlist * playlist) str_unref (playlist->filename); str_unref (playlist->title); - - for (int count = 0; count < index_count (playlist->entries); count ++) - entry_free (index_get (playlist->entries, count)); - - index_free (playlist->entries); + index_free_full (playlist->entries, (IndexFreeFunc) entry_free); g_list_free (playlist->queued); g_slice_free (Playlist, playlist); } @@ -655,6 +643,9 @@ void playlist_init (void) scan_playlist = scan_row = 0; LEAVE; + + /* initialize title formatter */ + playlist_reformat_titles (); } void playlist_end (void) @@ -670,17 +661,14 @@ void playlist_end (void) active_playlist = playing_playlist = NULL; resume_playlist = -1; - for (int i = 0; i < index_count (playlists); i ++) - playlist_free (index_get (playlists, i)); - - index_free (playlists); + index_free_full (playlists, (IndexFreeFunc) playlist_free); playlists = NULL; g_hash_table_destroy (unique_id_table); unique_id_table = NULL; - g_free (title_format); - title_format = NULL; + tuple_formatter_free (title_formatter); + title_formatter = NULL; LEAVE; } @@ -722,11 +710,11 @@ void playlist_reorder (int from, int to, int count) Index * displaced = index_new (); if (to < from) - index_copy_append (playlists, to, displaced, from - to); + index_copy_insert (playlists, to, displaced, -1, from - to); else - index_copy_append (playlists, from + count, displaced, to - from); + index_copy_insert (playlists, from + count, displaced, -1, to - from); - index_move (playlists, from, to, count); + index_copy_set (playlists, from, playlists, to, count); if (to < from) { @@ -751,8 +739,7 @@ void playlist_delete (int playlist_num) bool_t was_playing = (playlist == playing_playlist); - index_delete (playlists, playlist_num, 1); - playlist_free (playlist); + index_delete_full (playlists, playlist_num, 1, (IndexFreeFunc) playlist_free); if (! index_count (playlists)) index_insert (playlists, 0, playlist_new (-1)); @@ -1010,7 +997,7 @@ void playlist_entry_insert_batch_raw (int playlist_num, int at, char * filename = index_get (filenames, i); Tuple * tuple = tuples ? index_get (tuples, i) : NULL; PluginHandle * decoder = decoders ? index_get (decoders, i) : NULL; - index_append (add, entry_new (filename, tuple, decoder)); + index_insert (add, -1, entry_new (filename, tuple, decoder)); } index_free (filenames); @@ -1020,7 +1007,7 @@ void playlist_entry_insert_batch_raw (int playlist_num, int at, index_free (tuples); number = index_count (add); - index_merge_insert (playlist->entries, at, add); + index_copy_insert (add, 0, playlist->entries, at, -1); index_free (add); number_entries (playlist, at, entries + number - at); @@ -1083,10 +1070,9 @@ void playlist_entry_delete (int playlist_num, int at, int number) } playlist->total_length -= entry->length; - entry_free (entry); } - index_delete (playlist->entries, at, number); + index_delete_full (playlist->entries, at, number, (IndexFreeFunc) entry_free); number_entries (playlist, at, entries - at - number); if (position_changed && get_bool (NULL, "advance_on_delete")) @@ -1352,21 +1338,21 @@ int playlist_shift (int playlist_num, int entry_num, int distance) { entry = index_get (playlist->entries, i); if (! entry->selected) - index_append (temp, entry); + index_insert (temp, -1, entry); } for (int i = top; i < bottom; i ++) { entry = index_get (playlist->entries, i); if (entry->selected) - index_append (temp, entry); + index_insert (temp, -1, entry); } for (int i = center; i < bottom; i ++) { entry = index_get (playlist->entries, i); if (! entry->selected) - index_append (temp, entry); + index_insert (temp, -1, entry); } index_copy_set (temp, 0, playlist->entries, top, bottom - top); @@ -1446,7 +1432,7 @@ void playlist_delete_selected (int playlist_num) } else { - index_append (others, entry); + index_insert (others, -1, entry); if (found) after ++; @@ -1486,7 +1472,7 @@ void playlist_reverse (int playlist_num) index_allocate (reversed, entries); for (int count = entries; count --; ) - index_append (reversed, index_get (playlist->entries, count)); + index_insert (reversed, -1, index_get (playlist->entries, count)); index_free (playlist->entries); playlist->entries = reversed; @@ -1509,7 +1495,7 @@ void playlist_reverse_selected (int playlist_num) { Entry * entry = index_get (playlist->entries, count); if (entry->selected) - index_append (reversed, index_get (playlist->entries, count)); + index_insert (reversed, -1, index_get (playlist->entries, count)); } int count2 = 0; @@ -1560,7 +1546,7 @@ void playlist_randomize_selected (int playlist_num) { Entry * entry = index_get (playlist->entries, count); if (entry->selected) - index_append (selected, entry); + index_insert (selected, -1, entry); } for (int i = 0; i < playlist->selected_count; i ++) @@ -1637,7 +1623,7 @@ static void sort_selected (Playlist * playlist, CompareData * data) { Entry * entry = index_get (playlist->entries, count); if (entry->selected) - index_append (selected, entry); + index_insert (selected, -1, entry); } index_sort_with_data (selected, compare_cb, data); @@ -1750,11 +1736,14 @@ void playlist_reformat_titles (void) { ENTER; - g_free (title_format); - title_format = NULL; + if (title_formatter) + tuple_formatter_free (title_formatter); - for (int playlist_num = 0; playlist_num < index_count (playlists); - playlist_num ++) + char * format = get_str (NULL, "generic_title_format"); + title_formatter = tuple_formatter_new (format); + str_unref (format); + + for (int playlist_num = 0; playlist_num < index_count (playlists); playlist_num ++) { Playlist * playlist = index_get (playlists, playlist_num); int entries = index_count (playlist->entries); @@ -1763,7 +1752,11 @@ void playlist_reformat_titles (void) { Entry * entry = index_get (playlist->entries, count); str_unref (entry->formatted); - entry->formatted = entry->tuple ? title_from_tuple (entry->tuple) : NULL; + + if (entry->tuple) + entry->formatted = tuple_format_title (title_formatter, entry->tuple); + else + entry->formatted = NULL; } queue_update (PLAYLIST_UPDATE_METADATA, playlist_num, 0, entries); @@ -2251,9 +2244,10 @@ void playlist_save_state (void) ENTER; - char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR)); - FILE * handle = fopen (path, "w"); - g_free (path); + const char * user_dir = get_path (AUD_PATH_USER_DIR); + SCONCAT2 (path, user_dir, "/" STATE_FILE); + + FILE * handle = g_fopen (path, "w"); if (! handle) RETURN (); @@ -2323,9 +2317,10 @@ void playlist_load_state (void) ENTER; int playlist_num; - char * path = g_strdup_printf ("%s/" STATE_FILE, get_path (AUD_PATH_USER_DIR)); - FILE * handle = fopen (path, "r"); - g_free (path); + const char * user_dir = get_path (AUD_PATH_USER_DIR); + SCONCAT2 (path, user_dir, "/" STATE_FILE); + + FILE * handle = g_fopen (path, "r"); if (! handle) RETURN (); diff --git a/src/audacious/playlist-utils.c b/src/audacious/playlist-utils.c index e690c9f..c51dfa2 100644 --- a/src/audacious/playlist-utils.c +++ b/src/audacious/playlist-utils.c @@ -17,13 +17,13 @@ * the use of this software. */ -#include -#include -#include #include #include #include +#include +#include + #include #include @@ -39,13 +39,13 @@ static const char * get_basename (const char * filename) static int filename_compare_basename (const char * a, const char * b) { - return string_compare_encoded (get_basename (a), get_basename (b)); + return str_compare_encoded (get_basename (a), get_basename (b)); } static int tuple_compare_string (const Tuple * a, const Tuple * b, int field) { - char * string_a = tuple_get_str (a, field, NULL); - char * string_b = tuple_get_str (b, field, NULL); + char * string_a = tuple_get_str (a, field); + char * string_b = tuple_get_str (b, field); int ret; if (string_a == NULL) @@ -53,7 +53,7 @@ static int tuple_compare_string (const Tuple * a, const Tuple * b, int field) else if (string_b == NULL) ret = 1; else - ret = string_compare (string_a, string_b); + ret = str_compare (string_a, string_b); str_unref (string_a); str_unref (string_b); @@ -62,13 +62,13 @@ static int tuple_compare_string (const Tuple * a, const Tuple * b, int field) static int tuple_compare_int (const Tuple * a, const Tuple * b, int field) { - if (tuple_get_value_type (a, field, NULL) != TUPLE_INT) - return (tuple_get_value_type (b, field, NULL) != TUPLE_INT) ? 0 : -1; - if (tuple_get_value_type (b, field, NULL) != TUPLE_INT) + if (tuple_get_value_type (a, field) != TUPLE_INT) + return (tuple_get_value_type (b, field) != TUPLE_INT) ? 0 : -1; + if (tuple_get_value_type (b, field) != TUPLE_INT) return 1; - int int_a = tuple_get_int (a, field, NULL); - int int_b = tuple_get_int (b, field, NULL); + int int_a = tuple_get_int (a, field); + int int_b = tuple_get_int (b, field); return (int_a < int_b) ? -1 : (int_a > int_b); } @@ -104,7 +104,7 @@ static int tuple_compare_length (const Tuple * a, const Tuple * b) } static const PlaylistStringCompareFunc filename_comparisons[] = { - [PLAYLIST_SORT_PATH] = string_compare_encoded, + [PLAYLIST_SORT_PATH] = str_compare_encoded, [PLAYLIST_SORT_FILENAME] = filename_compare_basename, [PLAYLIST_SORT_TITLE] = NULL, [PLAYLIST_SORT_ALBUM] = NULL, @@ -133,7 +133,7 @@ static const PlaylistStringCompareFunc title_comparisons[] = { [PLAYLIST_SORT_ARTIST] = NULL, [PLAYLIST_SORT_DATE] = NULL, [PLAYLIST_SORT_TRACK] = NULL, - [PLAYLIST_SORT_FORMATTED_TITLE] = string_compare, + [PLAYLIST_SORT_FORMATTED_TITLE] = str_compare, [PLAYLIST_SORT_LENGTH] = NULL}; void playlist_sort_by_scheme (int playlist, int scheme) @@ -248,12 +248,13 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns) playlist_select_all (playlist, TRUE); - for (field = 0; field < G_N_ELEMENTS (fields); field ++) + for (field = 0; field < ARRAY_LEN (fields); field ++) { - char * pattern = tuple_get_str (patterns, fields[field], NULL); - regex_t regex; + char * pattern = tuple_get_str (patterns, fields[field]); + GRegex * regex; - if (! pattern || ! pattern[0] || regcomp (& regex, pattern, REG_ICASE)) + if (! pattern || ! pattern[0] || ! (regex = g_regex_new (pattern, + G_REGEX_CASELESS, 0, NULL))) { str_unref (pattern); continue; @@ -265,9 +266,9 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns) continue; Tuple * tuple = playlist_entry_get_tuple (playlist, entry, FALSE); - char * string = tuple ? tuple_get_str (tuple, fields[field], NULL) : NULL; + char * string = tuple ? tuple_get_str (tuple, fields[field]) : NULL; - if (! string || regexec (& regex, string, 0, NULL, 0)) + if (! string || ! g_regex_match (regex, string, 0, NULL)) playlist_entry_set_selected (playlist, entry, FALSE); str_unref (string); @@ -275,7 +276,7 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns) tuple_unref (tuple); } - regfree (& regex); + g_regex_unref (regex); str_unref (pattern); } } @@ -283,14 +284,16 @@ void playlist_select_by_patterns (int playlist, const Tuple * patterns) static char * make_playlist_path (int playlist) { if (! playlist) - return g_strdup_printf ("%s/playlist.xspf", get_path (AUD_PATH_USER_DIR)); + return filename_build (get_path (AUD_PATH_USER_DIR), "playlist.xspf"); - return g_strdup_printf ("%s/playlist_%02d.xspf", - get_path (AUD_PATH_PLAYLISTS_DIR), 1 + playlist); + SPRINTF (name, "playlist_%02d.xspf", 1 + playlist); + return filename_build (get_path (AUD_PATH_PLAYLISTS_DIR), name); } static void load_playlists_real (void) { + const char * folder = get_path (AUD_PATH_PLAYLISTS_DIR); + /* old (v3.1 and earlier) naming scheme */ int count; @@ -300,7 +303,7 @@ static void load_playlists_real (void) if (! g_file_test (path, G_FILE_TEST_EXISTS)) { - g_free (path); + str_unref (path); break; } @@ -310,47 +313,52 @@ static void load_playlists_real (void) playlist_insert_playlist_raw (count, 0, uri); playlist_set_modified (count, TRUE); - g_free (path); - g_free (uri); + str_unref (path); + str_unref (uri); } /* unique ID-based naming scheme */ - char * order_path = g_strdup_printf ("%s/order", get_path (AUD_PATH_PLAYLISTS_DIR)); + char * order_path = filename_build (folder, "order"); char * order_string; g_file_get_contents (order_path, & order_string, NULL, NULL); - g_free (order_path); + str_unref (order_path); if (! order_string) goto DONE; - char * * order = g_strsplit (order_string, " ", -1); + Index * order = str_list_to_index (order_string, " "); g_free (order_string); - for (int i = 0; order[i]; i ++) + for (int i = 0; i < index_count (order); i ++) { - char * path = g_strdup_printf ("%s/%s.audpl", get_path (AUD_PATH_PLAYLISTS_DIR), order[i]); + char * number = index_get (order, i); + + SCONCAT2 (name, number, ".audpl"); + char * path = filename_build (folder, name); if (! g_file_test (path, G_FILE_TEST_EXISTS)) { - g_free (path); - path = g_strdup_printf ("%s/%s.xspf", get_path (AUD_PATH_PLAYLISTS_DIR), order[i]); + str_unref (path); + + SCONCAT2 (name2, number, ".xspf"); + path = filename_build (folder, name2); } char * uri = filename_to_uri (path); - playlist_insert_with_id (count + i, atoi (order[i])); + playlist_insert_with_id (count + i, atoi (number)); playlist_insert_playlist_raw (count + i, 0, uri); playlist_set_modified (count + i, FALSE); if (g_str_has_suffix (path, ".xspf")) playlist_set_modified (count + i, TRUE); - g_free (path); - g_free (uri); + str_unref (path); + str_unref (uri); } - g_strfreev (order); + index_free_full (order, (IndexFreeFunc) str_unref); DONE: if (! playlist_count ()) @@ -366,35 +374,38 @@ static void save_playlists_real (void) /* save playlists */ - char * * order = g_malloc (sizeof (char *) * (lists + 1)); - GHashTable * saved = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + Index * order = index_new (); + GHashTable * saved = g_hash_table_new_full (g_str_hash, g_str_equal, + (GDestroyNotify) str_unref, NULL); for (int i = 0; i < lists; i ++) { int id = playlist_get_unique_id (i); - order[i] = g_strdup_printf ("%d", id); + char * number = int_to_str (id); + + SCONCAT2 (name, number, ".audpl"); if (playlist_get_modified (i)) { - char * path = g_strdup_printf ("%s/%d.audpl", folder, id); + char * path = filename_build (folder, name); char * uri = filename_to_uri (path); playlist_save (i, uri); playlist_set_modified (i, FALSE); - g_free (path); - g_free (uri); + str_unref (path); + str_unref (uri); } - g_hash_table_insert (saved, g_strdup_printf ("%d.audpl", id), NULL); + index_insert (order, -1, number); + g_hash_table_insert (saved, str_get (name), NULL); } - order[lists] = NULL; - char * order_string = g_strjoinv (" ", order); - g_strfreev (order); + char * order_string = index_to_str_list (order, " "); + index_free_full (order, (IndexFreeFunc) str_unref); GError * error = NULL; - char * order_path = g_strdup_printf ("%s/order", get_path (AUD_PATH_PLAYLISTS_DIR)); + char * order_path = filename_build (folder, "order"); char * old_order_string; g_file_get_contents (order_path, & old_order_string, NULL, NULL); @@ -408,36 +419,35 @@ static void save_playlists_real (void) } } - g_free (order_string); - g_free (order_path); + str_unref (order_string); + str_unref (order_path); g_free (old_order_string); /* clean up deleted playlists and files from old naming scheme */ char * path = make_playlist_path (0); - remove (path); - g_free (path); + g_unlink (path); + str_unref (path); - DIR * dir = opendir (folder); + GDir * dir = g_dir_open (folder, 0, NULL); if (! dir) goto DONE; - struct dirent * entry; - while ((entry = readdir (dir))) + const char * name; + while ((name = g_dir_read_name (dir))) { - if (! g_str_has_suffix (entry->d_name, ".audpl") - && ! g_str_has_suffix (entry->d_name, ".xspf")) + if (! g_str_has_suffix (name, ".audpl") && ! g_str_has_suffix (name, ".xspf")) continue; - if (! g_hash_table_lookup_extended (saved, entry->d_name, NULL, NULL)) + if (! g_hash_table_contains (saved, name)) { - char * path = g_strdup_printf ("%s/%s", folder, entry->d_name); - remove (path); - g_free (path); + char * path = filename_build (folder, name); + g_unlink (path); + str_unref (path); } } - closedir (dir); + g_dir_close (dir); DONE: g_hash_table_destroy (saved); diff --git a/src/audacious/plugin-init.c b/src/audacious/plugin-init.c index 0992bf5..10edb28 100644 --- a/src/audacious/plugin-init.c +++ b/src/audacious/plugin-init.c @@ -1,6 +1,6 @@ /* * plugin-init.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -27,7 +27,7 @@ #include "effect.h" #include "general.h" #include "interface.h" -#include "main.h" +#include "misc.h" #include "output.h" #include "plugin.h" #include "plugins.h" @@ -125,7 +125,7 @@ static bool_t start_multi_cb (PluginHandle * p, void * type) static void start_plugins (int type) { - if (headless && type == PLUGIN_TYPE_IFACE) + if (type == PLUGIN_TYPE_IFACE && headless_mode ()) return; if (table[type].is_single) @@ -177,7 +177,7 @@ static bool_t stop_multi_cb (PluginHandle * p, void * type) static void stop_plugins (int type) { - if (headless && type == PLUGIN_TYPE_IFACE) + if (type == PLUGIN_TYPE_IFACE && headless_mode ()) return; plugin_for_enabled (type, misc_cleanup_cb, GINT_TO_POINTER (type)); diff --git a/src/audacious/plugin-preferences.c b/src/audacious/plugin-preferences.c index d064c52..8a0bf81 100644 --- a/src/audacious/plugin-preferences.c +++ b/src/audacious/plugin-preferences.c @@ -1,6 +1,6 @@ /* * plugin-preferences.c - * Copyright 2012 John Lindgren + * Copyright 2012-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,6 +17,7 @@ * the use of this software. */ +#include #include #include "i18n.h" @@ -51,9 +52,8 @@ void plugin_make_about_window (PluginHandle * plugin) text = dgettext (header->domain, text); } - char * title = g_strdup_printf (_("About %s"), name); + SCONCAT3 (title, _("About"), " ", name); audgui_simple_message (& misc->about_window, GTK_MESSAGE_INFO, title, text); - g_free (title); } static void response_cb (GtkWidget * window, int response, const PluginPreferences * p) @@ -89,14 +89,23 @@ void plugin_make_config_window (PluginHandle * plugin) if (PLUGIN_HAS_FUNC (header, domain)) name = dgettext (header->domain, header->name); - char * title = g_strdup_printf (_("%s Settings"), name); + GtkWidget * window = gtk_dialog_new (); - GtkWidget * window = p->apply ? gtk_dialog_new_with_buttons (title, NULL, 0, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OK, GTK_RESPONSE_OK, NULL) - : gtk_dialog_new_with_buttons (title, NULL, 0, GTK_STOCK_CLOSE, - GTK_RESPONSE_CLOSE, NULL); + SCONCAT3 (title, name, " ", _("Settings")); + gtk_window_set_title ((GtkWindow *) window, title); - g_free (title); + if (p->apply) + { + GtkWidget * button1 = audgui_button_new (_("_Set"), "system-run", NULL, NULL); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL); + gtk_dialog_add_action_widget ((GtkDialog *) window, button2, GTK_RESPONSE_CANCEL); + gtk_dialog_add_action_widget ((GtkDialog *) window, button1, GTK_RESPONSE_OK); + } + else + { + GtkWidget * button = audgui_button_new (_("_Close"), "window-close", NULL, NULL); + gtk_dialog_add_action_widget ((GtkDialog *) window, button, GTK_RESPONSE_CLOSE); + } GtkWidget * content = gtk_dialog_get_content_area ((GtkDialog *) window); GtkWidget * box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); diff --git a/src/audacious/plugin-registry.c b/src/audacious/plugin-registry.c index 2b56928..bc0d441 100644 --- a/src/audacious/plugin-registry.c +++ b/src/audacious/plugin-registry.c @@ -1,6 +1,6 @@ /* * plugin-registry.c - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,11 +24,13 @@ * loaded; hence the mutex must be locked before checking that a plugin is * loaded and while loading it. */ -#include #include #include #include +#include +#include + #include #include "debug.h" @@ -99,7 +101,7 @@ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static PluginHandle * plugin_new (char * path, bool_t confirmed, bool_t loaded, int timestamp, int type, Plugin * header) { - PluginHandle * plugin = g_malloc (sizeof (PluginHandle)); + PluginHandle * plugin = g_slice_new (PluginHandle); plugin->path = path; plugin->confirmed = confirmed; @@ -144,31 +146,31 @@ static void plugin_free (PluginHandle * plugin) { plugin_list = g_list_remove (plugin_list, plugin); - g_list_free_full (plugin->watches, g_free); + for (GList * node = plugin->watches; node; node = node->next) + g_slice_free (PluginWatch, node->data); if (plugin->type == PLUGIN_TYPE_TRANSPORT) - g_list_free_full (plugin->u.t.schemes, g_free); + g_list_free_full (plugin->u.t.schemes, (GDestroyNotify) str_unref); else if (plugin->type == PLUGIN_TYPE_PLAYLIST) - g_list_free_full (plugin->u.p.exts, g_free); + g_list_free_full (plugin->u.p.exts, (GDestroyNotify) str_unref); else if (plugin->type == PLUGIN_TYPE_INPUT) { for (int key = 0; key < INPUT_KEYS; key ++) - g_list_free_full (plugin->u.i.keys[key], g_free); + g_list_free_full (plugin->u.i.keys[key], (GDestroyNotify) str_unref); } - g_free (plugin->path); - g_free (plugin->name); - g_free (plugin->domain); + str_unref (plugin->path); + str_unref (plugin->name); + str_unref (plugin->domain); g_free (plugin->misc); - g_free (plugin); + g_slice_free (PluginHandle, plugin); } static FILE * open_registry_file (const char * mode) { - char * path = g_strdup_printf ("%s/" FILENAME, get_path (AUD_PATH_USER_DIR)); - FILE * file = fopen (path, mode); - g_free (path); - return file; + const char * user_dir = get_path (AUD_PATH_USER_DIR); + SCONCAT2 (path, user_dir, "/" FILENAME); + return g_fopen (path, mode); } static void transport_plugin_save (PluginHandle * plugin, FILE * handle) @@ -264,8 +266,7 @@ static bool_t parse_integer (const char * key, int * value) static char * parse_string (const char * key) { - return (parse_value && ! strcmp (parse_key, key)) ? g_strdup (parse_value) : - NULL; + return (parse_value && ! strcmp (parse_key, key)) ? str_get (parse_value) : NULL; } static void transport_plugin_parse (PluginHandle * plugin, FILE * handle) @@ -330,7 +331,7 @@ FOUND: int timestamp; if (! parse_integer ("stamp", & timestamp)) { - g_free (path); + str_unref (path); return FALSE; } @@ -405,10 +406,10 @@ int plugin_compare (PluginHandle * a, PluginHandle * b) return 1; int diff; - if ((diff = string_compare (dgettext (a->domain, a->name), dgettext (b->domain, b->name)))) + if ((diff = str_compare (dgettext (a->domain, a->name), dgettext (b->domain, b->name)))) return diff; - return string_compare (a->path, b->path); + return str_compare (a->path, b->path); } void plugin_registry_prune (void) @@ -432,16 +433,15 @@ PluginHandle * plugin_lookup (const char * path) static int plugin_lookup_basename_cb (PluginHandle * plugin, const char * basename) { - char * test = g_path_get_basename (plugin->path); - - char * dot = strrchr (test, '.'); - if (dot) - * dot = 0; + const char * slash = strrchr (plugin->path, G_DIR_SEPARATOR); + if (! slash) + return TRUE; - int ret = strcmp (test, basename); + const char * dot = strrchr (slash + 1, '.'); + if (! dot) + return TRUE; - g_free (test); - return ret; + return strncmp (slash + 1, basename, dot - (slash + 1)); } /* Note: If there are multiple plugins with the same basename, this returns only @@ -457,10 +457,10 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new) { Plugin * header = plugin->header; - g_free (plugin->name); - g_free (plugin->domain); - plugin->name = g_strdup (header->name); - plugin->domain = PLUGIN_HAS_FUNC (header, domain) ? g_strdup (header->domain) : NULL; + str_unref (plugin->name); + str_unref (plugin->domain); + plugin->name = str_get (header->name); + plugin->domain = PLUGIN_HAS_FUNC (header, domain) ? str_get (header->domain) : NULL; plugin->has_about = PLUGIN_HAS_FUNC (header, about) || PLUGIN_HAS_FUNC (header, about_text); plugin->has_configure = PLUGIN_HAS_FUNC (header, configure) || PLUGIN_HAS_FUNC (header, prefs); @@ -468,23 +468,21 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new) { TransportPlugin * tp = (TransportPlugin *) header; - g_list_free_full (plugin->u.t.schemes, g_free); + g_list_free_full (plugin->u.t.schemes, (GDestroyNotify) str_unref); plugin->u.t.schemes = NULL; for (int i = 0; tp->schemes[i]; i ++) - plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup - (tp->schemes[i])); + plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, str_get (tp->schemes[i])); } else if (header->type == PLUGIN_TYPE_PLAYLIST) { PlaylistPlugin * pp = (PlaylistPlugin *) header; - g_list_free_full (plugin->u.p.exts, g_free); + g_list_free_full (plugin->u.p.exts, (GDestroyNotify) str_unref); plugin->u.p.exts = NULL; for (int i = 0; pp->extensions[i]; i ++) - plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup - (pp->extensions[i])); + plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, str_get (pp->extensions[i])); } else if (header->type == PLUGIN_TYPE_INPUT) { @@ -493,7 +491,7 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new) for (int key = 0; key < INPUT_KEYS; key ++) { - g_list_free_full (plugin->u.i.keys[key], g_free); + g_list_free_full (plugin->u.i.keys[key], (GDestroyNotify) str_unref); plugin->u.i.keys[key] = NULL; } @@ -501,22 +499,22 @@ static void plugin_get_info (PluginHandle * plugin, bool_t new) { for (int i = 0; ip->extensions[i]; i ++) plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend - (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup - (ip->extensions[i])); + (plugin->u.i.keys[INPUT_KEY_EXTENSION], + str_get (ip->extensions[i])); } if (PLUGIN_HAS_FUNC (ip, mimes)) { for (int i = 0; ip->mimes[i]; i ++) plugin->u.i.keys[INPUT_KEY_MIME] = g_list_prepend - (plugin->u.i.keys[INPUT_KEY_MIME], g_strdup (ip->mimes[i])); + (plugin->u.i.keys[INPUT_KEY_MIME], str_get (ip->mimes[i])); } if (PLUGIN_HAS_FUNC (ip, schemes)) { for (int i = 0; ip->schemes[i]; i ++) plugin->u.i.keys[INPUT_KEY_SCHEME] = g_list_prepend - (plugin->u.i.keys[INPUT_KEY_SCHEME], g_strdup (ip->schemes[i])); + (plugin->u.i.keys[INPUT_KEY_SCHEME], str_get (ip->schemes[i])); } plugin->u.i.has_images = PLUGIN_HAS_FUNC (ip, get_song_image); @@ -572,7 +570,7 @@ void plugin_register (const char * path, int timestamp) if (! header) return; - plugin = plugin_new (g_strdup (path), TRUE, TRUE, timestamp, + plugin = plugin_new (str_get (path), TRUE, TRUE, timestamp, header->type, header); plugin_get_info (plugin, TRUE); @@ -620,6 +618,54 @@ PluginHandle * plugin_by_header (const void * header) return node ? node->data : NULL; } +int plugin_count (int type) +{ + int count = 0; + + for (GList * node = plugin_list; node; node = node->next) + { + PluginHandle * plugin = node->data; + if (plugin->type == type) + count ++; + } + + return count; +} + +int plugin_get_index (PluginHandle * plugin) +{ + int index = 0; + + for (GList * node = plugin_list; node; node = node->next) + { + PluginHandle * plugin2 = node->data; + if (plugin2->type == plugin->type) + { + if (plugin2 == plugin) + return index; + index ++; + } + } + + return -1; +} + +PluginHandle * plugin_by_index (int type, int index) +{ + for (GList * node = plugin_list; node; node = node->next) + { + PluginHandle * plugin = node->data; + if (plugin->type == type) + { + if (! index) + return plugin; + index --; + } + } + + return NULL; +} + void plugin_for_each (int type, PluginForEachFunc func, void * data) { for (GList * node = plugin_list; node; node = node->next) @@ -660,7 +706,7 @@ static void plugin_call_watches (PluginHandle * plugin) if (! watch->func (plugin, watch->data)) { - g_free (watch); + g_slice_free (PluginWatch, watch); plugin->watches = g_list_delete_link (plugin->watches, node); } @@ -696,7 +742,7 @@ void plugin_for_enabled (int type, PluginForEachFunc func, void * data) void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void * data) { - PluginWatch * watch = g_malloc (sizeof (PluginWatch)); + PluginWatch * watch = g_slice_new (PluginWatch); watch->func = func; watch->data = data; plugin->watches = g_list_prepend (plugin->watches, watch); @@ -712,7 +758,7 @@ void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void * if (watch->func == func && watch->data == data) { - g_free (watch); + g_slice_free (PluginWatch, watch); plugin->watches = g_list_delete_link (plugin->watches, node); } @@ -754,7 +800,8 @@ PluginHandle * transport_plugin_for_scheme (const char * scheme) typedef struct { const char * ext; - PluginHandle * plugin; + PluginForEachFunc func; + void * data; } PlaylistPluginForExtState; static bool_t playlist_plugin_for_ext_cb (PluginHandle * plugin, @@ -764,16 +811,14 @@ static bool_t playlist_plugin_for_ext_cb (PluginHandle * plugin, (GCompareFunc) g_ascii_strcasecmp)) return TRUE; - state->plugin = plugin; - return FALSE; + return state->func (plugin, state->data); } -PluginHandle * playlist_plugin_for_extension (const char * extension) +void playlist_plugin_for_ext (const char * ext, PluginForEachFunc func, void * data) { - PlaylistPluginForExtState state = {extension, NULL}; + PlaylistPluginForExtState state = {ext, func, data}; plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc) playlist_plugin_for_ext_cb, & state); - return state.plugin; } typedef struct { diff --git a/src/audacious/plugin-view.c b/src/audacious/plugin-view.c index 7d3a8af..36959af 100644 --- a/src/audacious/plugin-view.c +++ b/src/audacious/plugin-view.c @@ -1,6 +1,6 @@ /* * plugin-view.c - * Copyright 2010 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -19,6 +19,9 @@ #include +#include + +#include "i18n.h" #include "plugin.h" #include "plugins.h" #include "ui_preferences.h" @@ -27,7 +30,6 @@ enum { PVIEW_COL_NODE, PVIEW_COL_ENABLED, PVIEW_COL_NAME, - PVIEW_COL_PATH, PVIEW_COLS }; @@ -88,8 +90,8 @@ static bool_t fill_cb (PluginHandle * p, GtkTreeModel * model) GtkTreeIter iter; gtk_list_store_append ((GtkListStore *) model, & iter); gtk_list_store_set ((GtkListStore *) model, & iter, PVIEW_COL_NODE, n, - PVIEW_COL_ENABLED, plugin_get_enabled (p), PVIEW_COL_NAME, plugin_get_name - (p), PVIEW_COL_PATH, plugin_get_filename (p), -1); + PVIEW_COL_ENABLED, plugin_get_enabled (p), PVIEW_COL_NAME, + plugin_get_name (p), -1); n->p = p; n->model = model; @@ -103,7 +105,7 @@ static bool_t fill_cb (PluginHandle * p, GtkTreeModel * model) static void list_fill (GtkTreeView * tree, void * type) { GtkTreeModel * model = (GtkTreeModel *) gtk_list_store_new (PVIEW_COLS, - G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING); + G_TYPE_POINTER, G_TYPE_BOOLEAN, G_TYPE_STRING); gtk_tree_view_set_model (tree, model); GtkTreeViewColumn * col = gtk_tree_view_column_new (); @@ -117,17 +119,15 @@ static void list_fill (GtkTreeView * tree, void * type) gtk_tree_view_column_set_attributes (col, rend, "active", PVIEW_COL_ENABLED, NULL); - for (int i = PVIEW_COL_NAME; i <= PVIEW_COL_PATH; i ++) - { - col = gtk_tree_view_column_new (); - gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_GROW_ONLY); - gtk_tree_view_column_set_resizable (col, FALSE); - gtk_tree_view_append_column (tree, col); - - rend = gtk_cell_renderer_text_new (); - gtk_tree_view_column_pack_start (col, rend, FALSE); - gtk_tree_view_column_set_attributes (col, rend, "text", i, NULL); - } + col = gtk_tree_view_column_new (); + gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_expand (col, TRUE); + gtk_tree_view_column_set_resizable (col, FALSE); + gtk_tree_view_append_column (tree, col); + + rend = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (col, rend, FALSE); + gtk_tree_view_column_set_attributes (col, rend, "text", PVIEW_COL_NAME, NULL); plugin_for_each (GPOINTER_TO_INT (type), (PluginForEachFunc) fill_cb, model); } @@ -192,14 +192,14 @@ static void button_update (GtkTreeView * tree, GtkWidget * b) gtk_widget_set_sensitive (b, FALSE); } -static void do_config (GtkTreeView * tree) +static void do_config (void * tree) { PluginHandle * plugin = get_selected_plugin (tree); g_return_if_fail (plugin != NULL); plugin_do_configure (plugin); } -static void do_about (GtkTreeView * tree) +static void do_about (void * tree) { PluginHandle * plugin = get_selected_plugin (tree); g_return_if_fail (plugin != NULL); @@ -239,21 +239,19 @@ GtkWidget * plugin_view_new (int type) GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0); - GtkWidget * config = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES); + GtkWidget * config = audgui_button_new (_("_Settings"), + "preferences-system", do_config, tree); gtk_box_pack_start ((GtkBox *) hbox, config, FALSE, FALSE, 0); gtk_widget_set_sensitive (config, FALSE); g_object_set_data ((GObject *) config, "watcher", (void *) config_watcher); g_signal_connect (tree, "cursor-changed", (GCallback) button_update, config); - g_signal_connect_swapped (config, "clicked", (GCallback) - do_config, tree); g_signal_connect (config, "destroy", (GCallback) button_destroy, NULL); - GtkWidget * about = gtk_button_new_from_stock (GTK_STOCK_ABOUT); + GtkWidget * about = audgui_button_new (_("_About"), "help-about", do_about, tree); gtk_box_pack_start ((GtkBox *) hbox, about, FALSE, FALSE, 0); gtk_widget_set_sensitive (about, FALSE); g_object_set_data ((GObject *) about, "watcher", (void *) about_watcher); g_signal_connect (tree, "cursor-changed", (GCallback) button_update, about); - g_signal_connect_swapped (about, "clicked", (GCallback) do_about, tree); g_signal_connect (about, "destroy", (GCallback) button_destroy, NULL); return vbox; diff --git a/src/audacious/plugin.h b/src/audacious/plugin.h index 4591894..49f5f27 100644 --- a/src/audacious/plugin.h +++ b/src/audacious/plugin.h @@ -1,6 +1,6 @@ /* * plugin.h - * Copyright 2005-2012 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin, and + * Copyright 2005-2013 William Pitcock, Yoshiki Yazawa, Eugene Zagidullin, and * John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -31,22 +31,6 @@ /* "Magic" bytes identifying an Audacious plugin header. */ #define _AUD_PLUGIN_MAGIC 0x8EAC8DE2 -/* API version. Plugins are marked with this number at compile time. - * - * _AUD_PLUGIN_VERSION is the current version; _AUD_PLUGIN_VERSION_MIN is - * the oldest one we are backward compatible with. Plugins marked older than - * _AUD_PLUGIN_VERSION_MIN or newer than _AUD_PLUGIN_VERSION are not loaded. - * - * Before releases that add new pointers to the end of the API tables, increment - * _AUD_PLUGIN_VERSION but leave _AUD_PLUGIN_VERSION_MIN the same. - * - * Before releases that break backward compatibility (e.g. remove pointers from - * the API tables), increment _AUD_PLUGIN_VERSION *and* set - * _AUD_PLUGIN_VERSION_MIN to the same value. */ - -#define _AUD_PLUGIN_VERSION_MIN 40 /* 3.3-devel */ -#define _AUD_PLUGIN_VERSION 43 /* 3.4-devel */ - /* A NOTE ON THREADS * * How thread-safe a plugin must be depends on the type of plugin. Note that @@ -254,78 +238,6 @@ struct _EffectPlugin bool_t preserves_format; }; -struct OutputAPI -{ - /* Prepares the output system for playback in the specified format. Returns - * TRUE on success. If the call fails, no other output functions may be - * called. */ - bool_t (* open_audio) (int format, int rate, int channels); - - /* Informs the output system of replay gain values for the current song so - * that volume levels can be adjusted accordingly, if the user so desires. - * This may be called at any time during playback should the values change. */ - void (* set_replaygain_info) (const ReplayGainInfo * info); - - /* Passes audio data to the output system for playback. The data must be in - * the format passed to open_audio, and the length (in bytes) must be an - * integral number of frames. This function blocks until all the data has - * been written (though it may not yet be heard by the user). */ - void (* write_audio) (void * data, int length); - - /* Interrupts a call to write_audio() so that it returns immediately. - * Buffered audio data is discarded. Until set_written_time() or - * open_audio() is called, further calls to write_audio() will have no - * effect and will return immediately. */ - void (* abort_write) (void); - - /* Pauses or unpauses playback. If playback is paused during a call to - * write_audio(), the call will block until playback is unpaused again or - * abort_write() is called. */ - void (* pause) (bool_t pause); - - /* Returns the time counter. Note that this represents the amount of audio - * data passed to the output system, not the amount actually heard by the - * user. */ - int (* written_time) (void); - - /* Sets the time counter to a new value. Does not perform a flush; the name - * is kept only for compatibility. */ - void (* flush) (int time); -}; - -typedef const struct _InputPlayback InputPlayback; - -struct _InputPlayback -{ - /* Pointer to the output API functions. */ - const struct OutputAPI * output; - - /* Allows the plugin to associate data with a playback instance. */ - void (* set_data) (InputPlayback * p, void * data); - - /* Returns the pointer passed to set_data. */ - void * (* get_data) (InputPlayback * p); - - /* Signifies that the plugin has started playback is ready to accept mseek, - * pause, and stop calls. */ - void (* set_pb_ready) (InputPlayback * p); - - /* Updates attributes of the stream. "bitrate" is in bits per second. - * "samplerate" is in hertz. */ - void (* set_params) (InputPlayback * p, int bitrate, int samplerate, - int channels); - - /* Updates metadata for the stream. Caller gives up ownership of one - * reference to the tuple. */ - void (* set_tuple) (InputPlayback * playback, Tuple * tuple); - - /* If replay gain settings are stored in the tuple associated with the - * current song, this function can be called (after opening audio) to apply - * those settings. If the settings are changed in a call to set_tuple, this - * function must be called again to apply the updated settings. */ - void (* set_gain_from_playlist) (InputPlayback * playback); -}; - struct _InputPlugin { PLUGIN_COMMON_FIELDS @@ -355,8 +267,11 @@ struct _InputPlugin /* Pointer to an array (terminated with NULL) of MIME types the plugin can * handle. */ const char * const * mimes; + /* Pointer to an array (terminated with NULL) of custom URI schemes the - * plugin can handle. */ + * plugin supports. Plugins using custom URI schemes are expected to + * handle their own I/O. Hence, any VFSFile pointers passed to play(), + * probe_for_tuple(), etc. will be NULL. */ const char * const * schemes; /* How quickly the plugin should be tried in searching for a plugin to @@ -364,73 +279,28 @@ struct _InputPlugin * with priority 0 are tried first, 10 last. */ int priority; - /* Must return nonzero if the plugin can handle this file. If the file - * could not be opened, "file" will be NULL. (This is normal in the case of - * special URI schemes like cdda:// that do not represent actual files.) */ + /* Returns TRUE if the plugin can handle the file. */ bool_t (* is_our_file_from_vfs) (const char * filename, VFSFile * file); - /* Must return a tuple containing metadata for this file, or NULL if no - * metadata could be read. If the file could not be opened, "file" will be - * NULL. Audacious takes over one reference to the tuple returned. */ + /* Reads metadata from the file, returning a reference to the tuple produced. */ Tuple * (* probe_for_tuple) (const char * filename, VFSFile * file); - /* Optional. Must write metadata from a tuple to this file. Must return - * nonzero on success or zero on failure. "file" will never be NULL. */ - /* Bug: This function does not support special URI schemes like cdda://, - * since no file name is passed. */ - bool_t (* update_song_tuple) (const Tuple * tuple, VFSFile * file); + /* Plays the file. Returns FALSE on error. Also see input-api.h. */ + bool_t (* play) (const char * filename, VFSFile * file); - /* Optional, and not recommended. Must show a window with information about - * this file. If this function is provided, update_song_tuple should not be. */ - /* Bug: Implementing this function duplicates user interface code and code - * to open the file in each and every plugin. */ - void (* file_info_box) (const char * filename); + /* Optional. Writes metadata to the file, returning FALSE on error. */ + bool_t (* update_song_tuple) (const char * filename, VFSFile * file, const Tuple * tuple); - /* Optional. Must try to read an "album art" image embedded in this file. - * Must return nonzero on success or zero on failure. If the file could not - * be opened, "file" will be NULL. On success, must fill "data" with a - * pointer to a block of data allocated with g_malloc and "size" with the - * size in bytes of that block. The data may be in any format supported by - * GTK. Audacious will free the data when it is no longer needed. */ + /* Optional. Reads an album art image (JPEG or PNG data) from the file. + * Returns a pointer to the data along with its size in bytes. The returned + * data will be freed when no longer needed. Returns FALSE on error. */ bool_t (* get_song_image) (const char * filename, VFSFile * file, void * * data, int64_t * size); - /* Must try to play this file. "playback" is a structure containing output- - * related functions which the plugin may make use of. It also contains a - * "data" pointer which the plugin may use to refer private data associated - * with the playback state. This pointer can then be used from pause, - * mseek, and stop. If the file could not be opened, "file" will be NULL. - * "start_time" is the position in milliseconds at which to start from, or - * -1 to start from the beginning of the file. "stop_time" is the position - * in milliseconds at which to end playback, or -1 to play to the end of the - * file. "paused" specifies whether playback should immediately be paused. - * Must return nonzero if some of the file was successfully played or zero - * on failure. */ - bool_t (* play) (InputPlayback * playback, const char * filename, - VFSFile * file, int start_time, int stop_time, bool_t pause); - - /* Must pause or unpause a file currently being played. This function will - * be called from a different thread than play, but it will not be called - * before the plugin calls set_pb_ready or after stop is called. */ - void (* pause) (InputPlayback * playback, bool_t paused); - - /* Optional. Must seek to the given position in milliseconds within a file - * currently being played. This function will be called from a different - * thread than play, but it will not be called before the plugin calls - * set_pb_ready or after stop is called. */ - void (* mseek) (InputPlayback * playback, int time); - - /* Must signal a currently playing song to stop and cause play to return. - * This function will be called from a different thread than play. It will - * only be called once. It should not join the thread from which play is - * called. */ - void (* stop) (InputPlayback * playback); - - /* Advanced, for plugins that do not use Audacious's output system. Use at - * your own risk. */ - int (* get_time) (InputPlayback * playback); - int (* get_volume) (int * l, int * r); - int (* set_volume) (int l, int r); + /* Optional. Displays a window showing info about the file. In general, + * this function should be avoided since Audacious already provides a file + * info window. */ + void (* file_info_box) (const char * filename); }; struct _GeneralPlugin @@ -467,25 +337,10 @@ struct _IfacePlugin { PLUGIN_COMMON_FIELDS - /* is_shown() may return nonzero even if the interface is not actually - * visible; for example, if it is obscured by other windows or minimized. - * is_focused() only returns nonzero if the interface is actually visible; - * in X11, this should be determined by whether the interface has the - * toplevel focus. show() should show and raise the interface, so that both - * is_shown() and is_focused() will return nonzero. */ void (* show) (bool_t show); - bool_t (* is_shown) (void); - bool_t (* is_focused) (void); - - void (* show_error) (const char * markup); - void (* show_filebrowser) (bool_t play_button); - void (* show_jump_to_track) (void); void (* run_gtk_plugin) (void /* GtkWidget */ * widget, const char * name); void (* stop_gtk_plugin) (void /* GtkWidget */ * widget); - - void (* install_toolbar) (void /* GtkWidget */ * button); - void (* uninstall_toolbar) (void /* GtkWidget */ * button); }; #undef PLUGIN_COMMON_FIELDS diff --git a/src/audacious/pluginenum.c b/src/audacious/pluginenum.c index ed8df47..58216a5 100644 --- a/src/audacious/pluginenum.c +++ b/src/audacious/pluginenum.c @@ -1,6 +1,6 @@ /* * pluginenum.c - * Copyright 2007-2011 William Pitcock and John Lindgren + * Copyright 2007-2013 William Pitcock and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -19,12 +19,14 @@ #include #include -#include -#include #include #include #include +#include +#include +#include + #include #include @@ -34,17 +36,27 @@ #define AUD_API_DECLARE #include "drct.h" +#include "input.h" #include "misc.h" #include "playlist.h" #include "plugins.h" #undef AUD_API_DECLARE -static const char * plugin_dir_list[] = {PLUGINSUBS, NULL}; +static const char * plugin_dir_list[] = { + "Transport", + "Container", + "Input", + "Output", + "Effect", + "General", + "Visualization" +}; char verbose = 0; AudAPITable api_table = { .drct_api = & drct_api, + .input_api = & input_api, .misc_api = & misc_api, .playlist_api = & playlist_api, .plugins_api = & plugins_api, @@ -132,7 +144,9 @@ static void plugin2_unload (LoadedModule * loaded) } pthread_mutex_lock (& mutex); +#ifndef VALGRIND_FRIENDLY g_module_close (loaded->module); +#endif g_slice_free (LoadedModule, loaded); pthread_mutex_unlock (& mutex); } @@ -144,8 +158,8 @@ static bool_t scan_plugin_func(const char * path, const char * basename, void * if (!str_has_suffix_nocase(basename, PLUGIN_SUFFIX)) return FALSE; - struct stat st; - if (stat (path, & st)) + GStatBuf st; + if (g_stat (path, & st) < 0) { fprintf (stderr, "Unable to stat %s: %s\n", path, strerror (errno)); return FALSE; @@ -166,36 +180,17 @@ void plugin_system_init(void) { assert (g_module_supported ()); - char *dir; - int dirsel = 0; - - audgui_init (& api_table); + audgui_init (& api_table, _AUD_PLUGIN_VERSION); plugin_registry_load (); -#ifndef DISABLE_USER_PLUGIN_DIR - scan_plugins (get_path (AUD_PATH_USER_PLUGIN_DIR)); - /* - * This is in a separate loop so if the user puts them in the - * wrong dir we'll still get them in the right order (home dir - * first) - Zinx - */ - while (plugin_dir_list[dirsel]) - { - dir = g_build_filename (get_path (AUD_PATH_USER_PLUGIN_DIR), - plugin_dir_list[dirsel ++], NULL); - scan_plugins(dir); - g_free(dir); - } - dirsel = 0; -#endif + const char * path = get_path (AUD_PATH_PLUGIN_DIR); - while (plugin_dir_list[dirsel]) + for (int i = 0; i < ARRAY_LEN (plugin_dir_list); i ++) { - dir = g_build_filename (get_path (AUD_PATH_PLUGIN_DIR), - plugin_dir_list[dirsel ++], NULL); - scan_plugins(dir); - g_free(dir); + char * dir = filename_build (path, plugin_dir_list[i]); + scan_plugins (dir); + str_unref (dir); } plugin_registry_prune (); diff --git a/src/audacious/plugins-api.h b/src/audacious/plugins-api.h index 6a1d4de..af4c1b2 100644 --- a/src/audacious/plugins-api.h +++ b/src/audacious/plugins-api.h @@ -1,6 +1,6 @@ /* * plugins-api.h - * Copyright 2010 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -37,9 +37,12 @@ AUD_FUNC1 (PluginHandle *, plugin_lookup_basename, const char *, basename) AUD_FUNC1 (const void *, plugin_get_header, PluginHandle *, plugin) AUD_FUNC1 (PluginHandle *, plugin_by_header, const void *, header) +AUD_FUNC1 (int, plugin_count, int, type) +AUD_FUNC1 (int, plugin_get_index, PluginHandle *, plugin) +AUD_FUNC2 (PluginHandle *, plugin_by_index, int, type, int, index) + AUD_FUNC2 (int, plugin_compare, PluginHandle *, a, PluginHandle *, b) -AUD_VFUNC3 (plugin_for_each, int, type, PluginForEachFunc, func, void *, - data) +AUD_VFUNC3 (plugin_for_each, int, type, PluginForEachFunc, func, void *, data) AUD_FUNC1 (bool_t, plugin_get_enabled, PluginHandle *, plugin) AUD_VFUNC3 (plugin_for_enabled, int, type, PluginForEachFunc, func, diff --git a/src/audacious/plugins.h b/src/audacious/plugins.h index ccda6b1..ea956ee 100644 --- a/src/audacious/plugins.h +++ b/src/audacious/plugins.h @@ -1,6 +1,6 @@ /* * plugins.h - * Copyright 2010 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,6 +24,7 @@ #include #include +/* returns TRUE to call again for the next plugin, FALSE to stop */ typedef bool_t (* PluginForEachFunc) (PluginHandle * plugin, void * data); #define AUD_API_NAME PluginsAPI @@ -58,9 +59,8 @@ void plugin_set_enabled (PluginHandle * plugin, bool_t enabled); void * plugin_get_misc_data (PluginHandle * plugin, int size); PluginHandle * transport_plugin_for_scheme (const char * scheme); -PluginHandle * playlist_plugin_for_extension (const char * extension); -void input_plugin_for_key (int key, const char * value, PluginForEachFunc - func, void * data); +void playlist_plugin_for_ext (const char * ext, PluginForEachFunc func, void * data); +void input_plugin_for_key (int key, const char * value, PluginForEachFunc func, void * data); bool_t input_plugin_has_images (PluginHandle * plugin); bool_t input_plugin_has_subtunes (PluginHandle * plugin); bool_t input_plugin_can_write_tuple (PluginHandle * plugin); diff --git a/src/audacious/preferences.c b/src/audacious/preferences.c new file mode 100644 index 0000000..eb1b651 --- /dev/null +++ b/src/audacious/preferences.c @@ -0,0 +1,641 @@ +/* + * preferences.c + * Copyright 2007-2012 Tomasz Moń, William Pitcock, and John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "preferences.h" + +#include +#include + +#include "i18n.h" +#include "misc.h" + +/* HELPERS */ + +static bool_t widget_get_bool (const PreferencesWidget * widget) +{ + g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE); + + if (widget->cfg) + return * (bool_t *) widget->cfg; + else if (widget->cname) + return get_bool (widget->csect, widget->cname); + else + return FALSE; +} + +static void widget_set_bool (const PreferencesWidget * widget, bool_t value) +{ + g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN); + + if (widget->cfg) + * (bool_t *) widget->cfg = value; + else if (widget->cname) + set_bool (widget->csect, widget->cname, value); + + if (widget->callback) + widget->callback (); +} + +static int widget_get_int (const PreferencesWidget * widget) +{ + g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0); + + if (widget->cfg) + return * (int *) widget->cfg; + else if (widget->cname) + return get_int (widget->csect, widget->cname); + else + return 0; +} + +static void widget_set_int (const PreferencesWidget * widget, int value) +{ + g_return_if_fail (widget->cfg_type == VALUE_INT); + + if (widget->cfg) + * (int *) widget->cfg = value; + else if (widget->cname) + set_int (widget->csect, widget->cname, value); + + if (widget->callback) + widget->callback (); +} + +static double widget_get_double (const PreferencesWidget * widget) +{ + g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0); + + if (widget->cfg) + return * (float *) widget->cfg; + else if (widget->cname) + return get_double (widget->csect, widget->cname); + else + return 0; +} + +static void widget_set_double (const PreferencesWidget * widget, double value) +{ + g_return_if_fail (widget->cfg_type == VALUE_FLOAT); + + if (widget->cfg) + * (float *) widget->cfg = value; + else if (widget->cname) + set_double (widget->csect, widget->cname, value); + + if (widget->callback) + widget->callback (); +} + +static char * widget_get_string (const PreferencesWidget * widget) +{ + g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL); + + if (widget->cfg) + return str_get (* (char * *) widget->cfg); + else if (widget->cname) + return get_str (widget->csect, widget->cname); + else + return NULL; +} + +static void widget_set_string (const PreferencesWidget * widget, const char * value) +{ + g_return_if_fail (widget->cfg_type == VALUE_STRING); + + if (widget->cfg) + { + g_free (* (char * *) widget->cfg); + * (char * *) widget->cfg = g_strdup (value); + } + else if (widget->cname) + set_str (widget->csect, widget->cname, value); + + if (widget->callback) + widget->callback (); +} + +/* WIDGET_CHK_BTN */ + +static void on_toggle_button_toggled (GtkToggleButton * button, const PreferencesWidget * widget) +{ + bool_t active = gtk_toggle_button_get_active (button); + widget_set_bool (widget, active); + + GtkWidget * child = g_object_get_data ((GObject *) button, "child"); + if (child) + gtk_widget_set_sensitive (child, active); +} + +static void init_toggle_button (GtkWidget * button, const PreferencesWidget * widget) +{ + if (widget->cfg_type != VALUE_BOOLEAN) + return; + + gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget)); + g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, (void *) widget); +} + +/* WIDGET_LABEL */ + +static void create_label (const PreferencesWidget * widget, GtkWidget * * label, + GtkWidget * * icon, const char * domain) +{ + if (widget->data.label.stock_id) + * icon = gtk_image_new_from_icon_name (widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON); + + * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label)); + gtk_label_set_use_markup ((GtkLabel *) * label, TRUE); + + if (widget->data.label.single_line == FALSE) + gtk_label_set_line_wrap ((GtkLabel *) * label, TRUE); + + gtk_misc_set_alignment ((GtkMisc *) * label, 0, 0.5); +} + +/* WIDGET_RADIO_BTN */ + +static void on_radio_button_toggled (GtkWidget * button, const PreferencesWidget * widget) +{ + if (gtk_toggle_button_get_active ((GtkToggleButton *) button)) + widget_set_int (widget, widget->data.radio_btn.value); +} + +static void init_radio_button (GtkWidget * button, const PreferencesWidget * widget) +{ + if (widget->cfg_type != VALUE_INT) + return; + + if (widget_get_int (widget) == widget->data.radio_btn.value) + gtk_toggle_button_set_active ((GtkToggleButton *) button, TRUE); + + g_signal_connect (button, "toggled", (GCallback) on_radio_button_toggled, (void *) widget); +} + +/* WIDGET_SPIN_BTN */ + +static void on_spin_btn_changed_int (GtkSpinButton * button, const PreferencesWidget * widget) +{ + widget_set_int (widget, gtk_spin_button_get_value_as_int (button)); +} + +static void on_spin_btn_changed_float (GtkSpinButton * button, const PreferencesWidget * widget) +{ + widget_set_double (widget, gtk_spin_button_get_value (button)); +} + +static void create_spin_button (const PreferencesWidget * widget, + GtkWidget * * label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, + const char * domain) +{ + * label_pre = gtk_label_new (dgettext (domain, widget->label)); + * spin_btn = gtk_spin_button_new_with_range (widget->data.spin_btn.min, + widget->data.spin_btn.max, widget->data.spin_btn.step); + + if (widget->tooltip) + gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain, widget->tooltip)); + + if (widget->data.spin_btn.right_label) + * label_past = gtk_label_new (dgettext (domain, widget->data.spin_btn.right_label)); + + switch (widget->cfg_type) + { + case VALUE_INT: + gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget)); + g_signal_connect (* spin_btn, "value_changed", (GCallback) + on_spin_btn_changed_int, (void *) widget); + break; + + case VALUE_FLOAT: + gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget)); + g_signal_connect (* spin_btn, "value_changed", (GCallback) + on_spin_btn_changed_float, (void *) widget); + break; + + default: + break; + } +} + +/* WIDGET_FONT_BTN */ + +static void on_font_btn_font_set (GtkFontButton * button, const PreferencesWidget * widget) +{ + widget_set_string (widget, gtk_font_button_get_font_name (button)); +} + +void create_font_btn (const PreferencesWidget * widget, GtkWidget * * label, + GtkWidget * * font_btn, const char * domain) +{ + * font_btn = gtk_font_button_new (); + gtk_font_button_set_use_font ((GtkFontButton *) * font_btn, TRUE); + gtk_font_button_set_use_size ((GtkFontButton *) * font_btn, TRUE); + gtk_widget_set_hexpand (* font_btn, TRUE); + + if (widget->label) + { + * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label)); + gtk_label_set_use_markup ((GtkLabel *) * label, TRUE); + gtk_misc_set_alignment ((GtkMisc *) * label, 1, 0.5); + gtk_label_set_justify ((GtkLabel *) * label, GTK_JUSTIFY_RIGHT); + gtk_label_set_mnemonic_widget ((GtkLabel *) * label, * font_btn); + } + + if (widget->data.font_btn.title) + gtk_font_button_set_title ((GtkFontButton *) * font_btn, + dgettext (domain, widget->data.font_btn.title)); + + char * name = widget_get_string (widget); + if (name) + { + gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name); + str_unref (name); + } + + g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, (void *) widget); +} + +/* WIDGET_ENTRY */ + +static void on_entry_changed (GtkEntry * entry, const PreferencesWidget * widget) +{ + widget_set_string (widget, gtk_entry_get_text (entry)); +} + +static void create_entry (const PreferencesWidget * widget, GtkWidget * * label, + GtkWidget * * entry, const char * domain) +{ + * entry = gtk_entry_new (); + gtk_entry_set_visibility ((GtkEntry *) * entry, ! widget->data.entry.password); + gtk_widget_set_hexpand (* entry, TRUE); + + if (widget->label) + * label = gtk_label_new (dgettext (domain, widget->label)); + + if (widget->tooltip) + gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip)); + + if (widget->cfg_type == VALUE_STRING) + { + char * value = widget_get_string (widget); + if (value) + { + gtk_entry_set_text ((GtkEntry *) * entry, value); + str_unref (value); + } + + g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, (void *) widget); + } +} + +/* WIDGET_COMBO_BOX */ + +static void on_cbox_changed_int (GtkComboBox * combobox, const PreferencesWidget * widget) +{ + int position = gtk_combo_box_get_active (combobox); + const ComboBoxElements * elements = g_object_get_data ((GObject *) combobox, "comboboxelements"); + widget_set_int (widget, GPOINTER_TO_INT (elements[position].value)); +} + +static void on_cbox_changed_string (GtkComboBox * combobox, const PreferencesWidget * widget) +{ + int position = gtk_combo_box_get_active (combobox); + const ComboBoxElements * elements = g_object_get_data ((GObject *) combobox, "comboboxelements"); + widget_set_string (widget, elements[position].value); +} + +static void fill_cbox (GtkWidget * combobox, const PreferencesWidget * widget, const char * domain) +{ + const ComboBoxElements * elements = widget->data.combo.elements; + int n_elements = widget->data.combo.n_elements; + + if (widget->data.combo.fill) + elements = widget->data.combo.fill (& n_elements); + + g_object_set_data ((GObject *) combobox, "comboboxelements", (void *) elements); + + for (int i = 0; i < n_elements; i ++) + gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox, + dgettext (domain, elements[i].label)); + + switch (widget->cfg_type) + { + case VALUE_INT:; + int ivalue = widget_get_int (widget); + + for (int i = 0; i < n_elements; i++) + { + if (GPOINTER_TO_INT (elements[i].value) == ivalue) + { + gtk_combo_box_set_active ((GtkComboBox *) combobox, i); + break; + } + } + + g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget); + break; + + case VALUE_STRING:; + char * value = widget_get_string (widget); + + for(int i = 0; i < n_elements; i++) + { + if (value && ! strcmp (elements[i].value, value)) + { + gtk_combo_box_set_active ((GtkComboBox *) combobox, i); + break; + } + } + + str_unref (value); + + g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget); + break; + + default: + break; + } +} + +static void create_cbox (const PreferencesWidget * widget, GtkWidget * * label, + GtkWidget * * combobox, const char * domain) +{ + * combobox = gtk_combo_box_text_new (); + + if (widget->label) + * label = gtk_label_new (dgettext (domain, widget->label)); + + fill_cbox (* combobox, widget, domain); +} + +/* WIDGET_TABLE */ + +static void fill_grid (GtkWidget * grid, const PreferencesWidget * elements, + int n_elements, const char * domain) +{ + for (int i = 0; i < n_elements; i ++) + { + GtkWidget * widget_left = NULL, * widget_middle = NULL, * widget_right = NULL; + + switch (elements[i].type) + { + case WIDGET_SPIN_BTN: + create_spin_button (& elements[i], & widget_left, + & widget_middle, & widget_right, domain); + break; + + case WIDGET_LABEL: + create_label (& elements[i], & widget_middle, & widget_left, domain); + break; + + case WIDGET_FONT_BTN: + create_font_btn (& elements[i], & widget_left, & widget_middle, domain); + break; + + case WIDGET_ENTRY: + create_entry (& elements[i], & widget_left, & widget_middle, domain); + break; + + case WIDGET_COMBO_BOX: + create_cbox (& elements[i], & widget_left, & widget_middle, domain); + break; + + default: + break; + } + + if (widget_left) + gtk_grid_attach ((GtkGrid *) grid, widget_left, 0, i, 1, 1); + + if (widget_middle) + gtk_grid_attach ((GtkGrid *) grid, widget_middle, 1, i, 1, 1); + + if (widget_right) + gtk_grid_attach ((GtkGrid *) grid, widget_right, 2, i, 1, 1); + } +} + +/* ALL WIDGETS */ + +/* box: a GtkBox */ +void create_widgets_with_domain (void * box, const PreferencesWidget * widgets, + int n_widgets, const char * domain) +{ + GtkWidget * widget = NULL, * child_box = NULL; + GSList * radio_btn_group = NULL; + + for (int i = 0; i < n_widgets; i ++) + { + GtkWidget * label = NULL; + + if (widget && widgets[i].child) + { + if (! child_box) + { + child_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + g_object_set_data ((GObject *) widget, "child", child_box); + + GtkWidget * alignment = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_box_pack_start (box, alignment, FALSE, FALSE, 0); + gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 12, 0); + gtk_container_add ((GtkContainer *) alignment, child_box); + + if (GTK_IS_TOGGLE_BUTTON (widget)) + gtk_widget_set_sensitive (child_box, + gtk_toggle_button_get_active ((GtkToggleButton *) widget)); + } + } + else + child_box = NULL; + + GtkWidget * alignment = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0); + gtk_box_pack_start (child_box ? (GtkBox *) child_box : box, alignment, FALSE, FALSE, 0); + + widget = NULL; + + if (radio_btn_group && widgets[i].type != WIDGET_RADIO_BTN) + radio_btn_group = NULL; + + switch (widgets[i].type) + { + case WIDGET_CHK_BTN: + widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[i].label)); + init_toggle_button (widget, & widgets[i]); + break; + + case WIDGET_LABEL: + if (strstr (widgets[i].label, "")) + gtk_alignment_set_padding ((GtkAlignment *) alignment, + (i == 0) ? 0 : 12, 0, 0, 0); + + GtkWidget * icon = NULL; + create_label (& widgets[i], & label, & icon, domain); + + if (icon) + { + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_box_pack_start ((GtkBox *) widget, icon, FALSE, FALSE, 0); + gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0); + } + else + widget = label; + + break; + + case WIDGET_RADIO_BTN: + widget = gtk_radio_button_new_with_mnemonic (radio_btn_group, + dgettext (domain, widgets[i].label)); + radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget); + init_radio_button (widget, & widgets[i]); + break; + + case WIDGET_SPIN_BTN: + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + + GtkWidget * label_pre = NULL, * spin_btn = NULL, * label_past = NULL; + create_spin_button (& widgets[i], & label_pre, & spin_btn, & label_past, domain); + + if (label_pre) + gtk_box_pack_start ((GtkBox *) widget, label_pre, FALSE, FALSE, 0); + if (spin_btn) + gtk_box_pack_start ((GtkBox *) widget, spin_btn, FALSE, FALSE, 0); + if (label_past) + gtk_box_pack_start ((GtkBox *) widget, label_past, FALSE, FALSE, 0); + + break; + + case WIDGET_CUSTOM: + if (widgets[i].data.populate) + widget = widgets[i].data.populate (); + + break; + + case WIDGET_FONT_BTN: + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + + GtkWidget * font_btn = NULL; + create_font_btn (& widgets[i], & label, & font_btn, domain); + + if (label) + gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0); + if (font_btn) + gtk_box_pack_start ((GtkBox *) widget, font_btn, FALSE, FALSE, 0); + + break; + + case WIDGET_TABLE: + widget = gtk_grid_new (); + gtk_grid_set_column_spacing ((GtkGrid *) widget, 6); + gtk_grid_set_row_spacing ((GtkGrid *) widget, 6); + + fill_grid (widget, widgets[i].data.table.elem, widgets[i].data.table.rows, domain); + + break; + + case WIDGET_ENTRY: + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + + GtkWidget * entry = NULL; + create_entry (& widgets[i], & label, & entry, domain); + + if (label) + gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0); + if (entry) + gtk_box_pack_start ((GtkBox *) widget, entry, TRUE, TRUE, 0); + + break; + + case WIDGET_COMBO_BOX: + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + + GtkWidget * combo = NULL; + create_cbox (& widgets[i], & label, & combo, domain); + + if (label) + gtk_box_pack_start ((GtkBox *) widget, label, FALSE, FALSE, 0); + if (combo) + gtk_box_pack_start ((GtkBox *) widget, combo, FALSE, FALSE, 0); + + break; + + case WIDGET_BOX: + if (widgets[i].data.box.horizontal) + widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + else + widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + create_widgets_with_domain ((GtkBox *) widget, + widgets[i].data.box.elem, widgets[i].data.box.n_elem, domain); + + if (widgets[i].data.box.frame) + { + GtkWidget * frame = gtk_frame_new (dgettext (domain, widgets[i].label)); + gtk_container_add ((GtkContainer *) frame, widget); + widget = frame; + } + + break; + + case WIDGET_NOTEBOOK: + gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 0, 0); + + widget = gtk_notebook_new (); + + for (int j = 0; j < widgets[i].data.notebook.n_tabs; j ++) + { + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_set_border_width ((GtkContainer *) vbox, 6); + + create_widgets_with_domain ((GtkBox *) vbox, + widgets[i].data.notebook.tabs[j].widgets, + widgets[i].data.notebook.tabs[j].n_widgets, domain); + + gtk_notebook_append_page ((GtkNotebook *) widget, vbox, + gtk_label_new (dgettext (domain, + widgets[i].data.notebook.tabs[j].name))); + } + + break; + + case WIDGET_SEPARATOR: + gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 6, 0, 0); + + widget = gtk_separator_new (widgets[i].data.separator.horizontal + ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + break; + + default: + break; + } + + if (widget) + { + /* use uniform spacing for horizontal boxes */ + if (gtk_orientable_get_orientation ((GtkOrientable *) box) == + GTK_ORIENTATION_HORIZONTAL) + gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 0, 0); + + gtk_container_add ((GtkContainer *) alignment, widget); + + if (widgets[i].tooltip && widgets[i].type != WIDGET_SPIN_BTN) + gtk_widget_set_tooltip_text (widget, dgettext (domain, + widgets[i].tooltip)); + } + } +} diff --git a/src/audacious/preferences.h b/src/audacious/preferences.h index bff4539..af58567 100644 --- a/src/audacious/preferences.h +++ b/src/audacious/preferences.h @@ -1,6 +1,6 @@ /* * preferences.h - * Copyright 2007-2011 Tomasz Moń, William Pitcock, and John Lindgren + * Copyright 2007-2012 Tomasz Moń, William Pitcock, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -93,8 +93,12 @@ struct _PreferencesWidget { } entry; struct { + /* static init */ const ComboBoxElements * elements; int n_elements; + + /* runtime init */ + const ComboBoxElements * (* fill) (int * n_elements); } combo; struct { diff --git a/src/audacious/probe-buffer.c b/src/audacious/probe-buffer.c index 98f65c8..c27995b 100644 --- a/src/audacious/probe-buffer.c +++ b/src/audacious/probe-buffer.c @@ -1,6 +1,6 @@ /* * probe-buffer.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,9 +17,10 @@ * the use of this software. */ -#include #include +#include + #include "debug.h" #include "probe-buffer.h" @@ -38,7 +39,7 @@ static int probe_buffer_fclose (VFSFile * file) ProbeBuffer * p = vfs_get_handle (file); int ret = vfs_fclose (p->file); - free (p); + g_free (p); return ret; } @@ -73,12 +74,6 @@ static int64_t probe_buffer_fwrite (const void * data, int64_t size, int64_t cou return 0; /* not allowed */ } -static int probe_buffer_getc (VFSFile * file) -{ - unsigned char c; - return (probe_buffer_fread (& c, 1, 1, file) == 1) ? c : EOF; -} - static int probe_buffer_fseek (VFSFile * file, int64_t offset, int whence) { ProbeBuffer * p = vfs_get_handle (file); @@ -101,16 +96,6 @@ static int probe_buffer_fseek (VFSFile * file, int64_t offset, int whence) return 0; } -static int probe_buffer_ungetc (int c, VFSFile * file) -{ - return (! probe_buffer_fseek (file, -1, SEEK_CUR)) ? c : EOF; -} - -static void probe_buffer_rewind (VFSFile * file) -{ - probe_buffer_fseek (file, 0, SEEK_SET); -} - static int64_t probe_buffer_ftell (VFSFile * file) { return ((ProbeBuffer *) vfs_get_handle (file))->at; @@ -152,10 +137,7 @@ static VFSConstructor probe_buffer_table = .vfs_fclose_impl = probe_buffer_fclose, .vfs_fread_impl = probe_buffer_fread, .vfs_fwrite_impl = probe_buffer_fwrite, - .vfs_getc_impl = probe_buffer_getc, - .vfs_ungetc_impl = probe_buffer_ungetc, .vfs_fseek_impl = probe_buffer_fseek, - .vfs_rewind_impl = probe_buffer_rewind, .vfs_ftell_impl = probe_buffer_ftell, .vfs_feof_impl = probe_buffer_feof, .vfs_ftruncate_impl = probe_buffer_ftruncate, @@ -170,7 +152,7 @@ VFSFile * probe_buffer_new (const char * filename) if (! file) return NULL; - ProbeBuffer * p = malloc (sizeof (ProbeBuffer)); + ProbeBuffer * p = g_new (ProbeBuffer, 1); p->file = file; p->filled = 0; p->at = 0; diff --git a/src/audacious/probe.c b/src/audacious/probe.c index 8c7fe77..c627abe 100644 --- a/src/audacious/probe.c +++ b/src/audacious/probe.c @@ -1,6 +1,6 @@ /* * probe.c - * Copyright 2009-2010 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -101,7 +101,7 @@ static bool_t probe_func_fast (PluginHandle * plugin, ProbeState * state) PluginHandle * prev = state->plugin; state->plugin = NULL; - if (prev != NULL && ! probe_func (prev, state)) + if (! probe_func (prev, state)) return FALSE; } @@ -113,15 +113,11 @@ static bool_t probe_func_fast (PluginHandle * plugin, ProbeState * state) static void probe_by_scheme (ProbeState * state) { const char * s = strstr (state->filename, "://"); - if (s == NULL) return; AUDDBG ("Probing by scheme.\n"); - char buf[s - state->filename + 1]; - memcpy (buf, state->filename, s - state->filename); - buf[s - state->filename] = 0; - + SNCOPY (buf, state->filename, s - state->filename); input_plugin_for_key (INPUT_KEY_SCHEME, buf, (PluginForEachFunc) probe_func_fast, state); } @@ -148,7 +144,7 @@ static void probe_by_mime (ProbeState * state) AUDDBG ("Probing by MIME type.\n"); input_plugin_for_key (INPUT_KEY_MIME, mime, (PluginForEachFunc) probe_func_fast, state); - g_free (mime); + str_unref (mime); } static void probe_by_content (ProbeState * state) @@ -196,13 +192,27 @@ DONE: return state.plugin; } +static bool_t open_file (const char * filename, InputPlugin * ip, + const char * mode, VFSFile * * handle) +{ + /* no need to open a handle for custom URI schemes */ + if (ip->schemes && ip->schemes[0]) + return TRUE; + + * handle = vfs_fopen (filename, mode); + return (* handle != NULL); +} + Tuple * file_read_tuple (const char * filename, PluginHandle * decoder) { InputPlugin * ip = plugin_get_header (decoder); g_return_val_if_fail (ip, NULL); g_return_val_if_fail (ip->probe_for_tuple, NULL); - VFSFile * handle = vfs_fopen (filename, "r"); + VFSFile * handle = NULL; + if (! open_file (filename, ip, "r", & handle)) + return FALSE; + Tuple * tuple = ip->probe_for_tuple (filename, handle); if (handle) @@ -214,6 +224,9 @@ Tuple * file_read_tuple (const char * filename, PluginHandle * decoder) bool_t file_read_image (const char * filename, PluginHandle * decoder, void * * data, int64_t * size) { + * data = NULL; + * size = 0; + if (! input_plugin_has_images (decoder)) return FALSE; @@ -221,18 +234,15 @@ bool_t file_read_image (const char * filename, PluginHandle * decoder, g_return_val_if_fail (ip, FALSE); g_return_val_if_fail (ip->get_song_image, FALSE); - VFSFile * handle = vfs_fopen (filename, "r"); + VFSFile * handle = NULL; + if (! open_file (filename, ip, "r", & handle)) + return FALSE; + bool_t success = ip->get_song_image (filename, handle, data, size); if (handle) vfs_fclose (handle); - if (! success) - { - * data = NULL; - * size = 0; - } - return success; } @@ -248,12 +258,11 @@ bool_t file_write_tuple (const char * filename, PluginHandle * decoder, g_return_val_if_fail (ip, FALSE); g_return_val_if_fail (ip->update_song_tuple, FALSE); - VFSFile * handle = vfs_fopen (filename, "r+"); - - if (! handle) + VFSFile * handle = NULL; + if (! open_file (filename, ip, "r+", & handle)) return FALSE; - bool_t success = ip->update_song_tuple (tuple, handle); + bool_t success = ip->update_song_tuple (filename, handle, tuple); if (handle) vfs_fclose (handle); diff --git a/src/audacious/scanner.c b/src/audacious/scanner.c index 2ba3718..e3257da 100644 --- a/src/audacious/scanner.c +++ b/src/audacious/scanner.c @@ -36,7 +36,7 @@ struct _ScanRequest { Tuple * tuple; void * image_data; int64_t image_len; - char * image_file; + char * image_file; /* pooled */ }; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; @@ -73,8 +73,8 @@ static void scan_request_free (ScanRequest * request) tuple_unref (request->tuple); str_unref (request->filename); - free (request->image_data); - free (request->image_file); + g_free (request->image_data); + str_unref (request->image_file); g_slice_free (ScanRequest, request); } @@ -144,11 +144,9 @@ void scan_request_get_image_data (ScanRequest * request, void * * data, int64_t request->image_len = 0; } -char * scan_request_get_image_file (ScanRequest * request) +const char * scan_request_get_image_file (ScanRequest * request) { - char * image_file = request->image_file; - request->image_file = NULL; - return image_file; + return request->image_file; } void scanner_init (void) diff --git a/src/audacious/scanner.h b/src/audacious/scanner.h index 488c8d9..3eee728 100644 --- a/src/audacious/scanner.h +++ b/src/audacious/scanner.h @@ -40,7 +40,7 @@ const char * scan_request_get_filename (ScanRequest * request); PluginHandle * scan_request_get_decoder (ScanRequest * request); Tuple * scan_request_get_tuple (ScanRequest * request); void scan_request_get_image_data (ScanRequest * request, void * * data, int64_t * len); -char * scan_request_get_image_file (ScanRequest * request); +const char * scan_request_get_image_file (ScanRequest * request); void scanner_init (void); void scanner_cleanup (void); diff --git a/src/audacious/signals.c b/src/audacious/signals.c index 89fc8af..2b5515e 100644 --- a/src/audacious/signals.c +++ b/src/audacious/signals.c @@ -17,14 +17,14 @@ * the use of this software. */ +#ifdef HAVE_SIGWAIT + #include #include -#include - +#include "drct.h" #include "main.h" -#ifdef HAVE_SIGWAIT static sigset_t signal_set; static void * signal_thread (void * data) @@ -32,16 +32,14 @@ static void * signal_thread (void * data) int signal; while (! sigwait (& signal_set, & signal)) - event_queue ("quit", NULL); + drct_quit (); return NULL; } -#endif /* Must be called before any threads are created. */ -void signals_init (void) +void signals_init_one (void) { -#ifdef HAVE_SIGWAIT sigemptyset (& signal_set); sigaddset (& signal_set, SIGHUP); sigaddset (& signal_set, SIGINT); @@ -49,8 +47,12 @@ void signals_init (void) sigaddset (& signal_set, SIGTERM); sigprocmask (SIG_BLOCK, & signal_set, NULL); +} +void signals_init_two (void) +{ pthread_t thread; pthread_create (& thread, NULL, signal_thread, NULL); -#endif } + +#endif /* HAVE_SIGWAIT */ diff --git a/src/audacious/types.h b/src/audacious/types.h index 03cad9a..d160bab 100644 --- a/src/audacious/types.h +++ b/src/audacious/types.h @@ -1,6 +1,6 @@ /* * types.h - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -51,6 +51,12 @@ typedef const struct _IfacePlugin IfacePlugin; typedef struct _PluginPreferences PluginPreferences; typedef struct _PreferencesWidget PreferencesWidget; +typedef struct { + char * name; + float preamp; + float bands[AUD_EQUALIZER_NBANDS]; +} EqualizerPreset; + typedef struct { float track_gain; /* dB */ float track_peak; /* 0-1 */ diff --git a/src/audacious/ui_albumart.c b/src/audacious/ui_albumart.c index d8e4a36..a3f14a4 100644 --- a/src/audacious/ui_albumart.c +++ b/src/audacious/ui_albumart.c @@ -1,6 +1,6 @@ /* * ui_albumart.c - * Copyright 2006 Michael Hanselmann and Yoshiki Yazawa + * Copyright 2006-2013 Michael Hanselmann, Yoshiki Yazawa, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -23,187 +23,166 @@ #include #include "i18n.h" +#include "main.h" #include "misc.h" +#include "util.h" -static bool_t -has_front_cover_extension(const char *name) -{ - char *ext; +typedef struct { + const char * basename; + Index * include, * exclude; +} SearchParams; - ext = strrchr(name, '.'); - if (!ext) { - /* No file extension */ +static bool_t has_front_cover_extension (const char * name) +{ + char * ext = strrchr (name, '.'); + if (! ext) return FALSE; - } - return g_ascii_strcasecmp(ext, ".jpg") == 0 || - g_ascii_strcasecmp(ext, ".jpeg") == 0 || - g_ascii_strcasecmp(ext, ".png") == 0; + return ! g_ascii_strcasecmp (ext, ".jpg") || + ! g_ascii_strcasecmp (ext, ".jpeg") || ! g_ascii_strcasecmp (ext, ".png"); } -static bool_t -cover_name_filter(const char *name, const char *filter, const bool_t ret_on_empty) +static bool_t cover_name_filter (const char * name, Index * keywords, bool_t ret_on_empty) { - bool_t result = FALSE; - char **splitted; - char *current; - char *lname; - int i; - - if (!filter || strlen(filter) == 0) { + int count = index_count (keywords); + if (! count) return ret_on_empty; - } - - splitted = g_strsplit(filter, ",", 0); - lname = g_ascii_strdown (name, -1); - for (i = 0; ! result && (current = splitted[i]); i ++) + for (int i = 0; i < count; i ++) { - char * stripped = g_strstrip (g_ascii_strdown (current, -1)); - result = result || strstr(lname, stripped); - g_free(stripped); + if (strstr_nocase (name, index_get (keywords, i))) + return TRUE; } - g_free(lname); - g_strfreev(splitted); - - return result; -} - -/* Check wether it's an image we want */ -static bool_t is_front_cover_image (const char * file) -{ - char * include = get_string (NULL, "cover_name_include"); - char * exclude = get_string (NULL, "cover_name_exclude"); - bool_t accept = cover_name_filter (file, include, TRUE) && - ! cover_name_filter (file, exclude, FALSE); - g_free (include); - g_free (exclude); - return accept; + return FALSE; } -static bool_t -is_file_image(const char *imgfile, const char *file_name) +static bool_t is_file_image (const char * imgfile, const char * file_name) { - char *imgfile_ext, *file_name_ext; - size_t imgfile_len, file_name_len; - - imgfile_ext = strrchr(imgfile, '.'); - if (!imgfile_ext) { - /* No file extension */ + char * imgfile_ext = strrchr (imgfile, '.'); + if (! imgfile_ext) return FALSE; - } - file_name_ext = strrchr(file_name, '.'); - if (!file_name_ext) { - /* No file extension */ + char * file_name_ext = strrchr (file_name, '.'); + if (! file_name_ext) return FALSE; - } - imgfile_len = (imgfile_ext - imgfile); - file_name_len = (file_name_ext - file_name); + size_t imgfile_len = imgfile_ext - imgfile; + size_t file_name_len = file_name_ext - file_name; - if (imgfile_len == file_name_len) { - return (g_ascii_strncasecmp(imgfile, file_name, imgfile_len) == 0); - } else { - return FALSE; - } + return imgfile_len == file_name_len && ! g_ascii_strncasecmp (imgfile, file_name, imgfile_len); } -static char * fileinfo_recursive_get_image (const char * path, const char * - file_name, int depth) +static char * fileinfo_recursive_get_image (const char * path, + const SearchParams * params, int depth) { - GDir *d; - - if (get_bool (NULL, "recurse_for_cover") && depth > get_int (NULL, "recurse_for_cover_depth")) + GDir * d = g_dir_open (path, 0, NULL); + if (! d) return NULL; - d = g_dir_open(path, 0, NULL); - - if (d) { - const char *f; + const char * name; - if (get_bool (NULL, "use_file_cover") && file_name) + if (get_bool (NULL, "use_file_cover") && ! depth) + { + /* Look for images matching file name */ + while ((name = g_dir_read_name (d))) { - /* Look for images matching file name */ - while((f = g_dir_read_name(d))) { - char *newpath = g_strconcat(path, "/", f, NULL); - - if (!g_file_test(newpath, G_FILE_TEST_IS_DIR) && - has_front_cover_extension(f) && - is_file_image(f, file_name)) { - g_dir_close(d); - return newpath; - } - - g_free(newpath); - } - g_dir_rewind(d); - } - - /* Search for files using filter */ - while ((f = g_dir_read_name(d))) { - char *newpath = g_strconcat(path, "/", f, NULL); + char * newpath = filename_build (path, name); - if (!g_file_test(newpath, G_FILE_TEST_IS_DIR) && - has_front_cover_extension(f) && - is_front_cover_image(f)) { - g_dir_close(d); + if (! g_file_test (newpath, G_FILE_TEST_IS_DIR) && + has_front_cover_extension (name) && + is_file_image (name, params->basename)) + { + g_dir_close (d); return newpath; } - g_free(newpath); + str_unref (newpath); } - g_dir_rewind(d); - /* checks whether recursive or not. */ - if (! get_bool (NULL, "recurse_for_cover")) + g_dir_rewind (d); + } + + /* Search for files using filter */ + while ((name = g_dir_read_name (d))) + { + char * newpath = filename_build (path, name); + + if (! g_file_test (newpath, G_FILE_TEST_IS_DIR) && + has_front_cover_extension (name) && + cover_name_filter (name, params->include, TRUE) && + ! cover_name_filter (name, params->exclude, FALSE)) { - g_dir_close(d); - return NULL; + g_dir_close (d); + return newpath; } + str_unref (newpath); + } + + g_dir_rewind (d); + + if (get_bool (NULL, "recurse_for_cover") && depth < get_int (NULL, "recurse_for_cover_depth")) + { /* Descend into directories recursively. */ - while ((f = g_dir_read_name(d))) { - char *newpath = g_strconcat(path, "/", f, NULL); - - if(g_file_test(newpath, G_FILE_TEST_IS_DIR)) { - char *tmp = fileinfo_recursive_get_image(newpath, - NULL, depth + 1); - if(tmp) { - g_free(newpath); - g_dir_close(d); + while ((name = g_dir_read_name (d))) + { + char * newpath = filename_build (path, name); + + if (g_file_test (newpath, G_FILE_TEST_IS_DIR)) + { + char * tmp = fileinfo_recursive_get_image (newpath, params, depth + 1); + + if (tmp) + { + str_unref (newpath); + g_dir_close (d); return tmp; } } - g_free(newpath); + str_unref (newpath); } - - g_dir_close(d); } + g_dir_close (d); return NULL; } char * get_associated_image_file (const char * filename) { - if (strncmp (filename, "file://", 7)) - return NULL; + char * image_uri = NULL; - char * unesc = uri_to_filename (filename); - if (! unesc) - return NULL; + char * local = uri_to_filename (filename); + char * base = local ? last_path_element (local) : NULL; + + if (local && base) + { + char * include = get_str (NULL, "cover_name_include"); + char * exclude = get_str (NULL, "cover_name_exclude"); + + SearchParams params = { + .basename = base, + .include = str_list_to_index (include, ", "), + .exclude = str_list_to_index (exclude, ", ") + }; - char * path = g_path_get_dirname (unesc); - char * base = g_path_get_basename (unesc); - char * image_unesc = fileinfo_recursive_get_image (path, base, 0); - char * image_file = image_unesc ? filename_to_uri (image_unesc) : NULL; + str_unref (include); + str_unref (exclude); + + SNCOPY (path, local, base - 1 - local); + + char * image_local = fileinfo_recursive_get_image (path, & params, 0); + if (image_local) + image_uri = filename_to_uri (image_local); + + str_unref (image_local); + + index_free_full (params.include, (IndexFreeFunc) str_unref); + index_free_full (params.exclude, (IndexFreeFunc) str_unref); + } - g_free (unesc); - g_free (path); - g_free (base); - g_free (image_unesc); + str_unref (local); - return image_file; + return image_uri; } diff --git a/src/audacious/ui_plugin_menu.c b/src/audacious/ui_plugin_menu.c index 66475ab..9d81b65 100644 --- a/src/audacious/ui_plugin_menu.c +++ b/src/audacious/ui_plugin_menu.c @@ -20,27 +20,28 @@ #include #include -#include "misc.h" +#include -struct Item { - MenuFunc func; - const char * name; - const char * icon; -}; +#include "i18n.h" +#include "misc.h" -static GList * items[AUD_MENU_COUNT]; +static GList * items[AUD_MENU_COUNT]; /* of AudguiMenuItem */ static GtkWidget * menus[AUD_MENU_COUNT]; -static void add_to_menu (GtkWidget * menu, struct Item * item) +static void configure_plugins (void) { - GtkWidget * widget = gtk_image_menu_item_new_with_mnemonic (item->name); - g_object_set_data ((GObject *) widget, "func", (void *) item->func); - g_signal_connect (widget, "activate", item->func, NULL); + show_prefs_for_plugin_type (PLUGIN_TYPE_GENERAL); +} - if (item->icon) - gtk_image_menu_item_set_image ((GtkImageMenuItem *) widget, - gtk_image_new_from_stock (item->icon, GTK_ICON_SIZE_MENU)); +static const AudguiMenuItem main_items[] = { + {N_("_Plugins ..."), .func = configure_plugins}, + {.sep = TRUE} +}; +static void add_to_menu (GtkWidget * menu, const AudguiMenuItem * item) +{ + GtkWidget * widget = audgui_menu_item_new_with_domain (item, NULL, NULL); + g_object_set_data ((GObject *) widget, "func", (void *) item->func); gtk_widget_show (widget); gtk_menu_shell_append ((GtkMenuShell *) menu, widget); } @@ -54,6 +55,9 @@ void * get_plugin_menu (int id) g_signal_connect (menus[id], "destroy", (GCallback) gtk_widget_destroyed, & menus[id]); + if (id == AUD_MENU_MAIN) + audgui_menu_init (menus[id], main_items, ARRAY_LEN (main_items), NULL); + for (GList * node = items[id]; node; node = node->next) add_to_menu (menus[id], node->data); } @@ -64,7 +68,7 @@ void * get_plugin_menu (int id) void plugin_menu_add (int id, MenuFunc func, const char * name, const char * icon) { - struct Item * item = g_slice_new (struct Item); + AudguiMenuItem * item = g_slice_new0 (AudguiMenuItem); item->name = name; item->icon = icon; item->func = func; @@ -92,9 +96,9 @@ void plugin_menu_remove (int id, MenuFunc func) { next = node->next; - if (((struct Item *) node->data)->func == func) + if (((AudguiMenuItem *) node->data)->func == func) { - g_slice_free (struct Item, node->data); + g_slice_free (AudguiMenuItem, node->data); items[id] = g_list_delete_link (items[id], node); } } diff --git a/src/audacious/ui_preferences.c b/src/audacious/ui_preferences.c index 96dab5b..2949fdc 100644 --- a/src/audacious/ui_preferences.c +++ b/src/audacious/ui_preferences.c @@ -1,6 +1,6 @@ /* * ui_preferences.c - * Copyright 2006-2011 William Pitcock, Tomasz Moń, Michael Färber, and + * Copyright 2006-2012 William Pitcock, Tomasz Moń, Michael Färber, and * John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -23,7 +23,9 @@ #include #include +#include #include +#include #include "debug.h" #include "i18n.h" @@ -42,36 +44,57 @@ enum CategoryViewCols { CATEGORY_VIEW_COL_ICON, CATEGORY_VIEW_COL_NAME, - CATEGORY_VIEW_COL_ID, CATEGORY_VIEW_N_COLS }; typedef struct { - const char *icon_path; - const char *name; + const char * icon_path; + const char * name; } Category; typedef struct { - const char *name; - const char *tag; + int type; + const char * name; +} PluginCategory; + +typedef struct { + const char * name; + const char * tag; } TitleFieldTag; -static /* GtkWidget * */ void * prefswin = NULL; -static GtkWidget *category_treeview = NULL; -static GtkWidget *category_notebook = NULL; +static const char aud_version_string[] = + "Audacious " VERSION " (" BUILDSTAMP ")"; -/* prefswin widgets */ -GtkWidget *titlestring_entry; +static GtkWidget * prefswin; +static GtkWidget * category_treeview, * category_notebook, * plugin_notebook; +static GtkWidget * titlestring_entry; + +enum { + CATEGORY_APPEARANCE = 0, + CATEGORY_AUDIO, + CATEGORY_NETWORK, + CATEGORY_PLAYLIST, + CATEGORY_SONG_INFO, + CATEGORY_PLUGINS +}; -static Category categories[] = { - {"audio.png", N_("Audio")}, - {"connectivity.png", N_("Network")}, - {"playlist.png", N_("Playlist")}, - {"info.png", N_("Song Info")}, - {"plugins.png", N_("Plugins")}, +static const Category categories[] = { + { "appearance.png", N_("Appearance") }, + { "audio.png", N_("Audio") }, + { "connectivity.png", N_("Network") }, + { "playlist.png", N_("Playlist")} , + { "info.png", N_("Song Info") }, + { "plugins.png", N_("Plugins") } }; -static int n_categories = G_N_ELEMENTS(categories); +static const PluginCategory plugin_categories[] = { + { PLUGIN_TYPE_GENERAL, N_("General") }, + { PLUGIN_TYPE_EFFECT, N_("Effect") }, + { PLUGIN_TYPE_VIS, N_("Visualization") }, + { PLUGIN_TYPE_INPUT, N_("Input") }, + { PLUGIN_TYPE_PLAYLIST, N_("Playlist") }, + { PLUGIN_TYPE_TRANSPORT, N_("Transport") } +}; static TitleFieldTag title_field_tags[] = { { N_("Artist") , "${artist}" }, @@ -85,48 +108,76 @@ static TitleFieldTag title_field_tags[] = { { N_("Year") , "${year}" }, { N_("Comment") , "${comment}" }, { N_("Codec") , "${codec}" }, - { N_("Quality") , "${quality}" }, + { N_("Quality") , "${quality}" } }; -static const unsigned int n_title_field_tags = G_N_ELEMENTS(title_field_tags); #ifdef USE_CHARDET static ComboBoxElements chardet_detector_presets[] = { - {"", N_("None")}, - {GUESS_REGION_AR, N_("Arabic")}, - {GUESS_REGION_BL, N_("Baltic")}, - {GUESS_REGION_CN, N_("Chinese")}, - {GUESS_REGION_GR, N_("Greek")}, - {GUESS_REGION_HW, N_("Hebrew")}, - {GUESS_REGION_JP, N_("Japanese")}, - {GUESS_REGION_KR, N_("Korean")}, - {GUESS_REGION_PL, N_("Polish")}, - {GUESS_REGION_RU, N_("Russian")}, - {GUESS_REGION_TW, N_("Taiwanese")}, - {GUESS_REGION_TR, N_("Turkish")}}; + { "", N_("None")}, + { GUESS_REGION_AR, N_("Arabic") }, + { GUESS_REGION_BL, N_("Baltic") }, + { GUESS_REGION_CN, N_("Chinese") }, + { GUESS_REGION_GR, N_("Greek") }, + { GUESS_REGION_HW, N_("Hebrew") }, + { GUESS_REGION_JP, N_("Japanese") }, + { GUESS_REGION_KR, N_("Korean") }, + { GUESS_REGION_PL, N_("Polish") }, + { GUESS_REGION_RU, N_("Russian") }, + { GUESS_REGION_TW, N_("Taiwanese") }, + { GUESS_REGION_TR, N_("Turkish") } +}; #endif static ComboBoxElements bitdepth_elements[] = { - { GINT_TO_POINTER(16), "16" }, - { GINT_TO_POINTER(24), "24" }, - { GINT_TO_POINTER(32), "32" }, - {GINT_TO_POINTER (0), N_("Floating point")}, + { GINT_TO_POINTER (16), "16" }, + { GINT_TO_POINTER (24), "24" }, + { GINT_TO_POINTER (32), "32" }, + { GINT_TO_POINTER (0), N_("Floating point") } }; -static void * create_output_plugin_box (void); +static GArray * iface_combo_elements; +static int iface_combo_selected; +static GtkWidget * iface_prefs_box; + +static const ComboBoxElements * iface_combo_fill (int * n_elements); +static void iface_combo_changed (void); +static void * iface_create_prefs_box (void); + +static PreferencesWidget appearance_page_widgets[] = { + {WIDGET_LABEL, N_("Interface Settings")}, + {WIDGET_COMBO_BOX, N_("Interface plugin:"), + .cfg_type = VALUE_INT, .cfg = & iface_combo_selected, + .data.combo.fill = iface_combo_fill, .callback = iface_combo_changed}, + {WIDGET_CUSTOM, .data.populate = iface_create_prefs_box}}; + +static GArray * output_combo_elements; +static int output_combo_selected; +static GtkWidget * output_config_button; +static GtkWidget * output_about_button; + +static const ComboBoxElements * output_combo_fill (int * n_elements); +static void output_combo_changed (void); +static void * output_create_config_button (void); +static void * output_create_about_button (void); static void output_bit_depth_changed (void); -static PreferencesWidget rg_mode_widgets[] = { - {WIDGET_CHK_BTN, N_("Album mode"), .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"}}; +static PreferencesWidget output_combo_widgets[] = { + {WIDGET_COMBO_BOX, N_("Output plugin:"), + .cfg_type = VALUE_INT, .cfg = & output_combo_selected, + .data.combo.fill = output_combo_fill, .callback = output_combo_changed}, + {WIDGET_CUSTOM, .data.populate = output_create_config_button}, + {WIDGET_CUSTOM, .data.populate = output_create_about_button}}; static PreferencesWidget audio_page_widgets[] = { {WIDGET_LABEL, N_("Output Settings")}, - {WIDGET_CUSTOM, .data = {.populate = create_output_plugin_box}}, + {WIDGET_BOX, .data.box = {.elem = output_combo_widgets, + .n_elem = ARRAY_LEN (output_combo_widgets), .horizontal = TRUE}}, {WIDGET_COMBO_BOX, N_("Bit depth:"), .cfg_type = VALUE_INT, .cname = "output_bit_depth", .callback = output_bit_depth_changed, - .data = {.combo = {bitdepth_elements, G_N_ELEMENTS (bitdepth_elements)}}}, + .data.combo = {bitdepth_elements, ARRAY_LEN (bitdepth_elements)}}, {WIDGET_SPIN_BTN, N_("Buffer size:"), .cfg_type = VALUE_INT, .cname = "output_buffer_size", - .data = {.spin_btn = {100, 10000, 1000, N_("ms")}}}, + .data.spin_btn = {100, 10000, 1000, N_("ms")}}, {WIDGET_CHK_BTN, N_("Soft clipping"), .cfg_type = VALUE_BOOLEAN, .cname = "soft_clipping"}, {WIDGET_CHK_BTN, N_("Use software volume control (not recommended)"), @@ -134,16 +185,17 @@ static PreferencesWidget audio_page_widgets[] = { {WIDGET_LABEL, N_("Replay Gain")}, {WIDGET_CHK_BTN, N_("Enable Replay Gain"), .cfg_type = VALUE_BOOLEAN, .cname = "enable_replay_gain"}, - {WIDGET_BOX, .child = TRUE, .data = {.box = {rg_mode_widgets, G_N_ELEMENTS (rg_mode_widgets), TRUE}}}, + {WIDGET_CHK_BTN, N_("Album mode"), .child = TRUE, + .cfg_type = VALUE_BOOLEAN, .cname = "replay_gain_album"}, {WIDGET_CHK_BTN, N_("Prevent clipping (recommended)"), .child = TRUE, .cfg_type = VALUE_BOOLEAN, .cname = "enable_clipping_prevention"}, {WIDGET_LABEL, N_("Adjust Levels"), .child = TRUE}, {WIDGET_SPIN_BTN, N_("Amplify all files:"), .child = TRUE, .cfg_type = VALUE_FLOAT, .cname = "replay_gain_preamp", - .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}}, + .data.spin_btn = {-15, 15, 0.1, N_("dB")}}, {WIDGET_SPIN_BTN, N_("Amplify untagged files:"), .child = TRUE, .cfg_type = VALUE_FLOAT, .cname = "default_gain", - .data = {.spin_btn = {-15, 15, 0.1, N_("dB")}}}}; + .data.spin_btn = {-15, 15, 0.1, N_("dB")}}}; static PreferencesWidget proxy_host_port_elements[] = { {WIDGET_ENTRY, N_("Proxy hostname:"), .cfg_type = VALUE_STRING, .cname = "proxy_host"}, @@ -152,47 +204,44 @@ static PreferencesWidget proxy_host_port_elements[] = { static PreferencesWidget proxy_auth_elements[] = { {WIDGET_ENTRY, N_("Proxy username:"), .cfg_type = VALUE_STRING, .cname = "proxy_user"}, {WIDGET_ENTRY, N_("Proxy password:"), .cfg_type = VALUE_STRING, .cname = "proxy_pass", - .data = {.entry = {.password = TRUE}}}}; + .data.entry.password = TRUE}}; static PreferencesWidget connectivity_page_widgets[] = { - {WIDGET_LABEL, N_("Proxy Configuration"), NULL, NULL, NULL, FALSE}, - {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"}, - {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_host_port_elements, - G_N_ELEMENTS (proxy_host_port_elements)}}}, - {WIDGET_CHK_BTN, N_("Use authentication with proxy"), - .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"}, - {WIDGET_TABLE, .child = TRUE, .data = {.table = {proxy_auth_elements, - G_N_ELEMENTS (proxy_auth_elements)}}} -}; + {WIDGET_LABEL, N_("Proxy Configuration"), NULL, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Enable proxy usage"), .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy"}, + {WIDGET_TABLE, .child = TRUE, .data.table = {proxy_host_port_elements, + ARRAY_LEN (proxy_host_port_elements)}}, + {WIDGET_CHK_BTN, N_("Use authentication with proxy"), + .cfg_type = VALUE_BOOLEAN, .cname = "use_proxy_auth"}, + {WIDGET_TABLE, .child = TRUE, .data.table = {proxy_auth_elements, + ARRAY_LEN (proxy_auth_elements)}}}; static PreferencesWidget chardet_elements[] = { #ifdef USE_CHARDET {WIDGET_COMBO_BOX, N_("Auto character encoding detector for:"), .cfg_type = VALUE_STRING, .cname = "chardet_detector", .child = TRUE, - .data = {.combo = {chardet_detector_presets, G_N_ELEMENTS (chardet_detector_presets)}}}, + .data.combo = {chardet_detector_presets, ARRAY_LEN (chardet_detector_presets)}}, #endif {WIDGET_ENTRY, N_("Fallback character encodings:"), .cfg_type = VALUE_STRING, .cname = "chardet_fallback", .child = TRUE}}; static PreferencesWidget playlist_page_widgets[] = { - {WIDGET_LABEL, N_("Behavior"), NULL, NULL, NULL, FALSE}, - {WIDGET_CHK_BTN, N_("Continue playback on startup"), - .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"}, - {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"), - .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"}, - {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"), - .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"}, - {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"), - .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"}, - {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"), - .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play", - .callback = playlist_trigger_scan}, - {WIDGET_LABEL, N_("Compatibility"), NULL, NULL, NULL, FALSE}, - {WIDGET_CHK_BTN, N_("Interpret \\ (backward slash) as a folder delimiter"), - .cfg_type = VALUE_BOOLEAN, .cname = "convert_backslash"}, - {WIDGET_TABLE, .data = {.table = {chardet_elements, - G_N_ELEMENTS (chardet_elements)}}} -}; + {WIDGET_LABEL, N_("Behavior"), NULL, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Continue playback on startup"), + .cfg_type = VALUE_BOOLEAN, .cname = "resume_playback_on_startup"}, + {WIDGET_CHK_BTN, N_("Advance when the current song is deleted"), + .cfg_type = VALUE_BOOLEAN, .cname = "advance_on_delete"}, + {WIDGET_CHK_BTN, N_("Clear the playlist when opening files"), + .cfg_type = VALUE_BOOLEAN, .cname = "clear_playlist"}, + {WIDGET_CHK_BTN, N_("Open files in a temporary playlist"), + .cfg_type = VALUE_BOOLEAN, .cname = "open_to_temporary"}, + {WIDGET_CHK_BTN, N_("Do not load metadata for songs until played"), + .cfg_type = VALUE_BOOLEAN, .cname = "metadata_on_play", + .callback = playlist_trigger_scan}, + {WIDGET_LABEL, N_("Compatibility"), NULL, NULL, NULL, FALSE}, + {WIDGET_CHK_BTN, N_("Interpret \\ (backward slash) as a folder delimiter"), + .cfg_type = VALUE_BOOLEAN, .cname = "convert_backslash"}, + {WIDGET_TABLE, .data.table = {chardet_elements, ARRAY_LEN (chardet_elements)}}}; static PreferencesWidget song_info_page_widgets[] = { {WIDGET_LABEL, N_("Album Art")}, @@ -206,72 +255,89 @@ static PreferencesWidget song_info_page_widgets[] = { .cfg_type = VALUE_BOOLEAN, .cname = "recurse_for_cover"}, {WIDGET_SPIN_BTN, N_("Search depth:"), .child = TRUE, .cfg_type = VALUE_INT, .cname = "recurse_for_cover_depth", - .data = {.spin_btn = {0, 100, 1}}}, + .data.spin_btn = {0, 100, 1}}, {WIDGET_LABEL, N_("Popup Information")}, {WIDGET_CHK_BTN, N_("Show popup information"), .cfg_type = VALUE_BOOLEAN, .cname = "show_filepopup_for_tuple"}, {WIDGET_SPIN_BTN, N_("Popup delay (tenths of a second):"), .child = TRUE, .cfg_type = VALUE_INT, .cname = "filepopup_delay", - .data = {.spin_btn = {0, 100, 1}}}, + .data.spin_btn = {0, 100, 1}}, {WIDGET_CHK_BTN, N_("Show time scale for current song"), .child = TRUE, .cfg_type = VALUE_BOOLEAN, .cname = "filepopup_showprogressbar"}}; #define TITLESTRING_NPRESETS 6 static const char * const titlestring_presets[TITLESTRING_NPRESETS] = { - "${title}", - "${?artist:${artist} - }${title}", - "${?artist:${artist} - }${?album:${album} - }${title}", - "${?artist:${artist} - }${?album:${album} - }${?track-number:${track-number}. }${title}", - "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }${?track-number:${track-number}. }${title}", - "${?album:${album} - }${title}"}; + "${title}", + "${?artist:${artist} - }${title}", + "${?artist:${artist} - }${?album:${album} - }${title}", + "${?artist:${artist} - }${?album:${album} - }${?track-number:${track-number}. }${title}", + "${?artist:${artist} }${?album:[ ${album} ] }${?artist:- }${?track-number:${track-number}. }${title}", + "${?album:${album} - }${title}" +}; static const char * const titlestring_preset_names[TITLESTRING_NPRESETS] = { - N_("TITLE"), - N_("ARTIST - TITLE"), - N_("ARTIST - ALBUM - TITLE"), - N_("ARTIST - ALBUM - TRACK. TITLE"), - N_("ARTIST [ ALBUM ] - TRACK. TITLE"), - N_("ALBUM - TITLE")}; - -static void -change_category(GtkNotebook * notebook, - GtkTreeSelection * selection) + N_("TITLE"), + N_("ARTIST - TITLE"), + N_("ARTIST - ALBUM - TITLE"), + N_("ARTIST - ALBUM - TRACK. TITLE"), + N_("ARTIST [ ALBUM ] - TRACK. TITLE"), + N_("ALBUM - TITLE") +}; + +static GArray * fill_plugin_combo (int type) { - GtkTreeModel *model; - GtkTreeIter iter; - int index; + GArray * array = g_array_new (FALSE, FALSE, sizeof (ComboBoxElements)); + g_array_set_size (array, plugin_count (type)); - if (!gtk_tree_selection_get_selected(selection, &model, &iter)) - return; + for (int i = 0; i < array->len; i ++) + { + ComboBoxElements * elem = & g_array_index (array, ComboBoxElements, i); + elem->label = plugin_get_name (plugin_by_index (type, i)); + elem->value = GINT_TO_POINTER (i); + } - gtk_tree_model_get(model, &iter, CATEGORY_VIEW_COL_ID, &index, -1); - gtk_notebook_set_current_page(notebook, index); + return array; } -static void -titlestring_tag_menu_callback(GtkMenuItem * menuitem, - void * data) +static void change_category (int category) { - const char *separator = " - "; - int item = GPOINTER_TO_INT(data); - int pos; + GtkTreeSelection * selection = gtk_tree_view_get_selection ((GtkTreeView *) category_treeview); + GtkTreePath * path = gtk_tree_path_new_from_indices (category, -1); + gtk_tree_selection_select_path (selection, path); + gtk_tree_path_free (path); +} - pos = gtk_editable_get_position(GTK_EDITABLE(titlestring_entry)); +static void category_changed (GtkTreeSelection * selection) +{ + GtkTreeModel * model; + GtkTreeIter iter; + + if (gtk_tree_selection_get_selected (selection, & model, & iter)) + { + GtkTreePath * path = gtk_tree_model_get_path (model, & iter); + int category = gtk_tree_path_get_indices (path)[0]; + gtk_notebook_set_current_page ((GtkNotebook *) category_notebook, category); + gtk_tree_path_free (path); + } +} + +static void titlestring_tag_menu_cb (GtkMenuItem * menuitem, void * data) +{ + const char * separator = " - "; + int item = GPOINTER_TO_INT (data); + int pos = gtk_editable_get_position ((GtkEditable *) titlestring_entry); /* insert separator as needed */ - if (gtk_entry_get_text(GTK_ENTRY(titlestring_entry))[0]) - gtk_editable_insert_text(GTK_EDITABLE(titlestring_entry), separator, -1, &pos); + if (gtk_entry_get_text ((GtkEntry *) titlestring_entry)[0]) + gtk_editable_insert_text ((GtkEditable *) titlestring_entry, separator, -1, & pos); - gtk_editable_insert_text(GTK_EDITABLE(titlestring_entry), _(title_field_tags[item].tag), -1, &pos); - gtk_editable_set_position(GTK_EDITABLE(titlestring_entry), pos); + gtk_editable_insert_text ((GtkEditable *) titlestring_entry, _(title_field_tags[item].tag), -1, & pos); + gtk_editable_set_position ((GtkEditable *) titlestring_entry, pos); } -static void -on_titlestring_help_button_clicked(GtkButton * button, - void * data) +static void on_titlestring_help_button_clicked (GtkButton * button, void * menu) { - GtkMenu * menu = data; gtk_menu_popup (menu, NULL, NULL, NULL, NULL, 0, GDK_CURRENT_TIME); } @@ -291,7 +357,7 @@ static void update_titlestring_cbox (GtkComboBox * cbox, const char * format) static void on_titlestring_entry_changed (GtkEntry * entry, GtkComboBox * cbox) { const char * format = gtk_entry_get_text (entry); - set_string (NULL, "generic_title_format", format); + set_str (NULL, "generic_title_format", format); update_titlestring_cbox (cbox, format); playlist_reformat_titles (); } @@ -303,638 +369,68 @@ static void on_titlestring_cbox_changed (GtkComboBox * cbox, GtkEntry * entry) gtk_entry_set_text (entry, titlestring_presets[preset]); } -static void widget_set_bool (const PreferencesWidget * widget, bool_t value) -{ - g_return_if_fail (widget->cfg_type == VALUE_BOOLEAN); - - if (widget->cfg) - * (bool_t *) widget->cfg = value; - else if (widget->cname) - set_bool (widget->csect, widget->cname, value); - - if (widget->callback) - widget->callback (); -} - -static bool_t widget_get_bool (const PreferencesWidget * widget) -{ - g_return_val_if_fail (widget->cfg_type == VALUE_BOOLEAN, FALSE); - - if (widget->cfg) - return * (bool_t *) widget->cfg; - else if (widget->cname) - return get_bool (widget->csect, widget->cname); - else - return FALSE; -} - -static void widget_set_int (const PreferencesWidget * widget, int value) -{ - g_return_if_fail (widget->cfg_type == VALUE_INT); - - if (widget->cfg) - * (int *) widget->cfg = value; - else if (widget->cname) - set_int (widget->csect, widget->cname, value); - - if (widget->callback) - widget->callback (); -} - -static int widget_get_int (const PreferencesWidget * widget) -{ - g_return_val_if_fail (widget->cfg_type == VALUE_INT, 0); - - if (widget->cfg) - return * (int *) widget->cfg; - else if (widget->cname) - return get_int (widget->csect, widget->cname); - else - return 0; -} - -static void widget_set_double (const PreferencesWidget * widget, double value) -{ - g_return_if_fail (widget->cfg_type == VALUE_FLOAT); - - if (widget->cfg) - * (float *) widget->cfg = value; - else if (widget->cname) - set_double (widget->csect, widget->cname, value); - - if (widget->callback) - widget->callback (); -} - -static double widget_get_double (const PreferencesWidget * widget) -{ - g_return_val_if_fail (widget->cfg_type == VALUE_FLOAT, 0); - - if (widget->cfg) - return * (float *) widget->cfg; - else if (widget->cname) - return get_double (widget->csect, widget->cname); - else - return 0; -} - -static void widget_set_string (const PreferencesWidget * widget, const char * value) -{ - g_return_if_fail (widget->cfg_type == VALUE_STRING); - - if (widget->cfg) - { - g_free (* (char * *) widget->cfg); - * (char * *) widget->cfg = g_strdup (value); - } - else if (widget->cname) - set_string (widget->csect, widget->cname, value); - - if (widget->callback) - widget->callback (); -} - -static char * widget_get_string (const PreferencesWidget * widget) -{ - g_return_val_if_fail (widget->cfg_type == VALUE_STRING, NULL); - - if (widget->cfg) - return g_strdup (* (char * *) widget->cfg); - else if (widget->cname) - return get_string (widget->csect, widget->cname); - else - return NULL; -} - -static void on_font_btn_font_set (GtkFontButton * button, const PreferencesWidget * widget) -{ - widget_set_string (widget, gtk_font_button_get_font_name (button)); -} - -static void on_spin_btn_changed_int (GtkSpinButton * button, const PreferencesWidget * widget) -{ - widget_set_int (widget, gtk_spin_button_get_value_as_int (button)); -} - -static void on_spin_btn_changed_float (GtkSpinButton * button, const PreferencesWidget * widget) -{ - widget_set_double (widget, gtk_spin_button_get_value (button)); -} - static void fill_category_list (GtkTreeView * treeview, GtkNotebook * notebook) { - GtkListStore *store; - GtkCellRenderer *renderer; - GtkTreeViewColumn *column; - GtkTreeSelection *selection; - GtkTreeIter iter; - GdkPixbuf *img; - int i; + GtkTreeViewColumn * column = gtk_tree_view_column_new (); + gtk_tree_view_column_set_title (column, _("Category")); + gtk_tree_view_append_column (treeview, column); + gtk_tree_view_column_set_spacing (column, 2); - column = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(column, _("Category")); - gtk_tree_view_append_column(treeview, column); - gtk_tree_view_column_set_spacing(column, 2); + GtkCellRenderer * renderer = gtk_cell_renderer_pixbuf_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, "pixbuf", 0, NULL); - renderer = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(column, renderer, FALSE); - gtk_tree_view_column_set_attributes(column, renderer, "pixbuf", 0, NULL); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(column, renderer, FALSE); - gtk_tree_view_column_set_attributes(column, renderer, "text", 1, NULL); + renderer = gtk_cell_renderer_text_new (); + gtk_tree_view_column_pack_start (column, renderer, FALSE); + gtk_tree_view_column_set_attributes (column, renderer, "text", 1, NULL); g_object_set ((GObject *) renderer, "wrap-width", 96, "wrap-mode", PANGO_WRAP_WORD_CHAR, NULL); - store = gtk_list_store_new(CATEGORY_VIEW_N_COLS, - GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT); - gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store)); + GtkListStore * store = gtk_list_store_new (CATEGORY_VIEW_N_COLS, + GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_INT); + gtk_tree_view_set_model (treeview, (GtkTreeModel *) store); - for (i = 0; i < n_categories; i ++) - { - char * path = g_strdup_printf ("%s/images/%s", - get_path (AUD_PATH_DATA_DIR), categories[i].icon_path); - img = gdk_pixbuf_new_from_file (path, NULL); - g_free (path); - - gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, - CATEGORY_VIEW_COL_ICON, img, - CATEGORY_VIEW_COL_NAME, - gettext(categories[i].name), CATEGORY_VIEW_COL_ID, - i, -1); - g_object_unref(img); - } - - selection = gtk_tree_view_get_selection(treeview); - - g_signal_connect_swapped(selection, "changed", - G_CALLBACK(change_category), notebook); -} + const char * data_dir = get_path (AUD_PATH_DATA_DIR); -static void on_radio_button_toggled (GtkWidget * button, const PreferencesWidget * widget) -{ - if (gtk_toggle_button_get_active ((GtkToggleButton *) button)) - widget_set_int (widget, widget->data.radio_btn.value); -} - -static void init_radio_button (GtkWidget * button, const PreferencesWidget * widget) -{ - if (widget->cfg_type != VALUE_INT) - return; - - if (widget_get_int (widget) == widget->data.radio_btn.value) - gtk_toggle_button_set_active ((GtkToggleButton *) button, TRUE); - - g_signal_connect (button, "toggled", (GCallback) on_radio_button_toggled, (void *) widget); -} - -static void on_toggle_button_toggled (GtkToggleButton * button, const PreferencesWidget * widget) -{ - bool_t active = gtk_toggle_button_get_active (button); - widget_set_bool (widget, active); - - GtkWidget * child = g_object_get_data ((GObject *) button, "child"); - if (child) - gtk_widget_set_sensitive (child, active); -} - -static void init_toggle_button (GtkWidget * button, const PreferencesWidget * widget) -{ - if (widget->cfg_type != VALUE_BOOLEAN) - return; - - gtk_toggle_button_set_active ((GtkToggleButton *) button, widget_get_bool (widget)); - g_signal_connect (button, "toggled", (GCallback) on_toggle_button_toggled, (void *) widget); -} - -static void on_entry_changed (GtkEntry * entry, const PreferencesWidget * widget) -{ - widget_set_string (widget, gtk_entry_get_text (entry)); -} - -static void on_cbox_changed_int (GtkComboBox * combobox, const PreferencesWidget * widget) -{ - int position = gtk_combo_box_get_active (combobox); - widget_set_int (widget, GPOINTER_TO_INT (widget->data.combo.elements[position].value)); -} - -static void on_cbox_changed_string (GtkComboBox * combobox, const PreferencesWidget * widget) -{ - int position = gtk_combo_box_get_active (combobox); - widget_set_string (widget, widget->data.combo.elements[position].value); -} - -static void fill_cbox (GtkWidget * combobox, const PreferencesWidget * widget, const char * domain) -{ - for (int i = 0; i < widget->data.combo.n_elements; i ++) - gtk_combo_box_text_append_text ((GtkComboBoxText *) combobox, - dgettext (domain, widget->data.combo.elements[i].label)); - - switch (widget->cfg_type) + for (int i = 0; i < ARRAY_LEN (categories); i ++) { - case VALUE_INT:; - int ivalue = widget_get_int (widget); + SCONCAT3 (path, data_dir, "/images/", categories[i].icon_path); - for (int i = 0; i < widget->data.combo.n_elements; i++) - { - if (GPOINTER_TO_INT (widget->data.combo.elements[i].value) == ivalue) - { - gtk_combo_box_set_active ((GtkComboBox *) combobox, i); - break; - } - } + GtkTreeIter iter; - g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_int, (void *) widget); - break; + gtk_list_store_append (store, & iter); + gtk_list_store_set (store, & iter, CATEGORY_VIEW_COL_NAME, + gettext (categories[i].name), -1); - case VALUE_STRING:; - char * value = widget_get_string (widget); + GdkPixbuf * img = gdk_pixbuf_new_from_file (path, NULL); - for(int i = 0; i < widget->data.combo.n_elements; i++) + if (img) { - if (value && ! strcmp (widget->data.combo.elements[i].value, value)) - { - gtk_combo_box_set_active ((GtkComboBox *) combobox, i); - break; - } + gtk_list_store_set (store, & iter, CATEGORY_VIEW_COL_ICON, img, -1); + g_object_unref (img); } - - g_free (value); - - g_signal_connect (combobox, "changed", (GCallback) on_cbox_changed_string, (void *) widget); - break; - - default: - break; } -} -static void create_spin_button (const PreferencesWidget * widget, GtkWidget * * - label_pre, GtkWidget * * spin_btn, GtkWidget * * label_past, const char * - domain) -{ - g_return_if_fail(widget->type == WIDGET_SPIN_BTN); + g_object_unref (store); - * label_pre = gtk_label_new (dgettext (domain, widget->label)); - - *spin_btn = gtk_spin_button_new_with_range(widget->data.spin_btn.min, - widget->data.spin_btn.max, - widget->data.spin_btn.step); - - - if (widget->tooltip) - gtk_widget_set_tooltip_text (* spin_btn, dgettext (domain, - widget->tooltip)); - - if (widget->data.spin_btn.right_label) { - * label_past = gtk_label_new (dgettext (domain, - widget->data.spin_btn.right_label)); - } - - switch (widget->cfg_type) - { - case VALUE_INT: - gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_int (widget)); - g_signal_connect (* spin_btn, "value_changed", (GCallback) - on_spin_btn_changed_int, (void *) widget); - break; - case VALUE_FLOAT: - gtk_spin_button_set_value ((GtkSpinButton *) * spin_btn, widget_get_double (widget)); - g_signal_connect (* spin_btn, "value_changed", (GCallback) - on_spin_btn_changed_float, (void *) widget); - break; - default: - break; - } + GtkTreeSelection * selection = gtk_tree_view_get_selection (treeview); + g_signal_connect (selection, "changed", (GCallback) category_changed, NULL); } -void create_font_btn (const PreferencesWidget * widget, GtkWidget * * label, - GtkWidget * * font_btn, const char * domain) +static GtkWidget * create_titlestring_tag_menu (void) { - *font_btn = gtk_font_button_new(); - gtk_font_button_set_use_font(GTK_FONT_BUTTON(*font_btn), TRUE); - gtk_font_button_set_use_size(GTK_FONT_BUTTON(*font_btn), TRUE); - gtk_widget_set_hexpand(*font_btn, TRUE); - if (widget->label) { - * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label)); - gtk_label_set_use_markup(GTK_LABEL(*label), TRUE); - gtk_misc_set_alignment(GTK_MISC(*label), 1, 0.5); - gtk_label_set_justify(GTK_LABEL(*label), GTK_JUSTIFY_RIGHT); - gtk_label_set_mnemonic_widget(GTK_LABEL(*label), *font_btn); - } - - if (widget->data.font_btn.title) - gtk_font_button_set_title (GTK_FONT_BUTTON (* font_btn), - dgettext (domain, widget->data.font_btn.title)); - - char * name = widget_get_string (widget); - if (name) - { - gtk_font_button_set_font_name ((GtkFontButton *) * font_btn, name); - g_free (name); - } - - g_signal_connect (* font_btn, "font_set", (GCallback) on_font_btn_font_set, (void *) widget); -} - -static void create_entry (const PreferencesWidget * widget, GtkWidget * * label, - GtkWidget * * entry, const char * domain) -{ - *entry = gtk_entry_new(); - gtk_entry_set_visibility(GTK_ENTRY(*entry), !widget->data.entry.password); - gtk_widget_set_hexpand(*entry, TRUE); - - if (widget->label) - * label = gtk_label_new (dgettext (domain, widget->label)); + GtkWidget * titlestring_tag_menu = gtk_menu_new (); - if (widget->tooltip) - gtk_widget_set_tooltip_text (* entry, dgettext (domain, widget->tooltip)); - - if (widget->cfg_type == VALUE_STRING) + for (int i = 0; i < ARRAY_LEN (title_field_tags); i ++) { - char * value = widget_get_string (widget); - if (value) - { - gtk_entry_set_text ((GtkEntry *) * entry, value); - g_free (value); - } - - g_signal_connect (* entry, "changed", (GCallback) on_entry_changed, (void *) widget); - } -} - -static void create_label (const PreferencesWidget * widget, GtkWidget * * label, - GtkWidget * * icon, const char * domain) -{ - if (widget->data.label.stock_id) - *icon = gtk_image_new_from_stock(widget->data.label.stock_id, GTK_ICON_SIZE_BUTTON); - - * label = gtk_label_new_with_mnemonic (dgettext (domain, widget->label)); - gtk_label_set_use_markup(GTK_LABEL(*label), TRUE); - - if (widget->data.label.single_line == FALSE) - gtk_label_set_line_wrap(GTK_LABEL(*label), TRUE); - - gtk_misc_set_alignment(GTK_MISC(*label), 0, 0.5); -} - -static void create_cbox (const PreferencesWidget * widget, GtkWidget * * label, - GtkWidget * * combobox, const char * domain) -{ - * combobox = gtk_combo_box_text_new (); - - if (widget->label) { - * label = gtk_label_new (dgettext (domain, widget->label)); - } - - fill_cbox (* combobox, widget, domain); -} - -static void fill_grid (GtkWidget * grid, const PreferencesWidget * elements, int - amt, const char * domain) -{ - int x; - GtkWidget *widget_left, *widget_middle, *widget_right; - - for (x = 0; x < amt; ++x) { - widget_left = widget_middle = widget_right = NULL; - switch (elements[x].type) { - case WIDGET_SPIN_BTN: - create_spin_button (& elements[x], & widget_left, - & widget_middle, & widget_right, domain); - break; - case WIDGET_LABEL: - create_label (& elements[x], & widget_middle, & widget_left, - domain); - break; - case WIDGET_FONT_BTN: - create_font_btn (& elements[x], & widget_left, & widget_middle, - domain); - break; - case WIDGET_ENTRY: - create_entry (& elements[x], & widget_left, & widget_middle, - domain); - break; - case WIDGET_COMBO_BOX: - create_cbox (& elements[x], & widget_left, & widget_middle, - domain); - break; - default: - g_warning("Unsupported widget type %d in table", elements[x].type); - } - - if (widget_left) - gtk_grid_attach(GTK_GRID(grid), widget_left, 0, x, 1, 1); - - if (widget_middle) - gtk_grid_attach(GTK_GRID(grid), widget_middle, 1, x, 1, 1); - - if (widget_right) - gtk_grid_attach(GTK_GRID(grid), widget_right, 2, x, 1, 1); - } -} - -/* box: a GtkBox */ -void create_widgets_with_domain (void * box, const PreferencesWidget * widgets, - int amt, const char * domain) -{ - GtkWidget *alignment = NULL, *widget = NULL; - GtkWidget *child_box = NULL; - GSList *radio_btn_group = NULL; - - for (int x = 0; x < amt; x ++) - { - GtkWidget * label = NULL; - - if (widget && widgets[x].child) - { - if (!child_box) { - child_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - g_object_set_data(G_OBJECT(widget), "child", child_box); - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start(box, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 12, 0); - gtk_container_add (GTK_CONTAINER (alignment), child_box); - - if (GTK_IS_TOGGLE_BUTTON (widget)) - gtk_widget_set_sensitive (child_box, gtk_toggle_button_get_active ((GtkToggleButton *) widget)); - } - } else - child_box = NULL; - - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 6, 0, 12, 0); - gtk_box_pack_start(child_box ? GTK_BOX(child_box) : box, alignment, FALSE, FALSE, 0); - - if (radio_btn_group && widgets[x].type != WIDGET_RADIO_BTN) - radio_btn_group = NULL; - - switch(widgets[x].type) { - case WIDGET_CHK_BTN: - widget = gtk_check_button_new_with_mnemonic (dgettext (domain, widgets[x].label)); - init_toggle_button (widget, & widgets[x]); - break; - case WIDGET_LABEL: - if (strstr (widgets[x].label, "")) - gtk_alignment_set_padding ((GtkAlignment *) alignment, - (x == 0) ? 0 : 12, 0, 0, 0); - - GtkWidget * icon = NULL; - create_label (& widgets[x], & label, & icon, domain); - - if (icon == NULL) - widget = label; - else { - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start(GTK_BOX(widget), icon, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0); - } - break; - case WIDGET_RADIO_BTN: - widget = gtk_radio_button_new_with_mnemonic (radio_btn_group, - dgettext (domain, widgets[x].label)); - radio_btn_group = gtk_radio_button_get_group ((GtkRadioButton *) widget); - init_radio_button (widget, & widgets[x]); - break; - case WIDGET_SPIN_BTN: - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - GtkWidget *label_pre = NULL, *spin_btn = NULL, *label_past = NULL; - create_spin_button (& widgets[x], & label_pre, & spin_btn, - & label_past, domain); - - if (label_pre) - gtk_box_pack_start(GTK_BOX(widget), label_pre, FALSE, FALSE, 0); - if (spin_btn) - gtk_box_pack_start(GTK_BOX(widget), spin_btn, FALSE, FALSE, 0); - if (label_past) - gtk_box_pack_start(GTK_BOX(widget), label_past, FALSE, FALSE, 0); - - break; - case WIDGET_CUSTOM: /* custom widget. --nenolod */ - if (widgets[x].data.populate) - widget = widgets[x].data.populate(); - else - widget = NULL; - - break; - case WIDGET_FONT_BTN: - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - GtkWidget *font_btn = NULL; - create_font_btn (& widgets[x], & label, & font_btn, domain); - - if (label) - gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0); - if (font_btn) - gtk_box_pack_start(GTK_BOX(widget), font_btn, FALSE, FALSE, 0); - break; - case WIDGET_TABLE: - widget = gtk_grid_new(); - fill_grid(widget, widgets[x].data.table.elem, - widgets[x].data.table.rows, domain); - gtk_grid_set_column_spacing(GTK_GRID(widget), 6); - gtk_grid_set_row_spacing(GTK_GRID(widget), 6); - break; - case WIDGET_ENTRY: - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - GtkWidget *entry = NULL; - create_entry (& widgets[x], & label, & entry, domain); - - if (label) - gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0); - if (entry) - gtk_box_pack_start(GTK_BOX(widget), entry, TRUE, TRUE, 0); - break; - case WIDGET_COMBO_BOX: - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - GtkWidget *combo = NULL; - create_cbox (& widgets[x], & label, & combo, domain); - - if (label) - gtk_box_pack_start(GTK_BOX(widget), label, FALSE, FALSE, 0); - if (combo) - gtk_box_pack_start(GTK_BOX(widget), combo, FALSE, FALSE, 0); - break; - case WIDGET_BOX: - gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 0, 0); - - if (widgets[x].data.box.horizontal) { - widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - } else { - widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - } - - create_widgets_with_domain ((GtkBox *) widget, - widgets[x].data.box.elem, widgets[x].data.box.n_elem, domain); - - if (widgets[x].data.box.frame) { - GtkWidget *tmp; - tmp = widget; - - widget = gtk_frame_new (dgettext (domain, widgets[x].label)); - gtk_container_add(GTK_CONTAINER(widget), tmp); - } - break; - case WIDGET_NOTEBOOK: - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 6, 0, 0, 0); - - widget = gtk_notebook_new(); - - int i; - for (i = 0; iSong Display")); - gtk_container_add (GTK_CONTAINER (alignment55), label60); - gtk_label_set_use_markup (GTK_LABEL (label60), TRUE); - gtk_misc_set_alignment (GTK_MISC (label60), 0, 0.5); + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add ((GtkContainer *) category_notebook, vbox); - numbers_alignment = gtk_alignment_new (0, 0, 0, 0); + create_widgets ((GtkBox *) vbox, playlist_page_widgets, ARRAY_LEN (playlist_page_widgets)); + + GtkWidget * alignment = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_box_pack_start ((GtkBox *) vbox, alignment, FALSE, FALSE, 0); + gtk_alignment_set_padding ((GtkAlignment *) alignment, 12, 3, 0, 0); + + GtkWidget * label = gtk_label_new (_("Song Display")); + gtk_container_add ((GtkContainer *) alignment, label); + gtk_label_set_use_markup ((GtkLabel *) label, TRUE); + gtk_misc_set_alignment ((GtkMisc *) label, 0, 0.5); + + GtkWidget * numbers_alignment = gtk_alignment_new (0, 0, 0, 0); gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0); - gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3); + gtk_box_pack_start ((GtkBox *) vbox, numbers_alignment, 0, 0, 3); - numbers = gtk_check_button_new_with_label (_("Show song numbers")); + GtkWidget * numbers = gtk_check_button_new_with_label (_("Show song numbers")); gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "show_numbers_in_pl")); - g_signal_connect ((GObject *) numbers, "toggled", (GCallback) - show_numbers_cb, 0); + g_signal_connect (numbers, "toggled", (GCallback) show_numbers_cb, 0); gtk_container_add ((GtkContainer *) numbers_alignment, numbers); numbers_alignment = gtk_alignment_new (0, 0, 0, 0); gtk_alignment_set_padding ((GtkAlignment *) numbers_alignment, 0, 0, 12, 0); - gtk_box_pack_start ((GtkBox *) vbox5, numbers_alignment, 0, 0, 3); + gtk_box_pack_start ((GtkBox *) vbox, numbers_alignment, 0, 0, 3); - numbers = gtk_check_button_new_with_label (_("Show leading zeroes (02:00 " - "instead of 2:00)")); + numbers = gtk_check_button_new_with_label ( + _("Show leading zeroes (02:00 instead of 2:00)")); gtk_toggle_button_set_active ((GtkToggleButton *) numbers, get_bool (NULL, "leading_zero")); - g_signal_connect ((GObject *) numbers, "toggled", (GCallback) - leading_zero_cb, 0); + g_signal_connect (numbers, "toggled", (GCallback) leading_zero_cb, 0); gtk_container_add ((GtkContainer *) numbers_alignment, numbers); - alignment56 = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start (GTK_BOX (vbox5), alignment56, FALSE, FALSE, 0); - gtk_alignment_set_padding (GTK_ALIGNMENT (alignment56), 0, 0, 12, 0); + alignment = gtk_alignment_new (0.5, 0.5, 1, 1); + gtk_box_pack_start ((GtkBox *) vbox, alignment, FALSE, FALSE, 0); + gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 0, 12, 0); - grid6 = gtk_grid_new (); - gtk_container_add (GTK_CONTAINER (alignment56), grid6); - gtk_grid_set_row_spacing (GTK_GRID (grid6), 4); - gtk_grid_set_column_spacing (GTK_GRID (grid6), 12); + GtkWidget * grid = gtk_grid_new (); + gtk_container_add ((GtkContainer *) alignment, grid); + gtk_grid_set_row_spacing ((GtkGrid *) grid, 4); + gtk_grid_set_column_spacing ((GtkGrid *) grid, 12); - titlestring_help_button = gtk_button_new (); - gtk_grid_attach (GTK_GRID (grid6), titlestring_help_button, 2, 1, 1, 1); + label = gtk_label_new (_("Title format:")); + gtk_grid_attach ((GtkGrid *) grid, label, 0, 0, 1, 1); + gtk_label_set_justify ((GtkLabel *) label, GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment ((GtkMisc *) label, 1, 0.5); - gtk_widget_set_can_focus (titlestring_help_button, FALSE); - gtk_widget_set_tooltip_text (titlestring_help_button, _("Show information about titlestring format")); - gtk_button_set_relief (GTK_BUTTON (titlestring_help_button), GTK_RELIEF_HALF); - gtk_button_set_focus_on_click (GTK_BUTTON (titlestring_help_button), FALSE); - - image1 = gtk_image_new_from_stock ("gtk-index", GTK_ICON_SIZE_BUTTON); - gtk_container_add (GTK_CONTAINER (titlestring_help_button), image1); + label = gtk_label_new (_("Custom string:")); + gtk_grid_attach ((GtkGrid *) grid, label, 0, 1, 1, 1); + gtk_label_set_justify ((GtkLabel *) label, GTK_JUSTIFY_RIGHT); + gtk_misc_set_alignment ((GtkMisc *) label, 1, 0.5); GtkWidget * titlestring_cbox; create_titlestring_widgets (& titlestring_cbox, & titlestring_entry); gtk_widget_set_hexpand (titlestring_cbox, TRUE); gtk_widget_set_hexpand (titlestring_entry, TRUE); - gtk_grid_attach (GTK_GRID (grid6), titlestring_cbox, 1, 0, 1, 1); - gtk_grid_attach (GTK_GRID (grid6), titlestring_entry, 1, 1, 1, 1); - - label62 = gtk_label_new (_("Custom string:")); - gtk_grid_attach (GTK_GRID (grid6), label62, 0, 1, 1, 1); - gtk_label_set_justify (GTK_LABEL (label62), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (label62), 1, 0.5); - - label61 = gtk_label_new (_("Title format:")); - gtk_grid_attach (GTK_GRID (grid6), label61, 0, 0, 1, 1); - gtk_label_set_justify (GTK_LABEL (label61), GTK_JUSTIFY_RIGHT); - gtk_misc_set_alignment (GTK_MISC (label61), 1, 0.5); - - g_signal_connect(titlestring_help_button, "clicked", - G_CALLBACK(on_titlestring_help_button_clicked), - titlestring_tag_menu); + gtk_grid_attach ((GtkGrid *) grid, titlestring_cbox, 1, 0, 1, 1); + gtk_grid_attach ((GtkGrid *) grid, titlestring_entry, 1, 1, 1, 1); + + GtkWidget * titlestring_help_button = gtk_button_new (); + gtk_widget_set_can_focus (titlestring_help_button, FALSE); + gtk_button_set_focus_on_click ((GtkButton *) titlestring_help_button, FALSE); + gtk_button_set_relief ((GtkButton *) titlestring_help_button, GTK_RELIEF_HALF); + gtk_grid_attach ((GtkGrid *) grid, titlestring_help_button, 2, 1, 1, 1); + + GtkWidget * titlestring_tag_menu = create_titlestring_tag_menu (); + + g_signal_connect (titlestring_help_button, "clicked", + (GCallback) on_titlestring_help_button_clicked, titlestring_tag_menu); + + GtkWidget * image = gtk_image_new_from_icon_name ("list-add", GTK_ICON_SIZE_BUTTON); + gtk_container_add ((GtkContainer *) titlestring_help_button, image); } static void create_song_info_category (void) @@ -1069,54 +549,74 @@ static void create_song_info_category (void) GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add ((GtkContainer *) category_notebook, vbox); create_widgets ((GtkBox *) vbox, song_info_page_widgets, - G_N_ELEMENTS (song_info_page_widgets)); + ARRAY_LEN (song_info_page_widgets)); } -static GtkWidget * output_config_button, * output_about_button; - -static bool_t output_enum_cb (PluginHandle * plugin, GList * * list) +static void iface_fill_prefs_box (void) { - * list = g_list_prepend (* list, plugin); - return TRUE; + Plugin * header = plugin_get_header (plugin_get_current (PLUGIN_TYPE_IFACE)); + if (header && header->prefs) + create_widgets_with_domain (iface_prefs_box, header->prefs->widgets, + header->prefs->n_widgets, header->domain); } -static GList * output_get_list (void) +static void iface_combo_changed (void) { - static GList * list = NULL; + gtk_container_foreach ((GtkContainer *) iface_prefs_box, (GtkCallback) gtk_widget_destroy, NULL); - if (list == NULL) + plugin_enable (plugin_by_index (PLUGIN_TYPE_IFACE, iface_combo_selected), TRUE); + + iface_fill_prefs_box (); + gtk_widget_show_all (iface_prefs_box); +} + +static const ComboBoxElements * iface_combo_fill (int * n_elements) +{ + if (! iface_combo_elements) { - plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_enum_cb, - & list); - list = g_list_reverse (list); + iface_combo_elements = fill_plugin_combo (PLUGIN_TYPE_IFACE); + iface_combo_selected = plugin_get_index (plugin_get_current (PLUGIN_TYPE_IFACE)); } - return list; + * n_elements = iface_combo_elements->len; + return (const ComboBoxElements *) iface_combo_elements->data; } -static void output_combo_update (GtkComboBox * combo) +static void * iface_create_prefs_box (void) { - PluginHandle * plugin = plugin_get_current (PLUGIN_TYPE_OUTPUT); - gtk_combo_box_set_active (combo, g_list_index (output_get_list (), plugin)); - gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin)); - gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin)); + iface_prefs_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + iface_fill_prefs_box (); + return iface_prefs_box; } -static void output_combo_changed (GtkComboBox * combo) +static void create_appearance_category (void) { - PluginHandle * plugin = g_list_nth_data (output_get_list (), - gtk_combo_box_get_active (combo)); - g_return_if_fail (plugin != NULL); + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add ((GtkContainer *) category_notebook, vbox); + create_widgets ((GtkBox *) vbox, appearance_page_widgets, ARRAY_LEN (appearance_page_widgets)); +} - plugin_enable (plugin, TRUE); - output_combo_update (combo); +static void output_combo_changed (void) +{ + PluginHandle * plugin = plugin_by_index (PLUGIN_TYPE_OUTPUT, output_combo_selected); + + if (plugin_enable (plugin, TRUE)) + { + gtk_widget_set_sensitive (output_config_button, plugin_has_configure (plugin)); + gtk_widget_set_sensitive (output_about_button, plugin_has_about (plugin)); + } } -static void output_combo_fill (GtkComboBox * combo) +static const ComboBoxElements * output_combo_fill (int * n_elements) { - for (GList * node = output_get_list (); node != NULL; node = node->next) - gtk_combo_box_text_append_text ((GtkComboBoxText *) combo, - plugin_get_name (node->data)); + if (! output_combo_elements) + { + output_combo_elements = fill_plugin_combo (PLUGIN_TYPE_OUTPUT); + output_combo_selected = plugin_get_index (output_plugin_get_current ()); + } + + * n_elements = output_combo_elements->len; + return (const ComboBoxElements *) output_combo_elements->data; } static void output_bit_depth_changed (void) @@ -1124,214 +624,191 @@ static void output_bit_depth_changed (void) output_reset (OUTPUT_RESET_SOFT); } -static void output_do_config (void) +static void output_do_config (void * unused) { - PluginHandle * plugin = output_plugin_get_current (); - g_return_if_fail (plugin != NULL); - plugin_do_configure (plugin); + plugin_do_configure (output_plugin_get_current ()); } -static void output_do_about (void) +static void output_do_about (void * unused) { - PluginHandle * plugin = output_plugin_get_current (); - g_return_if_fail (plugin != NULL); - plugin_do_about (plugin); + plugin_do_about (output_plugin_get_current ()); } -static void * create_output_plugin_box (void) +static void * output_create_config_button (void) { - GtkWidget * hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start ((GtkBox *) hbox1, gtk_label_new (_("Output plugin:")), FALSE, FALSE, 0); - - GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - gtk_box_pack_start ((GtkBox *) hbox1, vbox, FALSE, FALSE, 0); + bool_t enabled = plugin_has_configure (output_plugin_get_current ()); - GtkWidget * hbox2 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start ((GtkBox *) vbox, hbox2, FALSE, FALSE, 0); + output_config_button = audgui_button_new (_("_Settings"), + "preferences-system", output_do_config, NULL); + gtk_widget_set_sensitive (output_config_button, enabled); - GtkWidget * output_plugin_cbox = gtk_combo_box_text_new (); - gtk_box_pack_start ((GtkBox *) hbox2, output_plugin_cbox, FALSE, FALSE, 0); - - GtkWidget * hbox3 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start ((GtkBox *) vbox, hbox3, FALSE, FALSE, 0); - - output_config_button = gtk_button_new_from_stock (GTK_STOCK_PREFERENCES); - gtk_box_pack_start ((GtkBox *) hbox3, output_config_button, FALSE, FALSE, 0); - - output_about_button = gtk_button_new_from_stock (GTK_STOCK_ABOUT); - gtk_box_pack_start ((GtkBox *) hbox3, output_about_button, FALSE, FALSE, 0); + return output_config_button; +} - output_combo_fill ((GtkComboBox *) output_plugin_cbox); - output_combo_update ((GtkComboBox *) output_plugin_cbox); +static void * output_create_about_button (void) +{ + bool_t enabled = plugin_has_about (output_plugin_get_current ()); - g_signal_connect (output_plugin_cbox, "changed", (GCallback) output_combo_changed, NULL); - g_signal_connect (output_config_button, "clicked", (GCallback) output_do_config, NULL); - g_signal_connect (output_about_button, "clicked", (GCallback) output_do_about, NULL); + output_about_button = audgui_button_new (_("_About"), "help-about", output_do_about, NULL); + gtk_widget_set_sensitive (output_about_button, enabled); - return hbox1; + return output_about_button; } static void create_audio_category (void) { GtkWidget * audio_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, G_N_ELEMENTS (audio_page_widgets)); + create_widgets ((GtkBox *) audio_page_vbox, audio_page_widgets, ARRAY_LEN (audio_page_widgets)); gtk_container_add ((GtkContainer *) category_notebook, audio_page_vbox); } -static void -create_connectivity_category(void) +static void create_connectivity_category (void) { - GtkWidget *connectivity_page_vbox; - GtkWidget *vbox29; - - connectivity_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add (GTK_CONTAINER (category_notebook), connectivity_page_vbox); + GtkWidget * connectivity_page_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add ((GtkContainer *) category_notebook, connectivity_page_vbox); - vbox29 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_start (GTK_BOX (connectivity_page_vbox), vbox29, TRUE, TRUE, 0); + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_box_pack_start ((GtkBox *) connectivity_page_vbox, vbox, TRUE, TRUE, 0); - create_widgets(GTK_BOX(vbox29), connectivity_page_widgets, G_N_ELEMENTS(connectivity_page_widgets)); + create_widgets ((GtkBox *) vbox, connectivity_page_widgets, ARRAY_LEN (connectivity_page_widgets)); } static void create_plugin_category (void) { - GtkWidget * notebook = gtk_notebook_new (); - gtk_container_add ((GtkContainer *) category_notebook, notebook); - - int types[] = {PLUGIN_TYPE_TRANSPORT, PLUGIN_TYPE_PLAYLIST, - PLUGIN_TYPE_INPUT, PLUGIN_TYPE_EFFECT, PLUGIN_TYPE_VIS, PLUGIN_TYPE_GENERAL}; - const char * names[] = {N_("Transport"), N_("Playlist"), N_("Input"), - N_("Effect"), N_("Visualization"), N_("General")}; + plugin_notebook = gtk_notebook_new (); + gtk_container_add ((GtkContainer *) category_notebook, plugin_notebook); - for (int i = 0; i < G_N_ELEMENTS (types); i ++) - gtk_notebook_append_page ((GtkNotebook *) notebook, plugin_view_new - (types[i]), gtk_label_new (_(names[i]))); + for (int i = 0; i < ARRAY_LEN (plugin_categories); i ++) + { + const PluginCategory * cat = & plugin_categories[i]; + gtk_notebook_append_page ((GtkNotebook *) plugin_notebook, + plugin_view_new (cat->type), gtk_label_new (_(cat->name))); + } } -static bool_t -prefswin_destroy(GtkWidget *window, GdkEvent *event, void * data) +static void destroy_cb (void) { prefswin = NULL; + category_treeview = NULL; category_notebook = NULL; - gtk_widget_destroy(window); - return TRUE; + titlestring_entry = NULL; + + if (iface_combo_elements) + { + g_array_free (iface_combo_elements, TRUE); + iface_combo_elements = NULL; + } + + if (output_combo_elements) + { + g_array_free (output_combo_elements, TRUE); + output_combo_elements = NULL; + } } static void create_prefs_window (void) { - char *aud_version_string; - - GtkWidget *vbox; - GtkWidget *hbox1; - GtkWidget *scrolledwindow6; - GtkWidget *hseparator1; - GtkWidget *hbox4; - GtkWidget *audversionlabel; - GtkWidget *prefswin_button_box; - GtkWidget *close; - GtkAccelGroup *accel_group; - - accel_group = gtk_accel_group_new (); - prefswin = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_window_set_type_hint (GTK_WINDOW (prefswin), GDK_WINDOW_TYPE_HINT_DIALOG); - gtk_container_set_border_width (GTK_CONTAINER (prefswin), 12); - gtk_window_set_title (GTK_WINDOW (prefswin), _("Audacious Preferences")); - gtk_window_set_position (GTK_WINDOW (prefswin), GTK_WIN_POS_CENTER); - gtk_window_set_default_size (GTK_WINDOW (prefswin), 680, 400); + gtk_window_set_type_hint ((GtkWindow *) prefswin, GDK_WINDOW_TYPE_HINT_DIALOG); + gtk_container_set_border_width ((GtkContainer *) prefswin, 12); + gtk_window_set_title ((GtkWindow *) prefswin, _("Audacious Settings")); + gtk_window_set_default_size ((GtkWindow *) prefswin, 680, 400); - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add (GTK_CONTAINER (prefswin), vbox); + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + gtk_container_add ((GtkContainer *) prefswin, vbox); - hbox1 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); - gtk_box_pack_start (GTK_BOX (vbox), hbox1, TRUE, TRUE, 0); + GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8); + gtk_box_pack_start ((GtkBox *) vbox, hbox, TRUE, TRUE, 0); - scrolledwindow6 = gtk_scrolled_window_new (NULL, NULL); - gtk_box_pack_start (GTK_BOX (hbox1), scrolledwindow6, FALSE, FALSE, 0); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledwindow6), GTK_SHADOW_IN); + GtkWidget * scrolledwindow = gtk_scrolled_window_new (NULL, NULL); + gtk_box_pack_start ((GtkBox *) hbox, scrolledwindow, FALSE, FALSE, 0); + gtk_scrolled_window_set_policy ((GtkScrolledWindow *) scrolledwindow, + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) scrolledwindow, GTK_SHADOW_IN); category_treeview = gtk_tree_view_new (); - gtk_container_add (GTK_CONTAINER (scrolledwindow6), category_treeview); - gtk_widget_set_size_request (scrolledwindow6, 168, -1); - gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (category_treeview), FALSE); + gtk_container_add ((GtkContainer *) scrolledwindow, category_treeview); + gtk_widget_set_size_request (scrolledwindow, 168, -1); + gtk_tree_view_set_headers_visible ((GtkTreeView *) category_treeview, FALSE); category_notebook = gtk_notebook_new (); - gtk_box_pack_start (GTK_BOX (hbox1), category_notebook, TRUE, TRUE, 0); + gtk_box_pack_start ((GtkBox *) hbox, category_notebook, TRUE, TRUE, 0); gtk_widget_set_can_focus (category_notebook, FALSE); - gtk_notebook_set_show_tabs (GTK_NOTEBOOK (category_notebook), FALSE); - gtk_notebook_set_show_border (GTK_NOTEBOOK (category_notebook), FALSE); - gtk_notebook_set_scrollable (GTK_NOTEBOOK (category_notebook), TRUE); - - create_audio_category(); - create_connectivity_category(); - create_playlist_category(); - create_song_info_category(); - create_plugin_category(); - - hseparator1 = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); - gtk_box_pack_start (GTK_BOX (vbox), hseparator1, FALSE, FALSE, 6); - - hbox4 = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start (GTK_BOX (vbox), hbox4, FALSE, FALSE, 0); - - audversionlabel = gtk_label_new (""); - gtk_box_pack_start (GTK_BOX (hbox4), audversionlabel, FALSE, FALSE, 0); - gtk_label_set_use_markup (GTK_LABEL (audversionlabel), TRUE); - - prefswin_button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); - gtk_box_pack_start (GTK_BOX (hbox4), prefswin_button_box, TRUE, TRUE, 0); - gtk_button_box_set_layout (GTK_BUTTON_BOX (prefswin_button_box), GTK_BUTTONBOX_END); - gtk_box_set_spacing (GTK_BOX (prefswin_button_box), 6); - - close = gtk_button_new_from_stock ("gtk-close"); - gtk_container_add (GTK_CONTAINER (prefswin_button_box), close); - gtk_widget_set_can_default(close, TRUE); - gtk_widget_add_accelerator (close, "clicked", accel_group, - GDK_KEY_Escape, (GdkModifierType) 0, - GTK_ACCEL_VISIBLE); - - - gtk_window_add_accel_group (GTK_WINDOW (prefswin), accel_group); - - /* connect signals */ - g_signal_connect(G_OBJECT(prefswin), "delete_event", - G_CALLBACK(prefswin_destroy), - NULL); - g_signal_connect_swapped(G_OBJECT(close), "clicked", - G_CALLBACK(prefswin_destroy), - prefswin); - - /* create category view */ + gtk_notebook_set_show_tabs ((GtkNotebook *) category_notebook, FALSE); + gtk_notebook_set_show_border ((GtkNotebook *) category_notebook, FALSE); + + create_appearance_category (); + create_audio_category (); + create_connectivity_category (); + create_playlist_category (); + create_song_info_category (); + create_plugin_category (); + + GtkWidget * hseparator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start ((GtkBox *) vbox, hseparator, FALSE, FALSE, 6); + + hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0); + + GtkWidget * audversionlabel = gtk_label_new (aud_version_string); + gtk_box_pack_start ((GtkBox *) hbox, audversionlabel, FALSE, FALSE, 0); + gtk_label_set_use_markup ((GtkLabel *) audversionlabel, TRUE); + + GtkWidget * prefswin_button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + gtk_box_pack_start ((GtkBox *) hbox, prefswin_button_box, TRUE, TRUE, 0); + gtk_button_box_set_layout ((GtkButtonBox *) prefswin_button_box, GTK_BUTTONBOX_END); + gtk_box_set_spacing ((GtkBox *) prefswin_button_box, 6); + + GtkWidget * close = audgui_button_new (_("_Close"), "window-close", + (AudguiCallback) gtk_widget_destroy, prefswin); + gtk_container_add ((GtkContainer *) prefswin_button_box, close); + gtk_widget_set_can_default (close, TRUE); + fill_category_list ((GtkTreeView *) category_treeview, (GtkNotebook *) category_notebook); - /* audacious version label */ + gtk_widget_show_all (vbox); - aud_version_string = g_strdup_printf - ("%s (%s)", "Audacious " VERSION, BUILDSTAMP); + g_signal_connect (prefswin, "destroy", (GCallback) destroy_cb, NULL); - gtk_label_set_markup( GTK_LABEL(audversionlabel) , aud_version_string ); - g_free(aud_version_string); - gtk_widget_show_all(vbox); + audgui_destroy_on_escape (prefswin); } -void -destroy_prefs_window(void) +void show_prefs_window (void) { - prefswin_destroy(prefswin, NULL, NULL); + if (! prefswin) + create_prefs_window (); + + change_category (CATEGORY_APPEARANCE); + + gtk_window_present ((GtkWindow *) prefswin); } -void show_prefs_window (void) +void show_prefs_for_plugin_type (int type) { if (! prefswin) create_prefs_window (); + if (type == PLUGIN_TYPE_IFACE) + change_category (CATEGORY_APPEARANCE); + else if (type == PLUGIN_TYPE_OUTPUT) + change_category (CATEGORY_AUDIO); + else + { + change_category (CATEGORY_PLUGINS); + + for (int i = 0; i < ARRAY_LEN (plugin_categories); i ++) + { + if (plugin_categories[i].type == type) + gtk_notebook_set_current_page ((GtkNotebook *) plugin_notebook, i); + } + } + gtk_window_present ((GtkWindow *) prefswin); } void hide_prefs_window (void) { - g_return_if_fail(prefswin); - gtk_widget_hide(GTK_WIDGET(prefswin)); + if (prefswin) + gtk_widget_destroy (prefswin); } diff --git a/src/audacious/ui_preferences.h b/src/audacious/ui_preferences.h index ab91f1b..b9b2707 100644 --- a/src/audacious/ui_preferences.h +++ b/src/audacious/ui_preferences.h @@ -1,6 +1,6 @@ /* * ui_preferences.h - * Copyright 2006-2010 William Pitcock, Tomasz Moń, and John Lindgren + * Copyright 2006-2012 William Pitcock, Tomasz Moń, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,7 +22,7 @@ #include -#include "preferences.h" +#include "types.h" void show_prefs_window(void); void hide_prefs_window(void); diff --git a/src/audacious/util.c b/src/audacious/util.c index 37455b5..3556e68 100644 --- a/src/audacious/util.c +++ b/src/audacious/util.c @@ -1,6 +1,6 @@ /* * util.c - * Copyright 2009-2012 John Lindgren and Michał Lipski + * Copyright 2009-2013 John Lindgren and Michał Lipski * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,7 +17,10 @@ * the use of this software. */ -#include +#include +#include +#include +#include #include #ifdef _WIN32 @@ -29,11 +32,6 @@ #endif #include -#include -#include -#include - -#include #include @@ -45,64 +43,64 @@ bool_t dir_foreach (const char * path, DirForeachFunc func, void * user) { - DIR * dir = opendir (path); + GDir * dir = g_dir_open (path, 0, NULL); if (! dir) return FALSE; - struct dirent * entry; - while ((entry = readdir (dir))) + const char * name; + while ((name = g_dir_read_name (dir))) { - if (entry->d_name[0] == '.') - continue; - - char * full = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s", path, entry->d_name); - bool_t stop = func (full, entry->d_name, user); - g_free (full); + char * full = filename_build (path, name); + bool_t stop = func (full, name, user); + str_unref (full); if (stop) break; } - closedir (dir); + g_dir_close (dir); return TRUE; } -char * construct_uri (const char * string, const char * playlist_name) +char * construct_uri (const char * path, const char * reference) { /* URI */ - if (strstr (string, "://")) - return strdup (string); + if (strstr (path, "://")) + return str_get (path); - /* absolute filename (assumed UTF-8) */ + /* absolute filename */ #ifdef _WIN32 - if (string[0] && string[1] == ':' && string[2] == '\\') + if (path[0] && path[1] == ':' && path[2] == '\\') #else - if (string[0] == '/') + if (path[0] == '/') #endif - return filename_to_uri (string); + return filename_to_uri (path); - /* relative filename (assumed UTF-8) */ - const char * slash = strrchr (playlist_name, '/'); + /* relative path */ + const char * slash = strrchr (reference, '/'); if (! slash) return NULL; - int pathlen = slash + 1 - playlist_name; - int rellen = strlen (string); + char * utf8 = str_to_utf8 (path, -1); + if (! utf8) + return NULL; + + int pathlen = slash + 1 - reference; - char buf[pathlen + 3 * rellen + 1]; - memcpy (buf, playlist_name, pathlen); + char buf[pathlen + 3 * strlen (utf8) + 1]; + memcpy (buf, reference, pathlen); if (get_bool (NULL, "convert_backslash")) { - char tmp[rellen + 1]; - strcpy (tmp, string); - string_replace_char (tmp, '\\', '/'); + SCOPY (tmp, utf8); + str_replace_char (tmp, '\\', '/'); str_encode_percent (tmp, -1, buf + pathlen); } else - str_encode_percent (string, -1, buf + pathlen); + str_encode_percent (utf8, -1, buf + pathlen); - return strdup (buf); + str_unref (utf8); + return str_get (buf); } void @@ -117,13 +115,14 @@ make_directory(const char * path, mode_t mode) char * write_temp_file (void * data, int64_t len) { - char * name = g_strdup_printf ("%s/audacious-temp-XXXXXX", g_get_tmp_dir ()); + char * temp = filename_build (g_get_tmp_dir (), "audacious-temp-XXXXXX"); + SCOPY (name, temp); + str_unref (temp); int handle = g_mkstemp (name); if (handle < 0) { fprintf (stderr, "Error creating temporary file: %s\n", strerror (errno)); - g_free (name); return NULL; } @@ -134,7 +133,6 @@ char * write_temp_file (void * data, int64_t len) { fprintf (stderr, "Error writing %s: %s\n", name, strerror (errno)); close (handle); - g_free (name); return NULL; } @@ -145,72 +143,103 @@ char * write_temp_file (void * data, int64_t len) if (close (handle) < 0) { fprintf (stderr, "Error closing %s: %s\n", name, strerror (errno)); - g_free (name); return NULL; } - return name; + return str_get (name); } char * get_path_to_self (void) { -#if defined _WIN32 || defined HAVE_PROC_SELF_EXE +#ifdef HAVE_PROC_SELF_EXE int size = 256; - char * buf = g_malloc (size); while (1) { + char buf[size]; int len; -#ifdef _WIN32 - if (! (len = GetModuleFileName (NULL, buf, size))) - { - fprintf (stderr, "GetModuleFileName failed.\n"); - g_free (buf); - return NULL; - } -#else if ((len = readlink ("/proc/self/exe", buf, size)) < 0) { fprintf (stderr, "Cannot access /proc/self/exe: %s.\n", strerror (errno)); - g_free (buf); return NULL; } -#endif if (len < size) { buf[len] = 0; - return buf; + return str_get (buf); + } + + size += size; + } +#elif defined _WIN32 + int size = 256; + + while (1) + { + wchar_t buf[size]; + int len; + + if (! (len = GetModuleFileNameW (NULL, buf, size))) + { + fprintf (stderr, "GetModuleFileName failed.\n"); + return NULL; + } + + if (len < size) + { + char * temp = g_utf16_to_utf8 (buf, len, NULL, NULL, NULL); + char * path = str_get (temp); + g_free (temp); + return path; } size += size; - buf = g_realloc (buf, size); } #elif defined __APPLE__ unsigned int size = 256; - char * buf = g_malloc (size); while (1) { + char buf[size]; int res; if (! (res = _NSGetExecutablePath (buf, &size))) - return buf; + return str_get (buf); - if (res == -1) - buf = g_realloc (buf, size); - else - { - g_free (buf); + if (res != -1) return NULL; - } } #else return NULL; #endif } +#ifdef _WIN32 +void get_argv_utf8 (int * argc, char * * * argv) +{ + wchar_t * combined = GetCommandLineW (); + wchar_t * * split = CommandLineToArgvW (combined, argc); + + * argv = g_new (char *, argc + 1); + + for (int i = 0; i < * argc; i ++) + (* argv)[i] = g_utf16_to_utf8 (split[i], -1, NULL, NULL, NULL); + + (* argv)[* argc] = 0; + + LocalFree (split); +} + +void free_argv_utf8 (int * argc, char * * * argv) +{ + g_strfreev (* argv); + * argc = 0; + * argv = NULL; +} +#endif + /* Strips various common top-level folders from a filename. The string passed * will not be modified, but the string returned will share the same memory. * Examples: @@ -327,7 +356,7 @@ static char * stream_name (char * name) static char * get_nonblank_field (const Tuple * tuple, int field) { - char * str = tuple ? tuple_get_str (tuple, field, NULL) : NULL; + char * str = tuple ? tuple_get_str (tuple, field) : NULL; if (str && ! str[0]) { @@ -375,13 +404,15 @@ DONE: if (! filename) goto DONE; + SCOPY (buf, filename); + char * base, * first, * second; - split_filename (skip_top_folders (filename), & base, & first, & second); + split_filename (skip_top_folders (buf), & base, & first, & second); if (! title) title = str_get (base); - for (int i = 0; i < G_N_ELEMENTS (skip); i ++) + for (int i = 0; i < ARRAY_LEN (skip); i ++) { if (first && ! g_ascii_strcasecmp (first, skip[i])) first = NULL; @@ -402,12 +433,11 @@ DONE: album = str_get (first); } - free (filename); + str_unref (filename); } else { - char buf[strlen (name) + 1]; - strcpy (buf, name); + SCOPY (buf, name); if (! title) { @@ -424,3 +454,21 @@ DONE: goto DONE; } + +char * last_path_element (char * path) +{ + char * slash = strrchr (path, G_DIR_SEPARATOR); + return (slash && slash[1]) ? slash + 1 : NULL; +} + +void cut_path_element (char * path, char * elem) +{ +#ifdef _WIN32 + if (elem > path + 3) +#else + if (elem > path + 1) +#endif + elem[-1] = 0; /* overwrite slash */ + else + elem[0] = 0; /* leave [drive letter and] leading slash */ +} diff --git a/src/audacious/util.h b/src/audacious/util.h index 1c244f5..7f92cb3 100644 --- a/src/audacious/util.h +++ b/src/audacious/util.h @@ -1,6 +1,6 @@ /* * util.h - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -30,11 +30,19 @@ typedef bool_t(*DirForeachFunc) (const char * path, bool_t dir_foreach (const char * path, DirForeachFunc func, void * user_data); void make_directory(const char * path, mode_t mode); -char * write_temp_file (void * data, int64_t len); +char * write_temp_file (void * data, int64_t len); /* pooled */ -char * get_path_to_self (void); +char * get_path_to_self (void); /* pooled */ + +#ifdef _WIN32 +void get_argv_utf8 (int * argc, char * * * argv); +void free_argv_utf8 (int * argc, char * * * argv); +#endif void describe_song (const char * filename, const Tuple * tuple, char * * title, char * * artist, char * * album); +char * last_path_element (char * path); +void cut_path_element (char * path, char * elem); + #endif /* AUDACIOUS_UTIL_H */ diff --git a/src/audacious/vis_runner.c b/src/audacious/vis_runner.c index 0a6bead..ba8390c 100644 --- a/src/audacious/vis_runner.c +++ b/src/audacious/vis_runner.c @@ -1,6 +1,6 @@ /* * vis_runner.c - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,35 +17,85 @@ * the use of this software. */ -#include +#include #include #include #include +#include + #include "output.h" #include "vis_runner.h" #include "visualization.h" #define INTERVAL 30 /* milliseconds */ +#define FRAMES_PER_NODE 512 -typedef struct { - int time; - float * data; +struct _VisNode { + struct _VisNode * next; int channels; -} VisNode; + int time; + float data[]; +}; + +typedef struct _VisNode VisNode; static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; static bool_t enabled = FALSE; static bool_t playing = FALSE, paused = FALSE, active = FALSE; static VisNode * current_node = NULL; static int current_frames; -static GQueue vis_list = G_QUEUE_INIT; +static VisNode * vis_list = NULL; +static VisNode * vis_list_tail = NULL; +static VisNode * vis_pool = NULL; static int send_source = 0, clear_source = 0; -static void vis_node_free (VisNode * node) +static VisNode * alloc_node_locked (int channels) +{ + VisNode * node; + + if (vis_pool) + { + node = vis_pool; + assert (node->channels == channels); + vis_pool = node->next; + } + else + { + node = g_malloc (offsetof (VisNode, data) + sizeof (float) * channels * FRAMES_PER_NODE); + node->channels = channels; + } + + node->next = NULL; + return node; +} + +static void free_node_locked (VisNode * node) +{ + node->next = vis_pool; + vis_pool = node; +} + +static void push_node_locked (VisNode * node) { - g_free (node->data); - g_free (node); + if (vis_list) + vis_list_tail->next = node; + else + vis_list = node; + + vis_list_tail = node; +} + +static VisNode * pop_node_locked (void) +{ + VisNode * node = vis_list; + vis_list = node->next; + node->next = NULL; + + if (vis_list_tail == node) + vis_list_tail = NULL; + + return node; } static bool_t send_audio (void * unused) @@ -62,21 +112,20 @@ static bool_t send_audio (void * unused) } VisNode * vis_node = NULL; - VisNode * next; - while ((next = g_queue_peek_head (& vis_list))) + while (vis_list) { /* If we are considering a node, stop searching and use it if it is the * most recent (that is, the next one is in the future). Otherwise, * consider the next node if it is not in the future by more than the * length of an interval. */ - if (next->time > outputted + (vis_node ? 0 : INTERVAL)) + if (vis_list->time > outputted + (vis_node ? 0 : INTERVAL)) break; if (vis_node) - vis_node_free (vis_node); + free_node_locked (vis_node); - vis_node = g_queue_pop_head (& vis_list); + vis_node = pop_node_locked (); } pthread_mutex_unlock (& mutex); @@ -86,7 +135,10 @@ static bool_t send_audio (void * unused) vis_send_audio (vis_node->data, vis_node->channels); - vis_node_free (vis_node); + pthread_mutex_lock (& mutex); + free_node_locked (vis_node); + pthread_mutex_unlock (& mutex); + return TRUE; } @@ -101,16 +153,26 @@ static bool_t send_clear (void * unused) return FALSE; } -static void flush (void) +static void flush_locked (void) { - if (current_node) + g_free (current_node); + current_node = NULL; + + while (vis_list) { - vis_node_free (current_node); - current_node = NULL; + VisNode * node = vis_list; + vis_list = node->next; + g_free (node); } - g_queue_foreach (& vis_list, (GFunc) vis_node_free, NULL); - g_queue_clear (& vis_list); + vis_list_tail = NULL; + + while (vis_pool) + { + VisNode * node = vis_pool; + vis_pool = node->next; + g_free (node); + } if (! clear_source) clear_source = g_timeout_add (0, send_clear, NULL); @@ -119,11 +181,11 @@ static void flush (void) void vis_runner_flush (void) { pthread_mutex_lock (& mutex); - flush (); + flush_locked (); pthread_mutex_unlock (& mutex); } -static void start_stop (bool_t new_playing, bool_t new_paused) +static void start_stop_locked (bool_t new_playing, bool_t new_paused) { playing = new_playing; paused = new_paused; @@ -142,7 +204,7 @@ static void start_stop (bool_t new_playing, bool_t new_paused) } if (! active) - flush (); + flush_locked (); else if (! paused) send_source = g_timeout_add (INTERVAL, send_audio, NULL); } @@ -150,7 +212,7 @@ static void start_stop (bool_t new_playing, bool_t new_paused) void vis_runner_start_stop (bool_t new_playing, bool_t new_paused) { pthread_mutex_lock (& mutex); - start_stop (new_playing, new_paused); + start_stop_locked (new_playing, new_paused); pthread_mutex_unlock (& mutex); } @@ -166,20 +228,15 @@ void vis_runner_pass_audio (int time, float * data, int samples, int * multiple nodes from the same call. If current_node is present, it was * partly built in the last call and needs to be finished. */ - if (current_node && current_node->channels != channels) - { - vis_node_free (current_node); - current_node = NULL; - } - int at = 0; while (1) { - if (! current_node) + if (current_node) + assert (current_node->channels == channels); + else { int node_time = time; - VisNode * last; /* There is no partly-built node, so start a new one. Normally * there will be nodes in the queue already; if so, we want to copy @@ -188,8 +245,8 @@ void vis_runner_pass_audio (int time, float * data, int samples, int * queue, we are at the beginning of the song or had an underrun, * and we want to copy the earliest audio data we have. */ - if ((last = g_queue_peek_tail (& vis_list))) - node_time = last->time + INTERVAL; + if (vis_list_tail) + node_time = vis_list_tail->time + INTERVAL; at = channels * (int) ((int64_t) (node_time - time) * rate / 1000); @@ -198,10 +255,8 @@ void vis_runner_pass_audio (int time, float * data, int samples, int if (at >= samples) break; - current_node = g_malloc (sizeof (VisNode)); + current_node = alloc_node_locked (channels); current_node->time = node_time; - current_node->data = g_malloc (sizeof (float) * channels * 512); - current_node->channels = channels; current_frames = 0; } @@ -210,14 +265,14 @@ void vis_runner_pass_audio (int time, float * data, int samples, int * wait for more data to be passed in the next call. If we do fill the * node, we loop and start building a new one. */ - int copy = MIN (samples - at, channels * (512 - current_frames)); + int copy = MIN (samples - at, channels * (FRAMES_PER_NODE - current_frames)); memcpy (current_node->data + channels * current_frames, data + at, sizeof (float) * copy); current_frames += copy / channels; - if (current_frames < 512) + if (current_frames < FRAMES_PER_NODE) break; - g_queue_push_tail (& vis_list, current_node); + push_node_locked (current_node); current_node = NULL; } @@ -229,6 +284,6 @@ void vis_runner_enable (bool_t enable) { pthread_mutex_lock (& mutex); enabled = enable; - start_stop (playing, paused); + start_stop_locked (playing, paused); pthread_mutex_unlock (& mutex); } diff --git a/src/audtool/Makefile b/src/audtool/Makefile index fa78492..90c822c 100644 --- a/src/audtool/Makefile +++ b/src/audtool/Makefile @@ -6,18 +6,13 @@ SRCS = main.c \ handlers_playqueue.c \ handlers_vitals.c \ handlers_equalizer.c \ - report.c + report.c \ + wrappers.c + +EXT_DEPS += ../dbus/aud-dbus.a include ../../buildsys.mk include ../../extra.mk -CPPFLAGS := -I.. -I../.. \ - ${CPPFLAGS} \ - ${AUDACIOUS_DEFINES} \ - ${DBUS_CFLAGS} \ - ${GTK_CFLAGS} - -LIBS := -L../libaudclient -laudclient \ - ${LIBS} \ - ${DBUS_LIBS} \ - ${GTK_LIBS} +CPPFLAGS := -I../.. -I../dbus ${CPPFLAGS} ${GLIB_CFLAGS} ${GIO_CFLAGS} +LIBS := ../dbus/aud-dbus.a ${LIBS} ${GLIB_LIBS} ${GIO_LIBS} diff --git a/src/audtool/audtool.h b/src/audtool/audtool.h index 6db161e..cb62488 100644 --- a/src/audtool/audtool.h +++ b/src/audtool/audtool.h @@ -21,112 +21,117 @@ #ifndef AUDTOOL_H #define AUDTOOL_H -#include - -extern DBusGProxy *dbus_proxy; - -struct commandhandler { - gchar *name; - void (*handler)(gint argc, gchar **argv); - gchar *desc; - gint args; +#include "aud-dbus.h" + +struct commandhandler +{ + char * name; + void (* handler) (int argc, char * * argv); + char * desc; + int args; }; -extern struct commandhandler handlers[]; - -extern void audtool_report(const gchar *str, ...); -extern void audtool_whine(const gchar *str, ...); -extern void audtool_whine_args(const gchar *name, const gchar *str, ...); -extern void audtool_whine_tuple_fields(void); - -extern void get_handlers_list(gint, gchar **); -extern void get_current_song(gint, gchar **); -extern void get_current_song_filename(gint, gchar **); -extern void get_current_song_length(gint, gchar **); -extern void get_current_song_length_seconds(gint, gchar **); -extern void get_current_song_length_frames(gint, gchar **); -extern void get_current_song_output_length(gint, gchar **); -extern void get_current_song_output_length_seconds(gint, gchar **); -extern void get_current_song_output_length_frames(gint, gchar **); -extern void get_current_song_bitrate(gint, gchar **); -extern void get_current_song_bitrate_kbps(gint, gchar **); -extern void get_current_song_frequency(gint, gchar **); -extern void get_current_song_frequency_khz(gint, gchar **); -extern void get_current_song_channels(gint, gchar **); -extern void get_current_song_tuple_field_data(gint, gchar **argv); -extern void get_current_song_info(gint argc, gchar **argv); - -extern void get_volume(gint, gchar **); -extern void set_volume(gint, gchar **); - -extern void playlist_position(gint, gchar **); -extern void playlist_advance(gint, gchar **); -extern void playlist_auto_advance_status(gint, gchar **); -extern void playlist_auto_advance_toggle(gint, gchar **); -extern void playlist_reverse(gint, gchar **); -extern void playlist_length(gint, gchar **); -extern void playlist_song(gint, gchar **); -extern void playlist_song_filename(gint, gchar **); -extern void playlist_song_length(gint, gchar **); -extern void playlist_song_length_seconds(gint, gchar **); -extern void playlist_song_length_frames(gint, gchar **); -extern void playlist_display(gint, gchar **); -extern void playlist_position(gint, gchar **); -extern void playlist_jump(gint, gchar **); -extern void playlist_add_url_string(gint, gchar **); -extern void playlist_delete(gint, gchar **); -extern void playlist_clear(gint, gchar **); -extern void playlist_repeat_status(gint, gchar **); -extern void playlist_repeat_toggle(gint, gchar **); -extern void playlist_shuffle_status(gint, gchar **); -extern void playlist_shuffle_toggle(gint, gchar **); -void playlist_stop_after_status (gint argc, gchar * * argv); -void playlist_stop_after_toggle (gint argc, gchar * * argv); -extern void playlist_tuple_field_data(gint, gchar **argv); -extern void playlist_enqueue_to_temp(gint argc, gchar **argv); -extern void playlist_ins_url_string(gint argc, gchar **argv); -extern void playlist_title(gint, gchar **); - -extern void playqueue_add(gint, gchar **); -extern void playqueue_remove(gint, gchar **); -extern void playqueue_is_queued(gint, gchar **); -extern void playqueue_get_queue_position(gint, gchar **); -extern void playqueue_get_list_position(gint, gchar **); -extern void playqueue_display(gint, gchar **); -extern void playqueue_length(gint, gchar **); -extern void playqueue_clear(gint, gchar **); - -extern void playback_play(gint, gchar **); -extern void playback_pause(gint, gchar **); -extern void playback_playpause(gint, gchar **); -extern void playback_stop(gint, gchar **); -extern void playback_playing(gint, gchar **); -extern void playback_paused(gint, gchar **); -extern void playback_stopped(gint, gchar **); -extern void playback_status(gint, gchar **); -extern void playback_seek(gint, gchar **); -extern void playback_seek_relative(gint, gchar **); - -extern void mainwin_show(gint, gchar **); -extern void show_preferences_window(gint, gchar **); -extern void show_jtf_window(gint, gchar **); -extern void show_filebrowser(gint, gchar **); -extern void shutdown_audacious_server(gint, gchar **); -extern void show_about_window(gint, gchar **); - -extern void toggle_aot(gint argc, gchar **argv); -extern void get_skin(gint argc, gchar **argv); -extern void set_skin(gint argc, gchar **argv); -extern void get_version(gint argc, gchar **argv); - -extern void equalizer_get_eq(gint argc, gchar **argv); -extern void equalizer_get_eq_preamp(gint argc, gchar **argv); -extern void equalizer_get_eq_band(gint argc, gchar **argv); -extern void equalizer_set_eq(gint argc, gchar **argv); -extern void equalizer_set_eq_preamp(gint argc, gchar **argv); -extern void equalizer_set_eq_band(gint argc, gchar **argv); -extern void equalizer_active(gint argc, gchar **argv); - -extern gint check_args_playlist_pos(gint argc, gchar **argv); +extern const struct commandhandler handlers[]; +extern ObjAudacious * dbus_proxy; + +void audtool_report (const char * str, ...); +void audtool_whine (const char * str, ...); +void audtool_whine_args (const char * name, const char * str, ...); +void audtool_whine_tuple_fields (void); + +void get_handlers_list (int, char * *); +void get_current_song (int, char * *); +void get_current_song_filename (int, char * *); +void get_current_song_length (int, char * *); +void get_current_song_length_seconds (int, char * *); +void get_current_song_length_frames (int, char * *); +void get_current_song_output_length (int, char * *); +void get_current_song_output_length_seconds (int, char * *); +void get_current_song_output_length_frames (int, char * *); +void get_current_song_bitrate (int, char * *); +void get_current_song_bitrate_kbps (int, char * *); +void get_current_song_frequency (int, char * *); +void get_current_song_frequency_khz (int, char * *); +void get_current_song_channels (int, char * *); +void get_current_song_tuple_field_data (int, char * * argv); +void get_current_song_info (int argc, char * * argv); + +void get_volume (int, char * *); +void set_volume (int, char * *); + +void playlist_position (int, char * *); +void playlist_advance (int, char * *); +void playlist_auto_advance_status (int, char * *); +void playlist_auto_advance_toggle (int, char * *); +void playlist_reverse (int, char * *); +void playlist_length (int, char * *); +void playlist_song (int, char * *); +void playlist_song_filename (int, char * *); +void playlist_song_length (int, char * *); +void playlist_song_length_seconds (int, char * *); +void playlist_song_length_frames (int, char * *); +void playlist_display (int, char * *); +void playlist_position (int, char * *); +void playlist_jump (int, char * *); +void playlist_add_url_string (int, char * *); +void playlist_delete (int, char * *); +void playlist_clear (int, char * *); +void playlist_repeat_status (int, char * *); +void playlist_repeat_toggle (int, char * *); +void playlist_shuffle_status (int, char * *); +void playlist_shuffle_toggle (int, char * *); +void playlist_stop_after_status (int argc, char * * argv); +void playlist_stop_after_toggle (int argc, char * * argv); +void playlist_tuple_field_data (int, char * * argv); +void playlist_enqueue_to_temp (int argc, char * * argv); +void playlist_ins_url_string (int argc, char * * argv); + +void number_of_playlists (int argc, char * * argv); +void current_playlist (int argc, char * * argv); +void set_current_playlist (int argc, char * * argv); +void playlist_title (int argc, char * * argv); +void set_playlist_title (int argc, char * * argv); +void new_playlist (int argc, char * * argv); +void delete_current_playlist (int argc, char * * argv); +void play_current_playlist (int argc, char * * argv); + +void playqueue_add (int, char * *); +void playqueue_remove (int, char * *); +void playqueue_is_queued (int, char * *); +void playqueue_get_queue_position (int, char * *); +void playqueue_get_list_position (int, char * *); +void playqueue_display (int, char * *); +void playqueue_length (int, char * *); +void playqueue_clear (int, char * *); + +void playback_play (int, char * *); +void playback_pause (int, char * *); +void playback_playpause (int, char * *); +void playback_stop (int, char * *); +void playback_playing (int, char * *); +void playback_paused (int, char * *); +void playback_stopped (int, char * *); +void playback_status (int, char * *); +void playback_seek (int, char * *); +void playback_seek_relative (int, char * *); + +void mainwin_show (int, char * *); +void show_preferences_window (int, char * *); +void show_jtf_window (int, char * *); +void show_filebrowser (int, char * *); +void shutdown_audacious_server (int, char * *); +void show_about_window (int, char * *); + +void get_version (int argc, char * * argv); + +void equalizer_get_eq (int argc, char * * argv); +void equalizer_get_eq_preamp (int argc, char * * argv); +void equalizer_get_eq_band (int argc, char * * argv); +void equalizer_set_eq (int argc, char * * argv); +void equalizer_set_eq_preamp (int argc, char * * argv); +void equalizer_set_eq_band (int argc, char * * argv); +void equalizer_active (int argc, char * * argv); + +int check_args_playlist_pos (int argc, char * * argv); #endif diff --git a/src/audtool/handlers_equalizer.c b/src/audtool/handlers_equalizer.c index 2a35ff0..4c70126 100644 --- a/src/audtool/handlers_equalizer.c +++ b/src/audtool/handlers_equalizer.c @@ -1,6 +1,6 @@ /* * handlers_equalizer.c - * Copyright 2007-2008 Yoshiki Yazawa and Matti Hämäläinen + * Copyright 2007-2013 Yoshiki Yazawa, Matti Hämäläinen, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -19,125 +19,105 @@ #include #include -#include -#include -#include -#include "libaudclient/audctrl.h" + #include "audtool.h" +#include "wrappers.h" + +#define NUM_BANDS 10 -void equalizer_get_eq(gint argc, gchar **argv) +void equalizer_get_eq (int argc, char * * argv) { - double preamp; - GArray *bands; - int i; + double preamp = 0.0; + GVariant * var = NULL; - audacious_remote_get_eq(dbus_proxy, &preamp, &bands); + obj_audacious_call_get_eq_sync (dbus_proxy, & preamp, & var, NULL, NULL); - audtool_report("preamp = %.2f", preamp); - for(i=0; i<10; i++){ - printf("%.2f ", g_array_index(bands, gdouble, i)); - } - printf("\n"); - g_array_free(bands, TRUE); + if (! var || ! g_variant_is_of_type (var, G_VARIANT_TYPE ("ad"))) + exit (1); + + audtool_report ("preamp = %.2f", preamp); + + size_t nbands = 0; + const double * bands = g_variant_get_fixed_array (var, & nbands, sizeof (double)); + + if (nbands != NUM_BANDS) + exit (1); + + for (int i = 0; i < NUM_BANDS; i ++) + printf ("%.2f ", bands[i]); + + printf ("\n"); + g_variant_unref (var); } -void equalizer_get_eq_preamp(gint argc, gchar **argv) +void equalizer_get_eq_preamp (int argc, char * * argv) { - audtool_report("preamp = %.2f", audacious_remote_get_eq_preamp(dbus_proxy)); + double preamp = 0; + obj_audacious_call_get_eq_preamp_sync (dbus_proxy, & preamp, NULL, NULL); + audtool_report ("preamp = %.2f", preamp); } -void equalizer_get_eq_band(gint argc, gchar **argv) +void equalizer_get_eq_band (int argc, char * * argv) { - int band; - if (argc < 2) { - audtool_whine_args(argv[0], ""); - exit(1); - } - - band = atoi(argv[1]); - - /* FIXME, XXX, TODO: we should have a function for requesting - * the actual number of bands, if we support dynamic amount some day ... - * -- ccr - */ - if (band < 0 || band > 9) - { - audtool_whine("band number out of range\n"); - exit(1); + audtool_whine_args (argv[0], ""); + exit (1); } - audtool_report("band %d = %.2f", band, audacious_remote_get_eq_band(dbus_proxy, band)); + int band = atoi (argv[1]); + double level = 0; + obj_audacious_call_get_eq_band_sync (dbus_proxy, band, & level, NULL, NULL); + audtool_report ("band %d = %.2f", band, level); } -void equalizer_set_eq(gint argc, gchar **argv) +void equalizer_set_eq (int argc, char * * argv) { - gdouble preamp; - GArray *bands = g_array_sized_new(FALSE, FALSE, sizeof(gdouble), 10); - int i; - - if (argc < 12) + if (argc < 2 + NUM_BANDS) { - audtool_whine_args(argv[0], " "); - exit(1); + audtool_whine_args (argv[0], " " + " "); + exit (1); } - preamp = atof(argv[1]); + double preamp = atof (argv[1]); + double bands[NUM_BANDS]; - for(i=0; i<10; i++){ - gdouble val = atof(argv[i+2]); - g_array_append_val(bands, val); - } + for (int i = 0; i < NUM_BANDS; i ++) + bands[i] = atof (argv[i + 2]); - audacious_remote_set_eq(dbus_proxy, preamp, bands); + GVariant * var = g_variant_new_fixed_array (G_VARIANT_TYPE_DOUBLE, bands, + NUM_BANDS, sizeof (double)); + obj_audacious_call_set_eq_sync (dbus_proxy, preamp, var, NULL, NULL); } -void equalizer_set_eq_preamp(gint argc, gchar **argv) +void equalizer_set_eq_preamp (int argc, char * * argv) { - gdouble preamp; - if (argc < 2) { - audtool_whine_args(argv[0], ""); - exit(1); + audtool_whine_args (argv[0], ""); + exit (1); } - preamp = atof(argv[1]); - - audacious_remote_set_eq_preamp(dbus_proxy, preamp); + double preamp = atof (argv[1]); + obj_audacious_call_set_eq_preamp_sync (dbus_proxy, preamp, NULL, NULL); } -void equalizer_set_eq_band(gint argc, gchar **argv) +void equalizer_set_eq_band (int argc, char * * argv) { - int band; - gdouble preamp; - if (argc < 3) { - audtool_whine_args(argv[0], " "); - exit(1); + audtool_whine_args (argv[0], " "); + exit (1); } - band = atoi(argv[1]); - preamp = atof(argv[2]); - - audacious_remote_set_eq_band(dbus_proxy, band, preamp); + int band = atoi (argv[1]); + double level = atof (argv[2]); + obj_audacious_call_set_eq_band_sync (dbus_proxy, band, level, NULL, NULL); } -void equalizer_active(gint argc, gchar **argv) +void equalizer_active (int argc, char * * argv) { - if (argc < 2) - { - audtool_whine_args(argv[0], ""); - exit(1); - } - - if (!g_ascii_strcasecmp(argv[1], "on")) { - audacious_remote_eq_activate(dbus_proxy, TRUE); - } - else if (!g_ascii_strcasecmp(argv[1], "off")) { - audacious_remote_eq_activate(dbus_proxy, FALSE); - } + generic_on_off (argc, argv, obj_audacious_call_equalizer_activate_sync); } diff --git a/src/audtool/handlers_general.c b/src/audtool/handlers_general.c index 5933e54..e1ff2c2 100644 --- a/src/audtool/handlers_general.c +++ b/src/audtool/handlers_general.c @@ -1,7 +1,7 @@ /* * handlers_general.c - * Copyright 2005-2008 George Averill, William Pitcock, Giacomo Lozito, and - * Matti Hämäläinen + * Copyright 2005-2013 George Averill, William Pitcock, Giacomo Lozito, + * Matti Hämäläinen, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -19,211 +19,96 @@ */ #include -#include -#include -#include -#include "libaudclient/audctrl.h" + #include "audtool.h" +#include "wrappers.h" -void get_volume(gint argc, gchar **argv) +static int get_main_volume (void) { - gint i; - - i = audacious_remote_get_main_volume(dbus_proxy); - - audtool_report("%d", i); + int left = 0, right = 0; + obj_audacious_call_volume_sync (dbus_proxy, & left, & right, NULL, NULL); + return MAX (left, right); } -void set_volume(gint argc, gchar **argv) +void get_volume (int argc, char * * argv) { - gint i, current_volume; - - if (argc < 2) - { - audtool_whine_args(argv[0], "", argv[0]); - exit(1); - } - - current_volume = audacious_remote_get_main_volume(dbus_proxy); - switch (argv[1][0]) - { - case '+': - case '-': - i = current_volume + atoi(argv[1]); - break; - default: - i = atoi(argv[1]); - break; - } - - audacious_remote_set_main_volume(dbus_proxy, i); + audtool_report ("%d", get_main_volume ()); } -void mainwin_show(gint argc, gchar **argv) +void set_volume (int argc, char * * argv) { - if (argc < 2) { - audtool_whine_args(argv[0], ""); - exit(1); - } - - if (!g_ascii_strcasecmp(argv[1], "on")) { - audacious_remote_main_win_toggle(dbus_proxy, TRUE); - return; - } - else if (!g_ascii_strcasecmp(argv[1], "off")) { - audacious_remote_main_win_toggle(dbus_proxy, FALSE); - return; + if (argc < 2) + { + audtool_whine_args (argv[0], ""); + exit (1); } -} -void show_preferences_window(gint argc, gchar **argv) -{ - gboolean show = TRUE; - - if (argc < 2) { -#if 0 - audtool_whine_args(argv[0], ""); - exit(1); -#else - audacious_remote_toggle_prefs_box(dbus_proxy, show); - return; -#endif - } + int vol = atoi (argv[1]); - if (!g_ascii_strcasecmp(argv[1], "on")) - show = TRUE; - else if (!g_ascii_strcasecmp(argv[1], "off")) - show = FALSE; - else { - audtool_whine_args(argv[0], ""); - exit (1); + switch (argv[1][0]) + { + case '+': + case '-': + vol += get_main_volume (); + break; } - audacious_remote_toggle_prefs_box(dbus_proxy, show); + obj_audacious_call_set_volume_sync (dbus_proxy, vol, vol, NULL, NULL); } -void show_about_window(gint argc, gchar **argv) +void mainwin_show (int argc, char * * argv) { - gboolean show = TRUE; - - if (argc < 2) { -#if 0 - audtool_whine_args(argv[0], ""); - exit(1); -#else - audacious_remote_toggle_about_box(dbus_proxy, show); - return; -#endif - } - - if (!g_ascii_strcasecmp(argv[1], "on")) - show = TRUE; - else if (!g_ascii_strcasecmp(argv[1], "off")) - show = FALSE; - else { - audtool_whine_args(argv[0], ""); - exit (1); - } - - audacious_remote_toggle_about_box(dbus_proxy, show); + generic_on_off (argc, argv, obj_audacious_call_show_main_win_sync); } -void show_jtf_window(gint argc, gchar **argv) +void show_preferences_window (int argc, char * * argv) { - gboolean show = TRUE; - - if (argc < 2) { -#if 0 - audtool_whine_args(argv[0], ""); - exit(1); -#else - audacious_remote_toggle_jtf_box(dbus_proxy, show); - return; -#endif - } - if (!g_ascii_strcasecmp(argv[1], "on")) - show = TRUE; - else if (!g_ascii_strcasecmp(argv[1], "off")) - show = FALSE; - else { - audtool_whine_args(argv[0], ""); - exit (1); - } - - audacious_remote_toggle_jtf_box(dbus_proxy, show); + generic_on_off (argc, argv, obj_audacious_call_show_prefs_box_sync); } -void show_filebrowser(gint argc, gchar **argv) +void show_about_window (int argc, char * * argv) { - gboolean show = TRUE; - - if (argc < 2) { -#if 0 - audtool_whine_args(argv[0], ""); - exit(1); -#else - audacious_remote_toggle_filebrowser(dbus_proxy, show); - return; -#endif - } - - if (!g_ascii_strcasecmp(argv[1], "on")) - show = TRUE; - else if (!g_ascii_strcasecmp(argv[1], "off")) - show = FALSE; - else { - audtool_whine_args(argv[0], ""); - exit (1); - } + generic_on_off (argc, argv, obj_audacious_call_show_about_box_sync); +} - audacious_remote_toggle_filebrowser(dbus_proxy, show); +void show_jtf_window (int argc, char * * argv) +{ + generic_on_off (argc, argv, obj_audacious_call_show_jtf_box_sync); } -void shutdown_audacious_server(gint argc, gchar **argv) +void show_filebrowser (int argc, char * * argv) { - audacious_remote_quit(dbus_proxy); + generic_on_off (argc, argv, obj_audacious_call_show_filebrowser_sync); } -void get_handlers_list(gint argc, gchar **argv) +void shutdown_audacious_server (int argc, char * * argv) { - gint i; - - for (i = 0; handlers[i].name != NULL; i++) - { - if (!g_ascii_strcasecmp("", handlers[i].name)) - audtool_report("%s%s:", i == 0 ? "" : "\n", handlers[i].desc); - else - audtool_report(" %-34s - %s", handlers[i].name, handlers[i].desc); - } - - audtool_report(""); - audtool_report("Handlers may be prefixed with `--' (GNU-style long-options) or not, your choice."); - audtool_report("Report bugs to http://redmine.audacious-media-player.org/"); + obj_audacious_call_quit_sync (dbus_proxy, NULL, NULL); } -void toggle_aot(gint argc, gchar **argv) +void get_handlers_list (int argc, char * * argv) { - if (argc < 2) + for (int i = 0; handlers[i].name; i ++) { - audtool_whine_args(argv[0], ""); - exit(1); + if (! g_ascii_strcasecmp ("", handlers[i].name)) + audtool_report ("%s%s:", i == 0 ? "" : "\n", handlers[i].desc); + else + audtool_report (" %-34s - %s", handlers[i].name, handlers[i].desc); } - if (!g_ascii_strcasecmp(argv[1], "on")) { - audacious_remote_toggle_aot(dbus_proxy, TRUE); - return; - } - else if (!g_ascii_strcasecmp(argv[1], "off")) { - audacious_remote_toggle_aot(dbus_proxy, FALSE); - return; - } + audtool_report (""); + audtool_report ("Handlers may be prefixed with `--' (GNU-style long-options) or not, your choice."); + audtool_report ("Report bugs to http://redmine.audacious-media-player.org/"); } -void get_version(gint argc, gchar **argv) +void get_version (int argc, char * * argv) { - gchar *version = NULL; - version = audacious_remote_get_version(dbus_proxy); - if(version) - audtool_report("Audacious %s", version); - g_free(version); + char * version = NULL; + obj_audacious_call_version_sync (dbus_proxy, & version, NULL, NULL); + + if (! version) + exit (1); + + audtool_report ("Audacious %s", version); + g_free (version); } diff --git a/src/audtool/handlers_playback.c b/src/audtool/handlers_playback.c index 754169e..4e7e17f 100644 --- a/src/audtool/handlers_playback.c +++ b/src/audtool/handlers_playback.c @@ -1,6 +1,7 @@ /* * handlers_playback.c - * Copyright 2005-2008 George Averill, William Pitcock, and Matti Hämäläinen + * Copyright 2005-2013 George Averill, William Pitcock, Matti Hämäläinen, and + * John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -18,111 +19,85 @@ */ #include -#include -#include -#include -#include "libaudclient/audctrl.h" + #include "audtool.h" -void playback_play(gint argc, gchar **argv) +void playback_play (int argc, char * * argv) { - audacious_remote_play(dbus_proxy); + obj_audacious_call_play_sync (dbus_proxy, NULL, NULL); } -void playback_pause(gint argc, gchar **argv) +void playback_pause (int argc, char * * argv) { - audacious_remote_pause(dbus_proxy); + obj_audacious_call_pause_sync (dbus_proxy, NULL, NULL); } -void playback_playpause(gint argc, gchar **argv) +void playback_playpause (int argc, char * * argv) { - if (audacious_remote_is_playing(dbus_proxy)) - { - audacious_remote_pause(dbus_proxy); - } - else - { - audacious_remote_play(dbus_proxy); - } + obj_audacious_call_play_pause_sync (dbus_proxy, NULL, NULL); } -void playback_stop(gint argc, gchar **argv) +void playback_stop (int argc, char * * argv) { - audacious_remote_stop(dbus_proxy); + obj_audacious_call_stop_sync (dbus_proxy, NULL, NULL); } -void playback_playing(gint argc, gchar **argv) +void playback_playing (int argc, char * * argv) { - if (!audacious_remote_is_paused(dbus_proxy)) - { - exit(!audacious_remote_is_playing(dbus_proxy)); - } - else - { - exit(1); - } -} + gboolean playing = FALSE; + obj_audacious_call_playing_sync (dbus_proxy, & playing, NULL, NULL); -void playback_paused(gint argc, gchar **argv) -{ - exit(!audacious_remote_is_paused(dbus_proxy)); + exit (! playing); } -void playback_stopped(gint argc, gchar **argv) +void playback_paused (int argc, char * * argv) { - if (!audacious_remote_is_playing(dbus_proxy) && !audacious_remote_is_paused(dbus_proxy)) - { - exit(0); - } - else - { - exit(1); - } + gboolean paused = FALSE; + obj_audacious_call_paused_sync (dbus_proxy, & paused, NULL, NULL); + + exit (! paused); } -void playback_status(gint argc, gchar **argv) +void playback_stopped (int argc, char * * argv) { - if (audacious_remote_is_paused(dbus_proxy)) - { - audtool_report("paused"); - return; - } - else if (audacious_remote_is_playing(dbus_proxy)) - { - audtool_report("playing"); - return; - } - else - { - audtool_report("stopped"); - return; - } + gboolean stopped = FALSE; + obj_audacious_call_stopped_sync (dbus_proxy, & stopped, NULL, NULL); + + exit (! stopped); } -void playback_seek(gint argc, gchar **argv) +void playback_status (int argc, char * * argv) { - if (argc < 2) - { - audtool_whine_args(argv[0], ""); - exit(1); - } + char * status = NULL; + obj_audacious_call_status_sync (dbus_proxy, & status, NULL, NULL); + + if (! status) + exit (1); - audacious_remote_jump_to_time(dbus_proxy, atof(argv[1]) * 1000); + audtool_report ("%s", status); + g_free (status); } -void playback_seek_relative(gint argc, gchar **argv) +void playback_seek (int argc, char * * argv) { - gint oldtime, newtime, diff; + if (argc < 2) + { + audtool_whine_args (argv[0], ""); + exit (1); + } - if (argc < 2) - { - audtool_whine_args(argv[0], ""); - exit(1); - } + obj_audacious_call_seek_sync (dbus_proxy, atof (argv[1]) * 1000, NULL, NULL); +} - oldtime = audacious_remote_get_output_time(dbus_proxy); - diff = atof(argv[1]) * 1000; - newtime = oldtime + diff; +void playback_seek_relative (int argc, char * * argv) +{ + if (argc < 2) + { + audtool_whine_args (argv[0], ""); + exit (1); + } - audacious_remote_jump_to_time(dbus_proxy, newtime); + unsigned oldtime = 0; + obj_audacious_call_time_sync (dbus_proxy, & oldtime, NULL, NULL); + obj_audacious_call_seek_sync (dbus_proxy, oldtime + atof (argv[1]) * 1000, NULL, NULL); } diff --git a/src/audtool/handlers_playlist.c b/src/audtool/handlers_playlist.c index b834bd7..21e8f64 100644 --- a/src/audtool/handlers_playlist.c +++ b/src/audtool/handlers_playlist.c @@ -1,6 +1,6 @@ /* * handlers_playlist.c - * Copyright 2005-2011 George Averill, William Pitcock, Yoshiki Yazawa, + * Copyright 2005-2013 George Averill, William Pitcock, Yoshiki Yazawa, * Matti Hämäläinen, and John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -20,362 +20,347 @@ #include #include -#include -#include -#include -#include "libaudclient/audctrl.h" + #include "audtool.h" +#include "wrappers.h" -void playlist_reverse(gint argc, gchar **argv) +void playlist_reverse (int argc, char * * argv) { - audacious_remote_playlist_prev(dbus_proxy); + obj_audacious_call_reverse_sync (dbus_proxy, NULL, NULL); } -void playlist_advance(gint argc, gchar **argv) +void playlist_advance (int argc, char * * argv) { - audacious_remote_playlist_next(dbus_proxy); + obj_audacious_call_advance_sync (dbus_proxy, NULL, NULL); } -void playlist_auto_advance_status(gint argc, gchar **argv) +void playlist_auto_advance_status (int argc, char * * argv) { - if (audacious_remote_is_advance(dbus_proxy)) - { - audtool_report("on"); - } - else - { - audtool_report("off"); - } + gboolean advance = FALSE; + obj_audacious_call_auto_advance_sync (dbus_proxy, & advance, NULL, NULL); + audtool_report (advance ? "on" : "off"); } -void playlist_auto_advance_toggle(gint argc, gchar **argv) +void playlist_auto_advance_toggle (int argc, char * * argv) { - audacious_remote_toggle_advance(dbus_proxy); + obj_audacious_call_toggle_auto_advance_sync (dbus_proxy, NULL, NULL); } -void playlist_stop_after_status (gint argc, gchar * * argv) +void playlist_stop_after_status (int argc, char * * argv) { - audtool_report (audacious_remote_is_stop_after (dbus_proxy) ? "on" : "off"); + gboolean stop_after = FALSE; + obj_audacious_call_stop_after_sync (dbus_proxy, & stop_after, NULL, NULL); + audtool_report (stop_after ? "on" : "off"); } -void playlist_stop_after_toggle (gint argc, gchar * * argv) +void playlist_stop_after_toggle (int argc, char * * argv) { - audacious_remote_toggle_stop_after (dbus_proxy); + obj_audacious_call_toggle_stop_after_sync (dbus_proxy, NULL, NULL); } -gint check_args_playlist_pos(gint argc, gchar **argv) +int check_args_playlist_pos (int argc, char * * argv) { - gint playpos; - - if (argc < 2) - { - audtool_whine_args(argv[0], ""); - exit(1); - } - - playpos = atoi(argv[1]); + int pos; - if (playpos < 1 || playpos > audacious_remote_get_playlist_length(dbus_proxy)) - { - audtool_whine("invalid playlist position %d ('%s')\n", playpos, argv[1]); - exit(2); - } + if (argc < 2 || (pos = atoi (argv[1])) < 1) + { + audtool_whine_args (argv[0], ""); + exit (1); + } - return playpos; + return pos; } - -static gchar * construct_uri(gchar *string) +static char * construct_uri (char * string) { - gchar *filename = g_strdup(string); - gchar *tmp, *path; - gchar *uri = NULL; + char * filename = g_strdup (string); + char * tmp, * path; + char * uri = NULL; // case 1: filename is raw full path or uri - if (filename[0] == '/' || strstr(filename, "://")) { - uri = g_filename_to_uri(filename, NULL, NULL); - if(!uri) { - uri = g_strdup(filename); - } - g_free(filename); + if (filename[0] == '/' || strstr (filename, "://")) + { + uri = g_filename_to_uri (filename, NULL, NULL); + + if (! uri) + uri = g_strdup (filename); + + g_free (filename); } // case 2: filename is not raw full path nor uri. // make full path with pwd. (using g_build_filename) - else { + else + { path = g_get_current_dir(); - tmp = g_build_filename(path, filename, NULL); - g_free(path); g_free(filename); - uri = g_filename_to_uri(tmp, NULL, NULL); - g_free(tmp); + tmp = g_build_filename (path, filename, NULL); + g_free (path); + g_free (filename); + uri = g_filename_to_uri (tmp, NULL, NULL); + g_free (tmp); } return uri; } -void playlist_add_url_string(gint argc, gchar **argv) +void playlist_add_url_string (int argc, char * * argv) { - gchar *uri; - - if (argc < 2) - { - audtool_whine_args(argv[0], ""); - exit(1); - } + char * uri; - uri = construct_uri(argv[1]); - if (uri) { - audacious_remote_playlist_add_url_string(dbus_proxy, uri); + if (argc < 2) + { + audtool_whine_args (argv[0], ""); + exit (1); } - g_free(uri); + + uri = construct_uri (argv[1]); + + if (! uri) + exit (1); + + obj_audacious_call_add_sync (dbus_proxy, uri, NULL, NULL); + g_free (uri); } -void playlist_delete(gint argc, gchar **argv) +void playlist_delete (int argc, char * * argv) { - gint playpos = check_args_playlist_pos(argc, argv); - audacious_remote_playlist_delete(dbus_proxy, playpos - 1); + int pos = check_args_playlist_pos (argc, argv); + obj_audacious_call_delete_sync (dbus_proxy, pos - 1, NULL, NULL); } -void playlist_length(gint argc, gchar **argv) +void playlist_length (int argc, char * * argv) { - gint i; - - i = audacious_remote_get_playlist_length(dbus_proxy); + audtool_report ("%d", get_playlist_length ()); +} - audtool_report("%d", i); +void playlist_song (int argc, char * * argv) +{ + int pos = check_args_playlist_pos (argc, argv); + char * title = get_entry_title (pos - 1); + audtool_report ("%s", title); + g_free (title); } -void playlist_song(gint argc, gchar **argv) +void number_of_playlists (int argc, char * * argv) { - gint playpos = check_args_playlist_pos(argc, argv); - gchar *song; + int playlists = 0; + obj_audacious_call_number_of_playlists_sync (dbus_proxy, & playlists, NULL, NULL); + audtool_report ("%d", playlists); +} - song = audacious_remote_get_playlist_title(dbus_proxy, playpos - 1); - audtool_report("%s", song); +void current_playlist (int argc, char * * argv) +{ + int playlist = -1; + obj_audacious_call_get_active_playlist_sync (dbus_proxy, & playlist, NULL, NULL); + audtool_report ("%d", playlist + 1); } -void playlist_title(gint argc, gchar **argv) +void set_current_playlist (int argc, char * * argv) { - gchar *title; + if (argc < 2) + { + audtool_whine_args (argv[0], ""); + exit (1); + } - title = audacious_remote_playlist_get_active_name(dbus_proxy); - audtool_report("%s", title); + obj_audacious_call_set_active_playlist_sync (dbus_proxy, atoi (argv[1]) - 1, NULL, NULL); } -void playlist_song_length(gint argc, gchar **argv) +void playlist_title (int argc, char * * argv) { - gint playpos = check_args_playlist_pos(argc, argv); - gint frames, length; + char * title = NULL; + obj_audacious_call_get_active_playlist_name_sync (dbus_proxy, & title, NULL, NULL); - frames = audacious_remote_get_playlist_time(dbus_proxy, playpos - 1); - length = frames / 1000; + if (! title) + exit (1); - audtool_report("%d:%.2d", length / 60, length % 60); + audtool_report ("%s", title); + g_free (title); } -void playlist_song_length_seconds(gint argc, gchar **argv) +void set_playlist_title (int argc, char * * argv) { - gint playpos = check_args_playlist_pos(argc, argv); - gint frames, length; + if (argc < 2) + { + audtool_whine_args (argv[0], ""); + exit (1); + } - frames = audacious_remote_get_playlist_time(dbus_proxy, playpos - 1); - length = frames / 1000; + obj_audacious_call_set_active_playlist_name_sync (dbus_proxy, argv[1], NULL, NULL); +} - audtool_report("%d", length); +void new_playlist (int argc, char * * argv) +{ + obj_audacious_call_new_playlist_sync (dbus_proxy, NULL, NULL); } -void playlist_song_length_frames(gint argc, gchar **argv) +void delete_current_playlist (int argc, char * * argv) { - gint playpos = check_args_playlist_pos(argc, argv); - gint frames; + obj_audacious_call_delete_active_playlist_sync (dbus_proxy, NULL, NULL); +} - frames = audacious_remote_get_playlist_time(dbus_proxy, playpos - 1); +void play_current_playlist (int argc, char * * argv) +{ + obj_audacious_call_play_active_playlist_sync (dbus_proxy, NULL, NULL); +} - audtool_report("%d", frames); +void playlist_song_length (int argc, char * * argv) +{ + int pos = check_args_playlist_pos (argc, argv); + int length = get_entry_length (pos - 1) / 1000; + audtool_report ("%d:%.2d", length / 60, length % 60); } -void playlist_display(gint argc, gchar **argv) +void playlist_song_length_seconds (int argc, char * * argv) { - gint i, ii, frames, length, total; - gchar *songname; - gchar *fmt = NULL, *p; - gint column; + int pos = check_args_playlist_pos (argc, argv); + int length = get_entry_length (pos - 1) / 1000; + audtool_report ("%d", length); +} - i = audacious_remote_get_playlist_length(dbus_proxy); +void playlist_song_length_frames (int argc, char * * argv) +{ + int pos = check_args_playlist_pos (argc, argv); + int length = get_entry_length (pos - 1); + audtool_report ("%d", length); +} - audtool_report("%d track%s.", i, i != 1 ? "s" : ""); +void playlist_display (int argc, char * * argv) +{ + int entries = get_playlist_length (); - total = 0; + audtool_report ("%d track%s.", entries, entries != 1 ? "s" : ""); - for (ii = 0; ii < i; ii++) - { - songname = audacious_remote_get_playlist_title(dbus_proxy, ii); - frames = audacious_remote_get_playlist_time(dbus_proxy, ii); - length = frames / 1000; - total += length; + int total = 0; - /* adjust width for multi byte characters */ - column = 60; - if(songname){ - p = songname; - while(*p){ - gint stride; - stride = g_utf8_next_char(p) - p; - if(g_unichar_iswide(g_utf8_get_char(p)) - || g_unichar_iswide_cjk(g_utf8_get_char(p)) - ){ - column += (stride - 2); - } - else { - column += (stride - 1); - } - p = g_utf8_next_char(p); - } + for (int entry = 0; entry < entries; entry ++) + { + char * title = get_entry_title (entry); + int length = get_entry_length (entry) / 1000; - } + total += length; - fmt = g_strdup_printf("%%4d | %%-%ds | %%d:%%.2d", column); - audtool_report(fmt, ii + 1, songname, length / 60, length % 60); - g_free(fmt); - } + /* adjust width for multi byte characters */ + int column = 60; - audtool_report("Total length: %d:%.2d", total / 60, total % 60); -} + for (const char * p = title; * p; p = g_utf8_next_char (p)) + { + int stride = g_utf8_next_char (p) - p; -void playlist_position(gint argc, gchar **argv) -{ - gint i; + if (g_unichar_iswide (g_utf8_get_char (p)) || + g_unichar_iswide_cjk (g_utf8_get_char (p))) + column += (stride - 2); + else + column += (stride - 1); + } - i = audacious_remote_get_playlist_pos(dbus_proxy); + char * fmt = g_strdup_printf ("%%4d | %%-%ds | %%d:%%.2d", column); + audtool_report (fmt, entry + 1, title, length / 60, length % 60); - audtool_report("%d", i + 1); + g_free (fmt); + g_free (title); + } + + audtool_report ("Total length: %d:%.2d", total / 60, total % 60); } -void playlist_song_filename (gint argc, gchar * * argv) +void playlist_position (int argc, char * * argv) { - gint playpos = check_args_playlist_pos (argc, argv); - gchar * uri, * filename; - - uri = audacious_remote_get_playlist_file (dbus_proxy, playpos - 1); - filename = (uri != NULL) ? g_filename_from_uri (uri, NULL, NULL) : NULL; - - audtool_report ("%s", (filename != NULL) ? filename : (uri != NULL) ? uri : - _("Position not found.")); + audtool_report ("%d", get_current_entry () + 1); +} - g_free (uri); +void playlist_song_filename (int argc, char * * argv) +{ + int pos = check_args_playlist_pos (argc, argv); + char * filename = get_entry_filename (pos - 1); + audtool_report ("%s", filename); g_free (filename); } -void playlist_jump(gint argc, gchar **argv) +void playlist_jump (int argc, char * * argv) { - gint playpos = check_args_playlist_pos(argc, argv); - - audacious_remote_set_playlist_pos(dbus_proxy, playpos - 1); + int pos = check_args_playlist_pos (argc, argv); + obj_audacious_call_jump_sync (dbus_proxy, pos - 1, NULL, NULL); } -void playlist_clear(gint argc, gchar **argv) +void playlist_clear (int argc, char * * argv) { - audacious_remote_stop(dbus_proxy); - audacious_remote_playlist_clear(dbus_proxy); + obj_audacious_call_clear_sync (dbus_proxy, NULL, NULL); } -void playlist_repeat_status(gint argc, gchar **argv) +void playlist_repeat_status (int argc, char * * argv) { - if (audacious_remote_is_repeat(dbus_proxy)) - { - audtool_report("on"); - } - else - { - audtool_report("off"); - } + gboolean repeat = FALSE; + obj_audacious_call_repeat_sync (dbus_proxy, & repeat, NULL, NULL); + audtool_report (repeat ? "on" : "off"); } -void playlist_repeat_toggle(gint argc, gchar **argv) +void playlist_repeat_toggle (int argc, char * * argv) { - audacious_remote_toggle_repeat(dbus_proxy); + obj_audacious_call_toggle_repeat_sync (dbus_proxy, NULL, NULL); } -void playlist_shuffle_status(gint argc, gchar **argv) +void playlist_shuffle_status (int argc, char * * argv) { - if (audacious_remote_is_shuffle(dbus_proxy)) - { - audtool_report("on"); - } - else - { - audtool_report("off"); - } + gboolean shuffle = FALSE; + obj_audacious_call_shuffle_sync (dbus_proxy, & shuffle, NULL, NULL); + audtool_report (shuffle ? "on" : "off"); } -void playlist_shuffle_toggle(gint argc, gchar **argv) +void playlist_shuffle_toggle (int argc, char * * argv) { - audacious_remote_toggle_shuffle(dbus_proxy); + obj_audacious_call_toggle_shuffle_sync (dbus_proxy, NULL, NULL); } -void playlist_tuple_field_data(gint argc, gchar **argv) +void playlist_tuple_field_data (int argc, char * * argv) { - gint i; - gchar *data; - - if (argc < 3) - { - audtool_whine_args(argv[0], "<fieldname> <position>"); - audtool_whine_tuple_fields(); - exit(1); - } + int pos; - i = atoi(argv[2]); - - if (i < 1 || i > audacious_remote_get_playlist_length(dbus_proxy)) - { - audtool_whine("invalid playlist position %d\n", i); - exit(1); - } - - if (!(data = audacious_get_tuple_field_data(dbus_proxy, argv[1], i - 1))) - { - return; - } - - audtool_report("%s", data); + if (argc < 3 || (pos = atoi (argv[2])) < 1) + { + audtool_whine_args (argv[0], "<fieldname> <position>"); + audtool_whine_tuple_fields (); + exit (1); + } - g_free(data); + char * str = get_entry_field (pos - 1, argv[1]); + audtool_report ("%s", str); + g_free (str); } -void playlist_ins_url_string(gint argc, gchar **argv) +void playlist_ins_url_string (int argc, char * * argv) { - gint pos = -1; - gchar *uri; + int pos; - if (argc < 3) + if (argc < 3 || (pos = atoi (argv[2])) < 1) { - audtool_whine_args(argv[0], "<url> <position>"); - exit(1); + audtool_whine_args (argv[0], "<url> <position>"); + exit (1); } - pos = atoi(argv[2]) - 1; - if(pos >= 0) { - uri = construct_uri(argv[1]); - if (uri) { - audacious_remote_playlist_ins_url_string(dbus_proxy, uri, pos); - } - g_free(uri); - } + char * uri = construct_uri (argv[1]); + + if (! uri) + exit (1); + + obj_audacious_call_playlist_ins_url_string_sync (dbus_proxy, uri, pos - 1, NULL, NULL); + + g_free (uri); } -void playlist_enqueue_to_temp(gint argc, gchar **argv) +void playlist_enqueue_to_temp (int argc, char * * argv) { - gchar *uri; - if (argc < 2) { - audtool_whine_args(argv[0], "<url>"); - exit(1); + audtool_whine_args (argv[0], "<url>"); + exit (1); } - uri = construct_uri(argv[1]); - if (uri) { - audacious_remote_playlist_enqueue_to_temp(dbus_proxy, uri); - } - g_free(uri); + char * uri = construct_uri (argv[1]); + + if (! uri) + exit (1); + + obj_audacious_call_playlist_enqueue_to_temp_sync (dbus_proxy, uri, NULL, NULL); + + g_free (uri); } diff --git a/src/audtool/handlers_playqueue.c b/src/audtool/handlers_playqueue.c index 275404e..dd5e509 100644 --- a/src/audtool/handlers_playqueue.c +++ b/src/audtool/handlers_playqueue.c @@ -1,7 +1,7 @@ /* * handlers_playqueue.c - * Copyright 2005-2008 George Averill, William Pitcock, Yoshiki Yazawa, and - * Matti Hämäläinen + * Copyright 2005-2013 George Averill, William Pitcock, Yoshiki Yazawa, + * Matti Hämäläinen, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -19,122 +19,85 @@ */ #include <stdlib.h> -#include <string.h> -#include <glib.h> -#include <locale.h> -#include "libaudclient/audctrl.h" + #include "audtool.h" +#include "wrappers.h" -void playqueue_add(gint argc, gchar **argv) +void playqueue_add (int argc, char * * argv) { - gint i = check_args_playlist_pos(argc, argv); - - if (!(audacious_remote_playqueue_is_queued(dbus_proxy, i - 1))) - audacious_remote_playqueue_add(dbus_proxy, i - 1); + int pos = check_args_playlist_pos (argc, argv); + obj_audacious_call_playqueue_add_sync (dbus_proxy, pos - 1, NULL, NULL); } -void playqueue_remove(gint argc, gchar **argv) +void playqueue_remove (int argc, char * * argv) { - gint i = check_args_playlist_pos(argc, argv); + int pos = check_args_playlist_pos (argc, argv); + obj_audacious_call_playqueue_remove_sync (dbus_proxy, pos - 1, NULL, NULL); +} - if (audacious_remote_playqueue_is_queued(dbus_proxy, i - 1)) - audacious_remote_playqueue_remove(dbus_proxy, i - 1); +void playqueue_is_queued (int argc, char * * argv) +{ + int pos = check_args_playlist_pos (argc, argv); + find_in_queue (pos - 1); /* exits if not found */ + audtool_report ("OK"); } -void playqueue_is_queued(gint argc, gchar **argv) +void playqueue_get_queue_position (int argc, char * * argv) { - gint i = check_args_playlist_pos(argc, argv); + int pos = check_args_playlist_pos (argc, argv); + audtool_report ("%d", find_in_queue (pos - 1) + 1); +} - if (audacious_remote_playqueue_is_queued(dbus_proxy, i - 1)) { - audtool_report("OK"); - exit(0); - } - else - exit(1); +void playqueue_get_list_position (int argc, char * * argv) +{ + int qpos = check_args_playlist_pos (argc, argv); + audtool_report ("%d", get_queue_entry (qpos - 1) + 1); } -void playqueue_get_queue_position(gint argc, gchar **argv) +void playqueue_display (int argc, char * * argv) { - gint pos, i = check_args_playlist_pos(argc, argv); + int qlength = get_queue_length (); - pos = audacious_remote_get_playqueue_queue_position(dbus_proxy, i - 1) + 1; + audtool_report ("%d queued tracks.", qlength); - if (pos < 1) - return; + int total = 0; - audtool_report("%d", pos); -} + for (int qpos = 0; qpos < qlength; qpos ++) + { + int pos = get_queue_entry (qpos); + char * title = get_entry_title (pos); + int length = get_entry_length (pos) / 1000; -void playqueue_get_list_position(gint argc, gchar **argv) -{ - gint pos, i = check_args_playlist_pos(argc, argv); + total += length; - pos = audacious_remote_get_playqueue_list_position(dbus_proxy, i - 1) + 1; + /* adjust width for multi byte characters */ + int column = 60; - if (pos < 1) - return; + for (const char * p = title; * p; p = g_utf8_next_char (p)) + { + int stride = g_utf8_next_char (p) - p; - audtool_report("%d", pos); -} + if (g_unichar_iswide (g_utf8_get_char (p)) || + g_unichar_iswide_cjk (g_utf8_get_char (p))) + column += (stride - 2); + else + column += (stride - 1); + } -void playqueue_display(gint argc, gchar **argv) -{ - gint i, ii, position, frames, length, total; - gchar *songname; - gchar *fmt = NULL, *p; - gint column; - - i = audacious_remote_get_playqueue_length(dbus_proxy); - - audtool_report("%d queued tracks.", i); - - total = 0; - - for (ii = 0; ii < i; ii++) - { - position = audacious_remote_get_playqueue_list_position(dbus_proxy, ii); - songname = audacious_remote_get_playlist_title(dbus_proxy, position); - frames = audacious_remote_get_playlist_time(dbus_proxy, position); - length = frames / 1000; - total += length; - - /* adjust width for multi byte characters */ - column = 60; - if(songname) { - p = songname; - while(*p){ - gint stride; - stride = g_utf8_next_char(p) - p; - if(g_unichar_iswide(g_utf8_get_char(p)) - || g_unichar_iswide_cjk(g_utf8_get_char(p)) - ){ - column += (stride - 2); - } - else { - column += (stride - 1); - } - p = g_utf8_next_char(p); - } - } - - fmt = g_strdup_printf("%%4d | %%4d | %%-%ds | %%d:%%.2d", column); - audtool_report(fmt, ii + 1, position + 1, songname, length / 60, length % 60); - g_free(fmt); - } - - audtool_report("Total length: %d:%.2d", total / 60, total % 60); + char * fmt = g_strdup_printf ("%%4d | %%4d | %%-%ds | %%d:%%.2d", column); + audtool_report (fmt, qpos + 1, pos + 1, title, length / 60, length % 60); + g_free (fmt); + } + + audtool_report ("Total length: %d:%.2d", total / 60, total % 60); } -void playqueue_length(gint argc, gchar **argv) +void playqueue_length (int argc, char * * argv) { - gint i; - - i = audacious_remote_get_playqueue_length(dbus_proxy); - - audtool_report("%d", i); + audtool_report ("%d", get_queue_length ()); } -void playqueue_clear(gint argc, gchar **argv) +void playqueue_clear (int argc, char * * argv) { - audacious_remote_playqueue_clear(dbus_proxy); + obj_audacious_call_playqueue_clear_sync (dbus_proxy, NULL, NULL); } diff --git a/src/audtool/handlers_vitals.c b/src/audtool/handlers_vitals.c index f2325b4..68918dc 100644 --- a/src/audtool/handlers_vitals.c +++ b/src/audtool/handlers_vitals.c @@ -1,6 +1,6 @@ /* * handlers_vitals.c - * Copyright 2005-2007 George Averill and William Pitcock + * Copyright 2005-2013 George Averill, William Pitcock, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -18,162 +18,112 @@ */ #include <stdlib.h> -#include <string.h> -#include <glib.h> -#include <glib/gi18n.h> -#include <locale.h> -#include "libaudclient/audctrl.h" + #include "audtool.h" +#include "wrappers.h" -void get_current_song(gint argc, gchar **argv) +void get_current_song (int argc, char * * argv) { - gint playpos = audacious_remote_get_playlist_pos(dbus_proxy); - gchar *song = audacious_remote_get_playlist_title(dbus_proxy, playpos); - - if (!song) - { - audtool_report("No song playing."); - return; - } - - audtool_report("%s", song); + char * title = get_entry_title (get_current_entry ()); + audtool_report ("%s", title); + g_free (title); } -void get_current_song_filename (gint argc, gchar * * argv) +void get_current_song_filename (int argc, char * * argv) { - gint playpos = audacious_remote_get_playlist_pos (dbus_proxy); - gchar * uri, * filename; - - uri = audacious_remote_get_playlist_file (dbus_proxy, playpos); - filename = (uri != NULL) ? g_filename_from_uri (uri, NULL, NULL) : NULL; - - audtool_report ("%s", (filename != NULL) ? filename : (uri != NULL) ? uri : - _("No song playing.")); - - g_free (uri); + char * filename = get_entry_filename (get_current_entry ()); + audtool_report ("%s", filename); g_free (filename); } -void get_current_song_output_length(gint argc, gchar **argv) +void get_current_song_output_length (int argc, char * * argv) { - gint frames = audacious_remote_get_output_time(dbus_proxy); - gint length = frames / 1000; - - audtool_report("%d:%.2d", length / 60, length % 60); + int time = get_current_time () / 1000; + audtool_report ("%d:%.2d", time / 60, time % 60); } -void get_current_song_output_length_seconds(gint argc, gchar **argv) +void get_current_song_output_length_seconds (int argc, char * * argv) { - gint frames = audacious_remote_get_output_time(dbus_proxy); - gint length = frames / 1000; - - audtool_report("%d", length); + int time = get_current_time () / 1000; + audtool_report ("%d", time); } -void get_current_song_output_length_frames(gint argc, gchar **argv) +void get_current_song_output_length_frames (int argc, char * * argv) { - gint frames = audacious_remote_get_output_time(dbus_proxy); - - audtool_report("%d", frames); + int time = get_current_time (); + audtool_report ("%d", time); } -void get_current_song_length(gint argc, gchar **argv) +void get_current_song_length (int argc, char * * argv) { - gint playpos = audacious_remote_get_playlist_pos(dbus_proxy); - gint frames = audacious_remote_get_playlist_time(dbus_proxy, playpos); - gint length = frames / 1000; - - audtool_report("%d:%.2d", length / 60, length % 60); + int length = get_entry_length (get_current_entry ()) / 1000; + audtool_report ("%d:%.2d", length / 60, length % 60); } -void get_current_song_length_seconds(gint argc, gchar **argv) +void get_current_song_length_seconds (int argc, char * * argv) { - gint playpos = audacious_remote_get_playlist_pos(dbus_proxy); - gint frames = audacious_remote_get_playlist_time(dbus_proxy, playpos); - gint length = frames / 1000; - - audtool_report("%d", length); + int length = get_entry_length (get_current_entry ()) / 1000; + audtool_report ("%d", length); } -void get_current_song_length_frames(gint argc, gchar **argv) +void get_current_song_length_frames (int argc, char * * argv) { - gint playpos = audacious_remote_get_playlist_pos(dbus_proxy); - gint frames = audacious_remote_get_playlist_time(dbus_proxy, playpos); - - audtool_report("%d", frames); + int length = get_entry_length (get_current_entry ()); + audtool_report ("%d", length); } -void get_current_song_bitrate(gint argc, gchar **argv) +void get_current_song_bitrate (int argc, char * * argv) { - gint rate, freq, nch; - - audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch); - - audtool_report("%d", rate); + int bitrate; + get_current_info (& bitrate, NULL, NULL); + audtool_report ("%d", bitrate); } -void get_current_song_bitrate_kbps(gint argc, gchar **argv) +void get_current_song_bitrate_kbps (int argc, char * * argv) { - gint rate, freq, nch; - - audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch); - - audtool_report("%d", rate / 1000); + int bitrate; + get_current_info (& bitrate, NULL, NULL); + audtool_report ("%d", bitrate / 1000); } -void get_current_song_frequency(gint argc, gchar **argv) +void get_current_song_frequency (int argc, char * * argv) { - gint rate, freq, nch; - - audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch); - - audtool_report("%d", freq); + int samplerate; + get_current_info (NULL, & samplerate, NULL); + audtool_report ("%d", samplerate); } -void get_current_song_frequency_khz(gint argc, gchar **argv) +void get_current_song_frequency_khz (int argc, char * * argv) { - gint rate, freq, nch; - - audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch); - - audtool_report("%0.1f", (gfloat) freq / 1000); + int samplerate; + get_current_info (NULL, & samplerate, NULL); + audtool_report ("%d", samplerate / 1000); } -void get_current_song_channels(gint argc, gchar **argv) +void get_current_song_channels (int argc, char * * argv) { - gint rate, freq, nch; - - audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch); - - audtool_report("%d", nch); + int channels; + get_current_info (NULL, NULL, & channels); + audtool_report ("%d", channels); } -void get_current_song_tuple_field_data(gint argc, gchar **argv) +void get_current_song_tuple_field_data (int argc, char * * argv) { - gchar *data; - - if (argc < 2) - { - audtool_whine_args(argv[0], "<fieldname>"); - audtool_whine_tuple_fields(); - exit(1); - } - - if (!(data = audacious_get_tuple_field_data(dbus_proxy, argv[1], audacious_remote_get_playlist_pos(dbus_proxy)))) - { - return; - } - - audtool_report("%s", data); - - g_free(data); + if (argc < 2) + { + audtool_whine_args (argv[0], "<fieldname>"); + audtool_whine_tuple_fields(); + exit (1); + } + + char * str = get_entry_field (get_current_entry (), argv[1]); + audtool_report ("%s", str); + g_free (str); } -void get_current_song_info(gint argc, gchar **argv) +void get_current_song_info (int argc, char * * argv) { - gint rate, freq, nch; - - audacious_remote_get_info(dbus_proxy, &rate, &freq, &nch); - audtool_report("rate = %d freq = %d nch = %d", rate, freq, nch); + int bitrate, samplerate, channels; + get_current_info (& bitrate, & samplerate, & channels); + audtool_report ("rate = %d freq = %d nch = %d", bitrate, samplerate, channels); } - diff --git a/src/audtool/main.c b/src/audtool/main.c index 0fc45f7..83c959f 100644 --- a/src/audtool/main.c +++ b/src/audtool/main.c @@ -1,6 +1,6 @@ /* * main.c - * Copyright 2005-2011 George Averill, William Pitcock, Yoshiki Yazawa, and + * Copyright 2005-2013 George Averill, William Pitcock, Yoshiki Yazawa, and * John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -20,89 +20,93 @@ #include <stdio.h> #include <stdlib.h> -#include <string.h> -#include <glib.h> #include <locale.h> -#include "libaudclient/audctrl.h" + #include "audtool.h" -struct commandhandler handlers[] = { - {"<sep>", NULL, "Vital information", 0}, - {"current-song", get_current_song, "returns current song title", 0}, - {"current-song-filename", get_current_song_filename, "returns current song filename", 0}, - {"current-song-length", get_current_song_length, "returns current song length", 0}, - {"current-song-length-seconds", get_current_song_length_seconds, "returns current song length in seconds", 0}, - {"current-song-length-frames", get_current_song_length_frames, "returns current song length in frames", 0}, - {"current-song-output-length", get_current_song_output_length, "returns current song output length", 0}, - {"current-song-output-length-seconds", get_current_song_output_length_seconds, "returns current song output length in seconds", 0}, - {"current-song-output-length-frames", get_current_song_output_length_frames, "returns current song output length in frames", 0}, - {"current-song-bitrate", get_current_song_bitrate, "returns current song bitrate in bits per second", 0}, - {"current-song-bitrate-kbps", get_current_song_bitrate_kbps, "returns current song bitrate in kilobits per second", 0}, - {"current-song-frequency", get_current_song_frequency, "returns current song frequency in hertz", 0}, - {"current-song-frequency-khz", get_current_song_frequency_khz, "returns current song frequency in kilohertz", 0}, - {"current-song-channels", get_current_song_channels, "returns current song channels", 0}, - {"current-song-tuple-data", get_current_song_tuple_field_data, "returns the value of a tuple field for the current song", 1}, +const struct commandhandler handlers[] = +{ + {"<sep>", NULL, "Vital information", 0}, + {"current-song", get_current_song, "returns current song title", 0}, + {"current-song-filename", get_current_song_filename, "returns current song filename", 0}, + {"current-song-length", get_current_song_length, "returns current song length", 0}, + {"current-song-length-seconds", get_current_song_length_seconds, "returns current song length in seconds", 0}, + {"current-song-length-frames", get_current_song_length_frames, "returns current song length in frames", 0}, + {"current-song-output-length", get_current_song_output_length, "returns current song output length", 0}, + {"current-song-output-length-seconds", get_current_song_output_length_seconds, "returns current song output length in seconds", 0}, + {"current-song-output-length-frames", get_current_song_output_length_frames, "returns current song output length in frames", 0}, + {"current-song-bitrate", get_current_song_bitrate, "returns current song bitrate in bits per second", 0}, + {"current-song-bitrate-kbps", get_current_song_bitrate_kbps, "returns current song bitrate in kilobits per second", 0}, + {"current-song-frequency", get_current_song_frequency, "returns current song frequency in hertz", 0}, + {"current-song-frequency-khz", get_current_song_frequency_khz, "returns current song frequency in kilohertz", 0}, + {"current-song-channels", get_current_song_channels, "returns current song channels", 0}, + {"current-song-tuple-data", get_current_song_tuple_field_data, "returns the value of a tuple field for the current song", 1}, {"current-song-info", get_current_song_info, "returns current song bitrate, frequency and channels", 0}, - - {"<sep>", NULL, "Playlist manipulation", 0}, - {"playlist-advance", playlist_advance, "go to the next song in the playlist", 0}, - {"playlist-auto-advance-status", playlist_auto_advance_status, "returns the status of playlist auto-advance", 0}, - {"playlist-auto-advance-toggle", playlist_auto_advance_toggle, "toggles playlist auto-advance", 0}, - {"playlist-reverse", playlist_reverse, "go to the previous song in the playlist", 0}, - {"playlist-addurl", playlist_add_url_string, "adds a URL to the playlist", 1}, + {"<sep>", NULL, "Playlist manipulation", 0}, + {"playlist-advance", playlist_advance, "go to the next song in the playlist", 0}, + {"playlist-auto-advance-status", playlist_auto_advance_status, "returns the status of playlist auto-advance", 0}, + {"playlist-auto-advance-toggle", playlist_auto_advance_toggle, "toggles playlist auto-advance", 0}, + {"playlist-reverse", playlist_reverse, "go to the previous song in the playlist", 0}, + {"playlist-addurl", playlist_add_url_string, "adds a URL to the playlist", 1}, {"playlist-insurl", playlist_ins_url_string, "inserts a URL at specified position in the playlist", 2}, - {"playlist-addurl-to-new-playlist", playlist_enqueue_to_temp, "adds a URL to the newly created playlist", 1}, - {"playlist-delete", playlist_delete, "deletes a song from the playlist", 1}, - {"playlist-length", playlist_length, "returns the total length of the playlist", 0}, - {"playlist-song", playlist_song, "returns the title of a song in the playlist", 1}, - {"playlist-song-filename", playlist_song_filename, "returns the filename of a song in the playlist", 1}, - {"playlist-song-length", playlist_song_length, "returns the length of a song in the playlist", 1}, - {"playlist-song-length-seconds", playlist_song_length_seconds, "returns the length of a song in the playlist in seconds", 1}, - {"playlist-song-length-frames", playlist_song_length_frames, "returns the length of a song in the playlist in frames", 1}, - {"playlist-display", playlist_display, "returns the entire playlist", 0}, - {"playlist-position", playlist_position, "returns the position in the playlist", 0}, - {"playlist-jump", playlist_jump, "jumps to a position in the playlist", 1}, - {"playlist-clear", playlist_clear, "clears the playlist", 0}, - {"playlist-repeat-status", playlist_repeat_status, "returns the status of playlist repeat", 0}, - {"playlist-repeat-toggle", playlist_repeat_toggle, "toggles playlist repeat", 0}, - {"playlist-shuffle-status", playlist_shuffle_status, "returns the status of playlist shuffle", 0}, - {"playlist-shuffle-toggle", playlist_shuffle_toggle, "toggles playlist shuffle", 0}, - {"playlist-stop-after-status", playlist_stop_after_status, "queries if stopping after current song", 0}, - {"playlist-stop-after-toggle", playlist_stop_after_toggle, "toggles if stopping after current song", 0}, - {"playlist-tuple-data", playlist_tuple_field_data, "returns the value of a tuple field for a song in the playlist", 2}, - {"current-playlist-name", playlist_title, "returns the playlist title of the active playlist", 0}, - - {"<sep>", NULL, "Playqueue manipulation", 0}, - {"playqueue-add", playqueue_add, "adds a song to the playqueue", 1}, - {"playqueue-remove", playqueue_remove, "removes a song from the playqueue", 1}, - {"playqueue-is-queued", playqueue_is_queued, "returns OK if a song is queued", 1}, - {"playqueue-get-queue-position", playqueue_get_queue_position, "returns the playqueue position of a song in the given position in the playlist", 1}, - {"playqueue-get-list-position", playqueue_get_list_position, "returns the playlist position of a song in the given position in the playqueue", 1}, - {"playqueue-length", playqueue_length, "returns the length of the playqueue", 0}, - {"playqueue-display", playqueue_display, "returns a list of currently-queued songs", 0}, - {"playqueue-clear", playqueue_clear, "clears the playqueue", 0}, - - - {"<sep>", NULL, "Playback manipulation", 0}, - {"playback-play", playback_play, "starts/unpauses song playback", 0}, - {"playback-pause", playback_pause, "(un)pauses song playback", 0}, - {"playback-playpause", playback_playpause, "plays/(un)pauses song playback", 0}, - {"playback-stop", playback_stop, "stops song playback", 0}, - {"playback-playing", playback_playing, "returns OK if Audacious is playing", 0}, - {"playback-paused", playback_paused, "returns OK if Audacious is paused", 0}, - {"playback-stopped", playback_stopped, "returns OK if Audacious is stopped", 0}, - {"playback-status", playback_status, "returns the playback status", 0}, - {"playback-seek", playback_seek, "performs an absolute seek", 1}, - {"playback-seek-relative", playback_seek_relative, "performs a seek relative to the current position", 1}, - - - {"<sep>", NULL, "Volume control", 0}, - {"get-volume", get_volume, "returns the current volume level in percent", 0}, - {"set-volume", set_volume, "sets the current volume level in percent", 1}, - - - {"<sep>", NULL, "Equalizer manipulation", 0}, + {"playlist-addurl-to-new-playlist", playlist_enqueue_to_temp, "adds a URL to the newly created playlist", 1}, + {"playlist-delete", playlist_delete, "deletes a song from the playlist", 1}, + {"playlist-length", playlist_length, "returns the total length of the playlist", 0}, + {"playlist-song", playlist_song, "returns the title of a song in the playlist", 1}, + {"playlist-song-filename", playlist_song_filename, "returns the filename of a song in the playlist", 1}, + {"playlist-song-length", playlist_song_length, "returns the length of a song in the playlist", 1}, + {"playlist-song-length-seconds", playlist_song_length_seconds, "returns the length of a song in the playlist in seconds", 1}, + {"playlist-song-length-frames", playlist_song_length_frames, "returns the length of a song in the playlist in frames", 1}, + {"playlist-display", playlist_display, "returns the entire playlist", 0}, + {"playlist-position", playlist_position, "returns the position in the playlist", 0}, + {"playlist-jump", playlist_jump, "jumps to a position in the playlist", 1}, + {"playlist-clear", playlist_clear, "clears the playlist", 0}, + {"playlist-repeat-status", playlist_repeat_status, "returns the status of playlist repeat", 0}, + {"playlist-repeat-toggle", playlist_repeat_toggle, "toggles playlist repeat", 0}, + {"playlist-shuffle-status", playlist_shuffle_status, "returns the status of playlist shuffle", 0}, + {"playlist-shuffle-toggle", playlist_shuffle_toggle, "toggles playlist shuffle", 0}, + {"playlist-stop-after-status", playlist_stop_after_status, "queries if stopping after current song", 0}, + {"playlist-stop-after-toggle", playlist_stop_after_toggle, "toggles if stopping after current song", 0}, + {"playlist-tuple-data", playlist_tuple_field_data, "returns the value of a tuple field for a song in the playlist", 2}, + + {"<sep>", NULL, "More playlist manipulation", 0}, + {"number-of-playlists", number_of_playlists, "returns the number of open playlists", 0}, + {"current-playlist", current_playlist, "returns the number of the active playlist", 0}, + {"set-current-playlist", set_current_playlist, "activates the specified playlist", 1}, + {"current-playlist-name", playlist_title, "returns the title of the active playlist", 0}, + {"set-current-playlist-name", set_playlist_title, "sets the title of the active playlist", 1}, + {"new-playlist", new_playlist, "creates a new playlist", 0}, + {"delete-current-playlist", delete_current_playlist, "removes the active playlist", 0}, + {"play-current-playlist", play_current_playlist, "starts or resumes playing the active playlist", 0}, + + {"<sep>", NULL, "Playqueue manipulation", 0}, + {"playqueue-add", playqueue_add, "adds a song to the playqueue", 1}, + {"playqueue-remove", playqueue_remove, "removes a song from the playqueue", 1}, + {"playqueue-is-queued", playqueue_is_queued, "returns OK if a song is queued", 1}, + {"playqueue-get-queue-position", playqueue_get_queue_position, "returns the playqueue position of a song in the given position in the playlist", 1}, + {"playqueue-get-list-position", playqueue_get_list_position, "returns the playlist position of a song in the given position in the playqueue", 1}, + {"playqueue-length", playqueue_length, "returns the length of the playqueue", 0}, + {"playqueue-display", playqueue_display, "returns a list of currently-queued songs", 0}, + {"playqueue-clear", playqueue_clear, "clears the playqueue", 0}, + + {"<sep>", NULL, "Playback manipulation", 0}, + {"playback-play", playback_play, "starts/unpauses song playback", 0}, + {"playback-pause", playback_pause, "(un)pauses song playback", 0}, + {"playback-playpause", playback_playpause, "plays/(un)pauses song playback", 0}, + {"playback-stop", playback_stop, "stops song playback", 0}, + {"playback-playing", playback_playing, "returns OK if Audacious is playing", 0}, + {"playback-paused", playback_paused, "returns OK if Audacious is paused", 0}, + {"playback-stopped", playback_stopped, "returns OK if Audacious is stopped", 0}, + {"playback-status", playback_status, "returns the playback status", 0}, + {"playback-seek", playback_seek, "performs an absolute seek", 1}, + {"playback-seek-relative", playback_seek_relative, "performs a seek relative to the current position", 1}, + + {"<sep>", NULL, "Volume control", 0}, + {"get-volume", get_volume, "returns the current volume level in percent", 0}, + {"set-volume", set_volume, "sets the current volume level in percent", 1}, + + {"<sep>", NULL, "Equalizer manipulation", 0}, {"equalizer-activate", equalizer_active, "activates/deactivates the equalizer", 1}, {"equalizer-get", equalizer_get_eq, "gets the equalizer settings", 0}, {"equalizer-set", equalizer_set_eq, "sets the equalizer settings", 11}, @@ -111,100 +115,104 @@ struct commandhandler handlers[] = { {"equalizer-get-band", equalizer_get_eq_band, "gets the equalizer value in specified band", 1}, {"equalizer-set-band", equalizer_set_eq_band, "sets the equalizer value in the specified band", 2}, + {"<sep>", NULL, "Miscellaneous", 0}, + {"mainwin-show", mainwin_show, "shows/hides the main window", 1}, + {"filebrowser-show", show_filebrowser, "shows/hides the filebrowser", 1}, + {"jumptofile-show", show_jtf_window, "shows/hides the jump to file window", 1}, + {"preferences-show", show_preferences_window, "shows/hides the preferences window", 1}, + {"about-show", show_about_window, "shows/hides the about window", 1}, - {"<sep>", NULL, "Miscellaneous", 0}, - {"mainwin-show", mainwin_show, "shows/hides the main window", 1}, - {"filebrowser-show", show_filebrowser, "shows/hides the filebrowser", 1}, - {"jumptofile-show", show_jtf_window, "shows/hides the jump to file window", 1}, - {"preferences-show", show_preferences_window, "shows/hides the preferences window", 1}, - {"about-show", show_about_window, "shows/hides the about window", 1}, - - {"always-on-top", toggle_aot, "on/off always on top", 1}, {"version", get_version, "shows Audacious version", 0}, - {"shutdown", shutdown_audacious_server, "shuts down Audacious", 0}, + {"shutdown", shutdown_audacious_server, "shuts down Audacious", 0}, + {"<sep>", NULL, "Help system", 0}, + {"list-handlers", get_handlers_list, "shows handlers list", 0}, + {"help", get_handlers_list, "shows handlers list", 0}, - {"<sep>", NULL, "Help system", 0}, - {"list-handlers", get_handlers_list, "shows handlers list", 0}, - {"help", get_handlers_list, "shows handlers list", 0}, + {NULL, NULL, NULL, 0} +}; +ObjAudacious * dbus_proxy = NULL; +static GDBusConnection * connection = NULL; - {NULL, NULL, NULL, 0} -}; +static void audtool_disconnect (void) +{ + g_object_unref (dbus_proxy); + dbus_proxy = NULL; -DBusGProxy *dbus_proxy = NULL; -static DBusGConnection *connection = NULL; + g_dbus_connection_close_sync (connection, NULL, NULL); + connection = NULL; +} -static void -audtool_connect(void) +static void audtool_connect (void) { - GError *error = NULL; + GError * error = NULL; - connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, & error); - if (connection == NULL) - { - fprintf (stderr, "D-Bus Error: %s\n", error->message); - g_error_free (error); - exit (EXIT_FAILURE); - } + if (! connection) + { + fprintf (stderr, "D-Bus error: %s\n", error->message); + g_error_free (error); + exit (EXIT_FAILURE); + } - dbus_proxy = dbus_g_proxy_new_for_name(connection, AUDACIOUS_DBUS_SERVICE, - AUDACIOUS_DBUS_PATH, - AUDACIOUS_DBUS_INTERFACE); -} + dbus_proxy = obj_audacious_proxy_new_sync (connection, 0, + "org.atheme.audacious", "/org/atheme/audacious", NULL, & error); -static void -audtool_disconnect(void) -{ - g_object_unref(dbus_proxy); - dbus_proxy = NULL; + if (! dbus_proxy) + { + fprintf (stderr, "D-Bus error: %s\n", error->message); + g_error_free (error); + g_dbus_connection_close_sync (connection, NULL, NULL); + exit (EXIT_FAILURE); + } + + atexit (audtool_disconnect); } -gint -main(gint argc, gchar **argv) +int main (int argc, char * * argv) { - gint i, j = 0, k = 0; + int i, j = 0, k = 0; - setlocale(LC_CTYPE, ""); + setlocale (LC_CTYPE, ""); #if ! GLIB_CHECK_VERSION (2, 36, 0) - g_type_init(); + g_type_init(); #endif - audtool_connect(); - - if (argc < 2) - { - fprintf (stderr, "Not enough parameters. Try \"audtool help\".\n"); - exit (EXIT_FAILURE); - } - - for (j = 1; j < argc; j++) - { - for (i = 0; handlers[i].name != NULL; i++) - { - if ((!g_ascii_strcasecmp(handlers[i].name, argv[j]) || - !g_ascii_strcasecmp(g_strconcat("--", handlers[i].name, NULL), argv[j])) - && g_ascii_strcasecmp("<sep>", handlers[i].name)) - { - int numargs = handlers[i].args + 1 < argc - j ? handlers[i].args + 1 : argc - j; - handlers[i].handler(numargs, &argv[j]); - j += handlers[i].args; - k++; - if(j >= argc) - break; - } - } - } - - if (k == 0) - { - fprintf (stderr, "Unknown command \"%s\". Try \"audtool help\".\n", argv[1]); - exit (EXIT_FAILURE); - } - - audtool_disconnect(); - - return 0; + audtool_connect (); + + if (argc < 2) + { + fprintf (stderr, "Not enough parameters. Try \"audtool help\".\n"); + exit (EXIT_FAILURE); + } + + for (j = 1; j < argc; j ++) + { + for (i = 0; handlers[i].name != NULL; i++) + { + if ((! g_ascii_strcasecmp (handlers[i].name, argv[j]) || + ! g_ascii_strcasecmp (g_strconcat ("--", handlers[i].name, NULL), + argv[j])) && g_ascii_strcasecmp ("<sep>", handlers[i].name)) + { + int numargs = handlers[i].args + 1 < argc - j ? handlers[i].args + 1 : argc - j; + handlers[i].handler (numargs, & argv[j]); + j += handlers[i].args; + k ++; + + if (j >= argc) + break; + } + } + } + + if (k == 0) + { + fprintf (stderr, "Unknown command \"%s\". Try \"audtool help\".\n", argv[1]); + exit (EXIT_FAILURE); + } + + return 0; } diff --git a/src/audtool/report.c b/src/audtool/report.c index ecb4791..01a2116 100644 --- a/src/audtool/report.c +++ b/src/audtool/report.c @@ -18,81 +18,84 @@ */ #include <stdlib.h> -#include <string.h> -#include <glib.h> -#include <locale.h> -#include "libaudclient/audctrl.h" + #include "audtool.h" -void audtool_report(const gchar *str, ...) +void audtool_report (const char * str, ...) { - gchar *buf; - va_list va; + char * buf; + va_list va; - va_start(va, str); - buf = g_strdup_vprintf(str, va); - va_end(va); + va_start (va, str); + buf = g_strdup_vprintf (str, va); + va_end (va); - g_print("%s\n", buf); - g_free(buf); + g_print ("%s\n", buf); + g_free (buf); } -void audtool_whine(const gchar *str, ...) +void audtool_whine (const char * str, ...) { - gchar *buf; - va_list va; + char * buf; + va_list va; - va_start(va, str); - buf = g_strdup_vprintf(str, va); - va_end(va); + va_start (va, str); + buf = g_strdup_vprintf (str, va); + va_end (va); - g_printerr("audtool: %s", buf); - g_free(buf); + g_printerr ("audtool: %s", buf); + g_free (buf); } -void audtool_whine_args(const gchar *name, const gchar *fmt, ...) +void audtool_whine_args (const char * name, const char * fmt, ...) { - gchar *buf; - va_list va; + char * buf; + va_list va; - va_start(va, fmt); - buf = g_strdup_vprintf(fmt, va); - va_end(va); + va_start (va, fmt); + buf = g_strdup_vprintf (fmt, va); + va_end (va); - g_printerr("audtool: Invalid parameters for %s\n", name); - g_printerr(" syntax: %s %s\n", name, buf); - g_free(buf); + g_printerr ("audtool: Invalid parameters for %s\n", name); + g_printerr (" syntax: %s %s\n", name, buf); + g_free (buf); } -void audtool_whine_tuple_fields(void) +void audtool_whine_tuple_fields (void) { - gint nfields, i; - gchar **fields = audacious_remote_get_tuple_fields(dbus_proxy), - **tmp = fields; - - audtool_whine("Field names include, but are not limited to:\n"); - - for (nfields = 0; tmp && *tmp; nfields++, tmp++); - - tmp = fields; - i = 0; - g_printerr(" "); - while (tmp && *tmp) { - i += g_utf8_strlen(*tmp, -1); - if (i > 45) { - g_printerr("\n "); - i = 0; + char * * fields = NULL; + obj_audacious_call_get_tuple_fields_sync (dbus_proxy, & fields, NULL, NULL); + + if (! fields) + exit (1); + + audtool_whine ("Field names include, but are not limited to:\n"); + + char * * tmp = fields; + int col = 0; + + g_printerr (" "); + + while (tmp && * tmp) + { + col += g_utf8_strlen (* tmp, -1); + + if (col > 45) + { + g_printerr ("\n "); + col = 0; } - g_printerr("%s", *tmp); - if (--nfields > 0) - g_printerr(", "); - g_free(*tmp); - tmp++; + g_printerr ("%s", * tmp); + + g_free (* tmp); + tmp ++; + + if (* tmp) + g_printerr (", "); } - g_printerr("\n"); + g_printerr ("\n"); - g_free(fields); + g_free (fields); } - diff --git a/src/audtool/wrappers.c b/src/audtool/wrappers.c new file mode 100644 index 0000000..c255954 --- /dev/null +++ b/src/audtool/wrappers.c @@ -0,0 +1,194 @@ +/* + * wrappers.c + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include <stdlib.h> + +#include "audtool.h" +#include "wrappers.h" + +void generic_on_off (int argc, char * * argv, OnOffFunc func) +{ + gboolean show = TRUE; + + if (argc >= 2) + { + if (! g_ascii_strcasecmp (argv[1], "on")) + show = TRUE; + else if (! g_ascii_strcasecmp (argv[1], "off")) + show = FALSE; + else + { + audtool_whine_args (argv[0], "<on/off>"); + exit (1); + } + } + + func (dbus_proxy, show, NULL, NULL); +} + +int get_playlist_length (void) +{ + int length = -1; + obj_audacious_call_length_sync (dbus_proxy, & length, NULL, NULL); + + if (length < 0) + exit (1); + + return length; +} + +int get_queue_length (void) +{ + int length = -1; + obj_audacious_call_get_playqueue_length_sync (dbus_proxy, & length, NULL, NULL); + + if (length < 0) + exit (1); + + return length; +} + +int get_queue_entry (int qpos) +{ + unsigned entry = -1; + obj_audacious_call_queue_get_list_pos_sync (dbus_proxy, qpos, & entry, NULL, NULL); + + if (entry == (unsigned) -1) + exit (1); + + return entry; +} + +int find_in_queue (int entry) +{ + unsigned qpos = -1; + obj_audacious_call_queue_get_queue_pos_sync (dbus_proxy, entry, & qpos, NULL, NULL); + + if (qpos == (unsigned) -1) + exit (1); + + return qpos; +} + +int get_current_entry (void) +{ + unsigned entry = -1; + obj_audacious_call_position_sync (dbus_proxy, & entry, NULL, NULL); + + if (entry == (unsigned) -1) + exit (1); + + return entry; +} + +char * get_entry_filename (int entry) +{ + char * uri = NULL; + obj_audacious_call_song_filename_sync (dbus_proxy, entry, & uri, NULL, NULL); + + if (! uri) + exit (1); + + char * filename = g_filename_from_uri (uri, NULL, NULL); + + if (filename) + { + g_free (uri); + return filename; + } + + return uri; +} + +char * get_entry_title (int entry) +{ + char * title = NULL; + obj_audacious_call_song_title_sync (dbus_proxy, entry, & title, NULL, NULL); + + if (! title) + exit (1); + + return title; +} + +int get_entry_length (int entry) +{ + int length = -1; + obj_audacious_call_song_frames_sync (dbus_proxy, entry, & length, NULL, NULL); + + if (length < 0) + exit (1); + + return length; +} + +char * get_entry_field (int entry, const char * field) +{ + GVariant * var = NULL; + obj_audacious_call_song_tuple_sync (dbus_proxy, entry, field, & var, NULL, NULL); + + if (! var || ! g_variant_is_of_type (var, G_VARIANT_TYPE_VARIANT)) + exit (1); + + GVariant * var2 = g_variant_get_variant (var); + + if (! var2) + exit (1); + + char * str; + + if (g_variant_is_of_type (var2, G_VARIANT_TYPE_STRING)) + str = g_strdup (g_variant_get_string (var2, NULL)); + else if (g_variant_is_of_type (var2, G_VARIANT_TYPE_INT32)) + str = g_strdup_printf ("%d", (int) g_variant_get_int32 (var2)); + else + exit (1); + + g_variant_unref (var); + g_variant_unref (var2); + + return str; +} + +int get_current_time (void) +{ + unsigned time = -1; + obj_audacious_call_time_sync (dbus_proxy, & time, NULL, NULL); + + if (time == (unsigned) -1) + exit (1); + + return time; +} + +void get_current_info (int * bitrate_p, int * samplerate_p, int * channels_p) +{ + int bitrate = -1, samplerate = -1, channels = -1; + obj_audacious_call_get_info_sync (dbus_proxy, & bitrate, & samplerate, & channels, NULL, NULL); + + if (bitrate < 0 || samplerate < 0 || channels < 0) + exit (1); + + if (bitrate_p) + * bitrate_p = bitrate; + if (samplerate_p) + * samplerate_p = samplerate; + if (channels_p) + * channels_p = channels; +} diff --git a/src/audtool/wrappers.h b/src/audtool/wrappers.h new file mode 100644 index 0000000..e9a9659 --- /dev/null +++ b/src/audtool/wrappers.h @@ -0,0 +1,43 @@ +/* + * wrappers.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef AUDTOOL_WRAPPERS_H +#define AUDTOOL_WRAPPERS_H + +typedef gboolean (* OnOffFunc) (ObjAudacious * proxy, gboolean show, + GCancellable * cancellable, GError * * error); + +void generic_on_off (int argc, char * * argv, OnOffFunc func); + +int get_playlist_length (void); + +int get_queue_length (void); +int get_queue_entry (int qpos); +int find_in_queue (int entry); + +int get_current_entry (void); +char * get_entry_filename (int entry); +char * get_entry_title (int entry); +int get_entry_length (int entry); +char * get_entry_field (int entry, const char * field); + +int get_current_time (void); +void get_current_info (int * bitrate, int * samplerate, int * channels); + +#endif /* AUDTOOL_WRAPPERS_H */ diff --git a/src/dbus/Makefile b/src/dbus/Makefile new file mode 100644 index 0000000..45a7e46 --- /dev/null +++ b/src/dbus/Makefile @@ -0,0 +1,18 @@ +STATIC_LIB_NOINST = aud-dbus.a + +SRCS = aud-dbus.c +CLEAN = aud-dbus.c aud-dbus.h + +include ../../buildsys.mk +include ../../extra.mk + +CPPFLAGS := -I../.. ${CPPFLAGS} ${GLIB_CFLAGS} ${GIO_CFLAGS} +LIBS := ${LIBS} ${GLIB_LIBS} ${GIO_LIBS} + +pre-depend: aud-dbus.c aud-dbus.h + +aud-dbus.h: aud-dbus.xml + gdbus-codegen --interface-prefix org.atheme. --c-namespace Obj --generate-c-code aud-dbus aud-dbus.xml + +aud-dbus.c: aud-dbus.h + # nothing to do here diff --git a/src/dbus/aud-dbus.xml b/src/dbus/aud-dbus.xml new file mode 100644 index 0000000..36e2184 --- /dev/null +++ b/src/dbus/aud-dbus.xml @@ -0,0 +1,409 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<!-- + * aud-dbus.xml + * Copyright 2007-2014 Ben Tucker, Yoshiki Yazawa, Matti Hämäläinen, and + * John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + --> + +<node name="/"> + <!-- Audacious General Information --> + <interface name="org.atheme.audacious"> + <!-- Audacious version --> + <method name="Version"> + <arg type="s" direction="out" name="version"/> + </method> + + <!-- Quit Audacious --> + <method name="Quit" /> + + <!-- Open files (Eject) --> + <method name="Eject" /> + + <!-- Main window visibility --> + <method name="MainWinVisible"> + <arg type="b" direction="out" name="is_main_win"/> + </method> + + <!-- Toggle main window visibility --> + <method name="ShowMainWin"> + <arg type="b" direction="in" name="show"/> + </method> + + <!-- Get names of available 'standard' tuple fields --> + <method name="GetTupleFields"> + <!-- Return array of tuple field names --> + <arg type="as" direction="out" name="fields"/> + </method> + + <!-- Playback Information/Manipulation --> + <!-- Begin or resume playback --> + <method name="Play" /> + + <!-- Pause playback --> + <method name="Pause" /> + + <!-- Stop playback --> + <method name="Stop" /> + + <!-- Is playback playing? --> + <method name="Playing"> + <!-- Return true if playing, false otherwise --> + <arg type="b" direction="out" name="is_playing"/> + </method> + + <!-- Is playback paused? --> + <method name="Paused"> + <!-- Return true if paused, false otherwise --> + <arg type="b" direction="out" name="is_paused"/> + </method> + + <!-- Is playback stopped? --> + <method name="Stopped"> + <!-- Return true if stopped, false otherwise --> + <arg type="b" direction="out" name="is_stopped"/> + </method> + + <!-- What is the playback status? --> + <method name="Status"> + <!-- Return the status as a string: --> + <!-- one of {"playing" "paused" "stopped"} --> + <arg type="s" direction="out" name="status"/> + </method> + + <!-- What is the bitrate, frequency, and number of channels of the --> + <!-- current audio format? --> + <method name="Info"> + <arg type="i" direction="out" name="rate"/> + <arg type="i" direction="out" name="freq"/> + <arg type="i" direction="out" name="nch"/> + </method> + + <!-- What is the current output position? --> + <method name="Time"> + <!-- Position of song, in ms --> + <arg type="u" direction="out" name="time"/> + </method> + + <!-- Seek to some absolute position in the current song --> + <method name="Seek"> + <!-- Position of song, in ms, to seek to --> + <arg type="u" direction="in" name="pos"/> + </method> + + <!-- What is the playback volume? --> + <method name="Volume"> + <!-- Volume of the left channel --> + <arg type="i" direction="out" name="vl"/> + <!-- Volume of the right channel --> + <arg type="i" direction="out" name="vr"/> + </method> + + <!-- Set the playback volume --> + <method name="SetVolume"> + <!-- Volume of the left channel --> + <arg type="i" direction="in" name="vl"/> + <!-- Volume of the right channel --> + <arg type="i" direction="in" name="vr"/> + </method> + + <!-- Get the playback balance --> + <method name="Balance"> + <!-- Balance between left and right channels --> + <arg type="i" direction="out" name="balance"/> + </method> + + <!-- Playlist Information/Manipulation --> + <!-- Playlist position --> + <method name="Position"> + <!-- Return position of current song in current playlist --> + <arg type="u" direction="out" name="pos"/> + </method> + + <!-- Skip ahead one song in the current playlist --> + <method name="Advance" /> + + <!-- Skip backwards one song in the current playlist --> + <method name="Reverse" /> + + <!-- Playlist length --> + <method name="Length"> + <!-- Return length of current playlist --> + <arg type="i" direction="out" name="length"/> + </method> + + <!-- Get a song's title --> + <method name="SongTitle"> + <!-- Song position in the playlist --> + <arg type="u" direction="in" name="pos"/> + + <!-- Return title of desired song --> + <arg type="s" direction="out" name="title"/> + </method> + + <!-- Get a song's filename --> + <method name="SongFilename"> + <!-- Song position in the playlist --> + <arg type="u" direction="in" name="pos"/> + + <!-- Return filename of desired song --> + <arg type="s" direction="out" name="filename"/> + </method> + + <!-- Get the length of some song, in seconds --> + <method name="SongLength"> + <!-- Song position in the playlist --> + <arg type="u" direction="in" name="pos"/> + + <!-- Return length, in seconds, of desired song --> + <arg type="i" direction="out" name="length"/> + </method> + + <!-- Get the length of some song, in frames --> + <method name="SongFrames"> + <!-- Song position in the playlist --> + <arg type="u" direction="in" name="pos"/> + + <!-- Return length, in frames, of desired song --> + <arg type="i" direction="out" name="length"/> + </method> + + <!-- Get the value of a tuple field of some song --> + <method name="SongTuple"> + <!-- Song position in the playlist --> + <arg type="u" direction="in" name="pos"/> + + <!-- Tuple name --> + <arg type="s" direction="in" name="tuple"/> + + <!-- Return tuple value --> + <arg type="v" direction="out" name="value"/> + </method> + + <!-- Jump to some position in the playlist --> + <method name="Jump"> + <!-- Song position to jump to --> + <arg type="u" direction="in" name="pos"/> + </method> + + <!-- Add some file to the current playlist --> + <method name="Add"> + <!-- File to add --> + <arg type="s" direction="in" name="file"/> + </method> + + <!-- Add some URL to the current playlist --> + <method name="AddUrl"> + <!-- URL to add --> + <arg type="s" direction="in" name="url"/> + </method> + + <!-- Add a list of files --> + <method name="AddList"> + <!-- Array of filenames to add --> + <arg type="as" direction="in" name="filenames"/> + </method> + + <!-- Open a list of files --> + <method name="OpenList"> + <!-- Array of filenames to open --> + <arg type="as" direction="in" name="filenames"/> + </method> + + <!-- Open a list of files in a temporary playlist --> + <method name="OpenListToTemp"> + <!-- Array of filenames to open --> + <arg type="as" direction="in" name="filenames"/> + </method> + + <!-- Delete some song from the playlist --> + <method name="Delete"> + <!-- Position of song to delete --> + <arg type="u" direction="in" name="pos"/> + </method> + + <!-- Clear the playlist --> + <method name="Clear" /> + + <!-- Query auto-advance status --> + <method name="AutoAdvance"> + <arg type="b" direction="out" name="is_advance"/> + </method> + + <!-- Toggle auto-advance --> + <method name="ToggleAutoAdvance" /> + + <!-- Query repeat status --> + <method name="Repeat"> + <arg type="b" direction="out" name="is_repeat"/> + </method> + + <!-- Toggle repeat --> + <method name="ToggleRepeat" /> + + <!-- Query shuffle status --> + <method name="Shuffle"> + <arg type="b" direction="out" name="is_shuffle"/> + </method> + + <!-- Toggle shuffle --> + <method name="ToggleShuffle" /> + + <!-- Query stop-after-song status --> + <method name="StopAfter"> + <arg type="b" direction="out" name="is_stopping"/> + </method> + + <!-- Toggle stop-after-song --> + <method name="ToggleStopAfter" /> + + <!-- Show preferences window --> + <method name="ShowPrefsBox"> + <arg type="b" direction="in" name="show"/> + </method> + + <!-- Show about window --> + <method name="ShowAboutBox"> + <arg type="b" direction="in" name="show"/> + </method> + + <!-- Show jump to file window --> + <method name="ShowJtfBox"> + <arg type="b" direction="in" name="show"/> + </method> + + <!-- Show filebrowser --> + <method name="ShowFilebrowser"> + <arg type="b" direction="in" name="show"/> + </method> + + <!-- Either play or pause --> + <method name="PlayPause" /> + + <!-- Playqueue get playlist pos --> + <method name="QueueGetListPos"> + <arg type="u" direction="in" name="qpos"/> + <arg type="u" direction="out" name="pos"/> + </method> + + <!-- Playqueue get playqueue pos --> + <method name="QueueGetQueuePos"> + <arg type="u" direction="in" name="pos"/> + <arg type="u" direction="out" name="qpos"/> + </method> + + <!-- Get Info --> + <method name="GetInfo"> + <arg type="i" direction="out" name="rate"/> + <arg type="i" direction="out" name="freq"/> + <arg type="i" direction="out" name="nch"/> + </method> + + <method name="GetPlayqueueLength"> + <arg type="i" direction="out" name="length"/> + </method> + + <method name="PlaylistInsUrlString"> + <arg type="s" direction="in" name="url"/> + <arg type="i" direction="in" name="pos"/> + </method> + + <method name="PlaylistAdd"> + <arg type="s" direction="in" name="list"/> + </method> + + <method name="PlayqueueAdd"> + <arg type="i" direction="in" name="pos"/> + </method> + + <method name="PlayqueueRemove"> + <arg type="i" direction="in" name="pos"/> + </method> + + <method name="PlayqueueClear" /> + + <method name="PlayqueueIsQueued"> + <arg type="i" direction="in" name="pos"/> + <arg type="b" direction="out" name="is_queued"/> + </method> + + <method name="PlaylistEnqueueToTemp"> + <arg type="s" direction="in" name="url"/> + </method> + + <!-- equalizer --> + <method name="GetEq"> + <arg type="d" direction="out" name="preamp"/> + <arg type="ad" direction="out" name="bands"/> + </method> + + <method name="GetEqPreamp"> + <arg type="d" direction="out" name="preamp"/> + </method> + + <method name="GetEqBand"> + <arg type="i" direction="in" name="band"/> + <arg type="d" direction="out" name="value"/> + </method> + + <method name="SetEq"> + <arg type="d" direction="in" name="preamp"/> + <arg type="ad" direction="in" name="bands"/> + </method> + + <method name="SetEqPreamp"> + <arg type="d" direction="in" name="preamp"/> + </method> + + <method name="SetEqBand"> + <arg type="i" direction="in" name="band"/> + <arg type="d" direction="in" name="value"/> + </method> + + <!-- Activate/Deactivate Equalizer --> + <method name="EqualizerActivate"> + <arg type="b" direction="in" name="active"/> + </method> + + <method name="NumberOfPlaylists"> + <arg type="i" direction="out" name="count" /> + </method> + + <method name="GetActivePlaylist"> + <arg type="i" direction="out" name="plnum" /> + </method> + + <method name="SetActivePlaylist"> + <arg type="i" direction="in" name="plnum" /> + </method> + + <method name="GetActivePlaylistName"> + <arg type="s" direction="out" name="plname" /> + </method> + + <method name="SetActivePlaylistName"> + <arg type="s" direction="in" name="plname" /> + </method> + + <method name="NewPlaylist" /> + + <method name="DeleteActivePlaylist" /> + + <method name="PlayActivePlaylist" /> + + </interface> +</node> diff --git a/src/libaudclient/Makefile b/src/libaudclient/Makefile deleted file mode 100644 index 52f720d..0000000 --- a/src/libaudclient/Makefile +++ /dev/null @@ -1,23 +0,0 @@ -SHARED_LIB = ${LIB_PREFIX}audclient${LIB_SUFFIX} -LIB_MAJOR = 2 -LIB_MINOR = 0 - -SRCS = audctrl.c - -INCLUDES = audctrl.h - -include ../../buildsys.mk -include ../../extra.mk - -pre-depend: - cd ../audacious; ${MAKE} ${MFLAGS} dbus-client-bindings.h - -CPPFLAGS := -I.. -I../.. \ - ${CPPFLAGS} \ - ${DBUS_CFLAGS} \ - ${GLIB_CFLAGS} - -CFLAGS += ${LIB_CFLAGS} - -LIBS += ${GLIB_LIBS} \ - ${DBUS_LIBS} diff --git a/src/libaudclient/audctrl.c b/src/libaudclient/audctrl.c deleted file mode 100644 index 87b36a6..0000000 --- a/src/libaudclient/audctrl.c +++ /dev/null @@ -1,993 +0,0 @@ -/* - * audctrl.c - * Copyright 2007-2011 Ben Tucker, William Pitcock, Yoshiki Yazawa, - * Matti Hämäläinen, and John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include <stdlib.h> -#include <glib.h> -#include <string.h> -#include <dbus/dbus-glib.h> - -#include "audacious/dbus.h" -#include "audacious/dbus-client-bindings.h" -#include "audctrl.h" - -static GError *error = NULL; //it must be hidden from outside, otherwise symbol conflict likely to happen. - -/** - * Sends a list of URIs to Audacious, either replacing current - * playlist or enqueuing them. - * - * @param[in] proxy DBus proxy for Audacious - * @param[in] list An array of URI strings to add. - * @param[in] num Number of URIs to add. - * @param[in] enqueue Whether or not the new playlist should be added on, or replace the current playlist. - **/ -EXPORT void audacious_remote_playlist(DBusGProxy *proxy, gchar **list, gint num, gboolean enqueue) { - GList *glist = NULL; - gchar **data = list; - - g_return_if_fail(list != NULL); - g_return_if_fail(num > 0); - - if (!enqueue) - audacious_remote_playlist_clear(proxy); - - // construct a GList - while(data) { - glist = g_list_append(glist, (gpointer)data); - data++; - } - - org_atheme_audacious_playlist_add(proxy, (gpointer)glist, &error); - - g_list_free(glist); - glist = NULL; - - if (!enqueue) - audacious_remote_play(proxy); -} - -/** - * Queries Audacious for its version identifier. - * - * @param[in] proxy DBus proxy for audacious - * @return String describing the version of Audacious. - **/ -EXPORT gchar *audacious_remote_get_version(DBusGProxy *proxy) { - char *string = NULL; - org_atheme_audacious_version(proxy, &string, &error); - g_clear_error(&error); - - return (string ? string : NULL); -} - -/** - * Sends a list of URIs to Audacious to add to the playlist. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] list A GList of URIs to add to the playlist. - **/ -EXPORT void audacious_remote_playlist_add (DBusGProxy * proxy, GList * list) -{ - const gchar * filenames[g_list_length (list) + 1]; - int count; - - for (count = 0; list != NULL; count ++, list = list->next) - filenames[count] = list->data; - - filenames[count] = NULL; - - org_atheme_audacious_add_list (proxy, filenames, & error); - g_clear_error (& error); -} - -/** - * Sends a list of URIs for Audacious to open. New in Audacious 2.3. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] list A GList of URIs to open - **/ -EXPORT void audacious_remote_playlist_open_list (DBusGProxy * proxy, GList * list) -{ - const gchar * filenames[g_list_length (list) + 1]; - int count; - - for (count = 0; list != NULL; count ++, list = list->next) - filenames[count] = list->data; - - filenames[count] = NULL; - - org_atheme_audacious_open_list (proxy, filenames, & error); - g_clear_error (& error); -} - -/** - * Sends a list of URIs for Audacious to open in a temporary playlist. New in - * Audacious 2.3. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] list A GList of URIs to open - **/ -EXPORT void audacious_remote_playlist_open_list_to_temp (DBusGProxy * proxy, GList * - list) -{ - const gchar * filenames[g_list_length (list) + 1]; - int count; - - for (count = 0; list != NULL; count ++, list = list->next) - filenames[count] = list->data; - - filenames[count] = NULL; - - org_atheme_audacious_open_list_to_temp (proxy, filenames, & error); - g_clear_error (& error); -} - -/** - * Deletes a playlist entry from current playlist in given position. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The playlist position to delete. - **/ -EXPORT void audacious_remote_playlist_delete(DBusGProxy *proxy, guint pos) { - org_atheme_audacious_delete(proxy, pos, &error); - g_clear_error(&error); -} - -/** - * Requests audacious to begin playback. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_play(DBusGProxy *proxy) { - org_atheme_audacious_play(proxy, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to pause. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_pause(DBusGProxy *proxy) { - org_atheme_audacious_pause(proxy, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to stop. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_stop(DBusGProxy *proxy) { - org_atheme_audacious_stop(proxy, &error); - g_clear_error(&error); -} - -/** - * Queries audacious about whether it is playing or not. - * - * @param[in] proxy DBus proxy for audacious - * @return TRUE if playing, FALSE otherwise. - **/ -EXPORT gboolean audacious_remote_is_playing(DBusGProxy *proxy) { - gboolean is_playing = FALSE; - org_atheme_audacious_playing(proxy, &is_playing, &error); - g_clear_error(&error); - return is_playing; -} - -/** - * audacious_remote_is_paused: - * @param[in] proxy DBus proxy for audacious - * - * Queries audacious about whether it is paused or not. - * - * @return TRUE if playing, FALSE otherwise. - **/ -EXPORT gboolean audacious_remote_is_paused(DBusGProxy *proxy) { - gboolean is_paused = FALSE; - org_atheme_audacious_paused(proxy, &is_paused, &error); - g_clear_error(&error); - return is_paused; -} - -/** - * audacious_remote_get_playlist_pos: - * @param[in] proxy DBus proxy for audacious - * - * Queries audacious about the current playlist position. - * - * @return The current playlist position. - **/ -EXPORT gint audacious_remote_get_playlist_pos(DBusGProxy *proxy) { - guint pos = 0; - org_atheme_audacious_position(proxy, &pos, &error); - g_clear_error(&error); - return pos; -} - -/** - * audacious_remote_set_playlist_pos: - * @param[in] proxy DBus proxy for audacious - * @param[in] pos Playlist position to jump to. - * - * Tells audacious to jump to a different playlist position. - **/ -EXPORT void audacious_remote_set_playlist_pos(DBusGProxy *proxy, guint pos) { - org_atheme_audacious_jump (proxy, pos, &error); - g_clear_error(&error); -} - -/** - * audacious_remote_get_playlist_length: - * @param[in] proxy DBus proxy for audacious - * - * Queries audacious about the current playlist length. - * - * @return The amount of entries in the playlist. - **/ -EXPORT gint audacious_remote_get_playlist_length(DBusGProxy *proxy) { - gint len = 0; - org_atheme_audacious_length(proxy, &len, &error); - g_clear_error(&error); - return len; -} - -/** - * audacious_remote_playlist_clear: - * @param[in] proxy DBus proxy for audacious - * - * Clears the playlist. - **/ -EXPORT void audacious_remote_playlist_clear(DBusGProxy *proxy) { - org_atheme_audacious_clear(proxy, &error); - g_clear_error(&error); -} - -/** - * audacious_remote_get_output_time: - * @param[in] proxy DBus proxy for audacious - * - * Queries audacious about the current output position. - * - * @return The current output position. - **/ -EXPORT gint audacious_remote_get_output_time(DBusGProxy *proxy) { - guint time = 0; - org_atheme_audacious_time(proxy, &time, &error); - g_clear_error(&error); - return time; -} - -/** - * audacious_remote_jump_to_time: - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The time (in milliseconds) to jump to. - * - * Tells audacious to seek to a new time position. - **/ -EXPORT void audacious_remote_jump_to_time(DBusGProxy *proxy, guint pos) { - org_atheme_audacious_seek (proxy, pos, &error); - g_clear_error(&error); -} - -/** - * Queries audacious for the current volume settings. - * - * @param[in] proxy DBus proxy for audacious - * @param[out] vl Pointer to integer containing the left channel's volume (0-100). - * @param[out] vr Pointer to integer containing the right channel's volume (0-100). - **/ -EXPORT void audacious_remote_get_volume(DBusGProxy *proxy, gint * vl, gint * vr) { - org_atheme_audacious_volume(proxy, vl, vr, &error); - g_clear_error(&error); -} - -/** - * Queries audacious about the current volume. - * - * @param[in] proxy DBus proxy for audacious - * @return The current volume (0-100). - **/ -EXPORT gint audacious_remote_get_main_volume(DBusGProxy *proxy) { - gint vl = 0, vr = 0; - - audacious_remote_get_volume(proxy, &vl, &vr); - - return (vl > vr) ? vl : vr; -} - -/** - * Queries audacious about the current balance. - * - * @param[in] proxy DBus proxy for audacious - * @return The current balance. - **/ -EXPORT gint audacious_remote_get_balance(DBusGProxy *proxy) { - gint balance = 50; - org_atheme_audacious_balance(proxy, &balance, &error); - g_clear_error(&error); - return balance; -} - -/** - * Sets the volume for the left and right channels in Audacious. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] vl The volume for the left channel (0-100). - * @param[in] vr The volume for the right channel (0-100). - **/ -EXPORT void audacious_remote_set_volume(DBusGProxy *proxy, gint vl, gint vr) { - org_atheme_audacious_set_volume(proxy, vl, vr, &error); - g_clear_error(&error); -} - - -/** - * Sets the volume in Audacious. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] v The volume to set (0-100). - **/ -EXPORT void audacious_remote_set_main_volume(DBusGProxy *proxy, gint v) { - gint b = 50, vl = 0, vr = 0; - - b = audacious_remote_get_balance(proxy); - - if (b < 0) { - vl = v; - vr = (v * (100 - abs(b))) / 100; - } else if (b > 0) { - vl = (v * (100 - b)) / 100; - vr = v; - } else - vl = vr = v; - audacious_remote_set_volume(proxy, vl, vr); -} - -/** - * Sets the balance in Audacious. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] b The balance to set. - **/ -EXPORT void audacious_remote_set_balance(DBusGProxy *proxy, gint b) { - gint v = 0, vl = 0, vr = 0; - - if (b < -100) - b = -100; - if (b > 100) - b = 100; - - v = audacious_remote_get_main_volume(proxy); - - if (b < 0) { - vl = v; - vr = (v * (100 - abs(b))) / 100; - } else if (b > 0) { - vl = (v * (100 - b)) / 100; - vr = v; - } else - vl = vr = v; - audacious_remote_set_volume(proxy, vl, vr); -} - -/** - * Queries Audacious about a playlist entry's file. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The playlist position to query for. - * @return A path to the file in the playlist at %pos position. - **/ -EXPORT gchar *audacious_remote_get_playlist_file(DBusGProxy *proxy, guint pos) { - gchar *out = NULL; - org_atheme_audacious_song_filename(proxy, pos, &out, &error); - g_clear_error(&error); - return out; -} - -/** - * Queries Audacious about a playlist entry's title. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The playlist position to query for. - * @return The title for the entry in the playlist at %pos position. - **/ -EXPORT gchar *audacious_remote_get_playlist_title(DBusGProxy *proxy, guint pos) { - gchar *out = NULL; - org_atheme_audacious_song_title(proxy, pos, &out, &error); - g_clear_error(&error); - return out; -} - -/** - * Queries Audacious about a playlist entry's length. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The playlist position to query for. - * @return The length of the entry in the playlist at %pos position. - **/ -EXPORT gint audacious_remote_get_playlist_time(DBusGProxy *proxy, guint pos) { - gint out = 0; - org_atheme_audacious_song_frames(proxy, pos, &out, &error); - g_clear_error(&error); - return out; -} - -/** - * Queries Audacious about the current audio format. - * - * @param[in] proxy DBus proxy for audacious - * @param[out] rate Pointer to an integer containing the bitrate. - * @param[out] freq Pointer to an integer containing the frequency. - * @param[out] nch Pointer to an integer containing the number of channels. - **/ -EXPORT void audacious_remote_get_info(DBusGProxy *proxy, gint *rate, gint *freq, - gint *nch) { - org_atheme_audacious_info(proxy, rate, freq, nch, &error); - g_clear_error(&error); -} - -/** - * Toggles the main window's visibility. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] show Whether or not to show the main window. - **/ -EXPORT void audacious_remote_main_win_toggle(DBusGProxy *proxy, gboolean show) { - org_atheme_audacious_show_main_win(proxy, show, &error); - g_clear_error(&error); -} - -/** - * Queries Audacious about the main window's visibility. - * - * @param[in] proxy DBus proxy for audacious - * @return TRUE if visible, FALSE otherwise. - **/ -EXPORT gboolean audacious_remote_is_main_win(DBusGProxy *proxy) { - gboolean visible = TRUE; - org_atheme_audacious_main_win_visible(proxy, &visible, &error); - g_clear_error(&error); - return visible; -} - -/** - * Tells audacious to show the preferences pane. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_show_prefs_box(DBusGProxy *proxy) { - audacious_remote_toggle_prefs_box(proxy, TRUE); -} - -/** - * Tells audacious to show/hide the preferences pane. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] show shows/hides - **/ -EXPORT void audacious_remote_toggle_prefs_box(DBusGProxy *proxy, gboolean show) { - org_atheme_audacious_show_prefs_box(proxy, show, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to show the about box. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_show_about_box(DBusGProxy *proxy) { - audacious_remote_toggle_about_box(proxy, TRUE); -} - -/** - * Tells audacious to show/hide the about box. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] show shows/hides - **/ -EXPORT void audacious_remote_toggle_about_box(DBusGProxy *proxy, gboolean show) { - org_atheme_audacious_show_about_box(proxy, show, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to set the always-on-top feature. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] ontop Boolean value whether or not Audacious should be always-on-top. - **/ -EXPORT void audacious_remote_toggle_aot(DBusGProxy *proxy, gboolean ontop) { - org_atheme_audacious_toggle_aot(proxy, ontop, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to display the open files pane. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_eject(DBusGProxy *proxy) { - org_atheme_audacious_eject(proxy, &error); - g_clear_error(&error); -} - -/** - * audacious_remote_playlist_prev: - * @param[in] proxy DBus proxy for audacious - * - * Tells audacious to move backwards in the playlist. - **/ -EXPORT void audacious_remote_playlist_prev(DBusGProxy *proxy) { - org_atheme_audacious_reverse(proxy, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to move forward in the playlist. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_playlist_next(DBusGProxy *proxy) { - org_atheme_audacious_advance(proxy, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to add an URI to the playlist. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] string The URI to add. - **/ -EXPORT void audacious_remote_playlist_add_url_string(DBusGProxy *proxy, - gchar *string) { - org_atheme_audacious_add_url(proxy, string, &error); - g_clear_error(&error); -} - -/** - * Check if an Audacious instance is running. - * - * @param[in] proxy DBus proxy for audacious - * @return TRUE if yes, otherwise FALSE. - **/ -EXPORT gboolean audacious_remote_is_running(DBusGProxy *proxy) { - char *string = NULL; - org_atheme_audacious_version(proxy, &string, &error); - g_clear_error(&error); - if(string) { - g_free(string); - return TRUE; - } - else - return FALSE; -} - -/** - * Tells audacious to toggle the repeat feature. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_toggle_repeat(DBusGProxy *proxy) { - org_atheme_audacious_toggle_repeat(proxy, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to toggle the shuffle feature. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_toggle_shuffle(DBusGProxy *proxy) { - org_atheme_audacious_toggle_shuffle (proxy, &error); - g_clear_error(&error); -} - -EXPORT void audacious_remote_toggle_stop_after (DBusGProxy * proxy) -{ - org_atheme_audacious_toggle_stop_after (proxy, & error); - g_clear_error (& error); -} - -/** - * Queries audacious about whether or not the repeat feature is active. - * - * @param[in] proxy DBus proxy for audacious - * @return TRUE if yes, otherwise FALSE. - **/ -EXPORT gboolean audacious_remote_is_repeat(DBusGProxy *proxy) { - gboolean is_repeat; - org_atheme_audacious_repeat(proxy, &is_repeat, &error); - g_clear_error(&error); - return is_repeat; -} - -/** - * Queries audacious about whether or not the shuffle feature is active. - * - * @param[in] proxy DBus proxy for audacious - * @return TRUE if yes, otherwise FALSE. - **/ -EXPORT gboolean audacious_remote_is_shuffle(DBusGProxy *proxy) { - gboolean is_shuffle; - org_atheme_audacious_shuffle(proxy, &is_shuffle, &error); - g_clear_error(&error); - return is_shuffle; -} - -EXPORT gboolean audacious_remote_is_stop_after (DBusGProxy * proxy) -{ - gboolean is_stop_after; - org_atheme_audacious_stop_after (proxy, & is_stop_after, & error); - g_clear_error (& error); - return is_stop_after; -} - -/** - * Queries audacious about the equalizer settings. - * - * @param[in] proxy DBus proxy for audacious - * @param[out] preamp Pointer to value for preamp setting. - * @param[out] bands Pointer to array of band settings. - **/ -EXPORT void audacious_remote_get_eq(DBusGProxy *proxy, gdouble *preamp, GArray **bands) { - org_atheme_audacious_get_eq(proxy, preamp, bands, &error); - g_clear_error(&error); -} - -/** - * Queries audacious about the equalizer preamp's setting. - * - * @param[in] proxy DBus proxy for audacious - * @return The equalizer preamp's setting. - **/ -EXPORT gdouble audacious_remote_get_eq_preamp(DBusGProxy *proxy) { - gdouble preamp = 0.0; - - org_atheme_audacious_get_eq_preamp(proxy, &preamp, &error); - g_clear_error(&error); - - return preamp; -} - -/** - * Queries audacious about an equalizer band's value. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] band Which band to lookup the value for. - * @return The equalizer band's value. - **/ -EXPORT gdouble audacious_remote_get_eq_band(DBusGProxy *proxy, gint band) { - gdouble value = 0.0; - - org_atheme_audacious_get_eq_band(proxy, band, &value, &error); - g_clear_error(&error); - - return value; -} - -/** - * Tells audacious to set the equalizer up using the provided values. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] preamp Value for preamp setting. - * @param[in] bands Array of band settings. - **/ -EXPORT void audacious_remote_set_eq(DBusGProxy *proxy, gdouble preamp, GArray *bands) { - org_atheme_audacious_set_eq(proxy, preamp, bands, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to set the equalizer's preamp setting. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] preamp Value for preamp setting. - **/ -EXPORT void audacious_remote_set_eq_preamp(DBusGProxy *proxy, gdouble preamp) { - org_atheme_audacious_set_eq_preamp(proxy, preamp, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to set an equalizer band's setting. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] band The band to set the value for. - * @param[in] value The value to set that band to. - **/ -EXPORT void audacious_remote_set_eq_band(DBusGProxy *proxy, gint band, gdouble value) { - org_atheme_audacious_set_eq_band(proxy, band, value, &error); - g_clear_error(&error); -} - -/** - * Requests audacious to quit. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_quit(DBusGProxy *proxy) { - org_atheme_audacious_quit(proxy, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to toggle between play and pause. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_play_pause(DBusGProxy *proxy) { - org_atheme_audacious_play_pause(proxy, &error); -} - -/** - * Tells audacious to add an URI to the playlist at a specific position. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] string The URI to add. - * @param[in] pos The position to add the URI at. - **/ -EXPORT void audacious_remote_playlist_ins_url_string(DBusGProxy *proxy, - gchar *string, guint pos) { - org_atheme_audacious_playlist_ins_url_string (proxy, string, pos, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to add a playlist entry to the playqueue. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The playlist position to add to the queue. - **/ -EXPORT void audacious_remote_playqueue_add(DBusGProxy *proxy, guint pos) { - org_atheme_audacious_playqueue_add (proxy, pos, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to remove a playlist entry from the playqueue. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos The playlist position to remove from the queue. - **/ -EXPORT void audacious_remote_playqueue_remove(DBusGProxy *proxy, guint pos) { - org_atheme_audacious_playqueue_remove (proxy, pos, &error); - g_clear_error(&error); -} - -/** - * Queries audacious about the playqueue's length. - * - * @bug This returns the length of the playlist, NOT the length of the playqueue. - * - * @param[in] proxy DBus proxy for audacious - * @return The number of entries in the playqueue. - **/ -EXPORT gint audacious_remote_get_playqueue_length(DBusGProxy *proxy) { - gint len = 0; - org_atheme_audacious_length(proxy, &len, &error); - g_clear_error(&error); - return len; -} - -/** - * Tells audacious to toggle the no-playlist-advance feature. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_toggle_advance(DBusGProxy *proxy) { - org_atheme_audacious_toggle_auto_advance(proxy, &error); - g_clear_error(&error); -} - -/** - * audacious_remote_is_advance: - * @param[in] proxy DBus proxy for audacious - * - * Queries audacious about whether or not the no-playlist-advance feature is active. - * - * @return TRUE if yes, otherwise FALSE. - **/ -EXPORT gboolean audacious_remote_is_advance(DBusGProxy *proxy) { - gboolean is_advance = FALSE; - org_atheme_audacious_auto_advance(proxy, &is_advance, &error); - g_clear_error(&error); - return is_advance; -} - -/** - * Tells audacious to show the Jump-to-File pane. - * - * @param[in] proxy DBus proxy for audacious - **/ -EXPORT void audacious_remote_show_jtf_box(DBusGProxy *proxy) { - audacious_remote_toggle_jtf_box(proxy, TRUE); -} - -/** - * Tells audacious to show/hide the Jump-to-File pane. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] show shows/hides jtf pane - **/ -EXPORT void audacious_remote_toggle_jtf_box(DBusGProxy *proxy, gboolean show) { - org_atheme_audacious_show_jtf_box(proxy, show, &error); - g_clear_error(&error); -} - -/** - * Tells audacious to show the filebrowser dialog. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] show shows/hides filebrowser - **/ -EXPORT void audacious_remote_toggle_filebrowser(DBusGProxy *proxy, gboolean show) { - org_atheme_audacious_show_filebrowser(proxy, show, &error); - g_clear_error(&error); -} - -/** - * audacious_remote_playqueue_clear: - * @param[in] proxy DBus proxy for audacious - * - * Tells audacious to clear the playqueue. - **/ -EXPORT void audacious_remote_playqueue_clear(DBusGProxy *proxy) { - org_atheme_audacious_playqueue_clear(proxy, &error); - g_clear_error(&error); -} - -/** - * Queries audacious about whether or not a playlist entry is in the playqueue. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos Position to check queue for. - * @return TRUE if yes, FALSE otherwise. - **/ -EXPORT gboolean audacious_remote_playqueue_is_queued(DBusGProxy *proxy, guint pos) { - gboolean is_queued; - org_atheme_audacious_playqueue_is_queued (proxy, pos, &is_queued, &error); - g_clear_error(&error); - return is_queued; -} - -/** - * Queries audacious about what the playqueue position is for a playlist entry. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] pos Position to check queue for. - * @return the playqueue position for a playlist entry - **/ -EXPORT gint audacious_remote_get_playqueue_queue_position(DBusGProxy *proxy, guint pos) { - guint qpos = 0; - org_atheme_audacious_queue_get_queue_pos (proxy, pos, &qpos, &error); - g_clear_error(&error); - return qpos; -} - -/** - * Queries audacious what is the playlist position for given a playqueue entry index. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] qpos Position to check queue for. - * - * @return the playlist position for a playqueue entry - **/ -EXPORT gint audacious_remote_get_playqueue_list_position(DBusGProxy *proxy, guint qpos) { - guint pos = 0; - org_atheme_audacious_queue_get_list_pos (proxy, qpos, &pos, &error); - g_clear_error(&error); - return pos; -} - -/** - * Tells audacious to add an URI to a temporary playlist. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] string The URI to enqueue to a temporary playlist. - **/ -EXPORT void audacious_remote_playlist_enqueue_to_temp(DBusGProxy *proxy, - gchar *string) { - org_atheme_audacious_playlist_enqueue_to_temp(proxy, string, &error); - g_clear_error(&error); -} - -/** - * Queries Audacious about a playlist entry's tuple information. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] field The name of the tuple field to retrieve. - * @param[in] pos The playlist position to query for. - * @return The requested field's data for the entry in the playlist at %pos position. - **/ -EXPORT gchar *audacious_get_tuple_field_data(DBusGProxy *proxy, gchar *field, - guint pos) { - GValue value = {0}; - gchar *s = NULL; - - org_atheme_audacious_song_tuple(proxy, pos, field, &value, &error); - - g_clear_error(&error); - - if (G_IS_VALUE(&value) == FALSE) - return NULL; - - /* I think the original "purpose" of using g_strescape() here - * has probably been to escape only \n, \t, \r, etc. but the function - * actually escapes all non-ASCII characters. Which is bad, since we - * are using UTF-8. -- ccr - */ - if (G_VALUE_HOLDS_STRING(&value)) - //s = g_strescape(g_value_get_string(&value), NULL); - s = g_strdup(g_value_get_string(&value)); - else if (g_value_type_transformable(G_VALUE_TYPE(&value), G_TYPE_STRING)) - { - GValue tmp_value = { 0, }; - - g_value_init(&tmp_value, G_TYPE_STRING); - g_value_transform(&value, &tmp_value); - - //s = g_strescape(g_value_get_string(&tmp_value), NULL); - s = g_strdup(g_value_get_string(&tmp_value)); - - g_value_unset(&tmp_value); - } - else - s = g_strdup("<unknown type>"); - - g_value_unset(&value); - return s; -} - -/** - * Toggles the equalizer. - * - * @param[in] proxy DBus proxy for audacious - * @param[in] active Whether or not to activate the equalizer. - **/ -EXPORT void audacious_remote_eq_activate(DBusGProxy *proxy, gboolean active) { - org_atheme_audacious_equalizer_activate (proxy, active, &error); - g_clear_error(&error); -} - -/** - * Returns a array of strings with available tuple field names. - * - * @param[in] proxy DBus proxy for audacious - * @return Array of strings. - **/ -EXPORT gchar **audacious_remote_get_tuple_fields(DBusGProxy *proxy) { - gchar **res = NULL; - org_atheme_audacious_get_tuple_fields (proxy, &res, &error); - g_clear_error(&error); - return res; -} - -/** - * Returns the active playlist name. - */ -EXPORT gchar *audacious_remote_playlist_get_active_name(DBusGProxy *proxy) { - char *string = NULL; - org_atheme_audacious_get_active_playlist_name (proxy, &string, &error); - g_clear_error(&error); - - return (string ? string : NULL); -} diff --git a/src/libaudclient/audctrl.h b/src/libaudclient/audctrl.h deleted file mode 100644 index 6e316f8..0000000 --- a/src/libaudclient/audctrl.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * audctrl.h - * Copyright 2007-2011 Ben Tucker, William Pitcock, Yoshiki Yazawa, - * Matti Hämäläinen, and John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef AUDACIOUS_AUDCTRL_H -#define AUDACIOUS_AUDCTRL_H - -#include <glib.h> -#include <dbus/dbus-glib.h> - -G_BEGIN_DECLS - -void audacious_remote_playlist(DBusGProxy *proxy, gchar **list, gint num, - gboolean enqueue); -gchar *audacious_remote_get_version(DBusGProxy *proxy); -void audacious_remote_playlist_add(DBusGProxy *proxy, GList *list); -void audacious_remote_playlist_delete(DBusGProxy *proxy, guint pos); -void audacious_remote_play(DBusGProxy *proxy); -void audacious_remote_pause(DBusGProxy *proxy); -void audacious_remote_stop(DBusGProxy *proxy); -gboolean audacious_remote_is_playing(DBusGProxy *proxy); -gboolean audacious_remote_is_paused(DBusGProxy *proxy); -gint audacious_remote_get_playlist_pos(DBusGProxy *proxy); -void audacious_remote_set_playlist_pos(DBusGProxy *proxy, guint pos); -gint audacious_remote_get_playlist_length(DBusGProxy *proxy); -void audacious_remote_playlist_clear(DBusGProxy *proxy); -gint audacious_remote_get_output_time(DBusGProxy *proxy); -void audacious_remote_jump_to_time(DBusGProxy *proxy, guint pos); -void audacious_remote_get_volume(DBusGProxy *proxy, gint *vl, gint *vr); -gint audacious_remote_get_main_volume(DBusGProxy *proxy); -gint audacious_remote_get_balance(DBusGProxy *proxy); -void audacious_remote_set_volume(DBusGProxy *proxy, gint vl, gint vr); -void audacious_remote_set_main_volume(DBusGProxy *proxy, gint v); -void audacious_remote_set_balance(DBusGProxy *proxy, gint b); -gchar *audacious_remote_get_skin(DBusGProxy *proxy); -void audacious_remote_set_skin(DBusGProxy *proxy, gchar *skinfile); -gchar *audacious_remote_get_playlist_file(DBusGProxy *proxy, guint pos); -gchar *audacious_remote_get_playlist_title(DBusGProxy *proxy, guint pos); -gint audacious_remote_get_playlist_time(DBusGProxy *proxy, guint pos); -void audacious_remote_get_info(DBusGProxy *proxy, gint *rate, gint *freq, - gint *nch); -void audacious_remote_main_win_toggle(DBusGProxy *proxy, gboolean show); -gboolean audacious_remote_is_main_win(DBusGProxy *proxy); -void audacious_remote_show_prefs_box(DBusGProxy *proxy); -void audacious_remote_toggle_aot(DBusGProxy *proxy, gboolean ontop); -void audacious_remote_eject(DBusGProxy *proxy); -void audacious_remote_playlist_prev(DBusGProxy *proxy); -void audacious_remote_playlist_next(DBusGProxy *proxy); -void audacious_remote_playlist_add_url_string(DBusGProxy *proxy, - gchar *string); -gboolean audacious_remote_is_running(DBusGProxy *proxy); -void audacious_remote_toggle_repeat(DBusGProxy *proxy); -void audacious_remote_toggle_shuffle(DBusGProxy *proxy); -void audacious_remote_toggle_stop_after (DBusGProxy * proxy); -gboolean audacious_remote_is_repeat(DBusGProxy *proxy); -gboolean audacious_remote_is_shuffle(DBusGProxy *proxy); -gboolean audacious_remote_is_stop_after (DBusGProxy * proxy); - -void audacious_remote_get_eq(DBusGProxy *proxy, gdouble *preamp, - GArray **bands); -gdouble audacious_remote_get_eq_preamp(DBusGProxy *proxy); -gdouble audacious_remote_get_eq_band(DBusGProxy *proxy, gint band); -void audacious_remote_set_eq(DBusGProxy *proxy, gdouble preamp, - GArray *bands); -void audacious_remote_set_eq_preamp(DBusGProxy *proxy, gdouble preamp); -void audacious_remote_set_eq_band(DBusGProxy *proxy, gint band, - gdouble value); - -/* Added in XMMS 1.2.1 */ -void audacious_remote_quit(DBusGProxy *proxy); - -/* Added in XMMS 1.2.6 */ -void audacious_remote_play_pause(DBusGProxy *proxy); -void audacious_remote_playlist_ins_url_string(DBusGProxy *proxy, - gchar *string, guint pos); - -/* Added in XMMS 1.2.11 */ -void audacious_remote_playqueue_add(DBusGProxy *proxy, guint pos); -void audacious_remote_playqueue_remove(DBusGProxy *proxy, guint pos); -gint audacious_remote_get_playqueue_length(DBusGProxy *proxy); -void audacious_remote_toggle_advance(DBusGProxy *proxy); -gboolean audacious_remote_is_advance(DBusGProxy *proxy); - -/* Added in Audacious 1.1 */ -void audacious_remote_show_jtf_box(DBusGProxy *proxy); -void audacious_remote_playqueue_clear(DBusGProxy *proxy); -gboolean audacious_remote_playqueue_is_queued(DBusGProxy *proxy, guint pos); -gint audacious_remote_get_playqueue_list_position(DBusGProxy *proxy, guint qpos); -gint audacious_remote_get_playqueue_queue_position(DBusGProxy *proxy, guint pos); - -/* Added in Audacious 1.2 */ -void audacious_set_session_uri(DBusGProxy *proxy, gchar *uri); -gchar *audacious_get_session_uri(DBusGProxy *proxy); -void audacious_set_session_type(DBusGProxy *proxy, gint type); - -/* Added in Audacious 1.3 */ -void audacious_remote_playlist_enqueue_to_temp(DBusGProxy *proxy, - gchar *string); -gchar *audacious_get_tuple_field_data(DBusGProxy *proxy, gchar *field, - guint pos); -/* Added in Audacious 1.4 */ -void audacious_remote_show_about_box(DBusGProxy *proxy); -void audacious_remote_toggle_about_box(DBusGProxy *proxy, gboolean show); -void audacious_remote_toggle_jtf_box(DBusGProxy *proxy, gboolean show); -void audacious_remote_toggle_prefs_box(DBusGProxy *proxy, gboolean show); -void audacious_remote_toggle_filebrowser(DBusGProxy *proxy, gboolean show); -void audacious_remote_eq_activate(DBusGProxy *proxy, gboolean active); - -/* Added in Audacious 1.9 */ -gchar **audacious_remote_get_tuple_fields(DBusGProxy *proxy); - -/* Added in Audacious 2.3 */ -void audacious_remote_playlist_open_list (DBusGProxy * proxy, GList * list); -void audacious_remote_playlist_open_list_to_temp (DBusGProxy * proxy, GList * - list); - -/* Added in Audacious 2.4 */ -gchar *audacious_remote_playlist_get_active_name(DBusGProxy *proxy); - -G_END_DECLS - -#endif /* AUDACIOUS_AUDCTRL_H */ diff --git a/src/libaudcore/Makefile b/src/libaudcore/Makefile index c823ae7..8f380f7 100644 --- a/src/libaudcore/Makefile +++ b/src/libaudcore/Makefile @@ -1,25 +1,33 @@ SHARED_LIB = ${LIB_PREFIX}audcore${LIB_SUFFIX} -LIB_MAJOR = 1 +LIB_MAJOR = 2 LIB_MINOR = 0 SRCS = audio.c \ audstrings.c \ + charset.c \ eventqueue.c \ hook.c \ index.c \ + inifile.c \ + multihash.c \ strpool.c \ + tinylock.c \ tuple.c \ tuple_compiler.c \ tuple_formatter.c \ vfs.c \ vfs_async.c \ - vfs_common.c + vfs_common.c \ + vfs_local.c INCLUDES = audio.h \ audstrings.h \ core.h \ hook.h \ index.h \ + inifile.h \ + multihash.h \ + tinylock.h \ tuple.h \ vfs.h \ vfs_async.h @@ -32,7 +40,10 @@ includesubdir = libaudcore CPPFLAGS := -I.. -I../.. \ ${CPPFLAGS} \ ${GLIB_CFLAGS} \ + ${LIBGUESS_CFLAGS} CFLAGS += ${LIB_CFLAGS} -LIBS += ${GLIB_LIBS} -lm +LIBS += -lm \ + ${GLIB_LIBS} \ + ${LIBGUESS_LIBS} diff --git a/src/libaudcore/audio.c b/src/libaudcore/audio.c index f866509..25246be 100644 --- a/src/libaudcore/audio.c +++ b/src/libaudcore/audio.c @@ -1,6 +1,6 @@ /* * audio.c - * Copyright 2009-2012 John Lindgren, Michał Lipski, and Anders Johansson + * Copyright 2009-2013 John Lindgren, Michał Lipski, and Anders Johansson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,6 +22,7 @@ #include <math.h> #include "audio.h" +#include "core.h" #define INTERLACE_LOOP(TYPE) \ for (int c = 0; c < channels; c ++) \ @@ -173,7 +174,7 @@ EXPORT void audio_from_int (const void * in, int format, float * out, int sample { int entry; - for (entry = 0; entry < G_N_ELEMENTS (convert_table); entry ++) + for (entry = 0; entry < ARRAY_LEN (convert_table); entry ++) { if (convert_table[entry].format == format) { @@ -187,7 +188,7 @@ EXPORT void audio_to_int (const float * in, void * out, int format, int samples) { int entry; - for (entry = 0; entry < G_N_ELEMENTS (convert_table); entry ++) + for (entry = 0; entry < ARRAY_LEN (convert_table); entry ++) { if (convert_table[entry].format == format) { diff --git a/src/libaudcore/audio.h.in b/src/libaudcore/audio.h.in index 19cfef2..2e9dd5a 100644 --- a/src/libaudcore/audio.h.in +++ b/src/libaudcore/audio.h.in @@ -1,6 +1,6 @@ /* * audio.h - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/libaudcore/audstrings.c b/src/libaudcore/audstrings.c index eb4b8f1..c1d878b 100644 --- a/src/libaudcore/audstrings.c +++ b/src/libaudcore/audstrings.c @@ -1,7 +1,6 @@ /* * audstrings.c - * Copyright 2009-2011 John Lindgren - * Copyright 2010 William Pitcock + * Copyright 2009-2012 John Lindgren and William Pitcock * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,19 +21,67 @@ #include <math.h> #include <stdio.h> #include <stdlib.h> -#include <glib.h> #include <string.h> -#include <ctype.h> -#include <locale.h> + +#include <glib.h> #include <audacious/i18n.h> #include "audstrings.h" +#include "index.h" + +static const char ascii_to_hex[256] = { + ['0'] = 0x0, ['1'] = 0x1, ['2'] = 0x2, ['3'] = 0x3, ['4'] = 0x4, + ['5'] = 0x5, ['6'] = 0x6, ['7'] = 0x7, ['8'] = 0x8, ['9'] = 0x9, + ['a'] = 0xa, ['b'] = 0xb, ['c'] = 0xc, ['d'] = 0xd, ['e'] = 0xe, ['f'] = 0xf, + ['A'] = 0xa, ['B'] = 0xb, ['C'] = 0xc, ['D'] = 0xd, ['E'] = 0xe, ['F'] = 0xf +}; + +static const char hex_to_ascii[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' +}; + +static const char uri_legal_table[256] = { + ['0'] = 1, ['1'] = 1, ['2'] = 1, ['3'] = 1, ['4'] = 1, + ['5'] = 1, ['6'] = 1, ['7'] = 1, ['8'] = 1, ['9'] = 1, + ['a'] = 1, ['b'] = 1, ['c'] = 1, ['d'] = 1, ['e'] = 1, ['f'] = 1, ['g'] = 1, + ['h'] = 1, ['i'] = 1, ['j'] = 1, ['k'] = 1, ['l'] = 1, ['m'] = 1, ['n'] = 1, + ['o'] = 1, ['p'] = 1, ['q'] = 1, ['r'] = 1, ['s'] = 1, ['t'] = 1, ['u'] = 1, + ['v'] = 1, ['w'] = 1, ['x'] = 1, ['y'] = 1, ['z'] = 1, + ['A'] = 1, ['B'] = 1, ['C'] = 1, ['D'] = 1, ['E'] = 1, ['F'] = 1, ['G'] = 1, + ['H'] = 1, ['I'] = 1, ['J'] = 1, ['K'] = 1, ['L'] = 1, ['M'] = 1, ['N'] = 1, + ['O'] = 1, ['P'] = 1, ['Q'] = 1, ['R'] = 1, ['S'] = 1, ['T'] = 1, ['U'] = 1, + ['V'] = 1, ['W'] = 1, ['X'] = 1, ['Y'] = 1, ['Z'] = 1, + ['-'] = 1, ['_'] = 1, ['.'] = 1, ['~'] = 1, ['/'] = 1 +}; + +static const char swap_case[256] = + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" + "\0ABCDEFGHIJKLMNOPQRSTUVWXYZ\0\0\0\0\0"; + +#define FROM_HEX(c) (ascii_to_hex[(unsigned char) (c)]) +#define TO_HEX(i) (hex_to_ascii[(i) & 15]) +#define IS_LEGAL(c) (uri_legal_table[(unsigned char) (c)]) +#define SWAP_CASE(c) (swap_case[(unsigned char) (c)]) + +EXPORT char * str_printf (const char * format, ...) +{ + va_list args; + va_start (args, format); + + char * str = str_vprintf (format, args); -#define FROM_HEX(c) ((c) < 'A' ? (c) - '0' : (c) < 'a' ? 10 + (c) - 'A' : 10 + (c) - 'a') -#define TO_HEX(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10) -#define IS_LEGAL(c) (((c) >= 'A' && (c) <= 'Z') || ((c) >= 'a' && (c) <= 'z') \ - || ((c) >= '0' && (c) <= '9') || (strchr ("-_.~/", (c)))) + va_end (args); + return str; +} + +EXPORT char * str_vprintf (const char * format, va_list args) +{ + VSPRINTF (buf, format, args); + return str_get (buf); +} EXPORT bool_t str_has_prefix_nocase (const char * str, const char * prefix) { @@ -52,60 +99,154 @@ EXPORT bool_t str_has_suffix_nocase (const char * str, const char * suffix) return ! g_ascii_strcasecmp (str + len1 - len2, suffix); } -static char * (* str_to_utf8_impl) (const char *) = NULL; -static char * (* str_to_utf8_full_impl) (const char *, int, int *, int *) = NULL; - -EXPORT void str_set_utf8_impl (char * (* stu_impl) (const char *), - char * (* stuf_impl) (const char *, int, int *, int *)) +EXPORT char * strstr_nocase (const char * haystack, const char * needle) { - str_to_utf8_impl = stu_impl; - str_to_utf8_full_impl = stuf_impl; + while (1) + { + const char * ap = haystack; + const char * bp = needle; + + while (1) + { + char a = * ap ++; + char b = * bp ++; + + if (! b) /* all of needle matched */ + return (char *) haystack; + if (! a) /* end of haystack reached */ + return NULL; + + if (a != b && a != SWAP_CASE (b)) + break; + } + + haystack ++; + } } -EXPORT char * str_to_utf8 (const char * str) +EXPORT char * strstr_nocase_utf8 (const char * haystack, const char * needle) { - g_return_val_if_fail (str_to_utf8_impl, NULL); - return str_to_utf8_impl (str); + while (1) + { + const char * ap = haystack; + const char * bp = needle; + + while (1) + { + gunichar a = g_utf8_get_char (ap); + gunichar b = g_utf8_get_char (bp); + + if (! b) /* all of needle matched */ + return (char *) haystack; + if (! a) /* end of haystack reached */ + return NULL; + + if (a != b && (a < 128 ? SWAP_CASE (a) != b : + g_unichar_tolower (a) != g_unichar_tolower (b))) + break; + + ap = g_utf8_next_char (ap); + bp = g_utf8_next_char (bp); + } + + haystack = g_utf8_next_char (haystack); + } } -EXPORT char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written) +EXPORT char * str_tolower_utf8 (const char * str) { - g_return_val_if_fail (str_to_utf8_full_impl, NULL); - return str_to_utf8_full_impl (str, len, bytes_read, bytes_written); + char buf[6 * strlen (str) + 1]; + const char * get = str; + char * set = buf; + gunichar c; + + while ((c = g_utf8_get_char (get))) + { + if (c < 128) + * set ++ = g_ascii_tolower (c); + else + set += g_unichar_to_utf8 (g_unichar_tolower (c), set); + + get = g_utf8_next_char (get); + } + + * set = 0; + + return str_get (buf); } -EXPORT void string_replace_char (char * string, char old_c, char new_c) +EXPORT void str_replace_char (char * string, char old_c, char new_c) { while ((string = strchr (string, old_c))) * string ++ = new_c; } +EXPORT void str_itoa (int x, char * buf, int bufsize) +{ + if (! bufsize) + return; + + if (x < 0) + { + if (bufsize > 1) + { + * buf ++ = '-'; + bufsize --; + } + + x = -x; + } + + char * rev = buf + bufsize - 1; + * rev = 0; + + while (rev > buf) + { + * (-- rev) = '0' + x % 10; + if (! (x /= 10)) + break; + } + + while ((* buf ++ = * rev ++)); +} + /* Percent-decodes up to <len> bytes of <str> to <out>, which must be large * enough to hold the decoded string (i.e., (len + 1) bytes). If <len> is * negative, decodes all of <str>. */ EXPORT void str_decode_percent (const char * str, int len, char * out) { + const char * nul; + if (len < 0) - len = INT_MAX; + len = strlen (str); + else if ((nul = memchr (str, 0, len))) + len = nul - str; - while (len --) + while (1) { - char c = * str ++; - if (! c) + const char * p = memchr (str, '%', len); + if (! p) break; - if (c == '%' && len >= 2 && str[0] && str[1]) - { - c = (FROM_HEX (str[0]) << 4) | FROM_HEX (str[1]); - str += 2; - len -= 2; - } + int block = p - str; + memmove (out, str, block); + + str += block; + out += block; + len -= block; + + if (len < 3) + break; + + * out ++ = (FROM_HEX (str[1]) << 4) | FROM_HEX (str[2]); - * out ++ = c; + str += 3; + len -= 3; } - * out = 0; + memmove (out, str, len); + out[len] = 0; } /* Percent-encodes up to <len> bytes of <str> to <out>, which must be large @@ -114,14 +255,16 @@ EXPORT void str_decode_percent (const char * str, int len, char * out) EXPORT void str_encode_percent (const char * str, int len, char * out) { + const char * nul; + if (len < 0) - len = INT_MAX; + len = strlen (str); + else if ((nul = memchr (str, 0, len))) + len = nul - str; while (len --) { char c = * str ++; - if (! c) - break; if (IS_LEGAL (c)) * out ++ = c; @@ -136,62 +279,101 @@ EXPORT void str_encode_percent (const char * str, int len, char * out) * out = 0; } -/* Like g_filename_to_uri, but converts the filename from the system locale to - * UTF-8 before percent-encoding. On Windows, replaces '\' with '/' and adds a - * leading '/'. */ +EXPORT void filename_normalize (char * filename) +{ +#ifdef _WIN32 + /* convert slash to backslash on Windows */ + str_replace_char (filename, '/', '\\'); +#endif -EXPORT char * filename_to_uri (const char * name) + /* remove trailing slash */ + int len = strlen (filename); +#ifdef _WIN32 + if (len > 3 && filename[len - 1] == '\\') /* leave "C:\" */ +#else + if (len > 1 && filename[len - 1] == '/') /* leave leading "/" */ +#endif + filename[len - 1] = 0; +} + +EXPORT char * filename_build (const char * path, const char * name) { - char * utf8 = g_locale_to_utf8 (name, -1, NULL, NULL, NULL); - if (! utf8) + int len = strlen (path); + +#ifdef _WIN32 + if (! len || path[len - 1] == '/' || path[len - 1] == '\\') { - const char * locale = setlocale (LC_ALL, NULL); - fprintf (stderr, "Cannot convert filename from system locale (%s): %s\n", locale, name); - return NULL; + SCONCAT2 (filename, path, name); + return str_get (filename); + } + + SCONCAT3 (filename, path, "\\", name); + return str_get (filename); +#else + if (! len || path[len - 1] == '/') + { + SCONCAT2 (filename, path, name); + return str_get (filename); } + SCONCAT3 (filename, path, "/", name); + return str_get (filename); +#endif +} + #ifdef _WIN32 - string_replace_char (utf8, '\\', '/'); +#define URI_PREFIX "file:///" +#define URI_PREFIX_LEN 8 +#else +#define URI_PREFIX "file://" +#define URI_PREFIX_LEN 7 #endif - char enc[3 * strlen (utf8) + 1]; - str_encode_percent (utf8, -1, enc); - g_free (utf8); +/* Like g_filename_to_uri, but converts the filename from the system locale to + * UTF-8 before percent-encoding (except on Windows, where filenames are assumed + * to be UTF-8). On Windows, replaces '\' with '/' and adds a leading '/'. */ +EXPORT char * filename_to_uri (const char * name) +{ #ifdef _WIN32 - return g_strdup_printf ("file:///%s", enc); + SCOPY (utf8, name); + str_replace_char (utf8, '\\', '/'); #else - return g_strdup_printf ("file://%s", enc); + char * utf8 = str_from_locale (name, -1); + if (! utf8) + return NULL; #endif + + char enc[URI_PREFIX_LEN + 3 * strlen (utf8) + 1]; + strcpy (enc, URI_PREFIX); + str_encode_percent (utf8, -1, enc + URI_PREFIX_LEN); + +#ifndef _WIN32 + str_unref (utf8); +#endif + + return str_get (enc); } /* Like g_filename_from_uri, but converts the filename from UTF-8 to the system - * locale after percent-decoding. On Windows, strips the leading '/' and - * replaces '/' with '\'. */ + * locale after percent-decoding (except on Windows, where filenames are assumed + * to be UTF-8). On Windows, strips the leading '/' and replaces '/' with '\'. */ EXPORT char * uri_to_filename (const char * uri) { + if (strncmp (uri, URI_PREFIX, URI_PREFIX_LEN)) + return NULL; + + char buf[strlen (uri + URI_PREFIX_LEN) + 1]; + str_decode_percent (uri + URI_PREFIX_LEN, -1, buf); + + filename_normalize (buf); + #ifdef _WIN32 - g_return_val_if_fail (! strncmp (uri, "file:///", 8), NULL); - char buf[strlen (uri + 8) + 1]; - str_decode_percent (uri + 8, -1, buf); + return str_get (buf); #else - g_return_val_if_fail (! strncmp (uri, "file://", 7), NULL); - char buf[strlen (uri + 7) + 1]; - str_decode_percent (uri + 7, -1, buf); -#endif -#ifdef _WIN32 - string_replace_char (buf, '/', '\\'); + return str_to_locale (buf, -1); #endif - - char * name = g_locale_from_utf8 (buf, -1, NULL, NULL, NULL); - if (! name) - { - const char * locale = setlocale (LC_ALL, NULL); - fprintf (stderr, "Cannot convert filename to system locale (%s): %s\n", locale, buf); - } - - return name; } /* Formats a URI for human-readable display. Percent-decodes and, for file:// @@ -200,26 +382,26 @@ EXPORT char * uri_to_filename (const char * uri) EXPORT char * uri_to_display (const char * uri) { if (! strncmp (uri, "cdda://?", 8)) - return g_strdup_printf (_("Audio CD, track %s"), uri + 8); + return str_printf (_("Audio CD, track %s"), uri + 8); char buf[strlen (uri) + 1]; -#ifdef _WIN32 - if (! strncmp (uri, "file:///", 8)) + if (! strncmp (uri, URI_PREFIX, URI_PREFIX_LEN)) { - str_decode_percent (uri + 8, -1, buf); - string_replace_char (buf, '/', '\\'); - } -#else - if (! strncmp (uri, "file://", 7)) - str_decode_percent (uri + 7, -1, buf); + str_decode_percent (uri + URI_PREFIX_LEN, -1, buf); +#ifdef _WIN32 + str_replace_char (buf, '/', '\\'); #endif + } else str_decode_percent (uri, -1, buf); - return g_strdup (buf); + return str_get (buf); } +#undef URI_PREFIX +#undef URI_PREFIX_LEN + EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * ext_p, const char * * sub_p, int * isub_p) { @@ -238,9 +420,7 @@ EXPORT void uri_parse (const char * uri, const char * * base_p, const char * * e else sub = end; - char buf[sub - base + 1]; - memcpy (buf, base, sub - base); - buf[sub - base] = 0; + SNCOPY (buf, base, sub - base); if ((c = strrchr (buf, '.'))) ext = base + (c - buf); @@ -265,7 +445,8 @@ EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen) if (ext[0] != '.') return FALSE; - g_strlcpy (buf, ext + 1, buflen); + strncpy (buf, ext + 1, buflen - 1); + buf[buflen - 1] = 0; /* remove subtunes and HTTP query strings */ char * qmark; @@ -279,7 +460,7 @@ EXPORT bool_t uri_get_extension (const char * uri, char * buf, int buflen) /* Non-ASCII characters are treated exactly as is. */ /* Handles NULL gracefully. */ -EXPORT int string_compare (const char * ap, const char * bp) +EXPORT int str_compare (const char * ap, const char * bp) { if (ap == NULL) return (bp == NULL) ? 0 : -1; @@ -321,9 +502,9 @@ EXPORT int string_compare (const char * ap, const char * bp) return 0; } -/* Decodes percent-encoded strings, then compares then with string_compare. */ +/* Decodes percent-encoded strings, then compares then with str_compare. */ -EXPORT int string_compare_encoded (const char * ap, const char * bp) +EXPORT int str_compare_encoded (const char * ap, const char * bp) { if (ap == NULL) return (bp == NULL) ? 0 : -1; @@ -376,37 +557,72 @@ EXPORT int string_compare_encoded (const char * ap, const char * bp) return 0; } -EXPORT char * -str_replace_fragment(char *s, int size, const char *old, const char *new) +EXPORT Index * str_list_to_index (const char * list, const char * delims) { - char *ptr = s; - int left = strlen(s); - int avail = size - (left + 1); - int oldlen = strlen(old); - int newlen = strlen(new); - int diff = newlen - oldlen; - - while (left >= oldlen) + char dmap[256] = {0}; + + for (; * delims; delims ++) + dmap[(unsigned char) (* delims)] = 1; + + Index * index = index_new (); + const char * word = NULL; + + for (; * list; list ++) { - if (strncmp(ptr, old, oldlen)) + if (dmap[(unsigned char) (* list)]) { - left--; - ptr++; - continue; + if (word) + { + index_insert (index, -1, str_nget (word, list - word)); + word = NULL; + } } + else + { + if (! word) + { + word = list; + } + } + } - if (diff > avail) - break; + if (word) + index_insert (index, -1, str_get (word)); - if (diff != 0) - memmove(ptr + oldlen + diff, ptr + oldlen, left + 1 - oldlen); + return index; +} + +EXPORT char * index_to_str_list (Index * index, const char * sep) +{ + int count = index_count (index); + int seplen = strlen (sep); + int total = count ? seplen * (count - 1) : 0; + int lengths[count]; - memcpy(ptr, new, newlen); - ptr += newlen; - left -= oldlen; + for (int i = 0; i < count; i ++) + { + lengths[i] = strlen (index_get (index, i)); + total += lengths[i]; } - return s; + char buf[total + 1]; + int pos = 0; + + for (int i = 0; i < count; i ++) + { + if (i) + { + strcpy (buf + pos, sep); + pos += seplen; + } + + strcpy (buf + pos, index_get (index, i)); + pos += lengths[i]; + } + + buf[pos] = 0; + + return str_get (buf); } /* @@ -420,11 +636,11 @@ str_replace_fragment(char *s, int size, const char *old, const char *new) * architecture or locale we have to deal with. * - Readability, meaning that the number one is rendered "1", not "1.000". * - * Values are limited between -1,000,000,000 and 1,000,000,000 (inclusive) and + * Values between -1,000,000,000 and 1,000,000,000 (inclusive) are guaranteed to * have an accuracy of 6 decimal places. */ -EXPORT bool_t string_to_int (const char * string, int * addr) +EXPORT int str_to_int (const char * string) { bool_t neg = (string[0] == '-'); if (neg) @@ -433,88 +649,41 @@ EXPORT bool_t string_to_int (const char * string, int * addr) int val = 0; char c; - while ((c = * string ++)) - { - if (c < '0' || c > '9' || val > 100000000) - goto ERR; - + while ((c = * string ++) && c >= '0' && c <= '9') val = val * 10 + (c - '0'); - } - - if (val > 1000000000) - goto ERR; - * addr = neg ? -val : val; - return TRUE; - -ERR: - return FALSE; + return neg ? -val : val; } -EXPORT bool_t string_to_double (const char * string, double * addr) +EXPORT double str_to_double (const char * string) { bool_t neg = (string[0] == '-'); if (neg) string ++; + double val = str_to_int (string); const char * p = strchr (string, '.'); - int i, f; if (p) { - char buf[11]; - int len; - - len = p - string; - if (len > 10) - goto ERR; - - memcpy (buf, string, len); - buf[len] = 0; - - if (! string_to_int (buf, & i)) - goto ERR; - - len = strlen (p + 1); - if (len > 6) - goto ERR; - - memcpy (buf, p + 1, len); - memset (buf + len, '0', 6 - len); - buf[6] = 0; - - if (! string_to_int (buf, & f)) - goto ERR; + char buf[7] = "000000"; + const char * nul = memchr (p + 1, 0, 6); + memcpy (buf, p + 1, nul ? nul - (p + 1) : 6); + val += (double) str_to_int (buf) / 1000000; } - else - { - if (! string_to_int (string, & i)) - goto ERR; - - f = 0; - } - - double val = i + (double) f / 1000000; - if (val > 1000000000) - goto ERR; - - * addr = neg ? -val : val; - return TRUE; -ERR: - return FALSE; + return neg ? -val : val; } -EXPORT char * int_to_string (int val) +EXPORT char * int_to_str (int val) { - g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL); - return g_strdup_printf ("%d", val); + char buf[16]; + str_itoa (val, buf, sizeof buf); + return str_get (buf); } -EXPORT char * double_to_string (double val) +EXPORT char * double_to_str (double val) { - g_return_val_if_fail (val >= -1000000000 && val <= 1000000000, NULL); - bool_t neg = (val < 0); if (neg) val = -val; @@ -528,94 +697,88 @@ EXPORT char * double_to_string (double val) f = 0; } - char * s = neg ? g_strdup_printf ("-%d.%06d", i, f) : g_strdup_printf ("%d.%06d", i, f); + SPRINTF (buf, "%s%d.%06d", neg ? "-" : "", i, f); - char * c = s + strlen (s); + char * c = buf + strlen (buf); while (* (c - 1) == '0') c --; if (* (c - 1) == '.') c --; * c = 0; - return s; + return str_get (buf); } -EXPORT bool_t string_to_int_array (const char * string, int * array, int count) +EXPORT bool_t str_to_int_array (const char * string, int * array, int count) { - char * * split = g_strsplit (string, ",", -1); - if (g_strv_length (split) != count) - goto ERR; + Index * index = str_list_to_index (string, ", "); + bool_t okay = (index_count (index) == count); - for (int i = 0; i < count; i ++) + if (okay) { - if (! string_to_int (split[i], & array[i])) - goto ERR; + for (int i = 0; i < count; i ++) + array[i] = str_to_int (index_get (index, i)); } - g_strfreev (split); - return TRUE; - -ERR: - g_strfreev (split); - return FALSE; + index_free_full (index, (IndexFreeFunc) str_unref); + return okay; } -EXPORT char * int_array_to_string (const int * array, int count) +EXPORT char * int_array_to_str (const int * array, int count) { - char * * split = g_malloc0 (sizeof (char *) * (count + 1)); + Index * index = index_new (); for (int i = 0; i < count; i ++) { - split[i] = int_to_string (array[i]); - if (! split[i]) + char * value = int_to_str (array[i]); + if (! value) goto ERR; + + index_insert (index, -1, value); } - char * string = g_strjoinv (",", split); - g_strfreev (split); + char * string = index_to_str_list (index, ","); + index_free_full (index, (IndexFreeFunc) str_unref); return string; ERR: - g_strfreev (split); + index_free_full (index, (IndexFreeFunc) str_unref); return NULL; } -EXPORT bool_t string_to_double_array (const char * string, double * array, int count) +EXPORT bool_t str_to_double_array (const char * string, double * array, int count) { - char * * split = g_strsplit (string, ",", -1); - if (g_strv_length (split) != count) - goto ERR; + Index * index = str_list_to_index (string, ", "); + bool_t okay = (index_count (index) == count); - for (int i = 0; i < count; i ++) + if (okay) { - if (! string_to_double (split[i], & array[i])) - goto ERR; + for (int i = 0; i < count; i ++) + array[i] = str_to_double (index_get (index, i)); } - g_strfreev (split); - return TRUE; - -ERR: - g_strfreev (split); - return FALSE; + index_free_full (index, (IndexFreeFunc) str_unref); + return okay; } -EXPORT char * double_array_to_string (const double * array, int count) +EXPORT char * double_array_to_str (const double * array, int count) { - char * * split = g_malloc0 (sizeof (char *) * (count + 1)); + Index * index = index_new (); for (int i = 0; i < count; i ++) { - split[i] = double_to_string (array[i]); - if (! split[i]) + char * value = double_to_str (array[i]); + if (! value) goto ERR; + + index_insert (index, -1, value); } - char * string = g_strjoinv (",", split); - g_strfreev (split); + char * string = index_to_str_list (index, ","); + index_free_full (index, (IndexFreeFunc) str_unref); return string; ERR: - g_strfreev (split); + index_free_full (index, (IndexFreeFunc) str_unref); return NULL; } diff --git a/src/libaudcore/audstrings.h b/src/libaudcore/audstrings.h index c8e1035..9f6cae7 100644 --- a/src/libaudcore/audstrings.h +++ b/src/libaudcore/audstrings.h @@ -1,7 +1,6 @@ /* * audstrings.h - * Copyright 2009-2011 John Lindgren - * Copyright 2010 William Pitcock + * Copyright 2009-2012 John Lindgren and William Pitcock * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -21,21 +20,86 @@ #ifndef LIBAUDCORE_STRINGS_H #define LIBAUDCORE_STRINGS_H +#include <stdarg.h> +#include <stdio.h> +#include <string.h> + #include <libaudcore/core.h> +#define SPRINTF(s, ...) \ + char s[snprintf (NULL, 0, __VA_ARGS__) + 1]; \ + snprintf (s, sizeof s, __VA_ARGS__) + +#define VSPRINTF(s, f, v) \ + va_list v##2; \ + va_copy (v##2, v); \ + char s[vsnprintf (NULL, 0, f, v##2) + 1]; \ + va_end (v##2); \ + vsnprintf (s, sizeof s, f, v) + +#define SCOPY(s, a) \ + char s[strlen (a) + 1]; \ + strcpy (s, a) + +#define SNCOPY(s, a, x) \ + char s[(x) + 1]; \ + strncpy (s, a, sizeof s - 1); \ + s[sizeof s - 1] = 0 + +#define SCONCAT2(s, a, b) \ + int s##_1 = strlen (a), s##_2 = strlen (b); \ + char s[s##_1 + s##_2 + 1]; \ + memcpy (s, (a), s##_1); \ + strcpy (s + s##_1, (b)) + +#define SCONCAT3(s, a, b, c) \ + int s##_1 = strlen (a), s##_2 = strlen (b), s##_3 = strlen (c); \ + char s[s##_1 + s##_2 + s##_3 + 1]; \ + memcpy (s, (a), s##_1); \ + memcpy (s + s##_1, (b), s##_2); \ + strcpy (s + s##_1 + s##_2, (c)) + +#define SCONCAT4(s, a, b, c, d) \ + int s##_1 = strlen (a), s##_2 = strlen (b), s##_3 = strlen (c), s##_4 = strlen (d); \ + char s[s##_1 + s##_2 + s##_3 + s##_4 + 1]; \ + memcpy (s, (a), s##_1); \ + memcpy (s + s##_1, (b), s##_2); \ + memcpy (s + s##_1 + s##_2, (c), s##_3); \ + strcpy (s + s##_1 + s##_2 + s##_3, (d)) + +struct _Index; + +/* all (char *) return values must be freed with str_unref() */ + +char * str_printf (const char * format, ...) __attribute__ ((__format__ (__printf__, 1, 2))); +char * str_vprintf (const char * format, va_list args); + bool_t str_has_prefix_nocase(const char * str, const char * prefix); bool_t str_has_suffix_nocase(const char * str, const char * suffix); -void str_set_utf8_impl (char * (* stu_impl) (const char *), - char * (* stuf_impl) (const char *, int, int *, int *)); -char * str_to_utf8 (const char * str); -char * str_to_utf8_full (const char * str, int len, int * bytes_read, int * bytes_written); +char * strstr_nocase (const char * haystack, const char * needle); +char * strstr_nocase_utf8 (const char * haystack, const char * needle); + +char * str_tolower_utf8 (const char * str); -void string_replace_char (char * string, char old_c, char new_c); +void str_replace_char (char * string, char old_c, char new_c); + +void str_itoa (int x, char * buf, int bufsize); void str_decode_percent (const char * str, int len, char * out); void str_encode_percent (const char * str, int len, char * out); +char * str_convert (const char * str, int len, const char * from_charset, const char * to_charset); +char * str_from_locale (const char * str, int len); +char * str_to_locale (const char * str, int len); +char * str_to_utf8 (const char * str, int len); + +/* takes ownership of <fallbacks> and the pooled strings in it */ +void str_set_charsets (const char * region, struct _Index * fallbacks); + +void filename_normalize (char * filename); + +char * filename_build (const char * path, const char * name); char * filename_to_uri (const char * filename); char * uri_to_filename (const char * uri); char * uri_to_display (const char * uri); @@ -44,19 +108,20 @@ void uri_parse (const char * uri, const char * * base_p, const char * * ext_p, const char * * sub_p, int * isub_p); bool_t uri_get_extension (const char * uri, char * buf, int buflen); -int string_compare (const char * a, const char * b); -int string_compare_encoded (const char * a, const char * b); +int str_compare (const char * a, const char * b); +int str_compare_encoded (const char * a, const char * b); -char *str_replace_fragment(char *s, int size, const char *old_str, const char *new_str); +struct _Index * str_list_to_index (const char * list, const char * delims); +char * index_to_str_list (struct _Index * index, const char * sep); -bool_t string_to_int (const char * string, int * addr); -bool_t string_to_double (const char * string, double * addr); -char * int_to_string (int val); -char * double_to_string (double val); +int str_to_int (const char * string); +double str_to_double (const char * string); +char * int_to_str (int val); +char * double_to_str (double val); -bool_t string_to_int_array (const char * string, int * array, int count); -char * int_array_to_string (const int * array, int count); -bool_t string_to_double_array (const char * string, double * array, int count); -char * double_array_to_string (const double * array, int count); +bool_t str_to_int_array (const char * string, int * array, int count); +char * int_array_to_str (const int * array, int count); +bool_t str_to_double_array (const char * string, double * array, int count); +char * double_array_to_str (const double * array, int count); #endif /* LIBAUDCORE_STRINGS_H */ diff --git a/src/libaudcore/charset.c b/src/libaudcore/charset.c new file mode 100644 index 0000000..d3c6d5f --- /dev/null +++ b/src/libaudcore/charset.c @@ -0,0 +1,180 @@ +/* + * charset.c + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include <iconv.h> +#include <stdio.h> +#include <string.h> + +#include <glib.h> + +#ifdef USE_CHARDET +#include <libguess/libguess.h> +#endif + +#include "audstrings.h" +#include "index.h" +#include "tinylock.h" + +EXPORT char * str_convert (const char * str, int len, const char * from_charset, + const char * to_charset) +{ + iconv_t conv = iconv_open (to_charset, from_charset); + if (conv == (iconv_t) -1) + return NULL; + + if (len < 0) + len = strlen (str); + + // liberal estimate of how much space we will need + // are there obscure cases that require even more? + int maxlen = 4 * len; + + char buf[maxlen + 1]; + char * result = NULL; + + size_t inbytes = len; + size_t outbytes = maxlen; + char * in = (char *) str; + char * out = buf; + + if (iconv (conv, & in, & inbytes, & out, & outbytes) != (size_t) -1 && ! inbytes) + { + buf[maxlen - outbytes] = 0; + result = str_get (buf); + } + + iconv_close (conv); + return result; +} + +static void whine_locale (const char * str, int len, const char * dir, const char * charset) +{ + if (len < 0) + fprintf (stderr, "Cannot convert %s locale (%s): %s\n", dir, charset, str); + else + fprintf (stderr, "Cannot convert %s locale (%s): %.*s\n", dir, charset, len, str); +} + +EXPORT char * str_from_locale (const char * str, int len) +{ + const char * charset; + + if (g_get_charset (& charset)) + { + /* locale is UTF-8 */ + if (! g_utf8_validate (str, len, NULL)) + { + whine_locale (str, len, "from", "UTF-8"); + return NULL; + } + + return (len < 0) ? str_get (str) : str_nget (str, len); + } + else + { + char * utf8 = str_convert (str, len, charset, "UTF-8"); + if (! utf8) + whine_locale (str, len, "from", charset); + + return utf8; + } +} + +EXPORT char * str_to_locale (const char * str, int len) +{ + const char * charset; + + if (g_get_charset (& charset)) + { + /* locale is UTF-8 */ + return (len < 0) ? str_get (str) : str_nget (str, len); + } + else + { + char * local = str_convert (str, len, "UTF-8", charset); + if (! local) + whine_locale (str, len, "to", charset); + + return local; + } +} + +static TinyRWLock settings_lock; +static char * detect_region; +static Index * fallback_charsets; + +EXPORT void str_set_charsets (const char * region, Index * fallbacks) +{ + tiny_lock_write (& settings_lock); + + str_unref (detect_region); + detect_region = str_get (region); + +#ifdef USE_CHARDET + if (detect_region) + libguess_init (); +#endif + + if (fallback_charsets) + index_free_full (fallback_charsets, (IndexFreeFunc) str_unref); + + fallback_charsets = fallbacks; + + tiny_unlock_write (& settings_lock); +} + +EXPORT char * str_to_utf8 (const char * str, int len) +{ + /* check whether already UTF-8 */ + if (g_utf8_validate (str, len, NULL)) + return (len < 0) ? str_get (str) : str_nget (str, len); + + if (len < 0) + len = strlen (str); + + char * utf8 = NULL; + tiny_lock_read (& settings_lock); + +#ifdef USE_CHARDET + if (detect_region) + { + /* prefer libguess-detected charset */ + const char * detected = libguess_determine_encoding (str, len, detect_region); + if (detected && (utf8 = str_convert (str, len, detected, "UTF-8"))) + goto DONE; + } +#endif + + if (fallback_charsets) + { + /* try user-configured fallbacks */ + for (int i = 0; i < index_count (fallback_charsets); i ++) + { + if ((utf8 = str_convert (str, len, index_get (fallback_charsets, i), "UTF-8"))) + goto DONE; + } + } + + /* try system locale last (this one will print a warning if it fails) */ + utf8 = str_from_locale (str, len); + +DONE: + tiny_unlock_read (& settings_lock); + return utf8; +} diff --git a/src/libaudcore/core.h b/src/libaudcore/core.h index b92c500..f3c2615 100644 --- a/src/libaudcore/core.h +++ b/src/libaudcore/core.h @@ -1,6 +1,6 @@ /* * core.h - * Copyright 2011 John Lindgren + * Copyright 2011-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -43,14 +43,7 @@ #undef CLAMP #define CLAMP(a,min,max) ((a) < (min) ? (min) : (a) > (max) ? (max) : (a)) -#define SPRINTF(s,...) \ - char s[snprintf (NULL, 0, __VA_ARGS__) + 1]; \ - snprintf (s, sizeof s, __VA_ARGS__); - -/* Simple sanity check to catch (1) strings that are still in use after their - * reference count has dropped to zero and (2) strings that should have been - * pooled but never were. If the check fails, the program is aborted. */ -#define STR_CHECK(str) do {if ((str) && (str)[-1] != '@') strpool_abort (str);} while (0) +#define ARRAY_LEN(a) (sizeof (a) / sizeof (a)[0]) /* If the pool contains a copy of <str>, increments its reference count. * Otherwise, adds a copy of <str> to the pool with a reference count of one. @@ -63,7 +56,7 @@ char * str_get (const char * str); * string already in the pool. Faster than calling str_get() a second time. * Returns <str> for convenience. If <str> is NULL, simply returns NULL with no * side effects. */ -char * str_ref (char * str); +char * str_ref (const char * str); /* Decrements the reference count of <str>, where <str> is the address of a * string in the pool. If the reference count drops to zero, releases the @@ -71,16 +64,18 @@ char * str_ref (char * str); * effects. */ void str_unref (char * str); +/* Returns the cached hash value of a pooled string (or 0 for NULL). */ +unsigned str_hash (const char * str); + +/* Checks whether two pooled strings are equal. Since the pool never contains + * duplicate strings, this is a simple pointer comparison and thus much faster + * than strcmp(). NULL is considered equal to NULL but not equal to any string. */ +bool_t str_equal (const char * str1, const char * str2); + /* Calls str_get() on the first <len> characters of <str>. If <str> has less * than or equal to <len> characters, equivalent to str_get(). */ char * str_nget (const char * str, int len); -/* Calls sprintf() internally, then pools the produced string with str_get(). */ -char * str_printf (const char * format, ...); - -/* Used by STR_CHECK; should not be called directly. */ -void strpool_abort (char * str); - /* Releases all memory used by the string pool. If strings remain in the pool, * a warning may be printed to stderr in order to reveal memory leaks. */ void strpool_shutdown (void); diff --git a/src/libaudcore/eventqueue.c b/src/libaudcore/eventqueue.c index c2648b9..ee92c03 100644 --- a/src/libaudcore/eventqueue.c +++ b/src/libaudcore/eventqueue.c @@ -45,7 +45,7 @@ static bool_t event_execute (Event * event) hook_call (event->name, event->data); - g_free (event->name); + str_unref (event->name); if (event->destroy) event->destroy (event->data); @@ -56,7 +56,7 @@ static bool_t event_execute (Event * event) EXPORT void event_queue_full (int time, const char * name, void * data, void (* destroy) (void *)) { Event * event = g_slice_new (Event); - event->name = g_strdup (name); + event->name = str_get (name); event->data = data; event->destroy = destroy; @@ -83,7 +83,7 @@ EXPORT void event_queue_cancel (const char * name, void * data) g_source_remove (event->source); events = g_list_delete_link (events, node); - g_free (event->name); + str_unref (event->name); if (event->destroy) event->destroy (event->data); @@ -95,3 +95,28 @@ EXPORT void event_queue_cancel (const char * name, void * data) pthread_mutex_unlock (& mutex); } + +EXPORT void event_queue_cancel_all (void) +{ + pthread_mutex_lock (& mutex); + + GList * node = events; + while (node) + { + Event * event = node->data; + GList * next = node->next; + + g_source_remove (event->source); + events = g_list_delete_link (events, node); + + str_unref (event->name); + if (event->destroy) + event->destroy (event->data); + + g_slice_free (Event, event); + + node = next; + } + + pthread_mutex_unlock (& mutex); +} diff --git a/src/libaudcore/hook.c b/src/libaudcore/hook.c index 00d69c8..e9f4e39 100644 --- a/src/libaudcore/hook.c +++ b/src/libaudcore/hook.c @@ -1,6 +1,6 @@ /* * hook.c - * Copyright 2011 John Lindgren + * Copyright 2011-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/libaudcore/hook.h b/src/libaudcore/hook.h index 79fe8ac..646359b 100644 --- a/src/libaudcore/hook.h +++ b/src/libaudcore/hook.h @@ -47,4 +47,7 @@ void event_queue_full (int time, const char * name, void * data, void (* destroy * all hook calls matching <name> are canceled. */ void event_queue_cancel (const char * name, void * data); +/* Cancels all pending hook calls. */ +void event_queue_cancel_all (void); + #endif /* LIBAUDCORE_HOOK_H */ diff --git a/src/libaudcore/index.c b/src/libaudcore/index.c index 160a97d..c2f7f39 100644 --- a/src/libaudcore/index.c +++ b/src/libaudcore/index.c @@ -1,6 +1,6 @@ /* * index.c - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,16 +17,19 @@ * the use of this software. */ +#include <assert.h> #include <stdlib.h> #include <string.h> #include <glib.h> +#include "core.h" #include "index.h" struct _Index { void * * data; int count, size; + void * sdata[16]; }; typedef struct { @@ -40,19 +43,26 @@ typedef struct { EXPORT Index * index_new (void) { - Index * index = g_slice_new (Index); - - index->data = NULL; - index->count = 0; - index->size = 0; - + Index * index = g_new0 (Index, 1); + index->data = index->sdata; + index->size = ARRAY_LEN (index->sdata); return index; } EXPORT void index_free (Index * index) { - g_free (index->data); - g_slice_free (Index, index); + if (index->data != index->sdata) + g_free (index->data); + + g_free (index); +} + +EXPORT void index_free_full (Index * index, IndexFreeFunc func) +{ + for (int i = 0; i < index->count; i ++) + func (index->data[i]); + + index_free (index); } EXPORT int index_count (Index * index) @@ -62,26 +72,38 @@ EXPORT int index_count (Index * index) EXPORT void index_allocate (Index * index, int size) { - if (size <= index->size) + assert (size >= 0); + + if (index->size >= size) return; - if (! index->size) + if (index->size < 64) index->size = 64; - while (size > index->size) + while (index->size < size) index->size <<= 1; - index->data = g_realloc (index->data, sizeof (void *) * index->size); + if (index->data == index->sdata) + { + index->data = g_new (void *, index->size); + memcpy (index->data, index->sdata, sizeof index->sdata); + } + else + index->data = g_renew (void *, index->data, index->size); } -EXPORT void index_set (Index * index, int at, void * value) +EXPORT void * index_get (Index * index, int at) { - index->data[at] = value; + assert (at >= 0 && at < index->count); + + return index->data[at]; } -EXPORT void * index_get (Index * index, int at) +EXPORT void index_set (Index * index, int at, void * value) { - return index->data[at]; + assert (at >= 0 && at < index->count); + + index->data[at] = value; } static void make_room (Index * index, int at, int count) @@ -89,62 +111,80 @@ static void make_room (Index * index, int at, int count) index_allocate (index, index->count + count); if (at < index->count) - memmove (index->data + at + count, index->data + at, sizeof (void *) * - (index->count - at)); + memmove (index->data + at + count, index->data + at, sizeof (void *) * (index->count - at)); index->count += count; } EXPORT void index_insert (Index * index, int at, void * value) { + if (at == -1) + at = index->count; + + assert (at >= 0 && at <= index->count); + make_room (index, at, 1); index->data[at] = value; } -EXPORT void index_append (Index * index, void * value) +EXPORT void index_copy_set (Index * source, int from, Index * target, int to, int count) { - index_insert (index, index->count, value); -} + assert (count >= 0); + assert (from >= 0 && from + count <= source->count); + assert (to >= 0 && to + count <= target->count); -EXPORT void index_copy_set (Index * source, int from, Index * target, - int to, int count) -{ - memcpy (target->data + to, source->data + from, sizeof (void *) * count); + memmove (target->data + to, source->data + from, sizeof (void *) * count); } -EXPORT void index_copy_insert (Index * source, int from, Index * target, - int to, int count) +EXPORT void index_copy_insert (Index * source, int from, Index * target, int to, int count) { + if (to == -1) + to = target->count; + if (count == -1) + count = source->count - from; + + assert (count >= 0); + assert (from >= 0 && from + count <= source->count); + assert (to >= 0 && to <= target->count); + make_room (target, to, count); - memcpy (target->data + to, source->data + from, sizeof (void *) * count); -} -EXPORT void index_copy_append (Index * source, int from, Index * target, - int count) -{ - index_copy_insert (source, from, target, target->count, count); + if (source == target && to <= from) + index_copy_set (source, from + count, target, to, count); + else if (source == target && to <= from + count) + { + index_copy_set (source, from, target, to, to - from); + index_copy_set (source, to + count, target, to + (to - from), count - (to - from)); + } + else + index_copy_set (source, from, target, to, count); } -EXPORT void index_merge_insert (Index * first, int at, Index * second) +EXPORT void index_delete (Index * index, int at, int count) { - index_copy_insert (second, 0, first, at, second->count); -} + if (count == -1) + count = index->count - at; -EXPORT void index_merge_append (Index * first, Index * second) -{ - index_copy_insert (second, 0, first, first->count, second->count); -} + assert (count >= 0); + assert (at >= 0 && at + count <= index->count); -EXPORT void index_move (Index * index, int from, int to, int count) -{ - memmove (index->data + to, index->data + from, sizeof (void *) * count); + index_copy_set (index, at + count, index, at, index->count - (at + count)); + + index->count -= count; } -EXPORT void index_delete (Index * index, int at, int count) +EXPORT void index_delete_full (Index * index, int at, int count, IndexFreeFunc func) { - index->count -= count; - memmove (index->data + at, index->data + at + count, sizeof (void *) * - (index->count - at)); + if (count == -1) + count = index->count - at; + + assert (count >= 0); + assert (at >= 0 && at + count <= index->count); + + for (int i = at; i < at + count; i ++) + func (index->data[i]); + + index_delete (index, at, count); } static int index_compare (const void * ap, const void * bp, void * _wrapper) @@ -156,8 +196,7 @@ static int index_compare (const void * ap, const void * bp, void * _wrapper) EXPORT void index_sort (Index * index, int (* compare) (const void *, const void *)) { CompareWrapper wrapper = {compare}; - g_qsort_with_data (index->data, index->count, sizeof (void *), - index_compare, & wrapper); + g_qsort_with_data (index->data, index->count, sizeof (void *), index_compare, & wrapper); } static int index_compare2 (const void * ap, const void * bp, void * _wrapper) @@ -170,6 +209,5 @@ EXPORT void index_sort_with_data (Index * index, int (* compare) (const void * a, const void * b, void * data), void * data) { CompareWrapper2 wrapper = {compare, data}; - g_qsort_with_data (index->data, index->count, sizeof (void *), - index_compare2, & wrapper); + g_qsort_with_data (index->data, index->count, sizeof (void *), index_compare2, & wrapper); } diff --git a/src/libaudcore/index.h b/src/libaudcore/index.h index 68d21a6..3990ff8 100644 --- a/src/libaudcore/index.h +++ b/src/libaudcore/index.h @@ -1,6 +1,6 @@ /* * index.h - * Copyright 2009-2011 John Lindgren + * Copyright 2009-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -20,25 +20,77 @@ #ifndef LIBAUDCORE_INDEX_H #define LIBAUDCORE_INDEX_H +/* An "index" is an opaque structure representing a list of pointers. It is + * used primarily to store Audacious playlists, but can be useful for other + * purposes as well. */ + struct _Index; typedef struct _Index Index; +typedef void (* IndexFreeFunc) (void * value); + +/* Returns a new, empty index. */ Index * index_new (void); + +/* Destroys <index>. */ void index_free (Index * index); + +/* Destroys <index>, first calling <func> on each pointer stored in it. */ +void index_free_full (Index * index, IndexFreeFunc func); + +/* Returns the number of pointers stored in <index>. */ int index_count (Index * index); + +/* Preallocates space to store <size> pointers in <index>. This can be used to + * avoid repeated memory allocations when adding pointers to <index> one by one. + * The value returned by index_count() does not changed until the pointers are + * actually added. */ void index_allocate (Index * index, int size); -void index_set (Index * index, int at, void * value); + +/* Returns the value stored in <index> at position <at>. */ void * index_get (Index * index, int at); + +/* Stores <value> in <index> at position <at>, overwriting the previous value. */ +void index_set (Index * index, int at, void * value); + +/* Stores <value> in <index> at position <at>, shifting the previous value (if + * any) and those following it forward to make room. If <at> is -1, <value> is + * added to the end of <index>. */ void index_insert (Index * index, int at, void * value); -void index_append (Index * index, void * value); + +/* Copies <count> pointers from <source>, starting at position <from>, to + * <target>, starting at position <to>. Existing pointers in <target> are + * overwritten. Overlapping regions are handled correctly if <source> and + * <target> are the same index. */ void index_copy_set (Index * source, int from, Index * target, int to, int count); + +/* Copies <count> pointers from <source>, starting at position <from>, to + * <target>, starting at position <to>. Existing pointers in <target> are + * shifted forward to make room. All cases are handled correctly if <source> + * and <target> are the same index, including the case where <to> is between + * <from> and <from + count>. If <to> is -1, the pointers are added to the end + * of <target>. If <count> is -1, copying continues until the end of <source> + * is reached. */ void index_copy_insert (Index * source, int from, Index * target, int to, int count); -void index_copy_append (Index * source, int from, Index * target, int count); -void index_merge_insert (Index * first, int at, Index * second); -void index_merge_append (Index * first, Index * second); -void index_move (Index * index, int from, int to, int count); + +/* Removes <count> pointers from <index>, start at position <at>. Any following + * pointers are shifted backward to close the gap. If <count> is -1, all + * pointers from <at> to the end of <index> are removed. */ void index_delete (Index * index, int at, int count); + +/* Like index_delete(), but first calls <func> on any pointer that is being + * removed. */ +void index_delete_full (Index * index, int at, int count, IndexFreeFunc func); + +/* Sort the entries in <index> using the quick-sort algorithm with the given + * comparison function. The return value of <compare> should follow the + * convention used by strcmp(): negative if (a < b), zero if (a = b), positive + * if (a > b). */ void index_sort (Index * index, int (* compare) (const void * a, const void * b)); + +/* Exactly like index_sort() except that <data> is forwarded to the comparison + * function. This allows comparison functions whose behavior changes depending + * on context. */ void index_sort_with_data (Index * index, int (* compare) (const void * a, const void * b, void * data), void * data); diff --git a/src/libaudcore/inifile.c b/src/libaudcore/inifile.c new file mode 100644 index 0000000..6e13a48 --- /dev/null +++ b/src/libaudcore/inifile.c @@ -0,0 +1,134 @@ +/* + * inifile.c + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "audstrings.h" +#include "inifile.h" + +#include <glib.h> + +#include <string.h> + +static char * strskip (char * str) +{ + while (g_ascii_isspace (* str)) + str ++; + + return str; +} + +static char * strtrim (char * str) +{ + int len = strlen (str); + + while (len && g_ascii_isspace(str[len - 1])) + str[-- len] = 0; + + return str; +} + +EXPORT void inifile_parse (VFSFile * file, + void (* handle_heading) (const char * heading, void * data), + void (* handle_entry) (const char * key, const char * value, void * data), + void * data) +{ + int size = 512; + char * buf = g_new (char, size); + + char * pos = buf; + int len = 0; + bool_t eof = FALSE; + + while (1) + { + char * newline = memchr (pos, '\n', len); + + while (! newline && ! eof) + { + memmove (buf, pos, len); + pos = buf; + + if (len >= size - 1) + { + size <<= 1; + buf = g_renew (char, buf, size); + pos = buf; + } + + len += vfs_fread (buf + len, 1, size - 1 - len, file); + + if (len < size - 1) + eof = TRUE; + + newline = memchr (pos, '\n', len); + } + + if (newline) + * newline = 0; + else + pos[len] = 0; + + char * start = strskip (pos); + + switch (* start) + { + case 0: + case '#': + case ';': + break; + + case '[':; + char * end = strchr (start + 1, ']'); + if (! end) + break; + + * end = 0; + handle_heading (strtrim (strskip (start + 1)), data); + break; + + default:; + char * sep = strchr (start, '='); + if (! sep) + break; + + * sep = 0; + handle_entry (strtrim (start), strtrim (strskip (sep + 1)), data); + break; + } + + if (! newline) + break; + + len -= newline + 1 - pos; + pos = newline + 1; + } + + g_free (buf); +} + +EXPORT bool_t inifile_write_heading (VFSFile * file, const char * heading) +{ + SCONCAT3 (buf, "\n[", heading, "]\n"); + return (vfs_fwrite (buf, 1, sizeof buf - 1, file) == sizeof buf - 1); +} + +EXPORT bool_t inifile_write_entry (VFSFile * file, const char * key, const char * value) +{ + SCONCAT4 (buf, key, "=", value, "\n"); + return (vfs_fwrite (buf, 1, sizeof buf - 1, file) == sizeof buf - 1); +} diff --git a/src/libaudcore/inifile.h b/src/libaudcore/inifile.h new file mode 100644 index 0000000..87189c2 --- /dev/null +++ b/src/libaudcore/inifile.h @@ -0,0 +1,33 @@ +/* + * inifile.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef LIBAUDCORE_INIFILE_H +#define LIBAUDCORE_INIFILE_H + +#include "vfs.h" + +void inifile_parse (VFSFile * file, + void (* handle_heading) (const char * heading, void * data), + void (* handle_entry) (const char * key, const char * value, void * data), + void * data); + +bool_t inifile_write_heading (VFSFile * file, const char * heading); +bool_t inifile_write_entry (VFSFile * file, const char * key, const char * value); + +#endif /* LIBAUDCORE_INIFILE_H */ diff --git a/src/libaudcore/multihash.c b/src/libaudcore/multihash.c new file mode 100644 index 0000000..2be8839 --- /dev/null +++ b/src/libaudcore/multihash.c @@ -0,0 +1,153 @@ +/* + * multihash.c + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "multihash.h" + +#include <glib.h> + +#define INITIAL_SIZE 256 /* must be a power of two */ + +static void resize_channel (MultihashTable * table, MultihashChannel * channel, unsigned size) +{ + MultihashNode * * buckets = g_new0 (MultihashNode *, size); + + for (int b1 = 0; b1 < channel->size; b1 ++) + { + MultihashNode * node = channel->buckets[b1]; + + while (node) + { + MultihashNode * next = node->next; + + unsigned hash = table->hash_func (node); + unsigned b2 = (hash >> MULTIHASH_SHIFT) & (size - 1); + MultihashNode * * node_ptr = & buckets[b2]; + + node->next = * node_ptr; + * node_ptr = node; + + node = next; + } + } + + g_free (channel->buckets); + channel->buckets = buckets; + channel->size = size; +} + +EXPORT int multihash_lookup (MultihashTable * table, const void * data, + unsigned hash, MultihashAddFunc add, MultihashActionFunc action, void * state) +{ + unsigned c = hash & (MULTIHASH_CHANNELS - 1); + MultihashChannel * channel = & table->channels[c]; + + int status = 0; + tiny_lock (& channel->lock); + + if (! channel->buckets) + { + if (! add) + goto DONE; + + channel->buckets = g_new0 (MultihashNode *, INITIAL_SIZE); + channel->size = INITIAL_SIZE; + channel->used = 0; + } + + unsigned b = (hash >> MULTIHASH_SHIFT) & (channel->size - 1); + MultihashNode * * node_ptr = & channel->buckets[b]; + MultihashNode * node = * node_ptr; + + while (node && ! table->match_func (node, data, hash)) + { + node_ptr = & node->next; + node = * node_ptr; + } + + if (node) + { + status |= MULTIHASH_FOUND; + + MultihashNode * next = node->next; + + if (action && action (node, state)) + { + status |= MULTIHASH_REMOVED; + + * node_ptr = next; + + channel->used --; + if (channel->used < channel->size >> 2 && channel->size > INITIAL_SIZE) + resize_channel (table, channel, channel->size >> 1); + } + } + else if (add && (node = add (data, hash, state))) + { + status |= MULTIHASH_ADDED; + + * node_ptr = node; + node->next = NULL; + + channel->used ++; + if (channel->used > channel->size) + resize_channel (table, channel, channel->size << 1); + } + +DONE: + tiny_unlock (& channel->lock); + return status; +} + +EXPORT void multihash_iterate (MultihashTable * table, MultihashActionFunc action, void * state) +{ + for (int c = 0; c < MULTIHASH_CHANNELS; c ++) + tiny_lock (& table->channels[c].lock); + + for (int c = 0; c < MULTIHASH_CHANNELS; c ++) + { + MultihashChannel * channel = & table->channels[c]; + + for (int b = 0; b < channel->size; b ++) + { + MultihashNode * * node_ptr = & channel->buckets[b]; + MultihashNode * node = * node_ptr; + + while (node) + { + MultihashNode * next = node->next; + + if (action (node, state)) + { + * node_ptr = next; + channel->used --; + } + else + node_ptr = & node->next; + + node = next; + } + } + + if (channel->used < channel->size >> 2 && channel->size > INITIAL_SIZE) + resize_channel (table, channel, channel->size >> 1); + } + + for (int c = 0; c < MULTIHASH_CHANNELS; c ++) + tiny_unlock (& table->channels[c].lock); +} diff --git a/src/libaudcore/multihash.h b/src/libaudcore/multihash.h new file mode 100644 index 0000000..1e66b21 --- /dev/null +++ b/src/libaudcore/multihash.h @@ -0,0 +1,95 @@ +/* + * multihash.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef LIBAUDCORE_MULTIHASH_H +#define LIBAUDCORE_MULTIHASH_H + +#include <libaudcore/core.h> +#include <libaudcore/tinylock.h> + +/* Multihash is a generic, thread-safe hash table. It scales well to multiple + * processors by the use of multiple channels, each with a separate lock. The + * hash value of a given node decides what channel it is stored in. Hence, + * different processors will tend to hit different channels, keeping lock + * contention to a minimum. The data structures are public in order to allow + * static initialization (setting all bytes to zero gives the initial state). + * The all-purpose lookup function enables a variety of atomic operations, such + * as allocating and adding a node only if not already present. */ + +#define MULTIHASH_CHANNELS 16 /* must be a power of two */ +#define MULTIHASH_SHIFT 4 /* log (base 2) of MULTIHASH_CHANNELS */ + +#define MULTIHASH_FOUND (1 << 0) +#define MULTIHASH_ADDED (1 << 1) +#define MULTIHASH_REMOVED (1 << 2) + +/* Skeleton structure containing internal member(s) of a multihash node. Actual + * node structures should be defined with MultihashNode as the first member. */ +typedef struct _MultihashNode { + struct _MultihashNode * next; +} MultihashNode; + +/* Single channel of a multihash table. For internal use only. */ +typedef struct { + TinyLock lock; + MultihashNode * * buckets; + unsigned size, used; +} MultihashChannel; + +/* Callback. Calculates (or retrieves) the hash value of <node>. */ +typedef unsigned (* MultihashFunc) (const MultihashNode * node); + +/* Callback. Returns TRUE if <node> matches <data>, otherwise FALSE. */ +typedef bool_t (* MultihashMatchFunc) (const MultihashNode * node, + const void * data, unsigned hash); + +/* Multihash table. <hash_func> and <match_func> should be initialized to + * functions appropriate for the type of data to be stored in the table. */ +typedef struct { + MultihashFunc hash_func; + MultihashMatchFunc match_func; + MultihashChannel channels[MULTIHASH_CHANNELS]; +} MultihashTable; + +/* Callback. May create a new node representing <data> to be added to the + * table. Returns the new node or NULL. */ +typedef MultihashNode * (* MultihashAddFunc) (const void * data, unsigned hash, void * state); + +/* Callback. Performs a user-defined action when a matching node is found. + * Doubles as a node removal function. Returns TRUE if <node> was freed and is + * to be removed, otherwise FALSE. */ +typedef bool_t (* MultihashActionFunc) (MultihashNode * node, void * state); + +/* All-purpose lookup function. The caller passes in the data to be looked up + * along with its hash value. The two callbacks are optional. <add> (if not + * NULL) is called if no matching node is found, and may return a new node to + * add to the table. <action> (if not NULL) is called if a matching node is + * found, and may return TRUE to remove the node from the table. <state> is + * forwarded to either callback. Returns the status of the lookup as a bitmask + * of MULTIHASH_FOUND, MULTIHASH_ADDED, and MULTIHASH_REMOVED. */ +int multihash_lookup (MultihashTable * table, const void * data, unsigned hash, + MultihashAddFunc add, MultihashActionFunc action, void * state); + +/* All-purpose iteration function. All channels of the table are locked + * simultaneously during the iteration to freeze the table in a consistent + * state. <action> is called on each node in order, and may return TRUE to + * remove the node from the table. <state> is forwarded to <action>. */ +void multihash_iterate (MultihashTable * table, MultihashActionFunc action, void * state); + +#endif /* LIBAUDCORE_MULTIHASH_H */ diff --git a/src/libaudcore/strpool.c b/src/libaudcore/strpool.c index 634a84d..71e99b3 100644 --- a/src/libaudcore/strpool.c +++ b/src/libaudcore/strpool.c @@ -1,6 +1,6 @@ /* * strpool.c - * Copyright 2011 John Lindgren + * Copyright 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,136 +17,237 @@ * the use of this software. */ -#include <glib.h> -#include <pthread.h> -#include <stdarg.h> +#include <assert.h> #include <stdio.h> -#include <stdint.h> #include <stdlib.h> #include <string.h> -#include "core.h" +#include <glib.h> -/* Each string in the pool is allocated with five leading bytes: a 32-bit - * reference count and a one-byte signature, the '@' character. */ +#include "audstrings.h" +#include "multihash.h" -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; -static GHashTable * table; +#ifdef VALGRIND_FRIENDLY -static void str_destroy (void * str) -{ - * ((char *) str - 1) = 0; - free ((char *) str - 5); -} +typedef struct { + unsigned hash; + char magic; + char str[]; +} StrNode; + +#define NODE_SIZE_FOR(s) (offsetof (StrNode, str) + strlen (s) + 1) +#define NODE_OF(s) ((StrNode *) ((s) - offsetof (StrNode, str))) EXPORT char * str_get (const char * str) { if (! str) return NULL; - char * copy; - pthread_mutex_lock (& mutex); + StrNode * node = g_malloc (NODE_SIZE_FOR (str)); + node->magic = '@'; + node->hash = g_str_hash (str); - if (! table) - table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, str_destroy); + strcpy (node->str, str); + return node->str; +} - if ((copy = g_hash_table_lookup (table, str))) - { - void * mem = copy - 5; - (* (int32_t *) mem) ++; - } - else - { - void * mem = malloc (6 + strlen (str)); - (* (int32_t *) mem) = 1; +EXPORT char * str_ref (const char * str) +{ + StrNode * node = NODE_OF (str); + assert (node->magic == '@'); + assert (g_str_hash (str) == node->hash); - copy = (char *) mem + 5; - copy[-1] = '@'; - strcpy (copy, str); + return str_get (str); +} - g_hash_table_insert (table, copy, copy); - } +EXPORT void str_unref (char * str) +{ + if (! str) + return; + + StrNode * node = NODE_OF (str); + assert (node->magic == '@'); + assert (g_str_hash (str) == node->hash); - pthread_mutex_unlock (& mutex); - return copy; + node->magic = 0; + g_free (node); } -EXPORT char * str_ref (char * str) +EXPORT unsigned str_hash (const char * str) { if (! str) - return NULL; + return 0; - pthread_mutex_lock (& mutex); - STR_CHECK (str); + StrNode * node = NODE_OF (str); + assert (node->magic == '@'); + + return g_str_hash (str); +} - void * mem = str - 5; - (* (int32_t *) mem) ++; +EXPORT bool_t str_equal (const char * str1, const char * str2) +{ + assert (! str1 || NODE_OF (str1)->magic == '@'); + assert (! str2 || NODE_OF (str2)->magic == '@'); - pthread_mutex_unlock (& mutex); - return str; + return ! g_strcmp0 (str1, str2); } -EXPORT void str_unref (char * str) +EXPORT void strpool_shutdown (void) { - if (! str) - return; +} + +#else /* ! VALGRIND_FRIENDLY */ - pthread_mutex_lock (& mutex); - STR_CHECK (str); +typedef struct { + MultihashNode node; + unsigned hash, refs; + char magic; + char str[]; +} StrNode; - void * mem = str - 5; - if (! -- (* (int32_t *) mem)) - g_hash_table_remove (table, str); +#define NODE_SIZE_FOR(s) (offsetof (StrNode, str) + strlen (s) + 1) +#define NODE_OF(s) ((StrNode *) ((s) - offsetof (StrNode, str))) - pthread_mutex_unlock (& mutex); +static unsigned hash_cb (const MultihashNode * node) +{ + return ((const StrNode *) node)->hash; } -EXPORT char * str_nget (const char * str, int len) +static bool_t match_cb (const MultihashNode * node_, const void * data, unsigned hash) { - if (memchr (str, 0, len)) - return str_get (str); + const StrNode * node = (const StrNode *) node_; + return data == node->str || (hash == node->hash && ! strcmp (data, node->str)); +} - char buf[len + 1]; - memcpy (buf, str, len); - buf[len] = 0; +static MultihashTable strpool_table = { + .hash_func = hash_cb, + .match_func = match_cb +}; - return str_get (buf); +static MultihashNode * add_cb (const void * data, unsigned hash, void * state) +{ + StrNode * node = g_malloc (NODE_SIZE_FOR (data)); + node->hash = hash; + node->refs = 1; + node->magic = '@'; + strcpy (node->str, data); + + * ((char * *) state) = node->str; + return (MultihashNode *) node; } -EXPORT char * str_printf (const char * format, ...) +static bool_t ref_cb (MultihashNode * node_, void * state) { - va_list args; + StrNode * node = (StrNode *) node_; - va_start (args, format); - int len = vsnprintf (NULL, 0, format, args); - va_end (args); + __sync_fetch_and_add (& node->refs, 1); - char buf[len + 1]; + * ((char * *) state) = node->str; + return FALSE; +} - va_start (args, format); - vsnprintf (buf, sizeof buf, format, args); - va_end (args); +EXPORT char * str_get (const char * str) +{ + if (! str) + return NULL; - return str_get (buf); + char * ret = NULL; + multihash_lookup (& strpool_table, str, g_str_hash (str), add_cb, ref_cb, & ret); + return ret; } -EXPORT void strpool_abort (char * str) +EXPORT char * str_ref (const char * str) { - fprintf (stderr, "String not in pool: %s\n", str); - abort (); + if (! str) + return NULL; + + StrNode * node = NODE_OF (str); + assert (node->magic == '@'); + + __sync_fetch_and_add (& node->refs, 1); + + return (char *) str; } -static void str_leaked (void * key, void * str, void * unused) +static bool_t remove_cb (MultihashNode * node_, void * state) { - fprintf (stderr, "String not freed: %s\n", (char *) str); + StrNode * node = (StrNode *) node_; + + if (! __sync_bool_compare_and_swap (& node->refs, 1, 0)) + return FALSE; + + node->magic = 0; + g_free (node); + return TRUE; } -EXPORT void strpool_shutdown (void) +EXPORT void str_unref (char * str) { - if (! table) + if (! str) return; - g_hash_table_foreach (table, str_leaked, NULL); - g_hash_table_destroy (table); - table = NULL; + StrNode * node = NODE_OF (str); + assert (node->magic == '@'); + + while (1) + { + int refs = __sync_fetch_and_add (& node->refs, 0); + + if (refs > 1) + { + if (__sync_bool_compare_and_swap (& node->refs, refs, refs - 1)) + break; + } + else + { + int status = multihash_lookup (& strpool_table, node->str, + node->hash, NULL, remove_cb, NULL); + + assert (status & MULTIHASH_FOUND); + if (status & MULTIHASH_REMOVED) + break; + } + } +} + +static bool_t leak_cb (MultihashNode * node, void * state) +{ + fprintf (stderr, "String leaked: %s\n", ((StrNode *) node)->str); + return FALSE; +} + +EXPORT void strpool_shutdown (void) +{ + multihash_iterate (& strpool_table, leak_cb, NULL); +} + +EXPORT unsigned str_hash (const char * str) +{ + if (! str) + return 0; + + StrNode * node = NODE_OF (str); + assert (node->magic == '@'); + + return node->hash; +} + + +EXPORT bool_t str_equal (const char * str1, const char * str2) +{ + assert (! str1 || NODE_OF (str1)->magic == '@'); + assert (! str2 || NODE_OF (str2)->magic == '@'); + + return str1 == str2; +} + +#endif /* ! VALGRIND_FRIENDLY */ + +EXPORT char * str_nget (const char * str, int len) +{ + if (memchr (str, 0, len)) + return str_get (str); + + SNCOPY (buf, str, len); + return str_get (buf); } diff --git a/src/libaudcore/tinylock.c b/src/libaudcore/tinylock.c new file mode 100644 index 0000000..873aff1 --- /dev/null +++ b/src/libaudcore/tinylock.c @@ -0,0 +1,65 @@ +/* + * tinylock.c + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "tinylock.h" + +#ifndef VALGRIND_FRIENDLY + +#include <limits.h> +#include <sched.h> + +#define WRITE_BIT (SHRT_MAX + 1) + +EXPORT void tiny_lock (TinyLock * lock) +{ + while (__builtin_expect (__sync_lock_test_and_set (lock, 1), 0)) + sched_yield (); +} + +EXPORT void tiny_unlock (TinyLock * lock) +{ + __sync_lock_release (lock); +} + +EXPORT void tiny_lock_read (TinyRWLock * lock) +{ + while (__builtin_expect (__sync_fetch_and_add (lock, 1) & WRITE_BIT, 0)) + { + __sync_fetch_and_sub (lock, 1); + sched_yield (); + } +} + +EXPORT void tiny_unlock_read (TinyRWLock * lock) +{ + __sync_fetch_and_sub (lock, 1); +} + +EXPORT void tiny_lock_write (TinyRWLock * lock) +{ + while (! __builtin_expect (__sync_bool_compare_and_swap (lock, 0, WRITE_BIT), 1)) + sched_yield (); +} + +EXPORT void tiny_unlock_write (TinyRWLock * lock) +{ + __sync_fetch_and_sub (lock, WRITE_BIT); +} + +#endif /* ! VALGRIND_FRIENDLY */ diff --git a/src/libaudcore/tinylock.h.in b/src/libaudcore/tinylock.h.in new file mode 100644 index 0000000..873af1b --- /dev/null +++ b/src/libaudcore/tinylock.h.in @@ -0,0 +1,56 @@ +/* + * tinylock.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef LIBAUDCORE_TINYLOCK_H +#define LIBAUDCORE_TINYLOCK_H + +#if @VALGRIND_FRIENDLY@ /* VALGRIND_FRIENDLY */ + +#include <pthread.h> + +typedef pthread_mutex_t TinyLock; +typedef pthread_rwlock_t TinyRWLock; + +#define tiny_lock pthread_mutex_lock +#define tiny_unlock pthread_mutex_unlock +#define tiny_lock_read pthread_rwlock_rdlock +#define tiny_unlock_read pthread_rwlock_unlock +#define tiny_lock_write pthread_rwlock_wrlock +#define tiny_unlock_write pthread_rwlock_unlock + +#else /* ! VALGRIND_FRIENDLY */ + +/* TinyLock is an extremely low-overhead lock object (in terms of speed and + * memory usage). It makes no guarantees of fair scheduling, however. */ + +typedef char TinyLock; + +void tiny_lock (TinyLock * lock); +void tiny_unlock (TinyLock * lock); + +typedef unsigned short TinyRWLock; + +void tiny_lock_read (TinyRWLock * lock); +void tiny_unlock_read (TinyRWLock * lock); +void tiny_lock_write (TinyRWLock * lock); +void tiny_unlock_write (TinyRWLock * lock); + +#endif /* ! VALGRIND_FRIENDLY */ + +#endif /* LIBAUDCORE_TINYLOCK_H */ diff --git a/src/libaudcore/tuple.c b/src/libaudcore/tuple.c index f9a65dc..7974c8b 100644 --- a/src/libaudcore/tuple.c +++ b/src/libaudcore/tuple.c @@ -1,6 +1,6 @@ /* * tuple.c - * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti Hämäläinen, + * Copyright 2007-2013 William Pitcock, Christian Birchinger, Matti Hämäläinen, * Giacomo Lozito, Eugene Zagidullin, and John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -18,13 +18,7 @@ * the use of this software. */ -/** - * @file tuple.c - * @brief Basic Tuple handling API. - */ - #include <glib.h> -#include <pthread.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> @@ -33,8 +27,12 @@ #include <audacious/i18n.h> #include "audstrings.h" +#include "tinylock.h" #include "tuple.h" -#include "tuple_formatter.h" + +#if TUPLE_FIELDS > 64 +#error The current tuple implementation is limited to 64 fields +#endif #define BLOCK_VALS 4 @@ -61,20 +59,25 @@ struct _TupleBlock { * metadata. This is not the same as a playlist entry, though. */ struct _Tuple { - int refcount; int64_t setmask; TupleBlock * blocks; - int nsubtunes; /**< Number of subtunes, if any. Values greater than 0 - mean that there are subtunes and #subtunes array - may be set. */ int *subtunes; /**< Array of int containing subtune index numbers. Can be NULL if indexing is linear or if there are no subtunes. */ + int nsubtunes; /**< Number of subtunes, if any. Values greater than 0 + mean that there are subtunes and #subtunes array + may be set. */ + + int refcount; + TinyLock lock; }; #define BIT(i) ((int64_t) 1 << (i)) +#define LOCK(t) tiny_lock ((TinyLock *) & t->lock) +#define UNLOCK(t) tiny_unlock ((TinyLock *) & t->lock) + /** Ordered table of basic #Tuple field names and their #TupleValueType. */ static const TupleBasicType tuple_fields[TUPLE_FIELDS] = { @@ -154,8 +157,8 @@ static const FieldDictEntry field_dict[TUPLE_FIELDS] = { {"track-number", FIELD_TRACK_NUMBER}, {"year", FIELD_YEAR}}; -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - +#define VALID_FIELD(f) ((f) >= 0 && (f) < TUPLE_FIELDS) +#define FIELD_TYPE(f) (tuple_fields[f].type) static int field_dict_compare (const void * a, const void * b) { @@ -168,26 +171,18 @@ EXPORT int tuple_field_by_name (const char * name) FieldDictEntry * found = bsearch (& find, field_dict, TUPLE_FIELDS, sizeof (FieldDictEntry), field_dict_compare); - if (found) - return found->field; - - fprintf (stderr, "Unknown tuple field name \"%s\".\n", name); - return -1; + return found ? found->field : -1; } EXPORT const char * tuple_field_get_name (int field) { - if (field < 0 || field >= TUPLE_FIELDS) - return NULL; - + g_return_val_if_fail (VALID_FIELD (field), NULL); return tuple_fields[field].name; } EXPORT TupleValueType tuple_field_get_type (int field) { - if (field < 0 || field >= TUPLE_FIELDS) - return TUPLE_UNKNOWN; - + g_return_val_if_fail (VALID_FIELD (field), TUPLE_UNKNOWN); return tuple_fields[field].type; } @@ -240,7 +235,7 @@ static TupleVal * lookup_val (Tuple * tuple, int field, bool_t add, bool_t remov return & block->vals[0]; } -static void tuple_destroy_unlocked (Tuple * tuple) +static void tuple_destroy (Tuple * tuple) { TupleBlock * next; for (TupleBlock * block = tuple->blocks; block; block = next) @@ -254,13 +249,10 @@ static void tuple_destroy_unlocked (Tuple * tuple) str_unref (block->vals[i].str); } - memset (block, 0, sizeof (TupleBlock)); g_slice_free (TupleBlock, block); } - g_free(tuple->subtunes); - - memset (tuple, 0, sizeof (Tuple)); + g_free (tuple->subtunes); g_slice_free (Tuple, tuple); } @@ -273,11 +265,8 @@ EXPORT Tuple * tuple_new (void) EXPORT Tuple * tuple_ref (Tuple * tuple) { - pthread_mutex_lock (& mutex); + __sync_fetch_and_add (& tuple->refcount, 1); - tuple->refcount ++; - - pthread_mutex_unlock (& mutex); return tuple; } @@ -286,22 +275,10 @@ EXPORT void tuple_unref (Tuple * tuple) if (! tuple) return; - pthread_mutex_lock (& mutex); - - if (! -- tuple->refcount) - tuple_destroy_unlocked (tuple); - - pthread_mutex_unlock (& mutex); + if (! __sync_sub_and_fetch (& tuple->refcount, 1)) + tuple_destroy (tuple); } -/** - * Sets filename/URI related fields of a #Tuple structure, based - * on the given filename argument. The fields set are: - * #FIELD_FILE_PATH, #FIELD_FILE_NAME and #FIELD_FILE_EXT. - * - * @param[in] filename Filename URI. - * @param[in,out] tuple Tuple structure to manipulate. - */ EXPORT void tuple_set_filename (Tuple * tuple, const char * filename) { const char * base, * ext, * sub; @@ -311,34 +288,27 @@ EXPORT void tuple_set_filename (Tuple * tuple, const char * filename) char path[base - filename + 1]; str_decode_percent (filename, base - filename, path); - tuple_set_str (tuple, FIELD_FILE_PATH, NULL, path); + tuple_set_str (tuple, FIELD_FILE_PATH, path); char name[ext - base + 1]; str_decode_percent (base, ext - base, name); - tuple_set_str (tuple, FIELD_FILE_NAME, NULL, name); + tuple_set_str (tuple, FIELD_FILE_NAME, name); if (ext < sub) { char extbuf[sub - ext]; str_decode_percent (ext + 1, sub - ext - 1, extbuf); - tuple_set_str (tuple, FIELD_FILE_EXT, NULL, extbuf); + tuple_set_str (tuple, FIELD_FILE_EXT, extbuf); } if (sub[0]) - tuple_set_int (tuple, FIELD_SUBSONG_ID, NULL, isub); + tuple_set_int (tuple, FIELD_SUBSONG_ID, isub); } -/** - * Creates a copy of given Tuple structure, with copied data. - * - * @param[in] src Tuple structure to be made a copy of. - * @return Pointer to newly allocated Tuple. - */ EXPORT Tuple * tuple_copy (const Tuple * old) { - pthread_mutex_lock (& mutex); - Tuple * new = tuple_new (); + LOCK (old); for (int f = 0; f < TUPLE_FIELDS; f ++) { @@ -358,83 +328,56 @@ EXPORT Tuple * tuple_copy (const Tuple * old) if (old->subtunes) new->subtunes = g_memdup (old->subtunes, sizeof (int) * old->nsubtunes); - pthread_mutex_unlock (& mutex); + UNLOCK (old); return new; } -/** - * Allocates a new #Tuple structure, setting filename/URI related - * fields based on the given filename argument by calling #tuple_set_filename. - * - * @param[in] filename Filename URI. - * @return Pointer to newly allocated Tuple. - */ -EXPORT Tuple * -tuple_new_from_filename(const char *filename) +EXPORT Tuple * tuple_new_from_filename (const char * filename) { - Tuple *tuple = tuple_new(); - - tuple_set_filename(tuple, filename); + Tuple * tuple = tuple_new (); + tuple_set_filename (tuple, filename); return tuple; } -EXPORT void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x) +EXPORT void tuple_set_int (Tuple * tuple, int field, int x) { - if (nfield < 0) - nfield = tuple_field_by_name (field); - if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT) - return; + g_return_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_INT); + LOCK (tuple); - pthread_mutex_lock (& mutex); - - TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE); + TupleVal * val = lookup_val (tuple, field, TRUE, FALSE); val->x = x; - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); } -EXPORT void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str) +EXPORT void tuple_set_str (Tuple * tuple, int field, const char * str) { - if (! str) - { - tuple_unset (tuple, nfield, field); - return; - } + g_return_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_STRING); - if (! g_utf8_validate (str, -1, NULL)) + if (! str) { - fprintf (stderr, "Invalid UTF-8: %s\n", str); + tuple_unset (tuple, field); return; } - if (nfield < 0) - nfield = tuple_field_by_name (field); - if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING) - return; - - pthread_mutex_lock (& mutex); + LOCK (tuple); - TupleVal * val = lookup_val (tuple, nfield, TRUE, FALSE); - if (val->str) - str_unref (val->str); - val->str = str_get (str); + TupleVal * val = lookup_val (tuple, field, TRUE, FALSE); + str_unref (val->str); + val->str = str_to_utf8 (str, -1); - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); } -EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field) +EXPORT void tuple_unset (Tuple * tuple, int field) { - if (nfield < 0) - nfield = tuple_field_by_name (field); - if (nfield < 0 || nfield >= TUPLE_FIELDS) - return; + g_return_if_fail (VALID_FIELD (field)); + LOCK (tuple); - pthread_mutex_lock (& mutex); - - TupleVal * val = lookup_val (tuple, nfield, FALSE, TRUE); + TupleVal * val = lookup_val (tuple, field, FALSE, TRUE); if (val) { - if (tuple_fields[nfield].type == TUPLE_STRING) + if (tuple_fields[field].type == TUPLE_STRING) { str_unref (val->str); val->str = NULL; @@ -443,96 +386,52 @@ EXPORT void tuple_unset (Tuple * tuple, int nfield, const char * field) val->x = 0; } - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); } -/** - * Returns #TupleValueType of given #Tuple field. - * Desired field can be specified either by key name or if it is - * one of basic fields, by #TupleBasicType index. - * - * @param[in] tuple #Tuple structure pointer. - * @param[in] cnfield #TupleBasicType index or -1 if key name is to be used instead. - * @param[in] field String acting as key name or NULL if nfield is used. - * @return #TupleValueType of the field or TUPLE_UNKNOWN if there was an error. - */ -EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, const char * field) +EXPORT TupleValueType tuple_get_value_type (const Tuple * tuple, int field) { - if (nfield < 0) - nfield = tuple_field_by_name (field); - if (nfield < 0 || nfield >= TUPLE_FIELDS) - return TUPLE_UNKNOWN; - - pthread_mutex_lock (& mutex); + g_return_val_if_fail (VALID_FIELD (field), TUPLE_UNKNOWN); + LOCK (tuple); - TupleValueType type = TUPLE_UNKNOWN; + TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE); + TupleValueType type = val ? FIELD_TYPE (field) : TUPLE_UNKNOWN; - TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE); - if (val) - type = tuple_fields[nfield].type; - - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); return type; } -EXPORT char * tuple_get_str (const Tuple * tuple, int nfield, const char * field) +EXPORT char * tuple_get_str (const Tuple * tuple, int field) { - if (nfield < 0) - nfield = tuple_field_by_name (field); - if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_STRING) - return NULL; - - pthread_mutex_lock (& mutex); + g_return_val_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_STRING, NULL); + LOCK (tuple); - char * str = NULL; + TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE); + char * str = val ? str_ref (val->str) : NULL; - TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE); - if (val) - str = str_ref (val->str); - - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); return str; } -/** - * Returns integer associated to #Tuple field. - * Desired field can be specified either by key name or if it is - * one of basic fields, by #TupleBasicType index. - * - * @param[in] tuple #Tuple structure pointer. - * @param[in] cnfield #TupleBasicType index or -1 if key name is to be used instead. - * @param[in] field String acting as key name or NULL if nfield is used. - * @return Integer value or 0 if the field/key did not exist. - * - * @bug There is no way to distinguish error situations if the associated value is zero. - */ -EXPORT int tuple_get_int (const Tuple * tuple, int nfield, const char * field) +EXPORT int tuple_get_int (const Tuple * tuple, int field) { - if (nfield < 0) - nfield = tuple_field_by_name (field); - if (nfield < 0 || nfield >= TUPLE_FIELDS || tuple_fields[nfield].type != TUPLE_INT) - return 0; - - pthread_mutex_lock (& mutex); + g_return_val_if_fail (VALID_FIELD (field) && FIELD_TYPE (field) == TUPLE_INT, -1); + LOCK (tuple); - int x = 0; - - TupleVal * val = lookup_val ((Tuple *) tuple, nfield, FALSE, FALSE); - if (val) - x = val->x; + TupleVal * val = lookup_val ((Tuple *) tuple, field, FALSE, FALSE); + int x = val ? val->x : -1; - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); return x; } -#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), \ - __VA_ARGS__) +#define APPEND(b, ...) snprintf (b + strlen (b), sizeof b - strlen (b), __VA_ARGS__) EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rate, int brate) { if (format) - tuple_set_str (t, FIELD_CODEC, NULL, format); + tuple_set_str (t, FIELD_CODEC, format); char buf[32]; buf[0] = 0; @@ -544,8 +443,7 @@ EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rat else if (chans == 2) APPEND (buf, _("Stereo")); else - APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels", - chans), chans); + APPEND (buf, dngettext (PACKAGE, "%d channel", "%d channels", chans), chans); if (rate > 0) APPEND (buf, ", "); @@ -555,15 +453,15 @@ EXPORT void tuple_set_format (Tuple * t, const char * format, int chans, int rat APPEND (buf, "%d kHz", rate / 1000); if (buf[0]) - tuple_set_str (t, FIELD_QUALITY, NULL, buf); + tuple_set_str (t, FIELD_QUALITY, buf); if (brate > 0) - tuple_set_int (t, FIELD_BITRATE, NULL, brate); + tuple_set_int (t, FIELD_BITRATE, brate); } EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtunes) { - pthread_mutex_lock (& mutex); + LOCK (tuple); g_free (tuple->subtunes); tuple->subtunes = NULL; @@ -572,45 +470,27 @@ EXPORT void tuple_set_subtunes (Tuple * tuple, int n_subtunes, const int * subtu if (subtunes) tuple->subtunes = g_memdup (subtunes, sizeof (int) * n_subtunes); - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); } EXPORT int tuple_get_n_subtunes (Tuple * tuple) { - pthread_mutex_lock (& mutex); + LOCK (tuple); int n_subtunes = tuple->nsubtunes; - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); return n_subtunes; } EXPORT int tuple_get_nth_subtune (Tuple * tuple, int n) { - pthread_mutex_lock (& mutex); + LOCK (tuple); int subtune = -1; if (n >= 0 && n < tuple->nsubtunes) subtune = tuple->subtunes ? tuple->subtunes[n] : 1 + n; - pthread_mutex_unlock (& mutex); + UNLOCK (tuple); return subtune; } - -EXPORT char * tuple_format_title (Tuple * tuple, const char * format) -{ - static const gint fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME, FIELD_FILE_PATH}; - - char * title = tuple_formatter_process_string (tuple, format); - - for (int i = 0; i < G_N_ELEMENTS (fallbacks); i ++) - { - if (title && title[0]) - break; - - str_unref (title); - title = tuple_get_str (tuple, fallbacks[i], NULL); - } - - return title ? title : str_get (""); -} diff --git a/src/libaudcore/tuple.h b/src/libaudcore/tuple.h index 55f3e2e..873f699 100644 --- a/src/libaudcore/tuple.h +++ b/src/libaudcore/tuple.h @@ -1,6 +1,6 @@ /* * tuple.h - * Copyright 2007-2011 William Pitcock, Christian Birchinger, Matti Hämäläinen, + * Copyright 2007-2013 William Pitcock, Christian Birchinger, Matti Hämäläinen, * Giacomo Lozito, Eugene Zagidullin, and John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -90,6 +90,7 @@ const char * tuple_field_get_name (int field); TupleValueType tuple_field_get_type (int field); typedef struct _Tuple Tuple; +typedef struct _TupleFormatter TupleFormatter; /* Creates a new, blank tuple with a reference count of one. */ Tuple * tuple_new (void); @@ -105,45 +106,41 @@ void tuple_unref (Tuple * tuple); /* Makes a copy of <tuple>. Only use tuple_copy() if you need to modify one * copy of the tuple while not modifying the other. In most cases, tuple_ref() * is more appropriate. */ -Tuple *tuple_copy(const Tuple *); +Tuple * tuple_copy (const Tuple * tuple); /* Parses the URI <filename> and sets FIELD_FILE_NAME, FIELD_FILE_PATH, * FIELD_FILE_EXT, and FIELD_SUBSONG_ID accordingly. */ -void tuple_set_filename(Tuple *tuple, const char *filename); +void tuple_set_filename (Tuple * tuple, const char * filename); /* Convenience function, equivalent to calling tuple_new() and then * tuple_set_filename(). */ -Tuple *tuple_new_from_filename(const char *filename); +Tuple * tuple_new_from_filename (const char * filename); /* Sets a field to the integer value <x>. */ -void tuple_set_int (Tuple * tuple, int nfield, const char * field, int x); +void tuple_set_int (Tuple * tuple, int field, int x); -/* Sets the field specified by <nfield> (one of the FIELD_* constants) or - * <field> (one of the names returned by tuple_field_get_name() to the string - * value <str>. Only one of <nfield> or <field> may be set. If <nfield> is - * set, <field> must be NULL; if <field> is set, <nfield> must be -1. As a +/* Sets a field to the string value <str>. If <str> is not valid UTF-8, it will + * be converted according to the user's character set detection rules. As a * special case, if <str> is NULL, the result is equivalent to calling * tuple_unset(). */ -void tuple_set_str (Tuple * tuple, int nfield, const char * field, const char * str); +void tuple_set_str (Tuple * tuple, int field, const char * str); /* Clears any value that a field is currently set to. */ -void tuple_unset (Tuple * tuple, int nfield, const char * field); +void tuple_unset (Tuple * tuple, int field); /* Returns the value type of a field, or TUPLE_UNKNOWN if the field has not been * set to any value. */ -TupleValueType tuple_get_value_type (const Tuple * tuple, int nfield, - const char * field); +TupleValueType tuple_get_value_type (const Tuple * tuple, int field); /* Returns the string value of a field. The returned string is pooled and must * be released with str_unref() when no longer needed. If the field has not * been set to any value, returns NULL. */ -char * tuple_get_str (const Tuple * tuple, int nfield, const char * field); +char * tuple_get_str (const Tuple * tuple, int field); /* Returns the integer value of a field. If the field has not been set to any - * value, returns 0. (In hindsight, it would have been better to return -1 in - * this case. If you need to distinguish between a value of 0 and a field not - * set to any value, use tuple_get_value_type().) */ -int tuple_get_int (const Tuple * tuple, int nfield, const char * field); + * value, returns -1. If you need to distinguish between a value of -1 and a + * field not set to any value, use tuple_get_value_type(). */ +int tuple_get_int (const Tuple * tuple, int field); /* Fills in format-related fields (specifically FIELD_CODEC, FIELD_QUALITY, and * FIELD_BITRATE). Plugins should use this function instead of setting these @@ -168,10 +165,16 @@ int tuple_get_n_subtunes (Tuple * tuple); /* Returns the <n>th member of the subtune array. */ int tuple_get_nth_subtune (Tuple * tuple, int n); -/* Generates a formatted title string for <tuple> according to <format>. The - * syntax of <format> is documented in tuple_formatter.c. The returned string - * is pooled and must be released with str_unref() when no longer need. The - * returned string is never NULL, though it may be the empty string. */ -char * tuple_format_title (Tuple * tuple, const char * format); +/* Creates a tuple formatter object for the given format. The syntax of + * <format> is documented in tuple_formatter.c. */ +TupleFormatter * tuple_formatter_new (const char * format); + +/* Destroys a tuple formatter object. */ +void tuple_formatter_free (TupleFormatter * formatter); + +/* Generates a title string for <tuple> using the given formatter object. The + * returned string is pooled and must be released with str_unref() when no + * longer needed. Never returns NULL, but may return an empty string. */ +char * tuple_format_title (TupleFormatter * formatter, const Tuple * tuple); #endif /* LIBAUDCORE_TUPLE_H */ diff --git a/src/libaudcore/tuple_compiler.c b/src/libaudcore/tuple_compiler.c index 4834e17..74584c1 100644 --- a/src/libaudcore/tuple_compiler.c +++ b/src/libaudcore/tuple_compiler.c @@ -1,7 +1,7 @@ /* * tuple_compiler.c * Copyright (c) 2007 Matti 'ccr' Hämäläinen - * Copyright (c) 2011 John Lindgren + * Copyright (c) 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -31,7 +31,6 @@ * currently there is just a single context, is a "global" context needed? */ -#include <ctype.h> #include <stdarg.h> #include <stdlib.h> #include <stdio.h> @@ -39,18 +38,19 @@ #include <glib.h> +#include "audstrings.h" #include "tuple_compiler.h" -#define MAX_STR (256) -#define MIN_ALLOC_NODES (8) -#define MIN_ALLOC_BUF (64) +#define MAX_STR (256) #define TUPLEZ_MAX_VARS (4) +#define GET_VAR(c, i) (& g_array_index ((c), TupleEvalVar, (i))) + #define tuple_error(ctx, ...) fprintf (stderr, "Tuple compiler: " __VA_ARGS__) enum { - OP_RAW = 0, /* plain text */ - OP_FIELD, /* a field/variable */ + OP_RAW = 0, /* plain text */ + OP_FIELD, /* a field/variable */ OP_EXISTS, OP_EQUALS, OP_NOT_EQUALS, @@ -67,42 +67,28 @@ enum { }; struct _TupleEvalNode { - int opcode; /* operator, see OP_ enums */ - int var[TUPLEZ_MAX_VARS]; /* tuple variable references */ - char *text; /* raw text, if any (OP_RAW) */ + int opcode; /* operator, see OP_ enums */ + int var[TUPLEZ_MAX_VARS]; /* tuple variable references */ + char *text; /* raw text, if any (OP_RAW) */ struct _TupleEvalNode *children, *next, *prev; /* children of this struct, and pointer to next node. */ }; typedef struct { char *name; - int type; /* Type of variable, see VAR_* */ + int type; /* Type of variable, see VAR_* */ int defvali; - TupleValueType ctype; /* Type of constant/def value */ + TupleValueType ctype; /* Type of constant/def value */ - int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */ + int fieldidx; /* if >= 0: Index # of "pre-defined" Tuple fields */ bool_t fieldread, fieldvalid; char * fieldstr; } TupleEvalVar; -struct _TupleEvalContext { - int nvariables; - TupleEvalVar **variables; -}; - - -static void tuple_evalctx_free_var(TupleEvalVar *var) -{ - g_free(var->name); - str_unref (var->fieldstr); - g_free(var); -} - - /* Initialize an evaluation context */ TupleEvalContext * tuple_evalctx_new(void) { - return g_new0(TupleEvalContext, 1); + return g_array_new (FALSE, TRUE, sizeof (TupleEvalVar)); } @@ -110,15 +96,15 @@ TupleEvalContext * tuple_evalctx_new(void) */ void tuple_evalctx_reset(TupleEvalContext *ctx) { - int i; - - for (i = 0; i < ctx->nvariables; i++) - if (ctx->variables[i]) { - ctx->variables[i]->fieldread = FALSE; - ctx->variables[i]->fieldvalid = FALSE; - str_unref (ctx->variables[i]->fieldstr); - ctx->variables[i]->fieldstr = NULL; - } + for (int i = 0; i < ctx->len; i ++) + { + TupleEvalVar * var = GET_VAR (ctx, i); + + var->fieldread = FALSE; + var->fieldvalid = FALSE; + str_unref (var->fieldstr); + var->fieldstr = NULL; + } } @@ -126,57 +112,52 @@ void tuple_evalctx_reset(TupleEvalContext *ctx) */ void tuple_evalctx_free(TupleEvalContext *ctx) { - int i; - - if (!ctx) return; + for (int i = 0; i < ctx->len; i ++) + { + TupleEvalVar * var = GET_VAR (ctx, i); - /* Deallocate variables */ - for (i = 0; i < ctx->nvariables; i++) - if (ctx->variables[i]) - tuple_evalctx_free_var(ctx->variables[i]); + str_unref (var->name); + str_unref (var->fieldstr); + } - g_free(ctx->variables); - g_free(ctx); + g_array_free (ctx, TRUE); } +/* note: may invalidate TupleEvalVar pointers due to reallocation */ static int tuple_evalctx_add_var (TupleEvalContext * ctx, const char * name, const int type, const TupleValueType ctype) { - int i; - TupleEvalVar *tmp = g_new0(TupleEvalVar, 1); + int field = -1; + + if (type == TUPLE_VAR_FIELD) + { + field = tuple_field_by_name (name); + if (field < 0) + return -1; + } + + int i = ctx->len; + g_array_set_size (ctx, i + 1); - tmp->name = g_strdup(name); - tmp->type = type; - tmp->fieldidx = -1; - tmp->ctype = ctype; + TupleEvalVar * var = GET_VAR (ctx, i); + + var->name = str_get (name); + var->type = type; + var->fieldidx = field; + var->ctype = ctype; - /* Find fieldidx, if any */ switch (type) { case TUPLE_VAR_FIELD: - tmp->fieldidx = tuple_field_by_name (name); - tmp->ctype = tuple_field_get_type (tmp->fieldidx); + var->ctype = tuple_field_get_type (field); break; case TUPLE_VAR_CONST: if (ctype == TUPLE_INT) - tmp->defvali = atoi(name); + var->defvali = atoi (name); break; } - /* Find a free slot */ - for (i = 0; i < ctx->nvariables; i++) - if (!ctx->variables[i]) { - ctx->variables[i] = tmp; - return i; - } - - i = ctx->nvariables; - ctx->variables = g_renew(TupleEvalVar *, ctx->variables, ctx->nvariables + MIN_ALLOC_NODES); - memset(&(ctx->variables[ctx->nvariables]), 0, MIN_ALLOC_NODES * sizeof(TupleEvalVar *)); - ctx->nvariables += MIN_ALLOC_NODES; - ctx->variables[i] = tmp; - return i; } @@ -196,12 +177,6 @@ static void tuple_evalnode_insert(TupleEvalNode **nodes, TupleEvalNode *node) } -static TupleEvalNode *tuple_evalnode_new(void) -{ - return g_new0(TupleEvalNode, 1); -} - - void tuple_evalnode_free(TupleEvalNode *expr) { TupleEvalNode *curr = expr, *next; @@ -209,12 +184,12 @@ void tuple_evalnode_free(TupleEvalNode *expr) while (curr) { next = curr->next; - g_free(curr->text); + str_unref (curr->text); if (curr->children) tuple_evalnode_free(curr->children); - g_free(curr); + g_slice_free (TupleEvalNode, curr); curr = next; } @@ -246,11 +221,11 @@ static bool_t tc_get_item(TupleEvalContext *ctx, } if (*literal == FALSE) { - while (*s != '\0' && *s != tmpendch && (isalnum(*s) || *s == '-') && i < (max - 1)) { + while (*s != '\0' && *s != tmpendch && (g_ascii_isalnum(*s) || *s == '-') && i < (max - 1)) { buf[i++] = *(s++); } - if (*s != tmpendch && *s != '}' && !isalnum(*s) && *s != '-') { + if (*s != tmpendch && *s != '}' && !g_ascii_isalnum(*s) && *s != '-') { tuple_error(ctx, "Invalid field '%s' in '%s'.\n", *str, item); return FALSE; } else if (*s != tmpendch) { @@ -286,20 +261,17 @@ static bool_t tc_get_item(TupleEvalContext *ctx, static int tc_get_variable(TupleEvalContext *ctx, char *name, int type) { - int i; TupleValueType ctype = TUPLE_UNKNOWN; - if (name == '\0') return -1; - - if (isdigit(name[0])) { + if (g_ascii_isdigit(name[0])) { ctype = TUPLE_INT; type = TUPLE_VAR_CONST; } else ctype = TUPLE_STRING; if (type != TUPLE_VAR_CONST) { - for (i = 0; i < ctx->nvariables; i++) - if (ctx->variables[i] && !strcmp(ctx->variables[i]->name, name)) + for (int i = 0; i < ctx->len; i ++) + if (! strcmp (GET_VAR (ctx, i)->name, name)) return i; } @@ -317,7 +289,7 @@ static bool_t tc_parse_construct(TupleEvalContext *ctx, TupleEvalNode **res, if (tc_get_item(ctx, c, tmps1, MAX_STR, ',', &literal1, "tag1", item)) { (*c)++; if (tc_get_item(ctx, c, tmps2, MAX_STR, ':', &literal2, "tag2", item)) { - TupleEvalNode *tmp = tuple_evalnode_new(); + TupleEvalNode *tmp = g_slice_new0 (TupleEvalNode); (*c)++; tmp->opcode = opcode; @@ -374,7 +346,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co literal = FALSE; if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) { c++; - tmp = tuple_evalnode_new(); + tmp = g_slice_new0 (TupleEvalNode); tmp->opcode = OP_EXISTS; if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) { tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr); @@ -395,7 +367,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co if (*c == '"') { /* String */ c++; - } else if (isdigit(*c)) { + } else if (g_ascii_isdigit(*c)) { /* Integer */ } @@ -444,7 +416,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co literal = FALSE; if (tc_get_item(ctx, &c, tmps1, MAX_STR, ':', &literal, "tag", item)) { c++; - tmp = tuple_evalnode_new(); + tmp = g_slice_new0 (TupleEvalNode); tmp->opcode = OP_IS_EMPTY; if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) { tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr); @@ -467,7 +439,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co /* FIXME!! FIX ME! Check for external expressions */ /* I HAS A FIELD - A field. You has it. */ - tmp = tuple_evalnode_new(); + tmp = g_slice_new0 (TupleEvalNode); tmp->opcode = OP_FIELD; if ((tmp->var[0] = tc_get_variable(ctx, tmps1, TUPLE_VAR_FIELD)) < 0) { tuple_error(ctx, "Invalid variable '%s' in '%s'.\n", tmps1, expr); @@ -491,7 +463,7 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co gssize i = 0; c++; - while (*c != '\0' && (isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1)) + while (*c != '\0' && (g_ascii_isalnum(*c) || *c == '-') && *c != '}' && *c != ':' && i < (MAX_STR - 1)) tmps1[i++] = *(c++); tmps1[i] = '\0'; @@ -516,9 +488,9 @@ static TupleEvalNode *tuple_compiler_pass1(int *level, TupleEvalContext *ctx, co } tmps1[i] = '\0'; - tmp = tuple_evalnode_new(); + tmp = g_slice_new0 (TupleEvalNode); tmp->opcode = OP_RAW; - tmp->text = g_strdup(tmps1); + tmp->text = str_get (tmps1); tuple_evalnode_insert(&res, tmp); } } @@ -565,16 +537,16 @@ static bool_t tf_get_fieldval (TupleEvalVar * var, const Tuple * tuple) if (var->fieldread) return var->fieldvalid; - if (tuple_get_value_type (tuple, var->fieldidx, NULL) != var->ctype) { + if (tuple_get_value_type (tuple, var->fieldidx) != var->ctype) { var->fieldread = TRUE; var->fieldvalid = FALSE; return FALSE; } if (var->ctype == TUPLE_INT) - var->defvali = tuple_get_int (tuple, var->fieldidx, NULL); + var->defvali = tuple_get_int (tuple, var->fieldidx); else if (var->ctype == TUPLE_STRING) - var->fieldstr = tuple_get_str (tuple, var->fieldidx, NULL); + var->fieldstr = tuple_get_str (tuple, var->fieldidx); var->fieldread = TRUE; var->fieldvalid = TRUE; @@ -642,7 +614,7 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * break; case OP_FIELD: - var0 = ctx->variables[curr->var[0]]; + var0 = GET_VAR (ctx, curr->var[0]); switch (var0->type) { case TUPLE_VAR_FIELD: @@ -653,7 +625,7 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * break; case TUPLE_INT: - g_snprintf (tmps, sizeof (tmps), "%d", var0->defvali); + str_itoa (var0->defvali, tmps, sizeof (tmps)); str = tmps; break; @@ -669,8 +641,8 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * case OP_NOT_EQUALS: case OP_LT: case OP_LTEQ: case OP_GT: case OP_GTEQ: - var0 = ctx->variables[curr->var[0]]; - var1 = ctx->variables[curr->var[1]]; + var0 = GET_VAR (ctx, curr->var[0]); + var1 = GET_VAR (ctx, curr->var[1]); type0 = tf_get_var(&tmps0, &tmpi0, var0, tuple); type1 = tf_get_var(&tmps1, &tmpi1, var1, tuple); @@ -705,14 +677,15 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * break; case OP_EXISTS: - if (tf_get_fieldval (ctx->variables[curr->var[0]], tuple)) { + if (tf_get_fieldval (GET_VAR (ctx, curr->var[0]), tuple)) + { if (! tuple_formatter_eval_do (ctx, curr->children, tuple, out)) return FALSE; } break; case OP_IS_EMPTY: - var0 = ctx->variables[curr->var[0]]; + var0 = GET_VAR (ctx, curr->var[0]); if (tf_get_fieldval (var0, tuple)) { switch (var0->ctype) { @@ -725,9 +698,8 @@ static bool_t tuple_formatter_eval_do (TupleEvalContext * ctx, TupleEvalNode * tmps2 = var0->fieldstr; while (result && tmps2 && *tmps2 != '\0') { - gunichar uc = g_utf8_get_char(tmps2); - if (g_unichar_isspace(uc)) - tmps2 = g_utf8_next_char(tmps2); + if (g_ascii_isspace (* tmps2)) + tmps2 ++; else result = FALSE; } diff --git a/src/libaudcore/tuple_compiler.h b/src/libaudcore/tuple_compiler.h index 0e2ba54..1a951c1 100644 --- a/src/libaudcore/tuple_compiler.h +++ b/src/libaudcore/tuple_compiler.h @@ -23,12 +23,9 @@ #include <glib.h> #include <libaudcore/tuple.h> -struct _TupleEvalNode; +typedef GArray TupleEvalContext; typedef struct _TupleEvalNode TupleEvalNode; -struct _TupleEvalContext; -typedef struct _TupleEvalContext TupleEvalContext; - TupleEvalContext * tuple_evalctx_new(void); void tuple_evalctx_reset(TupleEvalContext *ctx); void tuple_evalctx_free(TupleEvalContext *ctx); diff --git a/src/libaudcore/tuple_formatter.c b/src/libaudcore/tuple_formatter.c index 04ea555..44c6e44 100644 --- a/src/libaudcore/tuple_formatter.c +++ b/src/libaudcore/tuple_formatter.c @@ -1,6 +1,6 @@ /* * tuple_formatter.c - * Copyright (c) 2007 William Pitcock + * Copyright (c) 2007-2013 William Pitcock and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -18,11 +18,9 @@ */ #include <glib.h> -#include <pthread.h> -#include <string.h> +#include "tuple.h" #include "tuple_compiler.h" -#include "tuple_formatter.h" /* * the tuple formatter: @@ -47,48 +45,52 @@ * - %{function:args,arg2,...}: runs function and inserts the result. * * everything else is treated as raw text. - * additionally, plugins can add additional instructions and functions! */ -/* - * Compile a tuplez string and cache the result. - * This caches the result for the last string, so that - * successive calls are sped up. - */ +struct _TupleFormatter +{ + TupleEvalContext * context; + TupleEvalNode * node; + GString * buf; +}; -char * tuple_formatter_process_string (const Tuple * tuple, const char * string) +EXPORT TupleFormatter * tuple_formatter_new (const char * format) { - static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; - pthread_mutex_lock (& mutex); + TupleFormatter * formatter = g_slice_new (TupleFormatter); - static char *last_string = NULL; - static TupleEvalContext *last_ctx = NULL; - static TupleEvalNode *last_ev = NULL; + formatter->context = tuple_evalctx_new (); + formatter->node = tuple_formatter_compile (formatter->context, format); + formatter->buf = g_string_sized_new (255); - if (! last_string || strcmp (string, last_string)) - { - g_free(last_string); + return formatter; +} + +EXPORT void tuple_formatter_free (TupleFormatter * formatter) +{ + tuple_evalctx_free (formatter->context); + tuple_evalnode_free (formatter->node); + g_string_free (formatter->buf, TRUE); - if (last_ctx != NULL) - { - tuple_evalctx_free(last_ctx); - tuple_evalnode_free(last_ev); - } + g_slice_free (TupleFormatter, formatter); +} - last_ctx = tuple_evalctx_new(); - last_string = g_strdup(string); - last_ev = tuple_formatter_compile(last_ctx, last_string); - } +EXPORT char * tuple_format_title (TupleFormatter * formatter, const Tuple * tuple) +{ + tuple_formatter_eval (formatter->context, formatter->node, tuple, formatter->buf); + tuple_evalctx_reset (formatter->context); - static GString * buf; - if (! buf) - buf = g_string_sized_new (255); + if (formatter->buf->len) + return str_get (formatter->buf->str); - tuple_formatter_eval (last_ctx, last_ev, tuple, buf); - tuple_evalctx_reset (last_ctx); + /* formatting failed, try fallbacks */ + static const int fallbacks[] = {FIELD_TITLE, FIELD_FILE_NAME}; - char * result = str_get (buf->str); + for (int i = 0; i < ARRAY_LEN (fallbacks); i ++) + { + char * title = tuple_get_str (tuple, fallbacks[i]); + if (title) + return title; + } - pthread_mutex_unlock (& mutex); - return result; + return str_get (""); } diff --git a/src/libaudcore/tuple_formatter.h b/src/libaudcore/tuple_formatter.h deleted file mode 100644 index bb3052b..0000000 --- a/src/libaudcore/tuple_formatter.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * tuple_formatter.h - * Copyright (c) 2007 William Pitcock - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef LIBAUDCORE_TUPLE_FORMATTER_H -#define LIBAUDCORE_TUPLE_FORMATTER_H - -#include <libaudcore/tuple.h> - -/* returned string be released with str_unref() */ -char * tuple_formatter_process_string (const Tuple * tuple, const char * string); - -#endif /* LIBAUDCORE_TUPLE_FORMATTER_H */ diff --git a/src/libaudcore/vfs.c b/src/libaudcore/vfs.c index b1c3b03..000205f 100644 --- a/src/libaudcore/vfs.c +++ b/src/libaudcore/vfs.c @@ -1,6 +1,6 @@ /* * vfs.c - * Copyright 2006-2011 William Pitcock, Daniel Barkalow, Ralf Ertzinger, + * Copyright 2006-2013 William Pitcock, Daniel Barkalow, Ralf Ertzinger, * Yoshiki Yazawa, Matti Hämäläinen, and John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -18,16 +18,20 @@ * the use of this software. */ -#include <glib.h> -#include <inttypes.h> - #include "vfs.h" -#include "audstrings.h" + +#include <inttypes.h> #include <stdio.h> -#include <unistd.h> +#include <string.h> #include <sys/stat.h> #include <sys/types.h> -#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "audstrings.h" +#include "vfs_local.h" #define VFS_SIG ('V' | ('F' << 8) | ('S' << 16)) @@ -123,24 +127,31 @@ vfs_fopen(const char * path, const char * mode) { g_return_val_if_fail (path && mode, NULL); - g_return_val_if_fail (lookup_func, NULL); - const char * s = strstr (path, "://"); - g_return_val_if_fail (s, NULL); - char scheme[s - path + 1]; - strncpy (scheme, path, s - path); - scheme[s - path] = 0; + VFSConstructor * vtable = NULL; - VFSConstructor * vtable = lookup_func (scheme); - if (! vtable) - return NULL; + if (! strncmp (path, "file://", 7)) + vtable = & vfs_local_vtable; + else + { + const char * s = strstr (path, "://"); + + if (! s) + { + fprintf (stderr, "Invalid URI: %s\n", path); + return NULL; + } + + SNCOPY (scheme, path, s - path); + + if (! (vtable = lookup_func (scheme))) + return NULL; + } const gchar * sub; uri_parse (path, NULL, NULL, & sub, NULL); - gchar buf[sub - path + 1]; - memcpy (buf, path, sub - path); - buf[sub - path] = 0; + SNCOPY (buf, path, sub - path); void * handle = vtable->vfs_fopen_impl (buf, mode); if (! handle) @@ -174,8 +185,6 @@ vfs_fclose(VFSFile * file) ret = -1; str_unref (file->uri); - - memset (file, 0, sizeof (VFSFile)); g_slice_free (VFSFile, file); return ret; @@ -234,12 +243,12 @@ EXPORT int64_t vfs_fwrite (const void * ptr, int64_t size, int64_t nmemb, VFSFil EXPORT int vfs_getc(VFSFile *file) { - g_return_val_if_fail (file && file->sig == VFS_SIG, EOF); + unsigned char c; - if (verbose) - logger ("VFS: <%p> getc\n", file); + if (vfs_fread (& c, 1, 1, file) != 1) + return EOF; - return file->base->vfs_getc_impl(file); + return c; } /** @@ -252,12 +261,10 @@ vfs_getc(VFSFile *file) EXPORT int vfs_ungetc(int c, VFSFile *file) { - g_return_val_if_fail (file && file->sig == VFS_SIG, EOF); - - if (verbose) - logger ("VFS: <%p> ungetc\n", file); + if (vfs_fseek (file, -1, SEEK_CUR) < 0) + return EOF; - return file->base->vfs_ungetc_impl(c, file); + return c; } /** @@ -285,23 +292,13 @@ vfs_fseek(VFSFile * file, SEEK_CUR ? "current" : whence == SEEK_SET ? "beginning" : whence == SEEK_END ? "end" : "invalid"); - return file->base->vfs_fseek_impl(file, offset, whence); -} - -/** - * Rewinds a VFS stream. - * - * @param file #VFSFile object that represents the VFS stream. - */ -EXPORT void -vfs_rewind(VFSFile * file) -{ - g_return_if_fail (file && file->sig == VFS_SIG); + if (! file->base->vfs_fseek_impl (file, offset, whence)) + return 0; if (verbose) - logger ("VFS: <%p> rewind\n", file); + logger ("VFS: <%p> seek failed!\n", file); - file->base->vfs_rewind_impl(file); + return -1; } /** @@ -415,8 +412,8 @@ vfs_file_test(const char * path, int test) #ifdef S_ISLNK if (test & VFS_IS_SYMLINK) { - struct stat st; - if (lstat (path2, & st) < 0) + GStatBuf st; + if (g_lstat (path2, & st) < 0) goto DONE; if (S_ISLNK (st.st_mode)) @@ -426,8 +423,8 @@ vfs_file_test(const char * path, int test) if (test & (VFS_IS_REGULAR | VFS_IS_DIR | VFS_IS_EXECUTABLE | VFS_EXISTS)) { - struct stat st; - if (stat (path2, & st) < 0) + GStatBuf st; + if (g_stat (path2, & st) < 0) goto DONE; if (S_ISREG (st.st_mode)) @@ -441,8 +438,7 @@ vfs_file_test(const char * path, int test) } DONE: - g_free (path2); - + str_unref (path2); return ! test; } @@ -455,14 +451,13 @@ DONE: EXPORT bool_t vfs_is_writeable(const char * path) { - struct stat info; + GStatBuf info; char * realfn = uri_to_filename (path); - if (stat(realfn, &info) == -1) + if (! realfn || g_stat (realfn, & info) < 0) return FALSE; - g_free(realfn); - + str_unref (realfn); return (info.st_mode & S_IWUSR); } diff --git a/src/libaudcore/vfs.h b/src/libaudcore/vfs.h index 006c3b7..8dcbc32 100644 --- a/src/libaudcore/vfs.h +++ b/src/libaudcore/vfs.h @@ -1,6 +1,6 @@ /* * vfs.h - * Copyright 2006-2011 William Pitcock, Daniel Barkalow, Ralf Ertzinger, + * Copyright 2006-2013 William Pitcock, Daniel Barkalow, Ralf Ertzinger, * Yoshiki Yazawa, Matti Hämäläinen, and John Lindgren * * Redistribution and use in source and binary forms, with or without @@ -62,15 +62,14 @@ struct _VFSConstructor { int64_t (* vfs_fwrite_impl) (const void * ptr, int64_t size, int64_t nmemb, VFSFile * file); - /** A function pointer which points to a getc implementation. */ - int (* vfs_getc_impl) (VFSFile * stream); - /** A function pointer which points to an ungetc implementation. */ - int (* vfs_ungetc_impl) (int c, VFSFile * stream); + void (* obs_getc) (void); // obsolete + void (* obs_ungetc) (void); // obsolete /** A function pointer which points to a fseek implementation. */ int (* vfs_fseek_impl) (VFSFile * file, int64_t offset, int whence); - /** function pointer which points to a rewind implementation. */ - void (* vfs_rewind_impl) (VFSFile * file); + + void (* obs_rewind) (void); // obsolete + /** A function pointer which points to a ftell implementation. */ int64_t (* vfs_ftell_impl) (VFSFile * file); /** A function pointer which points to a feof implementation. */ @@ -111,34 +110,21 @@ int vfs_fprintf (VFSFile * stream, char const * format, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int vfs_fseek (VFSFile * file, int64_t offset, int whence) WARN_RETURN; -void vfs_rewind (VFSFile * file); int64_t vfs_ftell (VFSFile * file) WARN_RETURN; int64_t vfs_fsize (VFSFile * file) WARN_RETURN; int vfs_ftruncate (VFSFile * file, int64_t length) WARN_RETURN; -bool_t vfs_fget_le16 (uint16_t * value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fget_le32 (uint32_t * value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fget_le64 (uint64_t * value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fget_be16 (uint16_t * value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fget_be32 (uint32_t * value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fget_be64 (uint64_t * value, VFSFile * stream) WARN_RETURN; - -bool_t vfs_fput_le16 (uint16_t value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fput_le32 (uint32_t value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fput_le64 (uint64_t value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fput_be16 (uint16_t value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fput_be32 (uint32_t value, VFSFile * stream) WARN_RETURN; -bool_t vfs_fput_be64 (uint64_t value, VFSFile * stream) WARN_RETURN; - bool_t vfs_is_streaming (VFSFile * file) WARN_RETURN; + +/* free returned string with str_unref() */ char * vfs_get_metadata (VFSFile * file, const char * field) WARN_RETURN; bool_t vfs_file_test (const char * path, int test) WARN_RETURN; bool_t vfs_is_writeable (const char * path) WARN_RETURN; bool_t vfs_is_remote (const char * path) WARN_RETURN; -void vfs_file_get_contents (const char * filename, void * * buf, int64_t * - size); +void vfs_file_read_all (VFSFile * file, void * * buf, int64_t * size); +void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size); void vfs_set_lookup_func (VFSConstructor * (* func) (const char * scheme)); void vfs_set_verbose (bool_t verbose); diff --git a/src/libaudcore/vfs_async.c b/src/libaudcore/vfs_async.c index 88a47ea..d8864c6 100644 --- a/src/libaudcore/vfs_async.c +++ b/src/libaudcore/vfs_async.c @@ -1,6 +1,6 @@ /* * vfs_async.c - * Copyright 2010 William Pitcock + * Copyright 2010-2012 William Pitcock and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/libaudcore/vfs_common.c b/src/libaudcore/vfs_common.c index 3e64b5c..18b05c7 100644 --- a/src/libaudcore/vfs_common.c +++ b/src/libaudcore/vfs_common.c @@ -1,7 +1,7 @@ /* * vfs_common.c - * Copyright 2006-2010 Tony Vroon, William Pitcock, Maciej Grela, - * Matti Hämäläinen, and John Lindgren + * Copyright 2006-2013 Tony Vroon, William Pitcock, Matti Hämäläinen, and + * John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -18,12 +18,12 @@ * the use of this software. */ -#include <glib.h> -#include <glib/gprintf.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> +#include <glib.h> + +#include "audstrings.h" #include "vfs.h" /** @@ -94,7 +94,7 @@ EXPORT char *vfs_fgets(char *s, int n, VFSFile *stream) */ EXPORT int vfs_fputs(const char *s, VFSFile *stream) { - gsize n = strlen(s); + int64_t n = strlen(s); return ((vfs_fwrite(s, 1, n, stream) == n) ? n : EOF); } @@ -109,12 +109,8 @@ EXPORT int vfs_fputs(const char *s, VFSFile *stream) */ EXPORT int vfs_vfprintf(VFSFile *stream, char const *format, va_list args) { - char *string; - int rv = g_vasprintf(&string, format, args); - if (rv < 0) return rv; - rv = vfs_fputs(string, stream); - g_free(string); - return rv; + VSPRINTF (buf, format, args); + return vfs_fputs (buf, stream); } /** @@ -137,242 +133,66 @@ EXPORT int vfs_fprintf(VFSFile *stream, char const *format, ...) return rv; } -/** - * Gets contents of the file into a buffer. Buffer of filesize bytes - * is allocated by this function as necessary. - * - * @param filename URI of the file to read in. - * @param buf Pointer to a pointer variable of buffer. - * @param size Pointer to gsize variable that will hold the amount of - * read data e.g. filesize. - */ -EXPORT void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size) +EXPORT void vfs_file_read_all (VFSFile * file, void * * bufp, int64_t * sizep) { - * buf = NULL; - * size = 0; - - VFSFile *fd; - gsize filled_size = 0, buf_size = 4096; - unsigned char * ptr; - - if ((fd = vfs_fopen(filename, "rb")) == NULL) - return; + char * buf = NULL; + int64_t size = vfs_fsize (file); - if ((* size = vfs_fsize (fd)) >= 0) + if (size >= 0) { - * buf = g_malloc (* size); - * size = vfs_fread (* buf, 1, * size, fd); - goto close_handle; + size = MIN (size, SIZE_MAX - 1); + buf = g_malloc (size + 1); + size = vfs_fread (buf, 1, size, file); } + else + { + size = 0; - if ((*buf = g_malloc(buf_size)) == NULL) - goto close_handle; - - ptr = *buf; - while (TRUE) { - gsize read_size = vfs_fread(ptr, 1, buf_size - filled_size, fd); - if (read_size == 0) break; - - filled_size += read_size; - ptr += read_size; - - if (filled_size == buf_size) { - buf_size += 4096; + size_t bufsize = 4096; + buf = g_malloc (bufsize); - *buf = g_realloc(*buf, buf_size); + size_t readsize; + while ((readsize = vfs_fread (buf + size, 1, bufsize - 1 - size, file))) + { + size += readsize; - if (*buf == NULL) - goto close_handle; + if (size == bufsize - 1) + { + if (bufsize > SIZE_MAX - 4096) + break; - ptr = (unsigned char *) (* buf) + filled_size; + bufsize += 4096; + buf = g_realloc (buf, bufsize); + } } } - *size = filled_size; + buf[size] = 0; // nul-terminate -close_handle: - vfs_fclose(fd); + * bufp = buf; + if (sizep) + * sizep = size; } - /** - * Reads an unsigned 16-bit Little Endian value from the stream - * into native endian format. - * - * @param value Pointer to the variable to read the value into. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fget_le16(uint16_t *value, VFSFile *stream) -{ - uint16_t tmp; - if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1) - return FALSE; - *value = GUINT16_FROM_LE(tmp); - return TRUE; -} - -/** - * Reads an unsigned 32-bit Little Endian value from the stream into native endian format. - * - * @param value Pointer to the variable to read the value into. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fget_le32(uint32_t *value, VFSFile *stream) -{ - uint32_t tmp; - if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1) - return FALSE; - *value = GUINT32_FROM_LE(tmp); - return TRUE; -} - -/** - * Reads an unsigned 64-bit Little Endian value from the stream into native endian format. - * - * @param value Pointer to the variable to read the value into. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fget_le64(uint64_t *value, VFSFile *stream) -{ - uint64_t tmp; - if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1) - return FALSE; - *value = GUINT64_FROM_LE(tmp); - return TRUE; -} - - -/** - * Reads an unsigned 16-bit Big Endian value from the stream into native endian format. - * - * @param value Pointer to the variable to read the value into. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fget_be16(uint16_t *value, VFSFile *stream) -{ - uint16_t tmp; - if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1) - return FALSE; - *value = GUINT16_FROM_BE(tmp); - return TRUE; -} - -/** - * Reads an unsigned 32-bit Big Endian value from the stream into native endian format. - * - * @param value Pointer to the variable to read the value into. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fget_be32(uint32_t *value, VFSFile *stream) -{ - uint32_t tmp; - if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1) - return FALSE; - *value = GUINT32_FROM_BE(tmp); - return TRUE; -} - -/** - * Reads an unsigned 64-bit Big Endian value from the stream into native endian format. - * - * @param value Pointer to the variable to read the value into. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fget_be64(uint64_t *value, VFSFile *stream) -{ - uint64_t tmp; - if (vfs_fread(&tmp, sizeof(tmp), 1, stream) != 1) - return FALSE; - *value = GUINT64_FROM_BE(tmp); - return TRUE; -} - -/** - * Writes an unsigned 16-bit native endian value into the stream as a - * Little Endian value. - * - * @param value Value to write into the stream. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fput_le16(uint16_t value, VFSFile *stream) -{ - uint16_t tmp = GUINT16_TO_LE(value); - return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1; -} - -/** - * Writes an unsigned 32-bit native endian value into the stream as a - * Big Endian value. - * - * @param value Value to write into the stream. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fput_le32(uint32_t value, VFSFile *stream) -{ - uint32_t tmp = GUINT32_TO_LE(value); - return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1; -} - -/** - * Writes an unsigned 64-bit native endian value into the stream as a - * Big Endian value. - * - * @param value Value to write into the stream. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fput_le64(uint64_t value, VFSFile *stream) -{ - uint64_t tmp = GUINT64_TO_LE(value); - return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1; -} - -/** - * Writes an unsigned 16-bit native endian value into the stream as a - * Big Endian value. + * Gets contents of the file into a buffer. Buffer of filesize bytes + * is allocated by this function as necessary. * - * @param value Value to write into the stream. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. + * @param filename URI of the file to read in. + * @param buf Pointer to a pointer variable of buffer. + * @param size Pointer to gsize variable that will hold the amount of + * read data e.g. filesize. */ -EXPORT bool_t vfs_fput_be16(uint16_t value, VFSFile *stream) +EXPORT void vfs_file_get_contents (const char * filename, void * * buf, int64_t * size) { - uint16_t tmp = GUINT16_TO_BE(value); - return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1; -} + * buf = NULL; + if (size) + * size = 0; -/** - * Writes an unsigned 32-bit native endian value into the stream as a - * Big Endian value. - * - * @param value Value to write into the stream. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fput_be32(uint32_t value, VFSFile *stream) -{ - uint32_t tmp = GUINT32_TO_BE(value); - return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1; -} + VFSFile * file = vfs_fopen (filename, "r"); + if (! file) + return; -/** - * Writes an unsigned 64-bit native endian value into the stream as a - * Big Endian value. - * - * @param value Value to write into the stream. - * @param stream A #VFSFile object representing the stream. - * @return TRUE if read was succesful, FALSE if there was an error. - */ -EXPORT bool_t vfs_fput_be64(uint64_t value, VFSFile *stream) -{ - uint64_t tmp = GUINT64_TO_BE(value); - return vfs_fwrite(&tmp, sizeof(tmp), 1, stream) == 1; + vfs_file_read_all (file, buf, size); + vfs_fclose (file); } diff --git a/src/libaudcore/vfs_local.c b/src/libaudcore/vfs_local.c new file mode 100644 index 0000000..d91317a --- /dev/null +++ b/src/libaudcore/vfs_local.c @@ -0,0 +1,231 @@ +/* + * vfs_local.c + * Copyright 2009-2014 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "vfs_local.h" + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#include <glib.h> +#include <glib/gstdio.h> + +#include "audstrings.h" + +#ifdef _WIN32 +#define fseeko fseeko64 +#define ftello ftello64 +#endif + +typedef enum { + OP_NONE, + OP_READ, + OP_WRITE +} LocalOp; + +typedef struct { + char * path; + FILE * stream; + int64_t cached_size; + LocalOp last_op; +} LocalFile; + +static void * local_fopen (const char * uri, const char * mode) +{ + char * path = uri_to_filename (uri); + g_return_val_if_fail (path, NULL); + + const char * suffix = ""; + +#ifdef _WIN32 + if (! strchr (mode, 'b')) /* binary mode (Windows) */ + suffix = "b"; +#else + if (! strchr (mode, 'e')) /* close on exec (POSIX) */ + suffix = "e"; +#endif + + SCONCAT2 (mode2, mode, suffix); + + FILE * stream = g_fopen (path, mode2); + + if (! stream) + { + perror (path); + str_unref (path); + return NULL; + } + + LocalFile * local = g_slice_new (LocalFile); + + local->path = path; + local->stream = stream; + local->cached_size = -1; + local->last_op = OP_NONE; + + return local; +} + +static int local_fclose (VFSFile * file) +{ + LocalFile * local = vfs_get_handle (file); + + int result = fclose (local->stream); + if (result < 0) + perror (local->path); + + str_unref (local->path); + g_slice_free (LocalFile, local); + + return result; +} + +static int64_t local_fread (void * ptr, int64_t size, int64_t nitems, VFSFile * file) +{ + LocalFile * local = vfs_get_handle (file); + + if (local->last_op == OP_WRITE) + { + if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */ + { + perror (local->path); + return 0; + } + } + + local->last_op = OP_READ; + + clearerr (local->stream); + + int64_t result = fread (ptr, size, nitems, local->stream); + if (result < nitems && ferror (local->stream)) + perror (local->path); + + return result; +} + +static int64_t local_fwrite (const void * ptr, int64_t size, int64_t nitems, VFSFile * file) +{ + LocalFile * local = vfs_get_handle (file); + + if (local->last_op == OP_READ) + { + if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */ + { + perror (local->path); + return 0; + } + } + + local->last_op = OP_WRITE; + local->cached_size = -1; + + clearerr (local->stream); + + int64_t result = fwrite (ptr, size, nitems, local->stream); + if (result < nitems && ferror (local->stream)) + perror (local->path); + + return result; +} + +static int local_fseek (VFSFile * file, int64_t offset, int whence) +{ + LocalFile * local = vfs_get_handle (file); + + int result = fseeko (local->stream, offset, whence); + if (result < 0) + perror (local->path); + + if (result == 0) + local->last_op = OP_NONE; + + return result; +} + +static int64_t local_ftell (VFSFile * file) +{ + LocalFile * local = vfs_get_handle (file); + return ftello (local->stream); +} + +static bool_t local_feof (VFSFile * file) +{ + LocalFile * local = vfs_get_handle (file); + return feof (local->stream); +} + +static int local_ftruncate (VFSFile * file, int64_t length) +{ + LocalFile * local = vfs_get_handle (file); + + if (local->last_op != OP_NONE) + { + if (fseeko (local->stream, 0, SEEK_CUR) < 0) /* flush buffers */ + { + perror (local->path); + return 0; + } + } + + int result = ftruncate (fileno (local->stream), length); + if (result < 0) + perror (local->path); + + if (result == 0) + { + local->last_op = OP_NONE; + local->cached_size = length; + } + + return result; +} + +static int64_t local_fsize (VFSFile * file) +{ + LocalFile * local = vfs_get_handle (file); + + if (local->cached_size >= 0) + return local->cached_size; + + int64_t saved_pos = ftello (local->stream); + + if (local_fseek (file, 0, SEEK_END) < 0) + return -1; + + int64_t length = ftello (local->stream); + + if (local_fseek (file, saved_pos, SEEK_SET) < 0) + return -1; + + return length; +} + +VFSConstructor vfs_local_vtable = { + .vfs_fopen_impl = local_fopen, + .vfs_fclose_impl = local_fclose, + .vfs_fread_impl = local_fread, + .vfs_fwrite_impl = local_fwrite, + .vfs_fseek_impl = local_fseek, + .vfs_ftell_impl = local_ftell, + .vfs_feof_impl = local_feof, + .vfs_ftruncate_impl = local_ftruncate, + .vfs_fsize_impl = local_fsize +}; diff --git a/src/libaudcore/vfs_local.h b/src/libaudcore/vfs_local.h new file mode 100644 index 0000000..6fa3686 --- /dev/null +++ b/src/libaudcore/vfs_local.h @@ -0,0 +1,27 @@ +/* + * vfs_local.h + * Copyright 2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef LIBAUDCORE_VFS_LOCAL_H +#define LIBAUDCORE_VFS_LOCAL_H + +#include "vfs.h" + +extern VFSConstructor vfs_local_vtable; + +#endif /* LIBAUDCORE_VFS_LOCAL_H */ diff --git a/src/libaudgui/Makefile b/src/libaudgui/Makefile index 8246dd3..9da027f 100644 --- a/src/libaudgui/Makefile +++ b/src/libaudgui/Makefile @@ -1,23 +1,23 @@ SHARED_LIB = ${LIB_PREFIX}audgui${LIB_SUFFIX} -LIB_MAJOR = 1 +LIB_MAJOR = 2 LIB_MINOR = 0 SRCS = about.c \ confirm.c \ - effects-menu.c \ equalizer.c \ - iface-menu.c \ infopopup.c \ infowin.c \ init.c \ jump-to-time.c \ list.c \ + menu.c \ + pixbufs.c \ playlists.c \ queue-manager.c \ + scaled-image.c \ ui_fileopener.c \ ui_jumptotrack.c \ ui_jumptotrack_cache.c \ - icons-stock.c \ ui_playlist_manager.c \ urilist.c \ url-opener.c \ @@ -25,7 +25,8 @@ SRCS = about.c \ INCLUDES = libaudgui.h \ libaudgui-gtk.h \ - list.h + list.h \ + menu.h include ../../buildsys.mk include ../../extra.mk @@ -35,13 +36,11 @@ includesubdir = libaudgui CPPFLAGS := -I.. -I../.. \ ${CPPFLAGS} \ ${GLIB_CFLASG} \ - ${GTK_CFLAGS} \ - ${REGEX_CFLAGS} + ${GTK_CFLAGS} CFLAGS += ${LIB_CFLAGS} LIBS := -L../libaudcore -laudcore \ ${LIBS} -lm \ ${GLIB_LIBS} \ - ${GTK_LIBS} \ - ${REGEX_LIBS} + ${GTK_LIBS} diff --git a/src/libaudgui/about.c b/src/libaudgui/about.c index 613bf4e..56cdbef 100644 --- a/src/libaudgui/about.c +++ b/src/libaudgui/about.c @@ -1,6 +1,6 @@ /* * about.c - * Copyright 2011-2012 John Lindgren + * Copyright 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -21,7 +21,9 @@ #include <audacious/i18n.h> #include <audacious/misc.h> +#include <libaudcore/audstrings.h> +#include "init.h" #include "libaudgui-gtk.h" static const char about_text[] = @@ -30,8 +32,6 @@ static const char about_text[] = static const char website[] = "http://audacious-media-player.org"; -static GtkWidget * about_window; - static GtkWidget * create_credits_notebook (const char * credits, const char * license) { const char * titles[2] = {_("Credits"), _("License")}; @@ -61,27 +61,21 @@ static GtkWidget * create_credits_notebook (const char * credits, const char * l return notebook; } -EXPORT void audgui_show_about_window (void) +static GtkWidget * create_about_window (void) { - if (about_window) - { - gtk_window_present ((GtkWindow *) about_window); - return; - } + const char * data_dir = aud_get_path (AUD_PATH_DATA_DIR); - about_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + GtkWidget * about_window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title ((GtkWindow *) about_window, _("About Audacious")); gtk_window_set_resizable ((GtkWindow *) about_window, FALSE); gtk_container_set_border_width ((GtkContainer *) about_window, 3); audgui_destroy_on_escape (about_window); - g_signal_connect (about_window, "destroy", (GCallback) gtk_widget_destroyed, - & about_window); GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add ((GtkContainer *) about_window, vbox); - SPRINTF (logo_path, "%s/images/about-logo.png", aud_get_path (AUD_PATH_DATA_DIR)); + SCONCAT2 (logo_path, data_dir, "/images/about-logo.png"); GtkWidget * image = gtk_image_new_from_file (logo_path); gtk_box_pack_start ((GtkBox *) vbox, image, FALSE, FALSE, 0); @@ -96,11 +90,11 @@ EXPORT void audgui_show_about_window (void) char * credits, * license; - SPRINTF (credits_path, "%s/AUTHORS", aud_get_path (AUD_PATH_DATA_DIR)); + SCONCAT2 (credits_path, data_dir, "/AUTHORS"); if (! g_file_get_contents (credits_path, & credits, NULL, NULL)) credits = g_strdup_printf ("Unable to load %s; check your installation.", credits_path); - SPRINTF (license_path, "%s/COPYING", aud_get_path (AUD_PATH_DATA_DIR)); + SCONCAT2 (license_path, data_dir, "/COPYING"); if (! g_file_get_contents (license_path, & license, NULL, NULL)) license = g_strdup_printf ("Unable to load %s; check your installation.", license_path); @@ -114,5 +108,16 @@ EXPORT void audgui_show_about_window (void) g_free (credits); g_free (license); - gtk_widget_show_all (about_window); + return about_window; +} + +EXPORT void audgui_show_about_window (void) +{ + if (! audgui_reshow_unique_window (AUDGUI_ABOUT_WINDOW)) + audgui_show_unique_window (AUDGUI_ABOUT_WINDOW, create_about_window ()); +} + +EXPORT void audgui_hide_about_window (void) +{ + audgui_hide_unique_window (AUDGUI_ABOUT_WINDOW); } diff --git a/src/libaudgui/confirm.c b/src/libaudgui/confirm.c index 695e7b6..fb92da4 100644 --- a/src/libaudgui/confirm.c +++ b/src/libaudgui/confirm.c @@ -1,6 +1,6 @@ /* * confirm.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren and Thomas Lange * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,6 +22,7 @@ #include <audacious/i18n.h> #include <audacious/misc.h> #include <audacious/playlist.h> +#include <libaudcore/audstrings.h> #include "libaudgui-gtk.h" @@ -33,85 +34,68 @@ static void no_confirm_cb (GtkToggleButton * toggle) static void confirm_delete_cb (void * data) { int list = aud_playlist_by_unique_id (GPOINTER_TO_INT (data)); - if (list < 0) - return; - - aud_playlist_delete (list); - if (list > 0) - aud_playlist_set_active (list - 1); -} - -static void confirm_playlist_delete_response (GtkWidget * dialog, gint response, gpointer data) -{ - if (response == GTK_RESPONSE_YES) - confirm_delete_cb (data); - gtk_widget_destroy (dialog); + if (list >= 0) + aud_playlist_delete (list); } EXPORT void audgui_confirm_playlist_delete (int playlist) { - GtkWidget * dialog, * vbox, * button; - char * message; - if (aud_get_bool ("audgui", "no_confirm_playlist_delete")) { aud_playlist_delete (playlist); - if (playlist > 0) - aud_playlist_set_active (playlist - 1); return; } char * title = aud_playlist_get_title (playlist); - message = g_strdup_printf (_("Are you sure you want to close %s? If you " - "do, any changes made since the playlist was exported will be lost."), title); + SPRINTF (message, _("Do you want to permanently remove “%s”?"), title); str_unref (title); - dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, - GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", message); - g_free (message); - gtk_window_set_title ((GtkWindow *) dialog, _("Close Playlist")); - gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_YES); - - vbox = gtk_message_dialog_get_message_area ((GtkMessageDialog *) dialog); - button = gtk_check_button_new_with_mnemonic (_("_Don't show this message again")); + int id = aud_playlist_get_unique_id (playlist); + GtkWidget * button1 = audgui_button_new (_("_Remove"), "edit-delete", + confirm_delete_cb, GINT_TO_POINTER (id)); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL); - gtk_container_add ((GtkContainer *) vbox, button); + GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_QUESTION, + _("Remove Playlist"), message, button1, button2); - g_signal_connect (button, "toggled", (GCallback) no_confirm_cb, NULL); - g_signal_connect (dialog, "response", (GCallback) confirm_playlist_delete_response, - GINT_TO_POINTER (aud_playlist_get_unique_id (playlist))); + GtkWidget * check = gtk_check_button_new_with_mnemonic (_("_Don’t ask again")); + g_signal_connect (check, "toggled", (GCallback) no_confirm_cb, NULL); + audgui_dialog_add_widget (dialog, check); gtk_widget_show_all (dialog); } -static void rename_cb (GtkDialog * dialog, int resp, void * list) +static void rename_cb (void * entry) { - if (resp == GTK_RESPONSE_ACCEPT && GPOINTER_TO_INT (list) < - aud_playlist_count ()) - aud_playlist_set_title (GPOINTER_TO_INT (list), gtk_entry_get_text - ((GtkEntry *) g_object_get_data ((GObject *) dialog, "entry"))); + void * data = g_object_get_data ((GObject *) entry, "playlist-id"); + int list = aud_playlist_by_unique_id (GPOINTER_TO_INT (data)); - gtk_widget_destroy ((GtkWidget *) dialog); + if (list >= 0) + aud_playlist_set_title (list, gtk_entry_get_text ((GtkEntry *) entry)); } EXPORT void audgui_show_playlist_rename (int playlist) { - GtkWidget * dialog = gtk_dialog_new_with_buttons (_("Rename Playlist"), - NULL, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, - GTK_RESPONSE_REJECT, NULL); - gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_ACCEPT); + char * title = aud_playlist_get_title (playlist); GtkWidget * entry = gtk_entry_new (); - char * title = aud_playlist_get_title (playlist); gtk_entry_set_text ((GtkEntry *) entry, title); - str_unref (title); gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE); - gtk_box_pack_start ((GtkBox *) gtk_dialog_get_content_area ((GtkDialog *) - dialog), entry, FALSE, FALSE, 0); - g_object_set_data ((GObject *) dialog, "entry", entry); - g_signal_connect (dialog, "response", (GCallback) rename_cb, GINT_TO_POINTER - (playlist)); + int id = aud_playlist_get_unique_id (playlist); + g_object_set_data ((GObject *) entry, "playlist-id", GINT_TO_POINTER (id)); + + GtkWidget * button1 = audgui_button_new (_("_Rename"), "insert-text", rename_cb, entry); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL); + + GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_QUESTION, + _("Rename Playlist"), _("What would you like to call this playlist?"), + button1, button2); + + audgui_dialog_add_widget (dialog, entry); + gtk_widget_show_all (dialog); + + str_unref (title); } diff --git a/src/libaudgui/effects-menu.c b/src/libaudgui/effects-menu.c deleted file mode 100644 index 3e10588..0000000 --- a/src/libaudgui/effects-menu.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * effects-menu.c - * Copyright 2010-2011 John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include <audacious/i18n.h> -#include <audacious/plugin.h> -#include <audacious/plugins.h> - -#include "libaudgui-gtk.h" - -static bool_t watch_cb (PluginHandle * plugin, GtkCheckMenuItem * item) -{ - bool_t enabled = aud_plugin_get_enabled (plugin); - gtk_check_menu_item_set_active (item, enabled); - - GtkWidget * settings = g_object_get_data ((GObject *) item, "settings"); - if (settings != NULL) - gtk_widget_set_sensitive (settings, enabled); - - return TRUE; -} - -static void enable_cb (GtkCheckMenuItem * item, PluginHandle * plugin) -{ - aud_plugin_enable (plugin, gtk_check_menu_item_get_active (item)); -} - -static void destroy_cb (GtkCheckMenuItem * item, PluginHandle * plugin) -{ - aud_plugin_remove_watch (plugin, (PluginForEachFunc) watch_cb, item); -} - -static void settings_cb (GtkMenuItem * settings, PluginHandle * plugin) -{ - if (! aud_plugin_get_enabled (plugin)) - return; - - aud_plugin_do_configure (plugin); -} - -static bool_t add_item_cb (PluginHandle * plugin, GtkWidget * menu) -{ - GtkWidget * item = gtk_check_menu_item_new_with_label (aud_plugin_get_name - (plugin)); - gtk_check_menu_item_set_active ((GtkCheckMenuItem *) item, - aud_plugin_get_enabled (plugin)); - gtk_menu_shell_append ((GtkMenuShell *) menu, item); - aud_plugin_add_watch (plugin, (PluginForEachFunc) watch_cb, item); - g_signal_connect (item, "toggled", (GCallback) enable_cb, plugin); - g_signal_connect (item, "destroy", (GCallback) destroy_cb, plugin); - gtk_widget_show (item); - - if (aud_plugin_has_configure (plugin)) - { - GtkWidget * settings = gtk_menu_item_new_with_label (_("settings ...")); - gtk_widget_set_sensitive (settings, aud_plugin_get_enabled (plugin)); - g_object_set_data ((GObject *) item, "settings", settings); - gtk_menu_shell_append ((GtkMenuShell *) menu, settings); - g_signal_connect (settings, "activate", (GCallback) settings_cb, plugin); - gtk_widget_show (settings); - } - - return TRUE; -} - -EXPORT GtkWidget * audgui_create_effects_menu (void) -{ - GtkWidget * menu = gtk_menu_new (); - aud_plugin_for_each (PLUGIN_TYPE_EFFECT, (PluginForEachFunc) add_item_cb, - menu); - return menu; -} - -EXPORT GtkWidget * audgui_create_vis_menu (void) -{ - GtkWidget * menu = gtk_menu_new (); - aud_plugin_for_each (PLUGIN_TYPE_VIS, (PluginForEachFunc) add_item_cb, menu); - return menu; -} diff --git a/src/libaudgui/equalizer.c b/src/libaudgui/equalizer.c index b5bcd7c..1c3a082 100644 --- a/src/libaudgui/equalizer.c +++ b/src/libaudgui/equalizer.c @@ -17,19 +17,15 @@ * the use of this software. */ -/* - * Note: Because some GTK developer had the bright idea to put the minimum at - * the top of a GtkVScale and the maximum at the bottom, we have to reverse the - * sign of the values we get. - */ - #include <math.h> #include <audacious/i18n.h> #include <audacious/misc.h> #include <audacious/types.h> +#include <libaudcore/audstrings.h> #include <libaudcore/hook.h> +#include "init.h" #include "libaudgui-gtk.h" static void on_off_cb (GtkToggleButton * on_off, void * unused) @@ -45,9 +41,7 @@ static void on_off_update (void * unused, GtkWidget * on_off) static GtkWidget * create_on_off (void) { - GtkWidget * on_off; - - on_off = gtk_check_button_new_with_mnemonic (_("_Enable")); + GtkWidget * on_off = gtk_check_button_new_with_mnemonic (_("_Enable")); g_signal_connect ((GObject *) on_off, "toggled", (GCallback) on_off_cb, NULL); hook_associate ("set equalizer_active", (HookFunction) on_off_update, on_off); @@ -58,7 +52,7 @@ static GtkWidget * create_on_off (void) static void slider_moved (GtkRange * slider, void * unused) { int band = GPOINTER_TO_INT (g_object_get_data ((GObject *) slider, "band")); - double value = round (-gtk_range_get_value (slider)); + double value = round (gtk_range_get_value (slider)); if (band == -1) aud_set_double (NULL, "equalizer_preamp", value); @@ -66,56 +60,58 @@ static void slider_moved (GtkRange * slider, void * unused) aud_eq_set_band (band, value); } -static void slider_update (void * unused, GtkRange * slider) +static GtkWidget * create_slider (const char * name, int band, GtkWidget * hbox) { - int band = GPOINTER_TO_INT (g_object_get_data ((GObject *) slider, "band")); - double value; + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - if (band == -1) - value = round (aud_get_double (NULL, "equalizer_preamp")); - else - value = round (aud_eq_get_band (band)); - - g_signal_handlers_block_by_func (slider, (void *) slider_moved, NULL); - gtk_range_set_value (slider, -value); - g_signal_handlers_unblock_by_func (slider, (void *) slider_moved, NULL); -} - -static char * format_value (GtkScale * slider, double value, void * unused) -{ - return g_strdup_printf ("%d", (int) -value); -} - -static GtkWidget * create_slider (const char * name, int band) -{ - GtkWidget * vbox, * slider, * label; - - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - - label = gtk_label_new (name); + GtkWidget * label = gtk_label_new (name); gtk_label_set_angle ((GtkLabel *) label, 90); gtk_box_pack_start ((GtkBox *) vbox, label, TRUE, FALSE, 0); - slider = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, + GtkWidget * slider = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, -EQUALIZER_MAX_GAIN, EQUALIZER_MAX_GAIN, 1); gtk_scale_set_draw_value ((GtkScale *) slider, TRUE); gtk_scale_set_value_pos ((GtkScale *) slider, GTK_POS_BOTTOM); + gtk_range_set_inverted ((GtkRange *) slider, TRUE); gtk_widget_set_size_request (slider, -1, 120); g_object_set_data ((GObject *) slider, "band", GINT_TO_POINTER (band)); - g_signal_connect ((GObject *) slider, "format-value", (GCallback) format_value, NULL); g_signal_connect ((GObject *) slider, "value-changed", (GCallback) slider_moved, NULL); - slider_update (NULL, (GtkRange *) slider); + gtk_box_pack_start ((GtkBox *) vbox, slider, FALSE, FALSE, 0); + gtk_box_pack_start ((GtkBox *) hbox, vbox, FALSE, FALSE, 0); - if (band == -1) - hook_associate ("set equalizer_preamp", (HookFunction) slider_update, slider); - else - hook_associate ("set equalizer_bands", (HookFunction) slider_update, slider); + return slider; +} - gtk_box_pack_start ((GtkBox *) vbox, slider, FALSE, FALSE, 0); +static void set_slider (GtkWidget * slider, double value) +{ + g_signal_handlers_block_by_func (slider, (void *) slider_moved, NULL); + gtk_range_set_value ((GtkRange *) slider, round (value)); + g_signal_handlers_unblock_by_func (slider, (void *) slider_moved, NULL); +} - return vbox; +static void update_sliders (void * unused, GtkWidget * window) +{ + GtkWidget * preamp = g_object_get_data ((GObject *) window, "preamp"); + set_slider (preamp, aud_get_double (NULL, "equalizer_preamp")); + + double values[AUD_EQUALIZER_NBANDS]; + aud_eq_get_bands (values); + + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) + { + SPRINTF (slider_id, "slider%d", i); + GtkWidget * slider = g_object_get_data ((GObject *) window, slider_id); + set_slider (slider, values[i]); + } +} + +static void destroy_cb (void) +{ + hook_dissociate ("set equalizer_active", (HookFunction) on_off_update); + hook_dissociate ("set equalizer_bands", (HookFunction) update_sliders); + hook_dissociate ("set equalizer_preamp", (HookFunction) update_sliders); } static GtkWidget * create_window (void) @@ -123,56 +119,52 @@ static GtkWidget * create_window (void) const char * const names[AUD_EQUALIZER_NBANDS] = {N_("31 Hz"), N_("63 Hz"), N_("125 Hz"), N_("250 Hz"), N_("500 Hz"), N_("1 kHz"), N_("2 kHz"), N_("4 kHz"), N_("8 kHz"), N_("16 kHz")}; - GtkWidget * window, * vbox, * hbox; - int i; - window = gtk_window_new (GTK_WINDOW_TOPLEVEL); + GtkWidget * window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_title ((GtkWindow *) window, _("Equalizer")); gtk_window_set_type_hint ((GtkWindow *) window, GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_resizable ((GtkWindow *) window, FALSE); gtk_container_set_border_width ((GtkContainer *) window, 6); - g_signal_connect ((GObject *) window, "delete-event", (GCallback) - gtk_widget_hide_on_delete, NULL); - audgui_hide_on_escape (window); + audgui_destroy_on_escape (window); - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add ((GtkContainer *) window, vbox); gtk_box_pack_start ((GtkBox *) vbox, create_on_off (), FALSE, FALSE, 0); - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start ((GtkBox *) vbox, hbox, FALSE, FALSE, 0); - gtk_box_pack_start ((GtkBox *) hbox, create_slider (_("Preamp"), -1), FALSE, FALSE, 0); + GtkWidget * preamp = create_slider (_("Preamp"), -1, hbox); + g_object_set_data ((GObject *) window, "preamp", preamp); + gtk_box_pack_start ((GtkBox *) hbox, gtk_separator_new (GTK_ORIENTATION_VERTICAL), FALSE, FALSE, 0); - for (i = 0; i < AUD_EQUALIZER_NBANDS; i ++) - gtk_box_pack_start ((GtkBox *) hbox, create_slider (_(names[i]), i), FALSE, FALSE, 0); + for (int i = 0; i < AUD_EQUALIZER_NBANDS; i ++) + { + GtkWidget * slider = create_slider (_(names[i]), i, hbox); + SPRINTF (slider_id, "slider%d", i); + g_object_set_data ((GObject *) window, slider_id, slider); + } + + update_sliders (NULL, window); + + hook_associate ("set equalizer_preamp", (HookFunction) update_sliders, window); + hook_associate ("set equalizer_bands", (HookFunction) update_sliders, window); + + g_signal_connect (window, "destroy", (GCallback) destroy_cb, NULL); - gtk_widget_show_all (vbox); return window; } -static GtkWidget * equalizer_window = NULL; - EXPORT void audgui_show_equalizer_window (void) { - if (equalizer_window == NULL) - equalizer_window = create_window (); - - gtk_window_present ((GtkWindow *) equalizer_window); + if (! audgui_reshow_unique_window (AUDGUI_EQUALIZER_WINDOW)) + audgui_show_unique_window (AUDGUI_EQUALIZER_WINDOW, create_window ()); } EXPORT void audgui_hide_equalizer_window (void) { - if (! equalizer_window) - return; - - hook_dissociate ("set equalizer_active", (HookFunction) on_off_update); - hook_dissociate ("set equalizer_bands", (HookFunction) slider_update); - hook_dissociate ("set equalizer_preamp", (HookFunction) slider_update); - - gtk_widget_destroy (equalizer_window); - equalizer_window = NULL; + audgui_hide_unique_window (AUDGUI_EQUALIZER_WINDOW); } diff --git a/src/libaudgui/icons-stock.c b/src/libaudgui/icons-stock.c deleted file mode 100644 index fb8200f..0000000 --- a/src/libaudgui/icons-stock.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * icons-stock.c - * Copyright 2007-2010 Michael Färber and John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include <gtk/gtk.h> -#include <audacious/misc.h> - -#include "libaudgui.h" - -static void load_stock_icon (char * id, char * filename, - GtkIconFactory * iconfactory) -{ - char * path = g_strdup_printf ("%s/images/%s", - aud_get_path (AUD_PATH_DATA_DIR), filename); - - GdkPixbuf * pixbuf = gdk_pixbuf_new_from_file (path, NULL); - if (pixbuf == NULL) - goto ERR; - - GtkIconSet * iconset = gtk_icon_set_new_from_pixbuf(pixbuf); - g_object_unref(pixbuf); - - gtk_icon_factory_add(iconfactory, id, iconset); - -ERR: - g_free (path); -} - -EXPORT void -audgui_register_stock_icons(void) -{ - GtkIconFactory *iconfactory = gtk_icon_factory_new(); - - load_stock_icon(AUD_STOCK_AUDACIOUS, - "audacious.png", iconfactory); - load_stock_icon(AUD_STOCK_PLAYLIST, - "menu_playlist.png", iconfactory); - load_stock_icon(AUD_STOCK_PLUGIN, - "menu_plugin.png", iconfactory); - load_stock_icon(AUD_STOCK_QUEUETOGGLE, - "menu_queue_toggle.png", iconfactory); - - gtk_icon_factory_add_default( iconfactory ); - g_object_unref( iconfactory ); -} diff --git a/src/libaudgui/iface-menu.c b/src/libaudgui/iface-menu.c deleted file mode 100644 index 448a634..0000000 --- a/src/libaudgui/iface-menu.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * iface-menu.c - * Copyright 2010 John Lindgren - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#include <gtk/gtk.h> -#include <audacious/plugins.h> - -#include "libaudgui-gtk.h" - -static void switch_cb (GtkMenuItem * item, PluginHandle * plugin) -{ - if (gtk_check_menu_item_get_active ((GtkCheckMenuItem *) item)) - aud_plugin_enable (plugin, TRUE); -} - -typedef struct { - GtkWidget * menu; - GSList * group; -} IfaceMenuAddState; - -static bool_t add_item_cb (PluginHandle * plugin, IfaceMenuAddState * state) -{ - GtkWidget * item = gtk_radio_menu_item_new_with_label (state->group, - aud_plugin_get_name (plugin)); - state->group = gtk_radio_menu_item_get_group ((GtkRadioMenuItem *) item); - if (aud_plugin_get_enabled (plugin)) - gtk_check_menu_item_set_active ((GtkCheckMenuItem *) item, TRUE); - gtk_menu_shell_append ((GtkMenuShell *) state->menu, item); - g_signal_connect (item, "activate", (GCallback) switch_cb, plugin); - gtk_widget_show (item); - return TRUE; -} - -EXPORT GtkWidget * audgui_create_iface_menu (void) -{ - IfaceMenuAddState state = {gtk_menu_new (), NULL}; - aud_plugin_for_each (PLUGIN_TYPE_IFACE, (PluginForEachFunc) add_item_cb, - & state); - return state.menu; -} diff --git a/src/libaudgui/infopopup.c b/src/libaudgui/infopopup.c index 6cc341d..d0f6fb0 100644 --- a/src/libaudgui/infopopup.c +++ b/src/libaudgui/infopopup.c @@ -1,6 +1,7 @@ /* * infopopup.c - * Copyright 2006-2012 William Pitcock, Giacomo Lozito, and John Lindgren + * Copyright 2006-2012 William Pitcock, Giacomo Lozito, John Lindgren, and + * Thomas Lange * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,15 +25,15 @@ #include <audacious/i18n.h> #include <audacious/misc.h> #include <audacious/playlist.h> +#include <libaudcore/audstrings.h> #include <libaudcore/hook.h> +#include "init.h" #include "libaudgui.h" #include "libaudgui-gtk.h" #define IMAGE_SIZE 96 -static GtkWidget * infopopup = NULL; - static struct { GtkWidget * title_header, * title_label; GtkWidget * artist_header, * artist_label; @@ -80,7 +81,8 @@ static bool_t infopopup_progress_cb (void * unused) gtk_progress_bar_set_fraction ((GtkProgressBar *) widgets.progress, time / (float) length); - SPRINTF (time_str, "%d:%02d", time / 60000, (time / 1000) % 60); + char time_str[16]; + audgui_format_time (time_str, sizeof time_str, time); gtk_progress_bar_set_text ((GtkProgressBar *) widgets.progress, time_str); gtk_widget_show (widgets.progress); @@ -121,16 +123,15 @@ static void infopopup_destroyed (void) progress_source = 0; } - infopopup = NULL; memset (& widgets, 0, sizeof widgets); str_unref (current_file); current_file = NULL; } -static void infopopup_create (void) +static GtkWidget * infopopup_create (void) { - infopopup = gtk_window_new (GTK_WINDOW_POPUP); + GtkWidget * infopopup = gtk_window_new (GTK_WINDOW_POPUP); gtk_window_set_type_hint ((GtkWindow *) infopopup, GDK_WINDOW_TYPE_HINT_TOOLTIP); gtk_window_set_decorated ((GtkWindow *) infopopup, FALSE); gtk_container_set_border_width ((GtkContainer *) infopopup, 4); @@ -163,7 +164,8 @@ static void infopopup_create (void) /* do not show the track progress */ gtk_widget_set_no_show_all (widgets.progress, TRUE); - gtk_widget_show_all (hbox); + + return infopopup; } /* calls str_unref() on <text> */ @@ -187,13 +189,13 @@ static void infopopup_set_field (GtkWidget * header, GtkWidget * label, char * t static void infopopup_set_fields (const Tuple * tuple, const char * title) { /* use title from tuple if possible */ - char * title2 = tuple_get_str (tuple, FIELD_TITLE, NULL); + char * title2 = tuple_get_str (tuple, FIELD_TITLE); if (! title2) title2 = str_get (title); - char * artist = tuple_get_str (tuple, FIELD_ARTIST, NULL); - char * album = tuple_get_str (tuple, FIELD_ALBUM, NULL); - char * genre = tuple_get_str (tuple, FIELD_GENRE, NULL); + char * artist = tuple_get_str (tuple, FIELD_ARTIST); + char * album = tuple_get_str (tuple, FIELD_ALBUM); + char * genre = tuple_get_str (tuple, FIELD_GENRE); infopopup_set_field (widgets.title_header, widgets.title_label, title2); infopopup_set_field (widgets.artist_header, widgets.artist_label, artist); @@ -203,20 +205,29 @@ static void infopopup_set_fields (const Tuple * tuple, const char * title) int value; char * tmp; - value = tuple_get_int (tuple, FIELD_LENGTH, NULL); - tmp = (value > 0) ? str_printf ("%d:%02d", value / 60000, value / 1000 % 60) : NULL; + value = tuple_get_int (tuple, FIELD_LENGTH); + + if (value > 0) + { + char buf[16]; + audgui_format_time (buf, sizeof buf, value); + tmp = str_get (buf); + } + else + tmp = NULL; + infopopup_set_field (widgets.length_header, widgets.length_label, tmp); - value = tuple_get_int (tuple, FIELD_YEAR, NULL); - tmp = (value > 0) ? str_printf ("%d", value) : NULL; + value = tuple_get_int (tuple, FIELD_YEAR); + tmp = (value > 0) ? int_to_str (value) : NULL; infopopup_set_field (widgets.year_header, widgets.year_label, tmp); - value = tuple_get_int (tuple, FIELD_TRACK_NUMBER, NULL); - tmp = (value > 0) ? str_printf ("%d", value) : NULL; + value = tuple_get_int (tuple, FIELD_TRACK_NUMBER); + tmp = (value > 0) ? int_to_str (value) : NULL; infopopup_set_field (widgets.track_header, widgets.track_label, tmp); } -static void infopopup_move_to_mouse (void) +static void infopopup_move_to_mouse (GtkWidget * infopopup) { int x, y, h, w; @@ -242,13 +253,12 @@ static void infopopup_move_to_mouse (void) static void infopopup_show (const char * filename, const Tuple * tuple, const char * title) { - if (infopopup) - gtk_widget_destroy (infopopup); + audgui_hide_unique_window (AUDGUI_INFOPOPUP_WINDOW); str_unref (current_file); current_file = str_get (filename); - infopopup_create (); + GtkWidget * infopopup = infopopup_create (); infopopup_set_fields (tuple, title); infopopup_display_image (filename); @@ -264,8 +274,9 @@ static void infopopup_show (const char * filename, const Tuple * tuple, /* immediately run the callback once to update progressbar status */ infopopup_progress_cb (NULL); - infopopup_move_to_mouse (); - gtk_widget_show (infopopup); + infopopup_move_to_mouse (infopopup); + + audgui_show_unique_window (AUDGUI_INFOPOPUP_WINDOW, infopopup); } EXPORT void audgui_infopopup_show (int playlist, int entry) @@ -298,6 +309,5 @@ EXPORT void audgui_infopopup_show_current (void) EXPORT void audgui_infopopup_hide (void) { - if (infopopup) - gtk_widget_destroy (infopopup); + audgui_hide_unique_window (AUDGUI_INFOPOPUP_WINDOW); } diff --git a/src/libaudgui/infowin.c b/src/libaudgui/infowin.c index 9156569..530a65e 100644 --- a/src/libaudgui/infowin.c +++ b/src/libaudgui/infowin.c @@ -1,7 +1,7 @@ /* * libaudgui/infowin.c - * Copyright 2006-2012 William Pitcock, Tomasz Moń, Eugene Zagidullin, and - * John Lindgren + * Copyright 2006-2013 William Pitcock, Tomasz Moń, Eugene Zagidullin, + * John Lindgren, and Thomas Lange * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -29,20 +29,24 @@ #include <libaudcore/audstrings.h> #include <libaudcore/hook.h> +#include "init.h" #include "libaudgui.h" #include "libaudgui-gtk.h" #define AUDGUI_STATUS_TIMEOUT 3000 -#define IMAGE_SIZE 150 -enum -{ - RAWDATA_KEY, - RAWDATA_VALUE, - RAWDATA_N_COLS +enum { + CODEC_FORMAT, + CODEC_QUALITY, + CODEC_BITRATE, + CODEC_ITEMS }; -static GtkWidget * infowin = NULL; +static const char * codec_labels[CODEC_ITEMS] = { + N_("Format:"), + N_("Quality:"), + N_("Bitrate:") +}; static struct { GtkWidget * location; @@ -54,9 +58,7 @@ static struct { GtkWidget * track; GtkWidget * genre; GtkWidget * image; - GtkWidget * format; - GtkWidget * quality; - GtkWidget * bitrate; + GtkWidget * codec[3]; GtkWidget * apply; GtkWidget * ministatus; } widgets; @@ -64,6 +66,7 @@ static struct { static char * current_file = NULL; static PluginHandle * current_decoder = NULL; static bool_t can_write = FALSE; +static int timeout_source = 0; /* This is by no means intended to be a complete list. If it is not short, it * is useless: scrolling through ten pages of dropdown list is more work than @@ -110,10 +113,27 @@ static const char * genre_table[] = { N_("Techno"), N_("Trip-hop")}; +static GtkWidget * small_label_new (const char * text) +{ + static PangoAttrList * attrs = NULL; + + if (! attrs) + { + attrs = pango_attr_list_new (); + pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL)); + } + + GtkWidget * label = gtk_label_new (text); + gtk_label_set_attributes ((GtkLabel *) label, attrs); + gtk_misc_set_alignment ((GtkMisc *) label, 0, 0.5); + + return label; +} + static void set_entry_str_from_field (GtkWidget * widget, const Tuple * tuple, int fieldn, bool_t editable) { - char * text = tuple_get_str (tuple, fieldn, NULL); + char * text = tuple_get_str (tuple, fieldn); gtk_entry_set_text ((GtkEntry *) widget, text != NULL ? text : ""); gtk_editable_set_editable ((GtkEditable *) widget, editable); @@ -126,9 +146,8 @@ static void set_entry_int_from_field (GtkWidget * widget, const Tuple * tuple, { char scratch[32]; - if (tuple_get_value_type (tuple, fieldn, NULL) == TUPLE_INT) - snprintf (scratch, sizeof scratch, "%d", tuple_get_int (tuple, fieldn, - NULL)); + if (tuple_get_value_type (tuple, fieldn) == TUPLE_INT) + str_itoa (tuple_get_int (tuple, fieldn), scratch, sizeof scratch); else scratch[0] = 0; @@ -142,9 +161,9 @@ static void set_field_str_from_entry (Tuple * tuple, int fieldn, GtkWidget * const char * text = gtk_entry_get_text ((GtkEntry *) widget); if (text[0]) - tuple_set_str (tuple, fieldn, NULL, text); + tuple_set_str (tuple, fieldn, text); else - tuple_unset (tuple, fieldn, NULL); + tuple_unset (tuple, fieldn); } static void set_field_int_from_entry (Tuple * tuple, int fieldn, GtkWidget * @@ -153,28 +172,9 @@ static void set_field_int_from_entry (Tuple * tuple, int fieldn, GtkWidget * const char * text = gtk_entry_get_text ((GtkEntry *) widget); if (text[0]) - tuple_set_int (tuple, fieldn, NULL, atoi (text)); - else - tuple_unset (tuple, fieldn, NULL); -} - -/* call str_unref() on <text> */ -static void infowin_label_set_text (GtkWidget * widget, char * text) -{ - char * tmp; - - if (text) - { - tmp = g_strdup_printf("<span size=\"small\">%s</span>", text); - gtk_label_set_text ((GtkLabel *) widget, tmp); - g_free (tmp); - str_unref (text); - } + tuple_set_int (tuple, fieldn, atoi (text)); else - gtk_label_set_text ((GtkLabel *) widget, - _("<span size=\"small\">n/a</span>")); - - gtk_label_set_use_markup ((GtkLabel *) widget, TRUE); + tuple_unset (tuple, fieldn); } static void entry_changed (GtkEditable * editable, void * unused) @@ -183,26 +183,23 @@ static void entry_changed (GtkEditable * editable, void * unused) gtk_widget_set_sensitive (widgets.apply, TRUE); } -static bool_t ministatus_timeout_proc (void * data) +static bool_t ministatus_timeout_proc (void) { - GtkLabel * status = data; - - gtk_label_set_text (status, "<span size=\"small\"></span>"); - gtk_label_set_use_markup (status, TRUE); + gtk_label_set_text ((GtkLabel *) widgets.ministatus, NULL); + timeout_source = 0; return FALSE; } static void ministatus_display_message (const char * text) { - char * tmp = g_strdup_printf ("<span size=\"small\">%s</span>", text); + gtk_label_set_text ((GtkLabel *) widgets.ministatus, text); - gtk_label_set_text ((GtkLabel *) widgets.ministatus, tmp); - gtk_label_set_use_markup ((GtkLabel *) widgets.ministatus, TRUE); - g_free (tmp); + if (timeout_source) + g_source_remove (timeout_source); - g_timeout_add (AUDGUI_STATUS_TIMEOUT, (GSourceFunc) ministatus_timeout_proc, - widgets.ministatus); + timeout_source = g_timeout_add (AUDGUI_STATUS_TIMEOUT, (GSourceFunc) + ministatus_timeout_proc, NULL); } static void infowin_update_tuple (void * unused) @@ -220,11 +217,11 @@ static void infowin_update_tuple (void * unused) if (aud_file_write_tuple (current_file, current_decoder, tuple)) { - ministatus_display_message (_("Metadata updated successfully")); + ministatus_display_message (_("Save successful")); gtk_widget_set_sensitive (widgets.apply, FALSE); } else - ministatus_display_message (_("Metadata updating failed")); + ministatus_display_message (_("Save error")); tuple_unref (tuple); } @@ -235,7 +232,7 @@ static bool_t genre_fill (GtkWidget * combo) GList * node; int i; - for (i = 0; i < G_N_ELEMENTS (genre_table); i ++) + for (i = 0; i < ARRAY_LEN (genre_table); i ++) list = g_list_prepend (list, _(genre_table[i])); list = g_list_sort (list, (GCompareFunc) strcmp); @@ -256,16 +253,23 @@ static void infowin_display_image (const char * filename) if (! pb) pb = audgui_pixbuf_fallback (); - audgui_pixbuf_scale_within (& pb, IMAGE_SIZE); - gtk_image_set_from_pixbuf ((GtkImage *) widgets.image, pb); - g_object_unref (pb); + if (pb) + { + audgui_scaled_image_set (widgets.image, pb); + g_object_unref (pb); + } } static void infowin_destroyed (void) { hook_dissociate ("art ready", (HookFunction) infowin_display_image); - infowin = NULL; + if (timeout_source) + { + g_source_remove (timeout_source); + timeout_source = 0; + } + memset (& widgets, 0, sizeof widgets); str_unref (current_file); @@ -274,226 +278,115 @@ static void infowin_destroyed (void) can_write = FALSE; } -static void create_infowin (void) +static void add_entry (GtkWidget * grid, const char * title, GtkWidget * entry, + int x, int y, int span) { - GtkWidget * hbox; - GtkWidget * hbox_status_and_bbox; - GtkWidget * vbox0; - GtkWidget * vbox2; - GtkWidget * label_title; - GtkWidget * label_artist; - GtkWidget * label_album; - GtkWidget * label_comment; - GtkWidget * label_genre; - GtkWidget * label_year; - GtkWidget * label_track; - GtkWidget * label_format; - GtkWidget * label_quality_label; - GtkWidget * label_bitrate_label; - GtkWidget * codec_hbox; - GtkWidget * codec_grid; - GtkWidget * grid1; - GtkWidget * bbox_close; - GtkWidget * btn_close; - GtkWidget * alignment; - - infowin = gtk_window_new (GTK_WINDOW_TOPLEVEL); - gtk_container_set_border_width ((GtkContainer *) infowin, 6); - gtk_window_set_title ((GtkWindow *) infowin, _("Track Information")); - gtk_window_set_type_hint ((GtkWindow *) infowin, - GDK_WINDOW_TYPE_HINT_DIALOG); + GtkWidget * label = small_label_new (title); - vbox0 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_container_add ((GtkContainer *) infowin, vbox0); + if (y > 0) + gtk_widget_set_margin_top (label, 6); - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start ((GtkBox *) vbox0, hbox, TRUE, TRUE, 0); + gtk_grid_attach ((GtkGrid *) grid, label, x, y, span, 1); + gtk_grid_attach ((GtkGrid *) grid, entry, x, y + 1, span, 1); - vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - gtk_box_pack_start ((GtkBox *) hbox, vbox2, TRUE, TRUE, 0); + g_signal_connect (entry, "changed", (GCallback) entry_changed, NULL); +} - GtkWidget * vbox3 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); - gtk_box_pack_start ((GtkBox *) vbox2, vbox3, TRUE, FALSE, 0); +static GtkWidget * create_infowin (void) +{ + GtkWidget * infowin = gtk_window_new (GTK_WINDOW_TOPLEVEL); + gtk_container_set_border_width ((GtkContainer *) infowin, 6); + gtk_window_set_title ((GtkWindow *) infowin, _("Song Info")); + gtk_window_set_type_hint ((GtkWindow *) infowin, + GDK_WINDOW_TYPE_HINT_DIALOG); - widgets.image = gtk_image_new (); - gtk_widget_set_size_request (widgets.image, IMAGE_SIZE, IMAGE_SIZE); - gtk_box_pack_start ((GtkBox *) vbox3, widgets.image, FALSE, FALSE, 0); + GtkWidget * main_grid = gtk_grid_new (); + gtk_grid_set_column_spacing ((GtkGrid *) main_grid, 6); + gtk_grid_set_row_spacing ((GtkGrid *) main_grid, 6); + gtk_container_add ((GtkContainer *) infowin, main_grid); + + widgets.image = audgui_scaled_image_new (NULL); + gtk_widget_set_hexpand (widgets.image, TRUE); + gtk_widget_set_vexpand (widgets.image, TRUE); + gtk_grid_attach ((GtkGrid *) main_grid, widgets.image, 0, 0, 1, 1); widgets.location = gtk_label_new (""); - gtk_widget_set_size_request (widgets.location, 200, -1); + gtk_label_set_max_width_chars ((GtkLabel *) widgets.location, 40); gtk_label_set_line_wrap ((GtkLabel *) widgets.location, TRUE); gtk_label_set_line_wrap_mode ((GtkLabel *) widgets.location, PANGO_WRAP_WORD_CHAR); gtk_label_set_selectable ((GtkLabel *) widgets.location, TRUE); - gtk_box_pack_start ((GtkBox *) vbox3, widgets.location, FALSE, FALSE, 0); - - codec_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - gtk_box_pack_start ((GtkBox *) vbox2, codec_hbox, FALSE, FALSE, 0); + gtk_grid_attach ((GtkGrid *) main_grid, widgets.location, 0, 1, 1, 1); - codec_grid = gtk_grid_new (); + GtkWidget * codec_grid = gtk_grid_new (); gtk_grid_set_row_spacing ((GtkGrid *) codec_grid, 3); gtk_grid_set_column_spacing ((GtkGrid *) codec_grid, 12); - gtk_box_pack_start ((GtkBox *) codec_hbox, codec_grid, FALSE, FALSE, 0); - - label_format = gtk_label_new (_("<span size=\"small\">Format:</span>")); - gtk_label_set_use_markup ((GtkLabel *) label_format, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_format, 0, 0.5); - label_quality_label = gtk_label_new - (_("<span size=\"small\">Quality:</span>")); - gtk_label_set_use_markup ((GtkLabel *) label_quality_label, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_quality_label, 0, 0.5); - label_bitrate_label = gtk_label_new (_("<span size=\"small\">Bitrate:</span>")); - gtk_label_set_use_markup ((GtkLabel *) label_bitrate_label, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_bitrate_label, 0, 0.5); - - widgets.format = gtk_label_new (_("<span size=\"small\">n/a</span>")); - gtk_label_set_use_markup ((GtkLabel *) widgets.format, TRUE); - gtk_misc_set_alignment ((GtkMisc *) widgets.format, 0, 0.5); - widgets.quality = gtk_label_new (_("<span size=\"small\">n/a</span>")); - gtk_label_set_use_markup ((GtkLabel *) widgets.quality, TRUE); - gtk_misc_set_alignment ((GtkMisc *) widgets.quality, 0, 0.5); - widgets.bitrate = gtk_label_new (_("<span size=\"small\">n/a</span>")); - gtk_label_set_use_markup ((GtkLabel *) widgets.bitrate, TRUE); - gtk_misc_set_alignment ((GtkMisc *) widgets.bitrate, 0, 0.5); - - gtk_grid_attach ((GtkGrid *) codec_grid, label_format, 0, 0, 1, 1); - gtk_grid_attach ((GtkGrid *) codec_grid, widgets.format, 1, 0, 1, 1); - gtk_grid_attach ((GtkGrid *) codec_grid, label_quality_label, 0, 1, 1, 1); - gtk_grid_attach ((GtkGrid *) codec_grid, widgets.quality, 1, 1, 1, 1); - gtk_grid_attach ((GtkGrid *) codec_grid, label_bitrate_label, 0, 2, 1, 1); - gtk_grid_attach ((GtkGrid *) codec_grid, widgets.bitrate, 1, 2, 1, 1); - - vbox2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); - gtk_box_pack_start ((GtkBox *) hbox, vbox2, TRUE, TRUE, 0); - - label_title = gtk_label_new (_("<span size=\"small\">Title</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_title, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_title, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_title, 0, 0); - - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); - widgets.title = gtk_entry_new (); - gtk_container_add ((GtkContainer *) alignment, widgets.title); - g_signal_connect (widgets.title, "changed", (GCallback) entry_changed, NULL); - - label_artist = gtk_label_new (_("<span size=\"small\">Artist</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_artist, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_artist, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_artist, 0, 0.5); + gtk_grid_attach ((GtkGrid *) main_grid, codec_grid, 0, 2, 1, 1); - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); - widgets.artist = gtk_entry_new (); - gtk_container_add ((GtkContainer *) alignment, widgets.artist); - g_signal_connect (widgets.artist, "changed", (GCallback) entry_changed, NULL); + for (int row = 0; row < CODEC_ITEMS; row ++) + { + GtkWidget * label = small_label_new (_(codec_labels[row])); + gtk_grid_attach ((GtkGrid *) codec_grid, label, 0, row, 1, 1); - label_album = gtk_label_new (_("<span size=\"small\">Album</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_album, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_album, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_album, 0, 0.5); + widgets.codec[row] = small_label_new (NULL); + gtk_grid_attach ((GtkGrid *) codec_grid, widgets.codec[row], 1, row, 1, 1); + } - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); - widgets.album = gtk_entry_new (); - gtk_container_add ((GtkContainer *) alignment, widgets.album); - g_signal_connect (widgets.album, "changed", (GCallback) entry_changed, NULL); + GtkWidget * grid = gtk_grid_new (); + gtk_grid_set_column_homogeneous ((GtkGrid *) grid, TRUE); + gtk_grid_set_column_spacing ((GtkGrid *) grid, 6); + gtk_grid_attach ((GtkGrid *) main_grid, grid, 1, 0, 1, 3); - label_comment = gtk_label_new (_("<span size=\"small\">Comment</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_comment, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_comment, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_comment, 0, 0.5); + widgets.title = gtk_entry_new (); + add_entry (grid, _("Title"), widgets.title, 0, 0, 2); - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); - widgets.comment = gtk_entry_new (); - gtk_container_add ((GtkContainer *) alignment, widgets.comment); - g_signal_connect (widgets.comment, "changed", (GCallback) entry_changed, NULL); + widgets.artist = gtk_entry_new (); + add_entry (grid, _("Artist"), widgets.artist, 0, 2, 2); - label_genre = gtk_label_new (_("<span size=\"small\">Genre</span>")); - gtk_box_pack_start ((GtkBox *) vbox2, label_genre, FALSE, FALSE, 0); - gtk_label_set_use_markup ((GtkLabel *) label_genre, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_genre, 0, 0.5); + widgets.album = gtk_entry_new (); + add_entry (grid, _("Album"), widgets.album, 0, 4, 2); - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); + widgets.comment = gtk_entry_new (); + add_entry (grid, _("Comment"), widgets.comment, 0, 6, 2); widgets.genre = gtk_combo_box_text_new_with_entry (); - gtk_container_add ((GtkContainer *) alignment, widgets.genre); - g_signal_connect (widgets.genre, "changed", (GCallback) entry_changed, NULL); + add_entry (grid, _("Genre"), widgets.genre, 0, 8, 2); g_idle_add ((GSourceFunc) genre_fill, widgets.genre); - alignment = gtk_alignment_new (0.5, 0.5, 1, 1); - gtk_box_pack_start ((GtkBox *) vbox2, alignment, FALSE, FALSE, 0); - gtk_alignment_set_padding ((GtkAlignment *) alignment, 0, 6, 0, 0); - grid1 = gtk_grid_new (); - gtk_grid_set_column_homogeneous ((GtkGrid *) grid1, TRUE); - gtk_container_add ((GtkContainer *) alignment, grid1); - gtk_grid_set_column_spacing ((GtkGrid *) grid1, 6); + widgets.year = gtk_entry_new (); + add_entry (grid, _("Year"), widgets.year, 0, 10, 1); - label_year = gtk_label_new (_("<span size=\"small\">Year</span>")); - gtk_grid_attach ((GtkGrid *) grid1, label_year, 0, 0, 1, 1); - gtk_label_set_use_markup ((GtkLabel *) label_year, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_year, 0, 0.5); + widgets.track = gtk_entry_new (); + add_entry (grid, _("Track Number"), widgets.track, 1, 10, 1); - widgets.year = gtk_entry_new (); - gtk_grid_attach ((GtkGrid *) grid1, widgets.year, 0, 1, 1, 1); - g_signal_connect (widgets.year, "changed", (GCallback) entry_changed, NULL); + GtkWidget * bottom_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + gtk_grid_attach ((GtkGrid *) main_grid, bottom_hbox, 0, 3, 2, 1); - label_track = gtk_label_new (_("<span size=\"small\">Track Number</span>")); - gtk_grid_attach ((GtkGrid *) grid1, label_track, 1, 0, 1, 1); - gtk_label_set_use_markup ((GtkLabel *) label_track, TRUE); - gtk_misc_set_alignment ((GtkMisc *) label_track, 0, 0.5); + widgets.ministatus = small_label_new (NULL); + gtk_box_pack_start ((GtkBox *) bottom_hbox, widgets.ministatus, TRUE, TRUE, 0); - widgets.track = gtk_entry_new (); - gtk_grid_attach ((GtkGrid *) grid1, widgets.track, 1, 1, 1, 1); - g_signal_connect (widgets.track, "changed", (GCallback) entry_changed, NULL); - - hbox_status_and_bbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); - gtk_box_pack_start ((GtkBox *) vbox0, hbox_status_and_bbox, FALSE, FALSE, 0); - - widgets.ministatus = gtk_label_new ("<span size=\"small\"></span>"); - gtk_label_set_use_markup ((GtkLabel *) widgets.ministatus, TRUE); - gtk_misc_set_alignment ((GtkMisc *) widgets.ministatus, 0, 0.5); - gtk_box_pack_start ((GtkBox *) hbox_status_and_bbox, widgets.ministatus, - TRUE, TRUE, 0); - - bbox_close = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); - gtk_box_set_spacing ((GtkBox *) bbox_close, 6); - gtk_box_pack_start ((GtkBox *) hbox_status_and_bbox, bbox_close, FALSE, - FALSE, 0); - gtk_button_box_set_layout ((GtkButtonBox *) bbox_close, GTK_BUTTONBOX_END); - - widgets.apply = gtk_button_new_from_stock (GTK_STOCK_SAVE); - gtk_container_add ((GtkContainer *) bbox_close, widgets.apply); - g_signal_connect (widgets.apply, "clicked", (GCallback) infowin_update_tuple, NULL); - gtk_widget_set_sensitive (widgets.apply, FALSE); + widgets.apply = audgui_button_new (_("_Save"), "document-save", + (AudguiCallback) infowin_update_tuple, NULL); - btn_close = gtk_button_new_from_stock (GTK_STOCK_CLOSE); - gtk_container_add ((GtkContainer *) bbox_close, btn_close); - g_signal_connect_swapped (btn_close, "clicked", (GCallback) gtk_widget_hide, - infowin); + GtkWidget * close_button = audgui_button_new (_("_Close"), "window-close", + (AudguiCallback) audgui_infowin_hide, NULL); - gtk_widget_show_all (vbox0); - gtk_widget_grab_focus (widgets.title); + gtk_box_pack_end ((GtkBox *) bottom_hbox, close_button, FALSE, FALSE, 0); + gtk_box_pack_end ((GtkBox *) bottom_hbox, widgets.apply, FALSE, FALSE, 0); audgui_destroy_on_escape (infowin); g_signal_connect (infowin, "destroy", (GCallback) infowin_destroyed, NULL); hook_associate ("art ready", (HookFunction) infowin_display_image, NULL); + + return infowin; } static void infowin_show (int list, int entry, const char * filename, const Tuple * tuple, PluginHandle * decoder, bool_t updating_enabled) { - char * tmp; + audgui_hide_unique_window (AUDGUI_INFO_WINDOW); - if (! infowin) - create_infowin (); + GtkWidget * infowin = create_infowin (); str_unref (current_file); current_file = str_get (filename); @@ -507,25 +400,37 @@ static void infowin_show (int list, int entry, const char * filename, set_entry_str_from_field (gtk_bin_get_child ((GtkBin *) widgets.genre), tuple, FIELD_GENRE, updating_enabled); - tmp = uri_to_display (filename); + char * tmp = uri_to_display (filename); gtk_label_set_text ((GtkLabel *) widgets.location, tmp); - g_free (tmp); + str_unref (tmp); set_entry_int_from_field (widgets.year, tuple, FIELD_YEAR, updating_enabled); set_entry_int_from_field (widgets.track, tuple, FIELD_TRACK_NUMBER, updating_enabled); - infowin_label_set_text (widgets.format, tuple_get_str (tuple, FIELD_CODEC, NULL)); - infowin_label_set_text (widgets.quality, tuple_get_str (tuple, FIELD_QUALITY, NULL)); + char * codec_values[CODEC_ITEMS] = { + [CODEC_FORMAT] = tuple_get_str (tuple, FIELD_CODEC), + [CODEC_QUALITY] = tuple_get_str (tuple, FIELD_QUALITY) + }; - if (tuple_get_value_type (tuple, FIELD_BITRATE, NULL) == TUPLE_INT) - infowin_label_set_text (widgets.bitrate, str_printf (_("%d kb/s"), - tuple_get_int (tuple, FIELD_BITRATE, NULL))); - else - infowin_label_set_text (widgets.bitrate, NULL); + if (tuple_get_value_type (tuple, FIELD_BITRATE) == TUPLE_INT) + { + int bitrate = tuple_get_int (tuple, FIELD_BITRATE); + codec_values[CODEC_BITRATE] = str_printf (_("%d kb/s"), bitrate); + } + + for (int row = 0; row < CODEC_ITEMS; row ++) + { + const char * text = codec_values[row] ? codec_values[row] : _("N/A"); + gtk_label_set_text ((GtkLabel *) widgets.codec[row], text); + str_unref (codec_values[row]); + } infowin_display_image (filename); - gtk_window_present ((GtkWindow *) infowin); + /* nothing has been changed yet */ + gtk_widget_set_sensitive (widgets.apply, FALSE); + + audgui_show_unique_window (AUDGUI_INFO_WINDOW, infowin); } EXPORT void audgui_infowin_show (int playlist, int entry) @@ -545,10 +450,8 @@ EXPORT void audgui_infowin_show (int playlist, int entry) if (tuple == NULL) { - char * message = g_strdup_printf (_("No info available for %s.\n"), - filename); + SPRINTF (message, _("No info available for %s.\n"), filename); aud_interface_show_error (message); - g_free (message); goto FREE; } @@ -578,6 +481,5 @@ EXPORT void audgui_infowin_show_current (void) EXPORT void audgui_infowin_hide (void) { - if (infowin) - gtk_widget_destroy (infowin); + audgui_hide_unique_window (AUDGUI_INFO_WINDOW); } diff --git a/src/libaudgui/init.c b/src/libaudgui/init.c index 1a5517f..3128a55 100644 --- a/src/libaudgui/init.c +++ b/src/libaudgui/init.c @@ -1,6 +1,6 @@ /* * init.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,8 +17,12 @@ * the use of this software. */ +#include <stdio.h> +#include <stdlib.h> + #include <audacious/misc.h> #include <audacious/playlist.h> +#include <libaudcore/audstrings.h> #include <libaudcore/hook.h> #include "init.h" @@ -33,8 +37,90 @@ static const char * const audgui_defaults[] = { "remember_jtf_entry", "TRUE", NULL}; +static const char * const window_names[AUDGUI_NUM_UNIQUE_WINDOWS] = { + "about_win", + "equalizer_win", + "filebrowser_win", + NULL, /* infopopup position is not saved */ + "info_win", + "jump_to_time_win", + "jump_to_track_win", + "playlist_export_win", + "playlist_import_win", + "playlist_manager_win", + "queue_manager_win", + "url_opener_win" +}; + AudAPITable * _aud_api_table = NULL; +static GtkWidget * windows[AUDGUI_NUM_UNIQUE_WINDOWS]; + +static bool_t configure_cb (GtkWidget * window, GdkEventConfigure * event, const char * name) +{ + if (gtk_widget_get_visible (window)) + { + int pos[4]; + gtk_window_get_position ((GtkWindow *) window, & pos[0], & pos[1]); + gtk_window_get_size ((GtkWindow *) window, & pos[2], & pos[3]); + + char * str = int_array_to_str (pos, 4); + aud_set_str ("audgui", name, str); + str_unref (str); + } + + return FALSE; +} + +void audgui_show_unique_window (int id, GtkWidget * widget) +{ + g_return_if_fail (id >= 0 && id < AUDGUI_NUM_UNIQUE_WINDOWS); + + if (windows[id]) + gtk_widget_destroy (windows[id]); + + windows[id] = widget; + g_signal_connect (widget, "destroy", (GCallback) gtk_widget_destroyed, & windows[id]); + + if (window_names[id]) + { + char * str = aud_get_str ("audgui", window_names[id]); + int pos[4]; + + if (str_to_int_array (str, pos, 4)) + { + gtk_window_move ((GtkWindow *) widget, pos[0], pos[1]); + gtk_window_set_default_size ((GtkWindow *) widget, pos[2], pos[3]); + } + + g_signal_connect (widget, "configure-event", (GCallback) configure_cb, + (void *) window_names[id]); + + str_unref (str); + } + + gtk_widget_show_all (widget); +} + +bool_t audgui_reshow_unique_window (int id) +{ + g_return_val_if_fail (id >= 0 && id < AUDGUI_NUM_UNIQUE_WINDOWS, FALSE); + + if (! windows[id]) + return FALSE; + + gtk_window_present ((GtkWindow *) windows[id]); + return TRUE; +} + +void audgui_hide_unique_window (int id) +{ + g_return_if_fail (id >= 0 && id < AUDGUI_NUM_UNIQUE_WINDOWS); + + if (windows[id]) + gtk_widget_destroy (windows[id]); +} + static void playlist_set_playing_cb (void * unused, void * unused2) { audgui_pixbuf_uncache (); @@ -46,13 +132,23 @@ static void playlist_position_cb (void * list, void * unused) audgui_pixbuf_uncache (); } -EXPORT void audgui_init (AudAPITable * table) +EXPORT void audgui_init (AudAPITable * table, int version) { + if (version != _AUD_PLUGIN_VERSION) + { + fprintf (stderr, "libaudgui version mismatch\n"); + abort (); + } + _aud_api_table = table; aud_config_set_defaults ("audgui", audgui_defaults); hook_associate ("playlist set playing", playlist_set_playing_cb, NULL); hook_associate ("playlist position", playlist_position_cb, NULL); + +#ifndef _WIN32 + gtk_window_set_default_icon_name ("audacious"); +#endif } EXPORT void audgui_cleanup (void) @@ -60,15 +156,8 @@ EXPORT void audgui_cleanup (void) hook_dissociate ("playlist set playing", playlist_set_playing_cb); hook_dissociate ("playlist position", playlist_position_cb); - audgui_hide_equalizer_window (); - audgui_infopopup_hide (); - audgui_infowin_hide (); - audgui_jump_to_time_cleanup (); - audgui_jump_to_track_hide (); - audgui_pixbuf_uncache (); - audgui_playlist_manager_cleanup (); - audgui_queue_manager_cleanup (); - audgui_url_opener_cleanup (); + for (int id = 0; id < AUDGUI_NUM_UNIQUE_WINDOWS; id ++) + audgui_hide_unique_window (id); _aud_api_table = NULL; } diff --git a/src/libaudgui/init.h b/src/libaudgui/init.h index 1018d0a..32632cf 100644 --- a/src/libaudgui/init.h +++ b/src/libaudgui/init.h @@ -1,6 +1,6 @@ /* * init.h - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,23 +17,37 @@ * the use of this software. */ -#include <audacious/plugin.h> +#ifndef AUDGUI_INIT_H +#define AUDGUI_INIT_H -/* init.c */ -void audgui_init (AudAPITable * table); -void audgui_cleanup (void); +#include <gtk/gtk.h> -/* jump-to-time.c */ -void audgui_jump_to_time_cleanup (void); +#include <audacious/api.h> -/* queue-manager.c */ -void audgui_queue_manager_cleanup (void); +enum { + AUDGUI_ABOUT_WINDOW, + AUDGUI_EQUALIZER_WINDOW, + AUDGUI_FILEBROWSER_WINDOW, + AUDGUI_INFOPOPUP_WINDOW, + AUDGUI_INFO_WINDOW, + AUDGUI_JUMP_TO_TIME_WINDOW, + AUDGUI_JUMP_TO_TRACK_WINDOW, + AUDGUI_PLAYLIST_EXPORT_WINDOW, + AUDGUI_PLAYLIST_IMPORT_WINDOW, + AUDGUI_PLAYLIST_MANAGER_WINDOW, + AUDGUI_QUEUE_MANAGER_WINDOW, + AUDGUI_URL_OPENER_WINDOW, + AUDGUI_NUM_UNIQUE_WINDOWS +}; -/* ui_playlist_manager.c */ -void audgui_playlist_manager_cleanup (void); +void audgui_show_unique_window (int id, GtkWidget * widget); +bool_t audgui_reshow_unique_window (int id); +void audgui_hide_unique_window (int id); -/* url-opener.c */ -void audgui_url_opener_cleanup (void); +void audgui_init (AudAPITable * table, int version); +void audgui_cleanup (void); -/* util.c */ +/* pixbufs.c */ void audgui_pixbuf_uncache (void); + +#endif /* AUDGUI_INIT_H */ diff --git a/src/libaudgui/jump-to-time.c b/src/libaudgui/jump-to-time.c index f551a07..357b422 100644 --- a/src/libaudgui/jump-to-time.c +++ b/src/libaudgui/jump-to-time.c @@ -1,6 +1,6 @@ /* * jump-to-time.c - * Copyright 2012 John Lindgren + * Copyright 2012-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,67 +22,43 @@ #include <audacious/drct.h> #include <audacious/i18n.h> +#include <libaudcore/audstrings.h> #include "init.h" #include "libaudgui.h" +#include "libaudgui-gtk.h" -static GtkWidget * window; - -static void response_cb (GtkWidget * dialog, int response) +static void jump_cb (void * entry) { - if (response == GTK_RESPONSE_ACCEPT) - { - GtkWidget * entry = g_object_get_data ((GObject *) dialog, "entry"); - const char * text = gtk_entry_get_text ((GtkEntry *) entry); - unsigned minutes, seconds; - - if (sscanf (text, "%u:%u", & minutes, & seconds) == 2 && aud_drct_get_playing ()) - aud_drct_seek ((minutes * 60 + seconds) * 1000); - } + const char * text = gtk_entry_get_text ((GtkEntry *) entry); + unsigned minutes, seconds; - gtk_widget_destroy (dialog); + if (sscanf (text, "%u:%u", & minutes, & seconds) == 2 && aud_drct_get_playing ()) + aud_drct_seek ((minutes * 60 + seconds) * 1000); } EXPORT void audgui_jump_to_time (void) { - if (window) - gtk_widget_destroy (window); + if (audgui_reshow_unique_window (AUDGUI_JUMP_TO_TIME_WINDOW)) + return; - window = gtk_dialog_new_with_buttons (_("Jump to Time"), NULL, 0, - GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_JUMP_TO, - GTK_RESPONSE_ACCEPT, NULL); - gtk_widget_set_size_request (window, 200, -1); - gtk_window_set_resizable ((GtkWindow *) window, FALSE); - gtk_dialog_set_default_response ((GtkDialog *) window, GTK_RESPONSE_ACCEPT); + GtkWidget * entry = gtk_entry_new (); + gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE); - GtkWidget * box = gtk_dialog_get_content_area ((GtkDialog *) window); + GtkWidget * button1 = audgui_button_new (_("_Jump"), "go-jump", jump_cb, entry); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL); - GtkWidget * label = gtk_label_new (_("Enter time (minutes:seconds):")); - gtk_misc_set_alignment ((GtkMisc *) label, 0, 0); - gtk_box_pack_start ((GtkBox *) box, label, FALSE, FALSE, 0); + GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_OTHER, + _("Jump to Time"), _("Enter time (minutes:seconds):"), button1, button2); - GtkWidget * entry = gtk_entry_new (); - gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE); - gtk_box_pack_start ((GtkBox *) box, entry, FALSE, FALSE, 0); + audgui_dialog_add_widget (dialog, entry); if (aud_drct_get_playing ()) { - char buf[16]; int time = aud_drct_get_time () / 1000; - snprintf (buf, sizeof buf, "%u:%02u", time / 60, time % 60); + SPRINTF (buf, "%u:%02u", time / 60, time % 60); gtk_entry_set_text ((GtkEntry *) entry, buf); } - g_object_set_data ((GObject *) window, "entry", entry); - - g_signal_connect (window, "response", (GCallback) response_cb, NULL); - g_signal_connect (window, "destroy", (GCallback) gtk_widget_destroyed, & window); - - gtk_widget_show_all (window); -} - -void audgui_jump_to_time_cleanup (void) -{ - if (window) - gtk_widget_destroy (window); + audgui_show_unique_window (AUDGUI_JUMP_TO_TIME_WINDOW, dialog); } diff --git a/src/libaudgui/libaudgui-gtk.h b/src/libaudgui/libaudgui-gtk.h index aee1c2a..7e65a03 100644 --- a/src/libaudgui/libaudgui-gtk.h +++ b/src/libaudgui/libaudgui-gtk.h @@ -1,6 +1,6 @@ /* * libaudgui-gtk.h - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2012 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,33 +24,31 @@ #include <gtk/gtk.h> #include <libaudcore/core.h> -/* effects-menu.c */ -GtkWidget * audgui_create_effects_menu (void); -GtkWidget * audgui_create_vis_menu (void); +typedef void (* AudguiCallback) (void * data); -/* iface-menu.c */ -GtkWidget * audgui_create_iface_menu (void); +/* pixbufs.c */ +GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size); +GdkPixbuf * audgui_pixbuf_fallback (void); +void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size); +GdkPixbuf * audgui_pixbuf_request (const char * filename); +GdkPixbuf * audgui_pixbuf_request_current (void); + +/* scaled-image.c */ +GtkWidget * audgui_scaled_image_new (GdkPixbuf * pixbuf); +void audgui_scaled_image_set (GtkWidget * widget, GdkPixbuf * pixbuf); /* util.c */ int audgui_get_digit_width (GtkWidget * widget); void audgui_get_mouse_coords (GtkWidget * widget, int * x, int * y); -void audgui_hide_on_delete (GtkWidget * widget); -void audgui_hide_on_escape (GtkWidget * widget); void audgui_destroy_on_escape (GtkWidget * widget); void audgui_simple_message (GtkWidget * * widget, GtkMessageType type, const char * title, const char * text); -void audgui_connect_check_box (GtkWidget * box, bool_t * setting); -GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size); -GdkPixbuf * audgui_pixbuf_fallback (void); -void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size); - -/* non-blocking (like the aud_art_request_* functions) */ -GdkPixbuf * audgui_pixbuf_request (const char * filename); -GdkPixbuf * audgui_pixbuf_request_current (void); +GtkWidget * audgui_button_new (const char * text, const char * icon, + AudguiCallback callback, void * data); -/* deprecated */ -GdkPixbuf * audgui_pixbuf_for_current (void) __attribute ((deprecated)); -GdkPixbuf * audgui_pixbuf_for_entry (int playlist, int entry) __attribute ((deprecated)); +GtkWidget * audgui_dialog_new (GtkMessageType type, const char * title, + const char * text, GtkWidget * button1, GtkWidget * button2); +void audgui_dialog_add_widget (GtkWidget * dialog, GtkWidget * widget); #endif diff --git a/src/libaudgui/libaudgui.h b/src/libaudgui/libaudgui.h index 686fe24..db88ed1 100644 --- a/src/libaudgui/libaudgui.h +++ b/src/libaudgui/libaudgui.h @@ -1,7 +1,7 @@ /* * libaudgui.h - * Copyright 2007-2009 Giacomo Lozito and Tomasz Moń - + * Copyright 2007-2013 Giacomo Lozito, Tomasz Moń, and John Lindgren + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * @@ -20,26 +20,20 @@ #ifndef LIBAUDGUI_H #define LIBAUDGUI_H +#include <stdint.h> #include <libaudcore/core.h> -/* macro defines for Audacious stock icons */ -#define AUD_STOCK_AUDACIOUS "aud-audacious" -#define AUD_STOCK_PLAYLIST "aud-playlist" -#define AUD_STOCK_PLUGIN "aud-plugin" -#define AUD_STOCK_QUEUETOGGLE "aud-queuetoggle" - -void audgui_register_stock_icons(void); - void audgui_show_add_url_window(bool_t open); void audgui_jump_to_track(void); void audgui_jump_to_track_hide(void); -void audgui_run_filebrowser(bool_t clear_pl_on_ok); +void audgui_run_filebrowser(bool_t open); void audgui_hide_filebrowser(void); /* about.c */ void audgui_show_about_window (void); +void audgui_hide_about_window (void); /* confirm.c */ void audgui_confirm_playlist_delete (int playlist); @@ -78,6 +72,6 @@ void audgui_urilist_insert (int playlist, int position, const char * list); char * audgui_urilist_create_from_selected (int playlist); /* util.c */ -void audgui_set_default_icon (void); +void audgui_format_time (char * buf, int bufsize, int64_t milliseconds); #endif /* LIBAUDGUI_H */ diff --git a/src/libaudgui/list.c b/src/libaudgui/list.c index 751a7d5..0fcc269 100644 --- a/src/libaudgui/list.c +++ b/src/libaudgui/list.c @@ -1,6 +1,6 @@ /* * list.c - * Copyright 2011-2012 John Lindgren and Michał Lipski + * Copyright 2011-2013 John Lindgren and Michał Lipski * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: diff --git a/src/libaudgui/menu.c b/src/libaudgui/menu.c new file mode 100644 index 0000000..6fe882f --- /dev/null +++ b/src/libaudgui/menu.c @@ -0,0 +1,134 @@ +/* + * menu.c + * Copyright 2011-2014 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include "menu.h" + +#include <audacious/i18n.h> +#include <audacious/misc.h> +#include <libaudcore/hook.h> + +/* we still use GtkImageMenuItem until there is a good alternative */ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +static GtkWidget * image_menu_item_new (const char * text, const char * icon) +{ + GtkWidget * widget = gtk_image_menu_item_new_with_mnemonic (text); + + if (icon) + { + GtkWidget * image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); + gtk_image_menu_item_set_image ((GtkImageMenuItem *) widget, image); + } + + return widget; +} + +#pragma GCC diagnostic pop + +static void toggled_cb (GtkCheckMenuItem * check, const AudguiMenuItem * item) +{ + bool_t on = gtk_check_menu_item_get_active (check); + + if (aud_get_bool (item->csect, item->cname) == on) + return; + + aud_set_bool (item->csect, item->cname, on); + + if (item->func) + item->func (); +} + +static void hook_cb (void * data, GtkWidget * check) +{ + const AudguiMenuItem * item = g_object_get_data ((GObject *) check, "item"); + gtk_check_menu_item_set_active ((GtkCheckMenuItem *) check, aud_get_bool + (item->csect, item->cname)); +} + +static void unhook_cb (GtkCheckMenuItem * check, const AudguiMenuItem * item) +{ + hook_dissociate_full (item->hook, (HookFunction) hook_cb, check); +} + +EXPORT GtkWidget * audgui_menu_item_new_with_domain + (const AudguiMenuItem * item, GtkAccelGroup * accel, const char * domain) +{ + const char * name = domain ? dgettext (domain, item->name) : item->name; + GtkWidget * widget = NULL; + + if (name && item->func && ! item->cname) /* normal widget */ + { + widget = image_menu_item_new (name, item->icon); + g_signal_connect (widget, "activate", item->func, NULL); + } + else if (name && item->cname) /* toggle widget */ + { + widget = gtk_check_menu_item_new_with_mnemonic (name); + gtk_check_menu_item_set_active ((GtkCheckMenuItem *) widget, + aud_get_bool (item->csect, item->cname)); + g_signal_connect (widget, "toggled", (GCallback) toggled_cb, (void *) item); + + if (item->hook) + { + g_object_set_data ((GObject *) widget, "item", (void *) item); + hook_associate (item->hook, (HookFunction) hook_cb, widget); + g_signal_connect (widget, "destroy", (GCallback) unhook_cb, (void *) item); + } + } + else if (name && (item->items || item->get_sub)) /* submenu */ + { + widget = image_menu_item_new (name, item->icon); + + GtkWidget * sub; + + if (item->get_sub) + sub = item->get_sub (); + else + { + sub = gtk_menu_new (); + audgui_menu_init_with_domain (sub, item->items, item->n_items, accel, domain); + } + + gtk_menu_item_set_submenu ((GtkMenuItem *) widget, sub); + } + else if (item->sep) /* separator */ + widget = gtk_separator_menu_item_new (); + + if (widget && accel && item->key) + gtk_widget_add_accelerator (widget, "activate", accel, item->key, + item->mod, GTK_ACCEL_VISIBLE); + + return widget; +} + +EXPORT void audgui_menu_init_with_domain (GtkWidget * shell, + const AudguiMenuItem * items, int n_items, GtkAccelGroup * accel, + const char * domain) +{ + for (int i = 0; i < n_items; i ++) + { + GtkWidget * widget = audgui_menu_item_new_with_domain (& items[i], accel, domain); + if (! widget) + continue; + + gtk_widget_show (widget); + gtk_menu_shell_append ((GtkMenuShell *) shell, widget); + } +} diff --git a/src/libaudgui/menu.h b/src/libaudgui/menu.h new file mode 100644 index 0000000..fc27ae5 --- /dev/null +++ b/src/libaudgui/menu.h @@ -0,0 +1,62 @@ +/* + * menu.h + * Copyright 2011-2014 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#ifndef AUDGUI_MENU_H +#define AUDGUI_MENU_H + +#include <gtk/gtk.h> +#include <libaudcore/core.h> + +typedef struct _AudguiMenuItem { + const char * name; + const char * icon; + unsigned key; + GdkModifierType mod; + + /* for normal items */ + void (* func) (void); + + /* for toggle items */ + const char * csect; + const char * cname; + const char * hook; + + /* for submenus */ + const struct _AudguiMenuItem * items; + int n_items; + + /* for custom submenus */ + GtkWidget * (* get_sub) (void); + + /* for separators */ + bool_t sep; +} AudguiMenuItem; + +/* use NULL for domain to skip translation */ +GtkWidget * audgui_menu_item_new_with_domain (const AudguiMenuItem * item, + GtkAccelGroup * accel, const char * domain); + +void audgui_menu_init_with_domain (GtkWidget * shell, + const AudguiMenuItem * items, int n_items, GtkAccelGroup * accel, + const char * domain); + +#define audgui_menu_item_new(i, a) audgui_menu_item_new_with_domain (i, a, PACKAGE) +#define audgui_menu_init(s, i, n, a) audgui_menu_init_with_domain (s, i, n, a, PACKAGE) + +#endif /* AUDGUI_MENU_H */ diff --git a/src/libaudgui/pixbufs.c b/src/libaudgui/pixbufs.c new file mode 100644 index 0000000..550dce0 --- /dev/null +++ b/src/libaudgui/pixbufs.c @@ -0,0 +1,141 @@ +/* + * pixbufs.c + * Copyright 2010-2012 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include <gdk-pixbuf/gdk-pixbuf.h> + +#include <audacious/debug.h> +#include <audacious/misc.h> +#include <audacious/playlist.h> +#include <libaudcore/audstrings.h> + +#include "libaudgui-gtk.h" + +static GdkPixbuf * current_pixbuf; + +EXPORT GdkPixbuf * audgui_pixbuf_fallback (void) +{ + static GdkPixbuf * fallback = NULL; + + if (! fallback) + { + const char * data_dir = aud_get_path (AUD_PATH_DATA_DIR); + SCONCAT2 (path, data_dir, "/images/album.png"); + fallback = gdk_pixbuf_new_from_file (path, NULL); + } + + if (fallback) + g_object_ref ((GObject *) fallback); + + return fallback; +} + +void audgui_pixbuf_uncache (void) +{ + if (current_pixbuf) + { + g_object_unref ((GObject *) current_pixbuf); + current_pixbuf = NULL; + } +} + +EXPORT void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size) +{ + int width = gdk_pixbuf_get_width (* pixbuf); + int height = gdk_pixbuf_get_height (* pixbuf); + + if (width <= size && height <= size) + return; + + if (width > height) + { + height = size * height / width; + width = size; + } + else + { + width = size * width / height; + height = size; + } + + if (width < 1) + width = 1; + if (height < 1) + height = 1; + + GdkPixbuf * pixbuf2 = gdk_pixbuf_scale_simple (* pixbuf, width, height, GDK_INTERP_BILINEAR); + g_object_unref (* pixbuf); + * pixbuf = pixbuf2; +} + +EXPORT GdkPixbuf * audgui_pixbuf_request (const char * filename) +{ + const void * data; + int64_t size; + + aud_art_request_data (filename, & data, & size); + if (! data) + return NULL; + + GdkPixbuf * p = audgui_pixbuf_from_data (data, size); + + aud_art_unref (filename); + return p; +} + +EXPORT GdkPixbuf * audgui_pixbuf_request_current (void) +{ + if (! current_pixbuf) + { + int list = aud_playlist_get_playing (); + int entry = aud_playlist_get_position (list); + if (entry < 0) + return NULL; + + char * filename = aud_playlist_entry_get_filename (list, entry); + current_pixbuf = audgui_pixbuf_request (filename); + str_unref (filename); + } + + if (current_pixbuf) + g_object_ref ((GObject *) current_pixbuf); + + return current_pixbuf; +} + +EXPORT GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size) +{ + GdkPixbuf * pixbuf = NULL; + GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); + GError * error = NULL; + + if (gdk_pixbuf_loader_write (loader, data, size, & error) && + gdk_pixbuf_loader_close (loader, & error)) + { + if ((pixbuf = gdk_pixbuf_loader_get_pixbuf (loader))) + g_object_ref (pixbuf); + } + else + { + AUDDBG ("error while loading pixbuf: %s\n", error->message); + g_error_free (error); + } + + g_object_unref (loader); + return pixbuf; +} diff --git a/src/libaudgui/playlists.c b/src/libaudgui/playlists.c index 5a7ef1d..fa3fa05 100644 --- a/src/libaudgui/playlists.c +++ b/src/libaudgui/playlists.c @@ -1,6 +1,6 @@ /* * playlists.c - * Copyright 2011 John Lindgren + * Copyright 2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,87 +22,163 @@ #include <audacious/i18n.h> #include <audacious/misc.h> #include <audacious/playlist.h> +#include <libaudcore/audstrings.h> #include <libaudcore/vfs.h> +#include "init.h" #include "libaudgui.h" +#include "libaudgui-gtk.h" -static char * select_file (bool_t save, const char * default_filename) +typedef struct { - GtkWidget * dialog = gtk_file_chooser_dialog_new (save ? - _("Export Playlist") : _("Import Playlist"), NULL, save ? - GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN, NULL, NULL); + bool_t save; + int list_id; + char * filename; + GtkWidget * selector, * confirm; +} +ImportExportJob; - if (default_filename) - gtk_file_chooser_set_uri ((GtkFileChooser *) dialog, default_filename); +/* "destroy" callback; do not call directly */ +static void cleanup_job (void * data) +{ + ImportExportJob * job = data; - gtk_dialog_add_button ((GtkDialog *) dialog, GTK_STOCK_CANCEL, - GTK_RESPONSE_REJECT); - gtk_dialog_add_button ((GtkDialog *) dialog, save ? GTK_STOCK_SAVE : - GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT); + char * folder = gtk_file_chooser_get_current_folder_uri ((GtkFileChooser *) job->selector); - gtk_dialog_set_default_response ((GtkDialog *) dialog, GTK_RESPONSE_ACCEPT); + if (folder) + aud_set_str ("audgui", "playlist_path", folder); - char * path = aud_get_string ("audgui", "playlist_path"); - if (path[0]) - gtk_file_chooser_set_current_folder_uri ((GtkFileChooser *) dialog, path); - g_free (path); + g_free (folder); - char * filename = NULL; - if (gtk_dialog_run ((GtkDialog *) dialog) == GTK_RESPONSE_ACCEPT) - filename = gtk_file_chooser_get_uri ((GtkFileChooser *) dialog); + if (job->confirm) + gtk_widget_destroy (job->confirm); - path = gtk_file_chooser_get_current_folder_uri ((GtkFileChooser *) dialog); - aud_set_string ("audgui", "playlist_path", path); - g_free (path); + g_free (job->filename); + g_slice_free (ImportExportJob, job); +} - gtk_widget_destroy (dialog); - return filename; +static void finish_job (void * data) +{ + ImportExportJob * job = data; + int list = aud_playlist_by_unique_id (job->list_id); + + if (list >= 0) + { + aud_playlist_set_filename (list, job->filename); + + if (job->save) + aud_playlist_save (list, job->filename); + else + { + aud_playlist_entry_delete (list, 0, aud_playlist_entry_count (list)); + aud_playlist_entry_insert (list, 0, job->filename, NULL, FALSE); + } + } + + gtk_widget_destroy (job->selector); } -static bool_t confirm_overwrite (const char * filename) +static void confirm_overwrite (ImportExportJob * job) { - GtkWidget * dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_QUESTION, - GTK_BUTTONS_YES_NO, _("Overwrite %s?"), filename); + if (job->confirm) + gtk_widget_destroy (job->confirm); + + SPRINTF (message, _("Overwrite %s?"), job->filename); + + GtkWidget * button1 = audgui_button_new (_("_Overwrite"), "document-save", finish_job, job); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL); + + job->confirm = audgui_dialog_new (GTK_MESSAGE_QUESTION, + _("Confirm Overwrite"), message, button1, button2); - int result = gtk_dialog_run ((GtkDialog *) dialog); - gtk_widget_destroy (dialog); - return (result == GTK_RESPONSE_YES); + g_signal_connect (job->confirm, "destroy", (GCallback) gtk_widget_destroyed, & job->confirm); + + gtk_widget_show_all (job->confirm); } -EXPORT void audgui_import_playlist (void) +static void check_overwrite (void * data) { - int list = aud_playlist_get_active (); - int id = aud_playlist_get_unique_id (list); + ImportExportJob * job = data; - char * filename = select_file (FALSE, NULL); - if (! filename) - return; + job->filename = gtk_file_chooser_get_uri ((GtkFileChooser *) job->selector); - if ((list = aud_playlist_by_unique_id (id)) < 0) + if (! job->filename) return; - aud_playlist_entry_delete (list, 0, aud_playlist_entry_count (list)); - aud_playlist_entry_insert (list, 0, filename, NULL, FALSE); - aud_playlist_set_filename (list, filename); - g_free (filename); + if (job->save && vfs_file_test (job->filename, G_FILE_TEST_EXISTS)) + confirm_overwrite (job); + else + finish_job (data); } -EXPORT void audgui_export_playlist (void) +static void create_selector (ImportExportJob * job, const char * filename, const char * folder) +{ + const char * title, * verb, * icon; + GtkFileChooserAction action; + + if (job->save) + { + title = _("Export Playlist"); + verb = _("_Export"); + icon = "document-save"; + action = GTK_FILE_CHOOSER_ACTION_SAVE; + } + else + { + title = _("Import Playlist"); + verb = _("_Import"); + icon = "document-open"; + action = GTK_FILE_CHOOSER_ACTION_OPEN; + } + + job->selector = gtk_file_chooser_dialog_new (title, NULL, action, NULL, NULL); + + if (filename) + gtk_file_chooser_set_uri ((GtkFileChooser *) job->selector, filename); + else if (folder) + gtk_file_chooser_set_current_folder_uri ((GtkFileChooser *) job->selector, folder); + + GtkWidget * button1 = audgui_button_new (verb, icon, check_overwrite, job); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", + (AudguiCallback) gtk_widget_destroy, job->selector); + + gtk_dialog_add_action_widget ((GtkDialog *) job->selector, button2, GTK_RESPONSE_NONE); + gtk_dialog_add_action_widget ((GtkDialog *) job->selector, button1, GTK_RESPONSE_NONE); + + gtk_widget_set_can_default (button1, TRUE); + gtk_widget_grab_default (button1); + + g_signal_connect_swapped (job->selector, "destroy", (GCallback) cleanup_job, job); + + gtk_widget_show_all (job->selector); +} + +static GtkWidget * start_job (bool_t save) { int list = aud_playlist_get_active (); - int id = aud_playlist_get_unique_id (list); - char * def = aud_playlist_get_filename (list); - char * filename = select_file (TRUE, def); - str_unref (def); + char * filename = aud_playlist_get_filename (list); + char * folder = aud_get_str ("audgui", "playlist_path"); - if (! filename || (vfs_file_test (filename, G_FILE_TEST_EXISTS) && - ! confirm_overwrite (filename))) - return; + ImportExportJob * job = g_slice_new0 (ImportExportJob); - if ((list = aud_playlist_by_unique_id (id)) < 0) - return; + job->save = save; + job->list_id = aud_playlist_get_unique_id (list); + + create_selector (job, filename, folder[0] ? folder : NULL); + + str_unref (filename); + str_unref (folder); + + return job->selector; +} - aud_playlist_save (list, filename); - g_free (filename); +EXPORT void audgui_import_playlist (void) +{ + audgui_show_unique_window (AUDGUI_PLAYLIST_IMPORT_WINDOW, start_job (FALSE)); +} + +EXPORT void audgui_export_playlist (void) +{ + audgui_show_unique_window (AUDGUI_PLAYLIST_EXPORT_WINDOW, start_job (TRUE)); } diff --git a/src/libaudgui/queue-manager.c b/src/libaudgui/queue-manager.c index 60e66c6..8718365 100644 --- a/src/libaudgui/queue-manager.c +++ b/src/libaudgui/queue-manager.c @@ -1,6 +1,6 @@ /* * queue-manager.c - * Copyright 2011 John Lindgren + * Copyright 2011-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,17 +26,13 @@ #include "init.h" #include "libaudgui.h" +#include "libaudgui-gtk.h" #include "list.h" enum { COLUMN_ENTRY, COLUMN_TITLE}; -#define RESPONSE_REMOVE 1 - -static GtkWidget * qm_win; -static GtkWidget * qm_list; - static void get_value (void * user, int row, int column, GValue * value) { int list = aud_playlist_get_active (); @@ -110,7 +106,7 @@ static const AudguiListCallbacks callbacks = { .select_all = select_all, .shift_rows = shift_rows}; -static void remove_selected (void) +static void remove_selected (void * unused) { int list = aud_playlist_get_active (); int count = aud_playlist_queue_count (list); @@ -132,6 +128,8 @@ static void remove_selected (void) static void update_hook (void * data, void * user) { + GtkWidget * qm_list = user; + int oldrows = audgui_list_row_count (qm_list); int newrows = aud_playlist_queue_count (aud_playlist_get_active ()); int focus = audgui_list_get_focus (qm_list); @@ -152,9 +150,6 @@ static void destroy_cb (void) { hook_dissociate ("playlist activate", update_hook); hook_dissociate ("playlist update", update_hook); - - qm_win = NULL; - qm_list = NULL; } static bool_t keypress_cb (GtkWidget * widget, GdkEventKey * event) @@ -162,39 +157,19 @@ static bool_t keypress_cb (GtkWidget * widget, GdkEventKey * event) if (event->keyval == GDK_KEY_A && (event->state & GDK_CONTROL_MASK)) select_all (NULL, TRUE); else if (event->keyval == GDK_KEY_Delete) - remove_selected (); + remove_selected (NULL); else if (event->keyval == GDK_KEY_Escape) - gtk_widget_destroy (qm_win); + gtk_widget_destroy (widget); else return FALSE; return TRUE; } -static void response_cb (GtkDialog * dialog, int response) -{ - switch (response) - { - case RESPONSE_REMOVE:; - remove_selected (); - break; - case GTK_RESPONSE_CLOSE: - gtk_widget_destroy (qm_win); - break; - } -} - -EXPORT void audgui_queue_manager_show (void) +static GtkWidget * create_queue_manager (void) { - if (qm_win) - { - gtk_window_present ((GtkWindow *) qm_win); - return; - } - - qm_win = gtk_dialog_new_with_buttons (_("Queue Manager"), NULL, 0, - GTK_STOCK_REMOVE, RESPONSE_REMOVE, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, - NULL); + GtkWidget * qm_win = gtk_dialog_new (); + gtk_window_set_title ((GtkWindow *) qm_win, _("Queue Manager")); gtk_window_set_default_size ((GtkWindow *) qm_win, 400, 250); GtkWidget * vbox = gtk_dialog_get_content_area ((GtkDialog *) qm_win); @@ -204,24 +179,30 @@ EXPORT void audgui_queue_manager_show (void) gtk_box_pack_start ((GtkBox *) vbox, scrolled, TRUE, TRUE, 0); int count = aud_playlist_queue_count (aud_playlist_get_active ()); - qm_list = audgui_list_new (& callbacks, NULL, count); + GtkWidget * qm_list = audgui_list_new (& callbacks, NULL, count); gtk_tree_view_set_headers_visible ((GtkTreeView *) qm_list, FALSE); audgui_list_add_column (qm_list, NULL, 0, G_TYPE_INT, 7); audgui_list_add_column (qm_list, NULL, 1, G_TYPE_STRING, -1); gtk_container_add ((GtkContainer *) scrolled, qm_list); - hook_associate ("playlist activate", update_hook, NULL); - hook_associate ("playlist update", update_hook, NULL); + GtkWidget * button1 = audgui_button_new (_("_Unqueue"), "list-remove", remove_selected, NULL); + GtkWidget * button2 = audgui_button_new (_("_Close"), "window-close", + (AudguiCallback) gtk_widget_destroy, qm_win); + + gtk_dialog_add_action_widget ((GtkDialog *) qm_win, button1, GTK_RESPONSE_NONE); + gtk_dialog_add_action_widget ((GtkDialog *) qm_win, button2, GTK_RESPONSE_NONE); + + hook_associate ("playlist activate", update_hook, qm_list); + hook_associate ("playlist update", update_hook, qm_list); g_signal_connect (qm_win, "destroy", (GCallback) destroy_cb, NULL); g_signal_connect (qm_win, "key-press-event", (GCallback) keypress_cb, NULL); - g_signal_connect (qm_win, "response", (GCallback) response_cb, NULL); - gtk_widget_show_all (qm_win); + return qm_win; } -void audgui_queue_manager_cleanup (void) +EXPORT void audgui_queue_manager_show (void) { - if (qm_win) - gtk_widget_destroy (qm_win); + if (! audgui_reshow_unique_window (AUDGUI_QUEUE_MANAGER_WINDOW)) + audgui_show_unique_window (AUDGUI_QUEUE_MANAGER_WINDOW, create_queue_manager ()); } diff --git a/src/libaudgui/scaled-image.c b/src/libaudgui/scaled-image.c new file mode 100644 index 0000000..70adcce --- /dev/null +++ b/src/libaudgui/scaled-image.c @@ -0,0 +1,114 @@ +/* + * scaled-image.c + * Copyright 2012-2013 John Lindgren + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions, and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions, and the following disclaimer in the documentation + * provided with the distribution. + * + * This software is provided "as is" and without any warranty, express or + * implied. In no event shall the authors be liable for any damages arising from + * the use of this software. + */ + +#include <libaudgui/libaudgui-gtk.h> + +static GdkPixbuf * get_scaled (GtkWidget * widget, int maxwidth, int maxheight) +{ + GdkPixbuf * unscaled = g_object_get_data ((GObject *) widget, "pixbuf-unscaled"); + + if (! unscaled) + return NULL; + + int width = gdk_pixbuf_get_width (unscaled); + int height = gdk_pixbuf_get_height (unscaled); + + if (width > maxwidth || height > maxheight) + { + if (width * maxheight > height * maxwidth) + { + height = height * maxwidth / width; + width = maxwidth; + } + else + { + width = width * maxheight / height; + height = maxheight; + } + } + + GdkPixbuf * scaled = g_object_get_data ((GObject *) widget, "pixbuf-scaled"); + + if (scaled) + { + if (gdk_pixbuf_get_width (scaled) == width && gdk_pixbuf_get_height (scaled) == height) + return scaled; + + g_object_unref (scaled); + } + + scaled = gdk_pixbuf_scale_simple (unscaled, width, height, GDK_INTERP_BILINEAR); + g_object_set_data ((GObject *) widget, "pixbuf-scaled", scaled); + + return scaled; +} + +static bool_t draw_cb (GtkWidget * widget, cairo_t * cr) +{ + GdkRectangle rect; + gtk_widget_get_allocation (widget, & rect); + + GdkPixbuf * scaled = get_scaled (widget, rect.width, rect.height); + + if (scaled) + { + int x = (rect.width - gdk_pixbuf_get_width (scaled)) / 2; + int y = (rect.height - gdk_pixbuf_get_height (scaled)) / 2; + gdk_cairo_set_source_pixbuf (cr, scaled, x, y); + cairo_paint (cr); + } + + return TRUE; +} + +EXPORT void audgui_scaled_image_set (GtkWidget * widget, GdkPixbuf * pixbuf) +{ + GdkPixbuf * old; + + if ((old = g_object_get_data ((GObject *) widget, "pixbuf-unscaled"))) + g_object_unref (old); + if ((old = g_object_get_data ((GObject *) widget, "pixbuf-scaled"))) + g_object_unref (old); + + if (pixbuf) + g_object_ref (pixbuf); + + g_object_set_data ((GObject *) widget, "pixbuf-unscaled", pixbuf); + g_object_set_data ((GObject *) widget, "pixbuf-scaled", NULL); + + if (! gtk_widget_in_destruction (widget)) + gtk_widget_queue_draw (widget); +} + +static void destroy_cb (GtkWidget * widget) +{ + audgui_scaled_image_set (widget, NULL); +} + +EXPORT GtkWidget * audgui_scaled_image_new (GdkPixbuf * pixbuf) +{ + GtkWidget * widget = gtk_drawing_area_new (); + + g_signal_connect (widget, "draw", (GCallback) draw_cb, NULL); + g_signal_connect (widget, "destroy", (GCallback) destroy_cb, NULL); + + audgui_scaled_image_set (widget, pixbuf); + + return widget; +} diff --git a/src/libaudgui/ui_fileopener.c b/src/libaudgui/ui_fileopener.c index eb1eea0..ae0ad1f 100644 --- a/src/libaudgui/ui_fileopener.c +++ b/src/libaudgui/ui_fileopener.c @@ -1,6 +1,6 @@ /* * ui_fileopener.c - * Copyright 2007-2011 Michael Färber and John Lindgren + * Copyright 2007-2013 Michael Färber and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -23,175 +23,133 @@ #include <audacious/drct.h> #include <audacious/misc.h> +#include "init.h" #include "libaudgui.h" #include "libaudgui-gtk.h" -static void filebrowser_add_files (GtkFileChooser * browser, GSList * files, - bool_t play) +static Index * get_files (GtkWidget * chooser) { - Index * list = index_new (); + Index * index = index_new (); + GSList * list = gtk_file_chooser_get_uris ((GtkFileChooser *) chooser); - for (GSList * node = files; node; node = node->next) - index_append (list, str_get (node->data)); + for (GSList * node = list; node; node = node->next) + index_insert (index, -1, str_get (node->data)); - if (play) - aud_drct_pl_open_list (list); - else - aud_drct_pl_add_list (list, -1); - - char * path = gtk_file_chooser_get_current_folder (browser); - if (path) - { - aud_set_string ("audgui", "filesel_path", path); - g_free (path); - } + g_slist_free_full (list, g_free); + return index; } -static void -action_button_cb(GtkWidget *widget, gpointer data) +static void open_cb (void * data) { - GtkWidget *window = g_object_get_data(data, "window"); - GtkWidget *chooser = g_object_get_data(data, "chooser"); - GtkWidget *toggle = g_object_get_data(data, "toggle-button"); - - GSList * files = gtk_file_chooser_get_uris ((GtkFileChooser *) chooser); - - bool_t play = GPOINTER_TO_INT (g_object_get_data (data, "play-button")); - filebrowser_add_files ((GtkFileChooser *) chooser, files, play); + GtkWidget * chooser = data; + Index * files = get_files (chooser); + bool_t open = GPOINTER_TO_INT (g_object_get_data ((GObject *) chooser, "do-open")); - g_slist_foreach(files, (GFunc) g_free, NULL); - g_slist_free(files); + if (open) + aud_drct_pl_open_list (files); + else + aud_drct_pl_add_list (files, -1); + GtkWidget * toggle = g_object_get_data ((GObject *) chooser, "toggle-button"); if (gtk_toggle_button_get_active ((GtkToggleButton *) toggle)) - gtk_widget_destroy (window); + audgui_hide_filebrowser (); } -static void toggled_cb (GtkToggleButton * toggle, void * option) +static void destroy_cb (GtkWidget * chooser) { - aud_set_bool ("audgui", (const char *) option, gtk_toggle_button_get_active (toggle)); + char * path = gtk_file_chooser_get_current_folder ((GtkFileChooser *) chooser); + if (path) + { + aud_set_str ("audgui", "filesel_path", path); + g_free (path); + } } -static void -close_button_cb(GtkWidget *widget, gpointer data) +static void toggled_cb (GtkToggleButton * toggle, void * option) { - gtk_widget_destroy(GTK_WIDGET(data)); + aud_set_bool ("audgui", (const char *) option, gtk_toggle_button_get_active (toggle)); } -static void -run_filebrowser_gtk2style(bool_t play_button, bool_t show) +static GtkWidget * create_filebrowser (bool_t open) { - static GtkWidget *window = NULL; - GtkWidget *vbox, *hbox, *bbox; - GtkWidget *chooser; - GtkWidget *action_button, *close_button; - GtkWidget *toggle; - char *window_title, *toggle_text; - gpointer action_stock, storage; - - if (!show) { - if (window){ - gtk_widget_hide(window); - return; - } - else - return; + const char * window_title, * verb, * icon, * toggle_text, * option; + + if (open) + { + window_title = _("Open Files"); + verb = _("_Open"); + icon = "document-open"; + toggle_text = _("Close _dialog on open"); + option = "close_dialog_open"; } - else { - if (window) { - gtk_window_present(GTK_WINDOW(window)); /* raise filebrowser */ - return; - } + else + { + window_title = _("Add Files"); + verb = _("_Add"); + icon = "list-add"; + toggle_text = _("Close _dialog on add"); + option = "close_dialog_add"; } - window_title = play_button ? _("Open Files") : _("Add Files"); - toggle_text = play_button ? - _("Close dialog on Open") : _("Close dialog on Add"); - action_stock = play_button ? GTK_STOCK_OPEN : GTK_STOCK_ADD; - - window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + GtkWidget * window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_type_hint (GTK_WINDOW (window), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_title(GTK_WINDOW(window), window_title); gtk_window_set_default_size(GTK_WINDOW(window), 700, 450); - gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_container_set_border_width(GTK_CONTAINER(window), 10); - vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + GtkWidget * vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add(GTK_CONTAINER(window), vbox); - chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN); + GtkWidget * chooser = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN); gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(chooser), TRUE); - char * path = aud_get_string ("audgui", "filesel_path"); + char * path = aud_get_str ("audgui", "filesel_path"); if (path[0]) gtk_file_chooser_set_current_folder ((GtkFileChooser *) chooser, path); - g_free (path); + str_unref (path); gtk_box_pack_start(GTK_BOX(vbox), chooser, TRUE, TRUE, 3); - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); + GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0); gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); - const char * option = play_button ? "close_dialog_open" : "close_dialog_add"; - toggle = gtk_check_button_new_with_label(toggle_text); - gtk_toggle_button_set_active ((GtkToggleButton *) toggle, aud_get_bool - ("audgui", option)); + GtkWidget * toggle = gtk_check_button_new_with_mnemonic (toggle_text); + gtk_toggle_button_set_active ((GtkToggleButton *) toggle, aud_get_bool ("audgui", option)); g_signal_connect (toggle, "toggled", (GCallback) toggled_cb, (void *) option); gtk_box_pack_start(GTK_BOX(hbox), toggle, TRUE, TRUE, 3); - bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); + GtkWidget * bbox = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL); gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END); gtk_box_set_spacing(GTK_BOX(bbox), 6); gtk_box_pack_end(GTK_BOX(hbox), bbox, TRUE, TRUE, 3); - close_button = gtk_button_new_from_stock(GTK_STOCK_CLOSE); - action_button = gtk_button_new_from_stock(action_stock); + GtkWidget * action_button = audgui_button_new (verb, icon, open_cb, chooser); + GtkWidget * close_button = audgui_button_new (_("_Close"), "window-close", + (AudguiCallback) audgui_hide_filebrowser, NULL); + gtk_container_add(GTK_CONTAINER(bbox), close_button); gtk_container_add(GTK_CONTAINER(bbox), action_button); gtk_widget_set_can_default (action_button, TRUE); gtk_widget_grab_default (action_button); - /* this storage object holds several other objects which are used in the - * callback functions - */ - storage = g_object_new(G_TYPE_OBJECT, NULL); - g_object_set_data(storage, "window", window); - g_object_set_data(storage, "chooser", chooser); - g_object_set_data(storage, "toggle-button", toggle); - g_object_set_data(storage, "play-button", GINT_TO_POINTER(play_button)); - - g_signal_connect(chooser, "file-activated", - G_CALLBACK(action_button_cb), storage); - g_signal_connect(action_button, "clicked", - G_CALLBACK(action_button_cb), storage); - g_signal_connect(close_button, "clicked", - G_CALLBACK(close_button_cb), window); - g_signal_connect(window, "destroy", - G_CALLBACK(gtk_widget_destroyed), &window); + g_object_set_data ((GObject *) chooser, "toggle-button", toggle); + g_object_set_data ((GObject *) chooser, "do-open", GINT_TO_POINTER (open)); + + g_signal_connect (chooser, "file-activated", (GCallback) open_cb, NULL); + g_signal_connect (chooser, "destroy", (GCallback) destroy_cb, NULL); audgui_destroy_on_escape (window); - gtk_widget_show_all (window); + + return window; } -/* - * run_filebrowser(bool_t play_button) - * - * Inputs: - * - bool_t play_button - * TRUE - open files - * FALSE - add files - * - * Outputs: - * - none - */ -EXPORT void -audgui_run_filebrowser(bool_t play_button) +EXPORT void audgui_run_filebrowser (bool_t open) { - run_filebrowser_gtk2style(play_button, TRUE); + audgui_show_unique_window (AUDGUI_FILEBROWSER_WINDOW, create_filebrowser (open)); } -EXPORT void -audgui_hide_filebrowser(void) +EXPORT void audgui_hide_filebrowser (void) { - run_filebrowser_gtk2style(FALSE, FALSE); + audgui_hide_unique_window (AUDGUI_FILEBROWSER_WINDOW); } diff --git a/src/libaudgui/ui_jumptotrack.c b/src/libaudgui/ui_jumptotrack.c index 8898843..83addd4 100644 --- a/src/libaudgui/ui_jumptotrack.c +++ b/src/libaudgui/ui_jumptotrack.c @@ -1,6 +1,6 @@ /* * ui_jumptotrack.c - * Copyright 2007-2011 Yoshiki Yazawa and John Lindgren + * Copyright 2007-2012 Yoshiki Yazawa and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -26,21 +26,21 @@ #include <audacious/playlist.h> #include <libaudcore/hook.h> +#include "init.h" #include "libaudgui.h" +#include "libaudgui-gtk.h" #include "list.h" #include "ui_jumptotrack_cache.h" static void update_cb (void * data, void * user); static void activate_cb (void * data, void * user); -static GtkWidget *jump_to_track_win = NULL; static JumpToTrackCache* cache = NULL; static const GArray * search_matches; static GtkWidget * treeview, * filter_entry, * queue_button; static bool_t watching = FALSE; -EXPORT void -audgui_jump_to_track_hide(void) +static void destroy_cb (void) { if (watching) { @@ -49,9 +49,6 @@ audgui_jump_to_track_hide(void) watching = FALSE; } - if (jump_to_track_win != NULL) - gtk_widget_hide (jump_to_track_win); - if (cache != NULL) { ui_jump_to_track_cache_free (cache); @@ -80,7 +77,7 @@ static int get_selected_entry (void) return g_array_index (search_matches, int, row); } -static void do_jump (void) +static void do_jump (void * unused) { int entry = get_selected_entry (); if (entry < 0) @@ -115,7 +112,7 @@ static void update_queue_button (int entry) } } -static void do_queue (void) +static void do_queue (void * unused) { int playlist = aud_playlist_get_active (); int entry = get_selected_entry (); @@ -169,13 +166,6 @@ static void fill_list (void) } } -static void clear_cb (GtkWidget * widget) -{ - g_return_if_fail (filter_entry); - gtk_entry_set_text ((GtkEntry *) filter_entry, ""); - gtk_widget_grab_focus (filter_entry); -} - static void update_cb (void * data, void * user) { g_return_if_fail (treeview); @@ -220,12 +210,6 @@ static void toggle_button_cb (GtkToggleButton * toggle, const char * setting) aud_set_bool ("audgui", setting, gtk_toggle_button_get_active (toggle)); } -static bool_t delete_cb (void) -{ - audgui_jump_to_track_hide (); - return TRUE; -} - static void list_get_value (void * user, int row, int column, GValue * value) { g_return_if_fail (search_matches); @@ -252,16 +236,16 @@ static void list_get_value (void * user, int row, int column, GValue * value) static const AudguiListCallbacks callbacks = { .get_value = list_get_value}; -static void create_window (void) +static GtkWidget * create_window (void) { - jump_to_track_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); + GtkWidget * jump_to_track_win = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_type_hint(GTK_WINDOW(jump_to_track_win), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_title(GTK_WINDOW(jump_to_track_win), _("Jump to Song")); g_signal_connect (jump_to_track_win, "key_press_event", (GCallback) keypress_cb, NULL); - g_signal_connect (jump_to_track_win, "delete-event", (GCallback) delete_cb, NULL); + g_signal_connect (jump_to_track_win, "destroy", (GCallback) destroy_cb, NULL); gtk_container_set_border_width(GTK_CONTAINER(jump_to_track_win), 10); gtk_window_set_default_size(GTK_WINDOW(jump_to_track_win), 600, 500); @@ -279,7 +263,7 @@ static void create_window (void) "changed", (GCallback) selection_changed, NULL); g_signal_connect (treeview, "row-activated", (GCallback) do_jump, NULL); - GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 3); + GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3); /* filter box */ @@ -291,21 +275,7 @@ static void create_window (void) gtk_label_set_mnemonic_widget ((GtkLabel *) search_label, filter_entry); g_signal_connect (filter_entry, "changed", (GCallback) fill_list, NULL); gtk_entry_set_activates_default ((GtkEntry *) filter_entry, TRUE); - gtk_box_pack_start ((GtkBox *) hbox, filter_entry, TRUE, TRUE, 3); - - /* remember text entry */ - GtkWidget * toggle2 = gtk_check_button_new_with_label (_("Remember")); - gtk_toggle_button_set_active ((GtkToggleButton *) toggle2, aud_get_bool - ("audgui", "remember_jtf_entry")); - gtk_box_pack_start(GTK_BOX(hbox), toggle2, FALSE, FALSE, 0); - g_signal_connect (toggle2, "clicked", (GCallback) toggle_button_cb, "remember_jtf_entry"); - - /* clear button */ - GtkWidget * rescan = gtk_button_new_with_mnemonic (_("Clea_r")); - gtk_button_set_image ((GtkButton *) rescan, gtk_image_new_from_stock - (GTK_STOCK_CLEAR, GTK_ICON_SIZE_BUTTON)); - gtk_box_pack_start(GTK_BOX(hbox), rescan, FALSE, FALSE, 0); - g_signal_connect (rescan, "clicked", (GCallback) clear_cb, NULL); + gtk_box_pack_start ((GtkBox *) hbox, filter_entry, TRUE, TRUE, 0); GtkWidget * scrollwin = gtk_scrolled_window_new (NULL, NULL); gtk_container_add(GTK_CONTAINER(scrollwin), treeview); @@ -321,49 +291,37 @@ static void create_window (void) gtk_box_pack_start(GTK_BOX(vbox), bbox, FALSE, FALSE, 0); /* close dialog toggle */ - GtkWidget * toggle = gtk_check_button_new_with_label(_("Close on Jump")); + GtkWidget * toggle = gtk_check_button_new_with_mnemonic (_("C_lose on jump")); gtk_toggle_button_set_active ((GtkToggleButton *) toggle, aud_get_bool ("audgui", "close_jtf_dialog")); gtk_box_pack_start(GTK_BOX(bbox), toggle, FALSE, FALSE, 0); g_signal_connect (toggle, "clicked", (GCallback) toggle_button_cb, "close_jtf_dialog"); /* queue button */ - queue_button = gtk_button_new_with_mnemonic(_("_Queue")); - gtk_button_set_image ((GtkButton *) queue_button, gtk_image_new_from_stock - (AUD_STOCK_QUEUETOGGLE, GTK_ICON_SIZE_BUTTON)); + queue_button = audgui_button_new (_("_Queue"), NULL, do_queue, NULL); gtk_box_pack_start ((GtkBox *) bbox, queue_button, FALSE, FALSE, 0); - g_signal_connect (queue_button, "clicked", (GCallback) do_queue, NULL); - /* jump button */ - GtkWidget * jump = gtk_button_new_from_stock (GTK_STOCK_JUMP_TO); + GtkWidget * jump = audgui_button_new (_("_Jump"), "go-jump", do_jump, NULL); gtk_box_pack_start(GTK_BOX(bbox), jump, FALSE, FALSE, 0); - g_signal_connect (jump, "clicked", (GCallback) do_jump, NULL); - gtk_widget_set_can_default(jump, TRUE); gtk_widget_grab_default(jump); /* close button */ - GtkWidget * close = gtk_button_new_from_stock (GTK_STOCK_CLOSE); + GtkWidget * close = audgui_button_new (_("_Close"), "window-close", + (AudguiCallback) audgui_jump_to_track_hide, NULL); gtk_box_pack_start(GTK_BOX(bbox), close, FALSE, FALSE, 0); - g_signal_connect (close, "clicked", (GCallback) audgui_jump_to_track_hide, - NULL); - gtk_widget_set_can_default(close, TRUE); + + return jump_to_track_win; } EXPORT void audgui_jump_to_track (void) { - bool_t create = (! jump_to_track_win); - if (create) - create_window (); + if (audgui_reshow_unique_window (AUDGUI_JUMP_TO_TRACK_WINDOW)) + return; - g_return_if_fail (filter_entry); - - if (aud_get_bool ("audgui", "remember_jtf_entry")) - gtk_editable_select_region ((GtkEditable *) filter_entry, 0, -1); - else - gtk_entry_set_text ((GtkEntry *) filter_entry, ""); + GtkWidget * jump_to_track_win = create_window (); if (! watching) { @@ -373,10 +331,12 @@ EXPORT void audgui_jump_to_track (void) watching = TRUE; } - if (create) - gtk_widget_show_all (jump_to_track_win); - else - gtk_window_present ((GtkWindow *) jump_to_track_win); - gtk_widget_grab_focus (filter_entry); + + audgui_show_unique_window (AUDGUI_JUMP_TO_TRACK_WINDOW, jump_to_track_win); +} + +EXPORT void audgui_jump_to_track_hide (void) +{ + audgui_hide_unique_window (AUDGUI_JUMP_TO_TRACK_WINDOW); } diff --git a/src/libaudgui/ui_jumptotrack_cache.c b/src/libaudgui/ui_jumptotrack_cache.c index f9aa160..21d4370 100644 --- a/src/libaudgui/ui_jumptotrack_cache.c +++ b/src/libaudgui/ui_jumptotrack_cache.c @@ -1,6 +1,6 @@ /* * ui_jumptotrack_cache.c - * Copyright 2008-2011 Jussi Judin and John Lindgren + * Copyright 2008-2012 Jussi Judin and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -22,29 +22,25 @@ #include <stdlib.h> #include <string.h> #include <assert.h> -#ifdef HAVE_SYS_TYPES_H -# include "sys/types.h" -#endif #include <audacious/debug.h> #include <audacious/playlist.h> #include <libaudcore/audstrings.h> #include "ui_jumptotrack_cache.h" -#include "ui_regex.h" // Struct to keep information about matches from searches. typedef struct { GArray * entries; // int - GArray * titles, * artists, * albums, * paths; // char * + GArray * titles, * artists, * albums, * paths; // char * (pooled) } KeywordMatches; static void ui_jump_to_track_cache_init (JumpToTrackCache * cache); static KeywordMatches * keyword_matches_new (void) { - KeywordMatches * k = g_malloc (sizeof (KeywordMatches)); + KeywordMatches * k = g_slice_new (KeywordMatches); k->entries = g_array_new (FALSE, FALSE, sizeof (int)); k->titles = g_array_new (FALSE, FALSE, sizeof (char *)); k->artists = g_array_new (FALSE, FALSE, sizeof (char *)); @@ -60,7 +56,7 @@ static void keyword_matches_free (KeywordMatches * k) g_array_free (k->artists, TRUE); g_array_free (k->albums, TRUE); g_array_free (k->paths, TRUE); - g_free (k); + g_slice_free (KeywordMatches, k); } /** @@ -89,15 +85,10 @@ ui_jump_to_track_cache_regex_list_create(const GString* keyword) if (words[i][0] == 0) { continue; } - regex_t *regex = g_malloc(sizeof(regex_t)); - #if defined(USE_REGEX_PCRE) - if ( regcomp( regex , words[i] , REG_NOSUB | REG_UTF8 ) == 0 ) - #else - if ( regcomp( regex , words[i] , REG_NOSUB ) == 0 ) - #endif - regex_list = g_slist_append( regex_list , regex ); - else - g_free( regex ); + + GRegex * regex = g_regex_new (words[i], G_REGEX_CASELESS, 0, NULL); + if (regex) + regex_list = g_slist_append (regex_list, regex); } g_strfreev(words); @@ -105,26 +96,6 @@ ui_jump_to_track_cache_regex_list_create(const GString* keyword) return regex_list; } -/** - * Frees the regular expression list used in searches. - */ -static void -ui_jump_to_track_cache_regex_list_free(GSList* regex_list) -{ - if ( regex_list != NULL ) - { - GSList* regex_list_tmp = regex_list; - while ( regex_list != NULL ) - { - regex_t *regex = regex_list->data; - regfree( regex ); - g_free( regex ); - regex_list = g_slist_next(regex_list); - } - g_slist_free( regex_list_tmp ); - } -} - /** * Checks if 'song' matches all regular expressions in 'regex_list'. */ @@ -136,8 +107,8 @@ ui_jump_to_track_match(const char * song, GSList *regex_list) for ( ; regex_list ; regex_list = g_slist_next(regex_list) ) { - regex_t *regex = regex_list->data; - if ( regexec( regex , song , 0 , NULL , 0 ) != 0 ) + GRegex * regex = regex_list->data; + if (! g_regex_match (regex, song, 0, NULL)) return FALSE; } @@ -190,30 +161,9 @@ ui_jump_to_track_cache_match_keyword(JumpToTrackCache* cache, g_hash_table_insert (cache->keywords, GINT_TO_POINTER (g_string_hash (keyword)), k); - ui_jump_to_track_cache_regex_list_free(regex_list); - return k->entries; -} - -/* calls str_unref() on <string> */ -/* returned string must be freed */ -static char * process_string (char * string, bool_t decode) -{ - if (! string) - return NULL; - - char * normal; + g_slist_free_full (regex_list, (GDestroyNotify) g_regex_unref); - if (decode) - { - char temp[strlen (string) + 1]; - str_decode_percent (string, -1, temp); - normal = g_utf8_casefold (temp, -1); - } - else - normal = g_utf8_casefold (string, -1); - - str_unref (string); - return normal; + return k->entries; } /** @@ -224,10 +174,10 @@ ui_jump_to_track_cache_free_keywordmatch_data(KeywordMatches* match_entry) { for (int i = 0; i < match_entry->entries->len; i ++) { - g_free (g_array_index (match_entry->titles, char *, i)); - g_free (g_array_index (match_entry->artists, char *, i)); - g_free (g_array_index (match_entry->albums, char *, i)); - g_free (g_array_index (match_entry->paths, char *, i)); + str_unref (g_array_index (match_entry->titles, char *, i)); + str_unref (g_array_index (match_entry->artists, char *, i)); + str_unref (g_array_index (match_entry->albums, char *, i)); + str_unref (g_array_index (match_entry->paths, char *, i)); } } @@ -239,7 +189,7 @@ ui_jump_to_track_cache_free_keywordmatch_data(KeywordMatches* match_entry) JumpToTrackCache* ui_jump_to_track_cache_new() { - JumpToTrackCache* cache = g_new(JumpToTrackCache, 1); + JumpToTrackCache * cache = g_slice_new (JumpToTrackCache); cache->keywords = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) keyword_matches_free); ui_jump_to_track_cache_init (cache); @@ -287,20 +237,18 @@ static void ui_jump_to_track_cache_init (JumpToTrackCache * cache) for (int entry = 0; entry < entries; entry ++) { - char * title, * artist, * album, * path; + char * title, * artist, * album; aud_playlist_entry_describe (playlist, entry, & title, & artist, & album, TRUE); - path = aud_playlist_entry_get_filename (playlist, entry); - title = process_string (title, FALSE); - artist = process_string (artist, FALSE); - album = process_string (album, FALSE); - path = process_string (path, TRUE); + char * uri = aud_playlist_entry_get_filename (playlist, entry); + char * decoded = uri_to_display (uri); + str_unref (uri); g_array_append_val (k->entries, entry); g_array_append_val (k->titles, title); g_array_append_val (k->artists, artist); g_array_append_val (k->albums, album); - g_array_append_val (k->paths, path); + g_array_append_val (k->paths, decoded); } // Finally insert all titles into cache into an empty key "" so that @@ -363,9 +311,8 @@ static void ui_jump_to_track_cache_init (JumpToTrackCache * cache) const GArray * ui_jump_to_track_cache_search (JumpToTrackCache * cache, const char * keyword) { - char * normalized_keyword = g_utf8_casefold (keyword, -1); - GString* keyword_string = g_string_new(normalized_keyword); - GString* match_string = g_string_new(normalized_keyword); + GString* keyword_string = g_string_new(keyword); + GString* match_string = g_string_new(keyword); int match_string_length = keyword_string->len; while (match_string_length >= 0) @@ -381,7 +328,6 @@ const GArray * ui_jump_to_track_cache_search (JumpToTrackCache * cache, const if (match_string_length == keyword_string->len) { g_string_free(keyword_string, TRUE); g_string_free(match_string, TRUE); - g_free(normalized_keyword); return matched_entries->entries; } @@ -392,7 +338,6 @@ const GArray * ui_jump_to_track_cache_search (JumpToTrackCache * cache, const keyword_string); g_string_free(keyword_string, TRUE); g_string_free(match_string, TRUE); - g_free(normalized_keyword); return result; } match_string_length--; @@ -409,5 +354,5 @@ void ui_jump_to_track_cache_free (JumpToTrackCache * cache) { ui_jump_to_track_cache_clear (cache); g_hash_table_unref (cache->keywords); - g_free (cache); + g_slice_free (JumpToTrackCache, cache); } diff --git a/src/libaudgui/ui_playlist_manager.c b/src/libaudgui/ui_playlist_manager.c index e5de3a2..7e42947 100644 --- a/src/libaudgui/ui_playlist_manager.c +++ b/src/libaudgui/ui_playlist_manager.c @@ -1,6 +1,6 @@ /* * ui_playlist_manager.c - * Copyright 2006-2011 Giacomo Lozito and John Lindgren + * Copyright 2006-2012 Giacomo Lozito, John Lindgren, and Thomas Lange * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -24,6 +24,7 @@ #include <audacious/misc.h> #include <audacious/drct.h> #include <audacious/playlist.h> +#include <libaudcore/audstrings.h> #include <libaudcore/hook.h> #include "init.h" @@ -31,55 +32,29 @@ #include "libaudgui-gtk.h" #include "list.h" -static GtkWidget * playman_win = NULL; static void activate_row (void * user, int row); -static void save_position (GtkWidget * window) -{ - int x, y, w, h; - gtk_window_get_position ((GtkWindow *) window, & x, & y); - gtk_window_get_size ((GtkWindow *) window, & w, & h); - - aud_set_int ("audgui", "playlist_manager_x", x); - aud_set_int ("audgui", "playlist_manager_y", y); - aud_set_int ("audgui", "playlist_manager_w", w); - aud_set_int ("audgui", "playlist_manager_h", h); -} - -static bool_t hide_cb (GtkWidget * window) -{ - save_position (window); - gtk_widget_hide (window); - return TRUE; -} - -static void play_cb (void) +static void play_cb (void * unused) { activate_row (NULL, aud_playlist_get_active ()); } -static void rename_cb (void) +static void rename_cb (void * unused) { audgui_show_playlist_rename (aud_playlist_get_active ()); } -static void new_cb (void) +static void new_cb (void * unused) { aud_playlist_insert (aud_playlist_get_active () + 1); aud_playlist_set_active (aud_playlist_get_active () + 1); } -static void delete_cb (void) +static void delete_cb (void * unused) { audgui_confirm_playlist_delete (aud_playlist_get_active ()); } -static void save_config_cb (void * hook_data, void * user_data) -{ - if (gtk_widget_get_visible ((GtkWidget *) user_data)) - save_position ((GtkWidget *) user_data); -} - static void get_value (void * user, int row, int column, GValue * value) { switch (column) @@ -116,7 +91,7 @@ static void activate_row (void * user, int row) aud_drct_play_playlist (row); if (aud_get_bool ("audgui", "playlist_manager_close_on_activate")) - hide_cb (playman_win); + audgui_hide_unique_window (AUDGUI_PLAYLIST_MANAGER_WINDOW); } static void shift_rows (void * user, int row, int before) @@ -147,23 +122,17 @@ static bool_t search_cb (GtkTreeModel * model, int column, const char * key, int row = gtk_tree_path_get_indices (path)[0]; gtk_tree_path_free (path); - char * temp = aud_playlist_get_title (row); - g_return_val_if_fail (temp, TRUE); - char * title = g_utf8_strdown (temp, -1); - str_unref (temp); + char * title = aud_playlist_get_title (row); + g_return_val_if_fail (title, TRUE); - temp = g_utf8_strdown (key, -1); - char * * keys = g_strsplit (temp, " ", 0); - g_free (temp); + Index * keys = str_list_to_index (key, " "); + int count = index_count (keys); bool_t match = FALSE; - for (int i = 0; keys[i]; i ++) + for (int i = 0; i < count; i ++) { - if (! keys[i][0]) - continue; - - if (strstr (title, keys[i])) + if (strstr_nocase_utf8 (title, index_get (keys, i))) match = TRUE; else { @@ -172,8 +141,8 @@ static bool_t search_cb (GtkTreeModel * model, int column, const char * key, } } - g_free (title); - g_strfreev (keys); + index_free_full (keys, (IndexFreeFunc) str_unref); + str_unref (title); return ! match; /* TRUE == not matched, FALSE == matched */ } @@ -240,49 +209,29 @@ static void close_on_activate_cb (GtkToggleButton * toggle) gtk_toggle_button_get_active (toggle)); } -EXPORT void audgui_playlist_manager (void) +static void destroy_cb (GtkWidget * window) { - GtkWidget * playman_vbox; - GtkWidget * playman_pl_lv, * playman_pl_lv_sw; - GtkWidget * playman_button_hbox; - GtkWidget * new_button, * delete_button, * rename_button, * play_button; - GtkWidget * hbox, * check_button; - GdkGeometry playman_win_hints; - - if (playman_win) - { - gtk_window_present ((GtkWindow *) playman_win); - return; - } + hook_dissociate ("playlist update", update_hook); + hook_dissociate ("playlist activate", activate_hook); + hook_dissociate ("playlist set playing", position_hook); +} - playman_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); +static GtkWidget * create_playlist_manager (void) +{ + GtkWidget * playman_win = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_type_hint ((GtkWindow *) playman_win, GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_set_title ((GtkWindow *) playman_win, _("Playlist Manager")); gtk_container_set_border_width ((GtkContainer *) playman_win, 6); - playman_win_hints.min_width = 400; - playman_win_hints.min_height = 250; - gtk_window_set_geometry_hints ((GtkWindow *) playman_win, playman_win, - &playman_win_hints , GDK_HINT_MIN_SIZE); - - int x = aud_get_int ("audgui", "playlist_manager_x"); - int y = aud_get_int ("audgui", "playlist_manager_y"); - int w = aud_get_int ("audgui", "playlist_manager_w"); - int h = aud_get_int ("audgui", "playlist_manager_h"); + gtk_widget_set_size_request (playman_win, 400, 250); - if (w && h) - { - gtk_window_move ((GtkWindow *) playman_win, x, y); - gtk_window_set_default_size ((GtkWindow *) playman_win, w, h); - } + g_signal_connect (playman_win, "destroy", (GCallback) destroy_cb, NULL); + audgui_destroy_on_escape (playman_win); - g_signal_connect (playman_win, "delete-event", (GCallback) hide_cb, NULL); - audgui_hide_on_escape (playman_win); - - playman_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); + GtkWidget * playman_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6); gtk_container_add ((GtkContainer *) playman_win, playman_vbox); /* ListView */ - playman_pl_lv = audgui_list_new (& callbacks, NULL, aud_playlist_count ()); + GtkWidget * playman_pl_lv = audgui_list_new (& callbacks, NULL, aud_playlist_count ()); audgui_list_add_column (playman_pl_lv, _("Title"), 0, G_TYPE_STRING, -1); audgui_list_add_column (playman_pl_lv, _("Entries"), 1, G_TYPE_INT, 7); audgui_list_set_highlight (playman_pl_lv, aud_playlist_get_playing ()); @@ -292,7 +241,7 @@ EXPORT void audgui_playlist_manager (void) hook_associate ("playlist activate", activate_hook, playman_pl_lv); hook_associate ("playlist set playing", position_hook, playman_pl_lv); - playman_pl_lv_sw = gtk_scrolled_window_new (NULL, NULL); + GtkWidget * playman_pl_lv_sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type ((GtkScrolledWindow *) playman_pl_lv_sw, GTK_SHADOW_IN); gtk_scrolled_window_set_policy ((GtkScrolledWindow *) playman_pl_lv_sw, @@ -301,13 +250,11 @@ EXPORT void audgui_playlist_manager (void) gtk_box_pack_start ((GtkBox *) playman_vbox, playman_pl_lv_sw, TRUE, TRUE, 0); /* ButtonBox */ - playman_button_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - new_button = gtk_button_new_from_stock (GTK_STOCK_NEW); - delete_button = gtk_button_new_from_stock (GTK_STOCK_DELETE); - rename_button = gtk_button_new_with_mnemonic (_("_Rename")); - gtk_button_set_image ((GtkButton *) rename_button, gtk_image_new_from_stock - (GTK_STOCK_EDIT, GTK_ICON_SIZE_BUTTON)); - play_button = gtk_button_new_from_stock (GTK_STOCK_MEDIA_PLAY); + GtkWidget * playman_button_hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + GtkWidget * new_button = audgui_button_new (_("_New"), "document-new", new_cb, NULL); + GtkWidget * delete_button = audgui_button_new (_("_Remove"), "edit-delete", delete_cb, NULL); + GtkWidget * rename_button = audgui_button_new (_("Ren_ame"), "insert-text", rename_cb, NULL); + GtkWidget * play_button = audgui_button_new (_("_Play"), "media-playback-start", play_cb, NULL); gtk_container_add ((GtkContainer *) playman_button_hbox, new_button); gtk_container_add ((GtkContainer *) playman_button_hbox, delete_button); @@ -315,36 +262,21 @@ EXPORT void audgui_playlist_manager (void) gtk_box_pack_end ((GtkBox *) playman_button_hbox, rename_button, FALSE, FALSE, 0); gtk_container_add ((GtkContainer *) playman_vbox, playman_button_hbox); - g_signal_connect (new_button, "clicked", (GCallback) new_cb, NULL); - g_signal_connect (delete_button, "clicked", (GCallback) delete_cb, NULL); - g_signal_connect (rename_button, "clicked", (GCallback) rename_cb, NULL); - g_signal_connect (play_button, "clicked", (GCallback) play_cb, NULL); - /* CheckButton */ - hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); + GtkWidget * hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); gtk_box_pack_start ((GtkBox *) playman_vbox, hbox, FALSE, FALSE, 0); - check_button = gtk_check_button_new_with_mnemonic + GtkWidget * check_button = gtk_check_button_new_with_mnemonic (_("_Close dialog on activating playlist")); gtk_box_pack_start ((GtkBox *) hbox, check_button, FALSE, FALSE, 0); gtk_toggle_button_set_active ((GtkToggleButton *) check_button, aud_get_bool ("audgui", "playlist_manager_close_on_activate")); g_signal_connect (check_button, "toggled", (GCallback) close_on_activate_cb, NULL); - gtk_widget_show_all (playman_win); - - hook_associate ("config save", save_config_cb, playman_win); + return playman_win; } -void audgui_playlist_manager_cleanup (void) +EXPORT void audgui_playlist_manager (void) { - if (! playman_win) - return; - - hook_dissociate ("playlist update", update_hook); - hook_dissociate ("playlist activate", activate_hook); - hook_dissociate ("playlist set playing", position_hook); - hook_dissociate ("config save", save_config_cb); - - gtk_widget_destroy (playman_win); - playman_win = NULL; + if (! audgui_reshow_unique_window (AUDGUI_PLAYLIST_MANAGER_WINDOW)) + audgui_show_unique_window (AUDGUI_PLAYLIST_MANAGER_WINDOW, create_playlist_manager ()); } diff --git a/src/libaudgui/ui_regex.h b/src/libaudgui/ui_regex.h deleted file mode 100644 index 801a2ff..0000000 --- a/src/libaudgui/ui_regex.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * ui_regex.h - * Copyright 2010 Carlo Bramini - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions, and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions, and the following disclaimer in the documentation - * provided with the distribution. - * - * This software is provided "as is" and without any warranty, express or - * implied. In no event shall the authors be liable for any damages arising from - * the use of this software. - */ - -#ifndef __UI_REGEX_H__ -#define __UI_REGEX_H__ - -#if defined USE_REGEX_ONIGURUMA - #include <onigposix.h> - -#elif defined USE_REGEX_PCRE - #include <pcreposix.h> - -#elif defined HAVE_REGEX_H - #include <regex.h> - -#elif defined HAVE_RXPOSIX_H - #include <rxposix.h> - -#elif defined HAVE_RX_RXPOSIX_H - #include <rx/rxposix.h> -#endif - -#endif diff --git a/src/libaudgui/urilist.c b/src/libaudgui/urilist.c index 2ffdfea..4f75764 100644 --- a/src/libaudgui/urilist.c +++ b/src/libaudgui/urilist.c @@ -1,6 +1,6 @@ /* * urilist.c - * Copyright 2010 John Lindgren + * Copyright 2010-2011 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -36,7 +36,7 @@ static char * check_uri (char * name) if (strstr (name, "://") || ! (new = filename_to_uri (name))) return name; - g_free (name); + str_unref (name); return new; } @@ -53,15 +53,14 @@ static void urilist_for_each (const char * list, ForEachFunc func, void * user) else next = end = strchr (list, 0); - func (check_uri (g_strndup (list, end - list)), user); + func (check_uri (str_nget (list, end - list)), user); list = next; } } static void add_to_index (char * name, Index * index) { - index_append (index, str_get (name)); - g_free (name); + index_insert (index, -1, name); } EXPORT void audgui_urilist_open (const char * list) diff --git a/src/libaudgui/url-opener.c b/src/libaudgui/url-opener.c index 0e69946..3baf7e8 100644 --- a/src/libaudgui/url-opener.c +++ b/src/libaudgui/url-opener.c @@ -1,6 +1,6 @@ /* * url-opener.c - * Copyright 2012 John Lindgren + * Copyright 2012-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -25,49 +25,39 @@ #include "init.h" #include "libaudgui.h" +#include "libaudgui-gtk.h" -static GtkWidget * window; - -static void response_cb (GtkWidget * dialog, int response) +static void open_cb (void * entry) { - if (response == GTK_RESPONSE_ACCEPT) - { - GtkWidget * entry = g_object_get_data ((GObject *) dialog, "entry"); - const char * text = gtk_entry_get_text ((GtkEntry *) entry); - bool_t open = GPOINTER_TO_INT (g_object_get_data ((GObject *) dialog, "open")); + const char * text = gtk_entry_get_text ((GtkEntry *) entry); + bool_t open = GPOINTER_TO_INT (g_object_get_data ((GObject *) entry, "open")); - if (open) - aud_drct_pl_open (text); - else - aud_drct_pl_add (text, -1); - - aud_history_add (text); - } + if (open) + aud_drct_pl_open (text); + else + aud_drct_pl_add (text, -1); - gtk_widget_destroy (dialog); + aud_history_add (text); } -EXPORT void audgui_show_add_url_window (bool_t open) +static GtkWidget * create_url_opener (bool_t open) { - if (window) - gtk_widget_destroy (window); - - window = gtk_dialog_new_with_buttons (open ? _("Open URL") : _("Add URL"), - NULL, 0, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, open ? GTK_STOCK_OPEN : - GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT, NULL); - gtk_widget_set_size_request (window, 300, -1); - gtk_window_set_resizable ((GtkWindow *) window, FALSE); - gtk_dialog_set_default_response ((GtkDialog *) window, GTK_RESPONSE_ACCEPT); - - GtkWidget * box = gtk_dialog_get_content_area ((GtkDialog *) window); + const char * title, * verb, * icon; - GtkWidget * label = gtk_label_new (_("Enter URL:")); - gtk_misc_set_alignment ((GtkMisc *) label, 0, 0); - gtk_box_pack_start ((GtkBox *) box, label, FALSE, FALSE, 0); + if (open) + { + title = _("Open URL"); + verb = _("_Open"); + icon = "document-open"; + } + else + { + title = _("Add URL"); + verb = _("_Add"); + icon = "list-add"; + } GtkWidget * combo = gtk_combo_box_text_new_with_entry (); - gtk_box_pack_start ((GtkBox *) box, combo, FALSE, FALSE, 0); - GtkWidget * entry = gtk_bin_get_child ((GtkBin *) combo); gtk_entry_set_activates_default ((GtkEntry *) entry, TRUE); @@ -75,17 +65,20 @@ EXPORT void audgui_show_add_url_window (bool_t open) for (int i = 0; (item = aud_history_get (i)); i++) gtk_combo_box_text_append_text ((GtkComboBoxText *) combo, item); - g_object_set_data ((GObject *) window, "entry", entry); - g_object_set_data ((GObject *) window, "open", GINT_TO_POINTER (open)); + g_object_set_data ((GObject *) entry, "open", GINT_TO_POINTER (open)); + + GtkWidget * button1 = audgui_button_new (verb, icon, open_cb, entry); + GtkWidget * button2 = audgui_button_new (_("_Cancel"), "process-stop", NULL, NULL); - g_signal_connect (window, "response", (GCallback) response_cb, NULL); - g_signal_connect (window, "destroy", (GCallback) gtk_widget_destroyed, & window); + GtkWidget * dialog = audgui_dialog_new (GTK_MESSAGE_OTHER, title, + _("Enter URL:"), button1, button2); + gtk_widget_set_size_request (dialog, 400, -1); + audgui_dialog_add_widget (dialog, combo); - gtk_widget_show_all (window); + return dialog; } -void audgui_url_opener_cleanup (void) +EXPORT void audgui_show_add_url_window (bool_t open) { - if (window) - gtk_widget_destroy (window); + audgui_show_unique_window (AUDGUI_URL_OPENER_WINDOW, create_url_opener (open)); } diff --git a/src/libaudgui/util.c b/src/libaudgui/util.c index 5a3061e..32d3cb8 100644 --- a/src/libaudgui/util.c +++ b/src/libaudgui/util.c @@ -1,6 +1,6 @@ /* * util.c - * Copyright 2010-2012 John Lindgren + * Copyright 2010-2012 John Lindgren and Michał Lipski * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -20,12 +20,10 @@ #include <string.h> #include <gdk/gdkkeysyms.h> -#include <gdk-pixbuf/gdk-pixbuf.h> #include <gtk/gtk.h> #include <audacious/debug.h> #include <audacious/i18n.h> -#include <audacious/playlist.h> #include <audacious/misc.h> #include <libaudcore/audstrings.h> #include <libaudcore/hook.h> @@ -34,8 +32,6 @@ #include "libaudgui.h" #include "libaudgui-gtk.h" -static GdkPixbuf * current_pixbuf; - EXPORT int audgui_get_digit_width (GtkWidget * widget) { int width; @@ -45,6 +41,7 @@ EXPORT int audgui_get_digit_width (GtkWidget * widget) pango_layout_set_font_description (layout, desc); pango_layout_get_pixel_size (layout, & width, NULL); pango_font_description_free (desc); + g_object_unref (layout); return (width + 9) / 10; } @@ -75,28 +72,6 @@ EXPORT void audgui_get_mouse_coords (GtkWidget * widget, int * x, int * y) } } -EXPORT void audgui_hide_on_delete (GtkWidget * widget) -{ - g_signal_connect (widget, "delete-event", (GCallback) - gtk_widget_hide_on_delete, NULL); -} - -static bool_t escape_hide_cb (GtkWidget * widget, GdkEventKey * event) -{ - if (event->keyval == GDK_KEY_Escape) - { - gtk_widget_hide (widget); - return TRUE; - } - - return FALSE; -} - -EXPORT void audgui_hide_on_escape (GtkWidget * widget) -{ - g_signal_connect (widget, "key-press-event", (GCallback) escape_hide_cb, NULL); -} - static bool_t escape_destroy_cb (GtkWidget * widget, GdkEventKey * event) { if (event->keyval == GDK_KEY_Escape) @@ -113,223 +88,97 @@ EXPORT void audgui_destroy_on_escape (GtkWidget * widget) g_signal_connect (widget, "key-press-event", (GCallback) escape_destroy_cb, NULL); } -static void toggle_cb (GtkToggleButton * toggle, bool_t * setting) -{ - * setting = gtk_toggle_button_get_active (toggle); -} - -EXPORT void audgui_connect_check_box (GtkWidget * box, bool_t * setting) -{ - gtk_toggle_button_set_active ((GtkToggleButton *) box, * setting); - g_signal_connect ((GObject *) box, "toggled", (GCallback) toggle_cb, setting); -} - -EXPORT void audgui_simple_message (GtkWidget * * widget, GtkMessageType type, - const char * title, const char * text) +EXPORT GtkWidget * audgui_button_new (const char * text, const char * icon, + AudguiCallback callback, void * data) { - AUDDBG ("%s\n", text); + GtkWidget * button = gtk_button_new_with_mnemonic (text); - if (* widget != NULL) + if (icon) { - const char * old = NULL; - g_object_get ((GObject *) * widget, "text", & old, NULL); - g_return_if_fail (old); - - int messages = GPOINTER_TO_INT (g_object_get_data ((GObject *) - * widget, "messages")); - - if (messages > 10) - text = _("\n(Further messages have been hidden.)"); - - if (strstr (old, text)) - goto CREATED; - - char both[strlen (old) + strlen (text) + 2]; - snprintf (both, sizeof both, "%s\n%s", old, text); - g_object_set ((GObject *) * widget, "text", both, NULL); - - g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER - (messages + 1)); - - goto CREATED; + GtkWidget * image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_MENU); + gtk_button_set_image ((GtkButton *) button, image); } - * widget = gtk_message_dialog_new (NULL, 0, type, GTK_BUTTONS_OK, "%s", text); - gtk_window_set_title ((GtkWindow *) * widget, title); - - g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER (1)); - - g_signal_connect (* widget, "response", (GCallback) gtk_widget_destroy, NULL); - audgui_destroy_on_escape (* widget); - g_signal_connect (* widget, "destroy", (GCallback) gtk_widget_destroyed, - widget); + if (callback) + g_signal_connect_swapped (button, "clicked", (GCallback) callback, data); -CREATED: - gtk_window_present ((GtkWindow *) * widget); + return button; } -EXPORT GdkPixbuf * audgui_pixbuf_from_data (const void * data, int64_t size) +EXPORT GtkWidget * audgui_dialog_new (GtkMessageType type, const char * title, + const char * text, GtkWidget * button1, GtkWidget * button2) { - GdkPixbuf * pixbuf = NULL; - GdkPixbufLoader * loader = gdk_pixbuf_loader_new (); - GError * error = NULL; + GtkWidget * dialog = gtk_message_dialog_new (NULL, 0, type, GTK_BUTTONS_NONE, "%s", text); + gtk_window_set_title ((GtkWindow *) dialog, title); - if (gdk_pixbuf_loader_write (loader, data, size, & error) && - gdk_pixbuf_loader_close (loader, & error)) - { - if ((pixbuf = gdk_pixbuf_loader_get_pixbuf (loader))) - g_object_ref (pixbuf); - } - else + if (button2) { - AUDDBG ("error while loading pixbuf: %s\n", error->message); - g_error_free (error); + gtk_dialog_add_action_widget ((GtkDialog *) dialog, button2, GTK_RESPONSE_NONE); + g_signal_connect_swapped (button2, "clicked", (GCallback) gtk_widget_destroy, dialog); } - g_object_unref (loader); - return pixbuf; -} - -/* deprecated */ -EXPORT GdkPixbuf * audgui_pixbuf_for_entry (int list, int entry) -{ - char * name = aud_playlist_entry_get_filename (list, entry); - g_return_val_if_fail (name, NULL); - - const void * data; - int64_t size; - - aud_art_get_data (name, & data, & size); + gtk_dialog_add_action_widget ((GtkDialog *) dialog, button1, GTK_RESPONSE_NONE); + g_signal_connect_swapped (button1, "clicked", (GCallback) gtk_widget_destroy, dialog); - if (data) - { - GdkPixbuf * p = audgui_pixbuf_from_data (data, size); - aud_art_unref (name); - - if (p) - { - str_unref (name); - return p; - } - } + gtk_widget_set_can_default (button1, TRUE); + gtk_widget_grab_default (button1); - str_unref (name); - return audgui_pixbuf_fallback (); + return dialog; } -EXPORT GdkPixbuf * audgui_pixbuf_fallback (void) +EXPORT void audgui_dialog_add_widget (GtkWidget * dialog, GtkWidget * widget) { - static GdkPixbuf * fallback = NULL; - - if (! fallback) - { - SPRINTF (path, "%s/images/album.png", aud_get_path (AUD_PATH_DATA_DIR)); - fallback = gdk_pixbuf_new_from_file (path, NULL); - } - - if (fallback) - g_object_ref ((GObject *) fallback); - - return fallback; + GtkWidget * box = gtk_message_dialog_get_message_area ((GtkMessageDialog *) dialog); + gtk_box_pack_start ((GtkBox *) box, widget, FALSE, FALSE, 0); } -void audgui_pixbuf_uncache (void) +EXPORT void audgui_simple_message (GtkWidget * * widget, GtkMessageType type, + const char * title, const char * text) { - if (current_pixbuf) - { - g_object_unref ((GObject *) current_pixbuf); - current_pixbuf = NULL; - } -} - -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + AUDDBG ("%s\n", text); -/* deprecated */ -EXPORT GdkPixbuf * audgui_pixbuf_for_current (void) -{ - if (! current_pixbuf) + if (* widget) { - int list = aud_playlist_get_playing (); - current_pixbuf = audgui_pixbuf_for_entry (list, aud_playlist_get_position (list)); - } - - if (current_pixbuf) - g_object_ref ((GObject *) current_pixbuf); - - return current_pixbuf; -} - -#pragma GCC diagnostic pop + const char * old = NULL; + g_object_get ((GObject *) * widget, "text", & old, NULL); + g_return_if_fail (old); -EXPORT void audgui_pixbuf_scale_within (GdkPixbuf * * pixbuf, int size) -{ - int width = gdk_pixbuf_get_width (* pixbuf); - int height = gdk_pixbuf_get_height (* pixbuf); + int messages = GPOINTER_TO_INT (g_object_get_data ((GObject *) * widget, "messages")); + if (messages > 10) + text = _("\n(Further messages have been hidden.)"); - if (width <= size && height <= size) - return; + if (! strstr (old, text)) + { + SCONCAT3 (both, old, "\n", text); + g_object_set ((GObject *) * widget, "text", both, NULL); + g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER (messages + 1)); + } - if (width > height) - { - height = size * height / width; - width = size; + gtk_window_present ((GtkWindow *) * widget); } else { - width = size * width / height; - height = size; - } + GtkWidget * button = audgui_button_new (_("_Close"), "window-close", NULL, NULL); + * widget = audgui_dialog_new (type, title, text, button, NULL); - if (width < 1) - width = 1; - if (height < 1) - height = 1; + g_object_set_data ((GObject *) * widget, "messages", GINT_TO_POINTER (1)); + g_signal_connect (* widget, "destroy", (GCallback) gtk_widget_destroyed, widget); - GdkPixbuf * pixbuf2 = gdk_pixbuf_scale_simple (* pixbuf, width, height, - GDK_INTERP_BILINEAR); - g_object_unref (* pixbuf); - * pixbuf = pixbuf2; + gtk_widget_show_all (* widget); + } } -EXPORT GdkPixbuf * audgui_pixbuf_request (const char * filename) +EXPORT void audgui_format_time (char * buf, int bufsize, int64_t milliseconds) { - const void * data; - int64_t size; - - aud_art_request_data (filename, & data, & size); - if (! data) - return NULL; + int hours = milliseconds / 3600000; + int minutes = (milliseconds / 60000) % 60; + int seconds = (milliseconds / 1000) % 60; - GdkPixbuf * p = audgui_pixbuf_from_data (data, size); - - aud_art_unref (filename); - return p; -} - -EXPORT GdkPixbuf * audgui_pixbuf_request_current (void) -{ - if (! current_pixbuf) + if (hours) + snprintf (buf, bufsize, "%d:%02d:%02d", hours, minutes, seconds); + else { - int list = aud_playlist_get_playing (); - int entry = aud_playlist_get_position (list); - if (entry < 0) - return NULL; - - char * filename = aud_playlist_entry_get_filename (list, entry); - current_pixbuf = audgui_pixbuf_request (filename); - str_unref (filename); + bool_t zero = aud_get_bool (NULL, "leading_zero"); + snprintf (buf, bufsize, zero ? "%02d:%02d" : "%d:%02d", minutes, seconds); } - - if (current_pixbuf) - g_object_ref ((GObject *) current_pixbuf); - - return current_pixbuf; -} - -EXPORT void audgui_set_default_icon (void) -{ -#ifndef _WIN32 - gtk_window_set_default_icon_name ("audacious"); -#endif } diff --git a/src/libaudtag/ape/ape.c b/src/libaudtag/ape/ape.c index d494403..b755a04 100644 --- a/src/libaudtag/ape/ape.c +++ b/src/libaudtag/ape/ape.c @@ -26,6 +26,7 @@ #include <stdlib.h> #include <string.h> +#include <libaudcore/audstrings.h> #include <libaudcore/vfs.h> #include "ape.h" @@ -73,8 +74,8 @@ static bool_t ape_read_header (VFSFile * handle, APEHeader * header) return TRUE; } -static bool_t ape_find_header (VFSFile * handle, APEHeader * header, int * - start, int * length, int * data_start, int * data_length) +static bool_t ape_find_header (VFSFile * handle, APEHeader * header, + int * start, int * length, int * data_start, int * data_length) { APEHeader secondary; @@ -83,15 +84,15 @@ static bool_t ape_find_header (VFSFile * handle, APEHeader * header, int * if (ape_read_header (handle, header)) { - TAGDBG ("Found header at 0, length = %d, version = %d.\n", (int) - header->length, (int) header->version); + TAGDBG ("Found header at 0, length = %d, version = %d.\n", + (int) header->length, (int) header->version); + * start = 0; * length = header->length; * data_start = sizeof (APEHeader); * data_length = header->length - sizeof (APEHeader); - if (! (header->flags & APE_FLAG_HAS_HEADER) || ! (header->flags & - APE_FLAG_IS_HEADER)) + if (! (header->flags & APE_FLAG_HAS_HEADER) || ! (header->flags & APE_FLAG_IS_HEADER)) { TAGDBG ("Invalid header flags (%u).\n", (unsigned int) header->flags); return FALSE; @@ -117,44 +118,50 @@ static bool_t ape_find_header (VFSFile * handle, APEHeader * header, int * if (vfs_fseek (handle, -(int) sizeof (APEHeader), SEEK_END)) return FALSE; - if (ape_read_header (handle, header)) + if (! ape_read_header (handle, header)) { - TAGDBG ("Found footer at %d, length = %d, version = %d.\n", (int) - vfs_ftell (handle) - (int) sizeof (APEHeader), (int) header->length, - (int) header->version); - * start = vfs_ftell (handle) - header->length; - * length = header->length; - * data_start = vfs_ftell (handle) - header->length; - * data_length = header->length - sizeof (APEHeader); + /* APE tag may be followed by an ID3v1 tag */ + if (vfs_fseek (handle, -128 - (int) sizeof (APEHeader), SEEK_END)) + return FALSE; - if ((header->flags & APE_FLAG_HAS_NO_FOOTER) || (header->flags & - APE_FLAG_IS_HEADER)) + if (! ape_read_header (handle, header)) { - TAGDBG ("Invalid footer flags (%u).\n", (unsigned int) header->flags); + TAGDBG ("No header found.\n"); return FALSE; } + } - if (header->flags & APE_FLAG_HAS_HEADER) - { - if (vfs_fseek (handle, -(int) header->length - sizeof (APEHeader), - SEEK_CUR)) - return FALSE; + TAGDBG ("Found footer at %d, length = %d, version = %d.\n", + (int) vfs_ftell (handle) - (int) sizeof (APEHeader), (int) header->length, + (int) header->version); - if (! ape_read_header (handle, & secondary)) - { - TAGDBG ("Expected header, but found none.\n"); - return FALSE; - } + * start = vfs_ftell (handle) - header->length; + * length = header->length; + * data_start = vfs_ftell (handle) - header->length; + * data_length = header->length - sizeof (APEHeader); - * start -= sizeof (APEHeader); - * length += sizeof (APEHeader); + if ((header->flags & APE_FLAG_HAS_NO_FOOTER) || (header->flags & APE_FLAG_IS_HEADER)) + { + TAGDBG ("Invalid footer flags (%u).\n", (unsigned) header->flags); + return FALSE; + } + + if (header->flags & APE_FLAG_HAS_HEADER) + { + if (vfs_fseek (handle, -(int) header->length - sizeof (APEHeader), SEEK_CUR)) + return FALSE; + + if (! ape_read_header (handle, & secondary)) + { + TAGDBG ("Expected header, but found none.\n"); + return FALSE; } - return TRUE; + * start -= sizeof (APEHeader); + * length += sizeof (APEHeader); } - TAGDBG ("No header found.\n"); - return FALSE; + return TRUE; } static bool_t ape_is_our_file (VFSFile * handle) @@ -195,9 +202,9 @@ static ValuePair * ape_read_item (void * * data, int length) return NULL; } - pair = g_malloc (sizeof (ValuePair)); - pair->key = g_strdup ((char *) (* data) + 8); - pair->value = g_strndup (value, header[0]); + pair = g_slice_new (ValuePair); + pair->key = str_get ((char *) (* data) + 8); + pair->value = str_nget (value, header[0]); * data = value + header[0]; @@ -245,15 +252,11 @@ static GList * ape_read_items (VFSFile * handle) return g_list_reverse (list); } -static void free_tag_list (GList * list) +static void free_value_pair (ValuePair * pair) { - while (list != NULL) - { - g_free (((ValuePair *) list->data)->key); - g_free (((ValuePair *) list->data)->value); - g_free (list->data); - list = g_list_delete_link (list, list); - } + str_unref (pair->key); + str_unref (pair->value); + g_slice_free (ValuePair, pair); } static void parse_gain_text (const char * text, int * value, int * unit) @@ -297,12 +300,12 @@ static void set_gain_info (Tuple * tuple, int field, int unit_field, parse_gain_text (text, & value, & unit); - if (tuple_get_value_type (tuple, unit_field, NULL) == TUPLE_INT) - value = value * (int64_t) tuple_get_int (tuple, unit_field, NULL) / unit; + if (tuple_get_value_type (tuple, unit_field) == TUPLE_INT) + value = value * (int64_t) tuple_get_int (tuple, unit_field) / unit; else - tuple_set_int (tuple, unit_field, NULL, unit); + tuple_set_int (tuple, unit_field, unit); - tuple_set_int (tuple, field, NULL, value); + tuple_set_int (tuple, field, value); } static bool_t ape_read_tag (Tuple * tuple, VFSFile * handle) @@ -315,34 +318,30 @@ static bool_t ape_read_tag (Tuple * tuple, VFSFile * handle) char * value = ((ValuePair *) node->data)->value; if (! strcmp (key, "Artist")) - tuple_set_str (tuple, FIELD_ARTIST, NULL, value); + tuple_set_str (tuple, FIELD_ARTIST, value); else if (! strcmp (key, "Title")) - tuple_set_str (tuple, FIELD_TITLE, NULL, value); + tuple_set_str (tuple, FIELD_TITLE, value); else if (! strcmp (key, "Album")) - tuple_set_str (tuple, FIELD_ALBUM, NULL, value); + tuple_set_str (tuple, FIELD_ALBUM, value); else if (! strcmp (key, "Comment")) - tuple_set_str (tuple, FIELD_COMMENT, NULL, value); + tuple_set_str (tuple, FIELD_COMMENT, value); else if (! strcmp (key, "Genre")) - tuple_set_str (tuple, FIELD_GENRE, NULL, value); + tuple_set_str (tuple, FIELD_GENRE, value); else if (! strcmp (key, "Track")) - tuple_set_int (tuple, FIELD_TRACK_NUMBER, NULL, atoi (value)); + tuple_set_int (tuple, FIELD_TRACK_NUMBER, atoi (value)); else if (! strcmp (key, "Year")) - tuple_set_int (tuple, FIELD_YEAR, NULL, atoi (value)); + tuple_set_int (tuple, FIELD_YEAR, atoi (value)); else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_TRACK_GAIN")) - set_gain_info (tuple, FIELD_GAIN_TRACK_GAIN, FIELD_GAIN_GAIN_UNIT, - value); + set_gain_info (tuple, FIELD_GAIN_TRACK_GAIN, FIELD_GAIN_GAIN_UNIT, value); else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_TRACK_PEAK")) - set_gain_info (tuple, FIELD_GAIN_TRACK_PEAK, FIELD_GAIN_PEAK_UNIT, - value); + set_gain_info (tuple, FIELD_GAIN_TRACK_PEAK, FIELD_GAIN_PEAK_UNIT, value); else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_ALBUM_GAIN")) - set_gain_info (tuple, FIELD_GAIN_ALBUM_GAIN, FIELD_GAIN_GAIN_UNIT, - value); + set_gain_info (tuple, FIELD_GAIN_ALBUM_GAIN, FIELD_GAIN_GAIN_UNIT, value); else if (! g_ascii_strcasecmp (key, "REPLAYGAIN_ALBUM_PEAK")) - set_gain_info (tuple, FIELD_GAIN_ALBUM_PEAK, FIELD_GAIN_PEAK_UNIT, - value); + set_gain_info (tuple, FIELD_GAIN_ALBUM_PEAK, FIELD_GAIN_PEAK_UNIT, value); } - free_tag_list (list); + g_list_free_full (list, (GDestroyNotify) free_value_pair); return TRUE; } @@ -374,7 +373,7 @@ static bool_t ape_write_item (VFSFile * handle, const char * key, static bool_t write_string_item (const Tuple * tuple, int field, VFSFile * handle, const char * key, int * written_length, int * written_items) { - char * value = tuple_get_str (tuple, field, NULL); + char * value = tuple_get_str (tuple, field); if (value == NULL) return TRUE; @@ -391,13 +390,13 @@ static bool_t write_string_item (const Tuple * tuple, int field, VFSFile * static bool_t write_integer_item (const Tuple * tuple, int field, VFSFile * handle, const char * key, int * written_length, int * written_items) { - int value = tuple_get_int (tuple, field, NULL); + int value = tuple_get_int (tuple, field); char scratch[32]; - if (! value) + if (value <= 0) return TRUE; - snprintf (scratch, sizeof scratch, "%d", value); + str_itoa (value, scratch, sizeof scratch); if (! ape_write_item (handle, key, scratch, written_length)) return FALSE; @@ -488,11 +487,11 @@ static bool_t ape_write_tag (const Tuple * tuple, VFSFile * handle) start, SEEK_SET) || ! write_header (length, items, TRUE, handle)) goto ERR; - free_tag_list (list); + g_list_free_full (list, (GDestroyNotify) free_value_pair); return TRUE; ERR: - free_tag_list (list); + g_list_free_full (list, (GDestroyNotify) free_value_pair); return FALSE; } diff --git a/src/libaudtag/audtag.c b/src/libaudtag/audtag.c index 2e0d6b0..0ef0f3e 100644 --- a/src/libaudtag/audtag.c +++ b/src/libaudtag/audtag.c @@ -17,6 +17,9 @@ * the use of this software. */ +#include <stdlib.h> +#include <string.h> + #include <glib.h> #include <libaudcore/tuple.h> @@ -38,8 +41,11 @@ EXPORT bool_t tag_tuple_read (Tuple * tuple, VFSFile * handle) { tag_module_t * module = find_tag_module (handle, TAG_TYPE_NONE); - if (module == NULL) + if (! module || ! module->read_tag) + { + TAGDBG ("read_tag() not supported for %s\n", vfs_get_filename (handle)); return FALSE; + } return module->read_tag (tuple, handle); } @@ -48,8 +54,11 @@ EXPORT bool_t tag_image_read (VFSFile * handle, void * * data, int64_t * size) { tag_module_t * module = find_tag_module (handle, TAG_TYPE_NONE); - if (module == NULL || module->read_image == NULL) + if (! module || ! module->read_image) + { + TAGDBG ("read_image() not supported for %s\n", vfs_get_filename (handle)); return FALSE; + } return module->read_image (handle, data, size); } @@ -58,12 +67,59 @@ EXPORT bool_t tag_tuple_write (const Tuple * tuple, VFSFile * handle, int new_ty { tag_module_t * module = find_tag_module (handle, new_type); - if (module == NULL) + if (! module || ! module->write_tag) + { + TAGDBG ("write_tag() not supported for %s\n", vfs_get_filename (handle)); return FALSE; + } return module->write_tag (tuple, handle); } +EXPORT bool_t tag_update_stream_metadata (Tuple * tuple, VFSFile * handle) +{ + bool_t updated = FALSE; + char * old, * new; + int value; + + old = tuple_get_str (tuple, FIELD_TITLE); + new = vfs_get_metadata (handle, "track-name"); + + if (new && (! old || strcmp (old, new))) + { + tuple_set_str (tuple, FIELD_TITLE, new); + updated = TRUE; + } + + str_unref (old); + str_unref (new); + + old = tuple_get_str (tuple, FIELD_ARTIST); + new = vfs_get_metadata (handle, "stream-name"); + + if (new && (! old || strcmp (old, new))) + { + tuple_set_str (tuple, FIELD_ARTIST, new); + updated = TRUE; + } + + str_unref (old); + str_unref (new); + + new = vfs_get_metadata (handle, "content-bitrate"); + value = new ? atoi (new) / 1000 : 0; + + if (value && value != tuple_get_int (tuple, FIELD_BITRATE)) + { + tuple_set_int (tuple, FIELD_BITRATE, value); + updated = TRUE; + } + + str_unref (new); + + return updated; +} + /* deprecated */ EXPORT bool_t tag_tuple_write_to_file (Tuple * tuple, VFSFile * handle) { diff --git a/src/libaudtag/audtag.h b/src/libaudtag/audtag.h index 626e89d..b746feb 100644 --- a/src/libaudtag/audtag.h +++ b/src/libaudtag/audtag.h @@ -27,6 +27,7 @@ enum { TAG_TYPE_NONE = 0, TAG_TYPE_APE, + TAG_TYPE_ID3V2 }; void tag_set_verbose (bool_t verbose); @@ -38,6 +39,8 @@ bool_t tag_image_read (VFSFile * handle, void * * data, int64_t * size); * written if the file does not have any existing tag. */ bool_t tag_tuple_write (const Tuple * tuple, VFSFile * handle, int new_type); +bool_t tag_update_stream_metadata (Tuple * tuple, VFSFile * handle); + /* deprecated, use tag_tuple_write */ bool_t tag_tuple_write_to_file (Tuple * tuple, VFSFile * handle); diff --git a/src/libaudtag/id3/id3-common.c b/src/libaudtag/id3/id3-common.c index aec6eb2..2a8a87e 100644 --- a/src/libaudtag/id3/id3-common.c +++ b/src/libaudtag/id3/id3-common.c @@ -1,6 +1,6 @@ /* * id3-common.c - * Copyright 2010-2011 John Lindgren + * Copyright 2010-2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,7 +17,10 @@ * the use of this software. */ +#include "id3-common.h" + #include <stdio.h> +#include <stdlib.h> #include <string.h> #include <glib.h> @@ -25,7 +28,11 @@ #include <libaudcore/audstrings.h> #include "../util.h" -#include "id3-common.h" + +#define ID3_ENCODING_LATIN1 0 +#define ID3_ENCODING_UTF16 1 +#define ID3_ENCODING_UTF16_BE 2 +#define ID3_ENCODING_UTF8 3 static void * memchr16 (const void * mem, int16_t chr, int len) { @@ -41,76 +48,246 @@ static void * memchr16 (const void * mem, int16_t chr, int len) return NULL; } -char * convert_text (const char * text, int length, int encoding, bool_t - nulled, int * _converted, const char * * after) +void id3_strnlen (const char * data, int size, int encoding, + int * bytes_without_nul, int * bytes_with_nul) { - char * buffer = NULL; - gsize converted = 0; + bool_t is16 = (encoding == ID3_ENCODING_UTF16 || encoding == ID3_ENCODING_UTF16_BE); + char * nul = is16 ? memchr16 (data, 0, size) : memchr (data, 0, size); - TAGDBG ("length = %d, encoding = %d, nulled = %d\n", length, encoding, - nulled); + if (nul) + { + if (bytes_without_nul) + * bytes_without_nul = nul - data; + if (bytes_with_nul) + * bytes_with_nul = is16 ? nul + 2 - data : nul + 1 - data; + } + else + { + if (bytes_without_nul) + * bytes_without_nul = size; + if (bytes_with_nul) + * bytes_with_nul = size; + } +} - if (nulled) +char * id3_convert (const char * data, int size, int encoding) +{ + if (encoding == ID3_ENCODING_UTF16) + return str_convert (data, size, "UTF-16", "UTF-8"); + else if (encoding == ID3_ENCODING_UTF16_BE) + return str_convert (data, size, "UTF-16BE", "UTF-8"); + else + return str_to_utf8 (data, size); +} + +char * id3_decode_text (const char * data, int size) +{ + if (size < 1) + return NULL; + + return id3_convert ((const char *) data + 1, size - 1, data[0]); +} + +void id3_associate_string (Tuple * tuple, int field, const char * data, int size) +{ + char * text = id3_decode_text (data, size); + + if (text && text[0]) { - const char * null; + TAGDBG ("Field %i = %s.\n", field, text); + tuple_set_str (tuple, field, text); + } - switch (encoding) - { - case 0: - case 3: - if ((null = memchr (text, 0, length)) == NULL) - return NULL; + str_unref (text); +} - length = null - text; - TAGDBG ("length before null = %d\n", length); +void id3_associate_int (Tuple * tuple, int field, const char * data, int size) +{ + char * text = id3_decode_text (data, size); - if (after != NULL) - * after = null + 1; + if (text && atoi (text) >= 0) + { + TAGDBG ("Field %i = %s.\n", field, text); + tuple_set_int (tuple, field, atoi (text)); + } - break; - case 1: - case 2: - if ((null = memchr16 (text, 0, length)) == NULL) - return NULL; + str_unref (text); +} - length = null - text; - TAGDBG ("length before null = %d\n", length); +void id3_decode_genre (Tuple * tuple, const char * data, int size) +{ + char * text = id3_decode_text (data, size); + int numericgenre; - if (after != NULL) - * after = null + 2; + if (! text) + return; - break; - } + if (text[0] == '(') + numericgenre = atoi (text + 1); + else + numericgenre = atoi (text); + + if (numericgenre > 0) + tuple_set_str (tuple, FIELD_GENRE, convert_numericgenre_to_text (numericgenre)); + else + tuple_set_str (tuple, FIELD_GENRE, text); + + str_unref (text); +} + +void id3_decode_comment (Tuple * tuple, const char * data, int size) +{ + if (size < 4) + return; + + int before_nul, after_nul; + id3_strnlen (data + 4, size - 4, data[0], & before_nul, & after_nul); + + const char * lang = data + 1; + char * type = id3_convert (data + 4, before_nul, data[0]); + char * value = id3_convert (data + 4 + after_nul, size - 4 - after_nul, data[0]); + + TAGDBG ("Comment: lang = %.3s, type = %s, value = %s.\n", lang, type, value); + + if (type && ! type[0] && value) /* blank type = actual comment */ + tuple_set_str (tuple, FIELD_COMMENT, value); + + str_unref (type); + str_unref (value); +} + +static bool_t decode_rva_block (const char * * _data, int * _size, + int * channel, int * adjustment, int * adjustment_unit, int * peak, + int * peak_unit) +{ + const char * data = * _data; + int size = * _size; + int peak_bits; + + if (size < 4) + return FALSE; + + * channel = data[0]; + * adjustment = (char) data[1]; /* first byte is signed */ + * adjustment = (* adjustment << 8) | data[2]; + * adjustment_unit = 512; + peak_bits = data[3]; + + data += 4; + size -= 4; + + TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n", + * channel, * adjustment, * adjustment_unit, peak_bits); + + if (peak_bits > 0 && peak_bits < sizeof (int) * 8) + { + int bytes = (peak_bits + 7) / 8; + int count; + + if (bytes > size) + return FALSE; + + * peak = 0; + * peak_unit = 1 << peak_bits; + + for (count = 0; count < bytes; count ++) + * peak = (* peak << 8) | data[count]; + + data += bytes; + size -= count; + + TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit); + } + else + { + * peak = 0; + * peak_unit = 0; } - switch (encoding) + * _data = data; + * _size = size; + return TRUE; +} + +void id3_decode_rva (Tuple * tuple, const char * data, int size) +{ + const char * domain; + int channel, adjustment, adjustment_unit, peak, peak_unit; + + if (memchr (data, 0, size) == NULL) + return; + + domain = data; + + TAGDBG ("RVA domain: %s\n", domain); + + size -= strlen (domain) + 1; + data += strlen (domain) + 1; + + while (size > 0) { - case 0: - case 3:; - int converted_int = 0; - buffer = str_to_utf8_full (text, length, NULL, & converted_int); - converted = converted_int; - break; - case 1: - if (text[0] == (char) 0xff) - buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16LE", NULL, - & converted, NULL); + if (! decode_rva_block (& data, & size, & channel, & adjustment, + & adjustment_unit, & peak, & peak_unit)) + break; + + if (channel != 1) /* specific channel? */ + continue; + + if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT) == TUPLE_INT) + adjustment = adjustment * (int64_t) tuple_get_int (tuple, + FIELD_GAIN_GAIN_UNIT) / adjustment_unit; else - buffer = g_convert (text + 2, length - 2, "UTF-8", "UTF-16BE", NULL, - & converted, NULL); - - break; - case 2: - buffer = g_convert (text, length, "UTF-8", "UTF-16BE", NULL, - & converted, NULL); - break; + tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, adjustment_unit); + + if (peak_unit) + { + if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT) == TUPLE_INT) + peak = peak * (int64_t) tuple_get_int (tuple, + FIELD_GAIN_PEAK_UNIT) / peak_unit; + else + tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, peak_unit); + } + + if (! g_ascii_strcasecmp (domain, "album")) + { + tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, adjustment); + + if (peak_unit) + tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, peak); + } + else if (! g_ascii_strcasecmp (domain, "track")) + { + tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, adjustment); + + if (peak_unit) + tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, peak); + } } +} + +bool_t id3_decode_picture (const char * data, int size, int * type, + void * * image_data, int64_t * image_size) +{ + const char * nul; + if (size < 2 || ! (nul = memchr (data + 1, 0, size - 2))) + return FALSE; + + const char * body = nul + 2; + int body_size = data + size - body; + + int before_nul2, after_nul2; + id3_strnlen (body, body_size, data[0], & before_nul2, & after_nul2); + + const char * mime = data + 1; + char * desc = id3_convert (body, before_nul2, data[0]); - TAGDBG ("length converted: %d\n", (int) converted); - TAGDBG ("string: %s\n", buffer); + * type = nul[1]; + * image_size = body_size - after_nul2; + * image_data = g_memdup (body + after_nul2, * image_size); - if (_converted != NULL) - * _converted = converted; + TAGDBG ("Picture: mime = %s, type = %d, desc = %s, size = %d.\n", mime, + * type, desc, (int) * image_size); - return buffer; + str_unref (desc); + return TRUE; } diff --git a/src/libaudtag/id3/id3-common.h b/src/libaudtag/id3/id3-common.h index 6a0c61e..1b100ab 100644 --- a/src/libaudtag/id3/id3-common.h +++ b/src/libaudtag/id3/id3-common.h @@ -1,6 +1,6 @@ /* * id3-common.h - * Copyright 2010 John Lindgren + * Copyright 2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -20,25 +20,16 @@ #ifndef AUDTAG_ID3_COMMON_H #define AUDTAG_ID3_COMMON_H -/* - * text: A pointer to the text data to be converted. - * length: The size in bytes of the text data. - * encoding: 0 = ISO-8859-1 or a similar 8-bit character set, 1 = UTF-16 with - * BOM, 2 = UTF-16BE without BOM, 3 = UTF-8. - * nulled: Whether the text data is null-terminated. If this flag is set and no - * null character is found, an error occurs. If this flag is not set, any null - * characters found are included in the converted text. - * converted: A location in which to store the number of bytes converted, or - * NULL. This count does not include the null character appended to the - * converted text, but may include null characters present in the text data. - * This count is undefined if an error occurs. - * after: A location in which to store a pointer to the next character after a - * terminating null character, or NULL. This pointer is undefined if the - * "nulled" flag is not used or if an error occurs. - * returns: A pointer to the converted text with a null character appended, or - * NULL if an error occurs. The text must be freed when no longer needed. - */ -char * convert_text (const char * text, int length, int encoding, bool_t - nulled, int * _converted, const char * * after); +#include <stdint.h> +#include <libaudcore/tuple.h> + +void id3_associate_string (Tuple * tuple, int field, const char * data, int size); +void id3_associate_int (Tuple * tuple, int field, const char * data, int size); +void id3_decode_genre (Tuple * tuple, const char * data, int size); +void id3_decode_comment (Tuple * tuple, const char * data, int size); +void id3_decode_rva (Tuple * tuple, const char * data, int size); + +bool_t id3_decode_picture (const char * data, int size, int * type, + void * * image_data, int64_t * image_size); #endif diff --git a/src/libaudtag/id3/id3v1.c b/src/libaudtag/id3/id3v1.c index 7517d19..16da2d2 100644 --- a/src/libaudtag/id3/id3v1.c +++ b/src/libaudtag/id3/id3v1.c @@ -1,6 +1,6 @@ /* * id3v1.c - * Copyright 2010-2011 Tony Vroon, Michał Lipski, and John Lindgren + * Copyright 2013 John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,150 +17,126 @@ * the use of this software. */ -#include <glib.h> -#include <glib/gstdio.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <glib.h> + #include <libaudcore/audstrings.h> -#include "id3v1.h" -#include "../util.h" -#include <inttypes.h> #include "../tag_module.h" +#include "../util.h" + +#include "id3v1.h" -bool_t id3v1_can_handle_file(VFSFile *f) +#pragma pack(push) +#pragma pack(1) + +typedef struct { + char header[3]; + char title[30]; + char artist[30]; + char album[30]; + char year[4]; + char comment[30]; + unsigned char genre; +} ID3v1Tag; + +typedef struct { + char header[4]; + char title[60]; + char artist[60]; + char album[60]; + unsigned char speed; + char genre[30]; + char start[6]; + char end[6]; +} ID3v1Ext; + +#pragma pack(pop) + +static bool_t read_id3v1_tag (VFSFile * file, ID3v1Tag * tag) { - if (vfs_fseek(f, -128, SEEK_END)) + if (vfs_fseek (file, -sizeof (ID3v1Tag), SEEK_END) < 0) + return FALSE; + if (vfs_fread (tag, 1, sizeof (ID3v1Tag), file) != sizeof (ID3v1Tag)) return FALSE; - char *tag = read_char_data(f, 3); - - if (tag && !strncmp(tag, "TAG", 3)) - { - g_free(tag); - return TRUE; - } - - g_free(tag); - return FALSE; + return ! strncmp (tag->header, "TAG", 3); } -static char *convert_to_utf8(char *str) +static bool_t read_id3v1_ext (VFSFile * file, ID3v1Ext * ext) { - if (!str) - return NULL; + if (vfs_fseek (file, -(sizeof (ID3v1Ext) + sizeof (ID3v1Tag)), SEEK_END) < 0) + return FALSE; + if (vfs_fread (ext, 1, sizeof (ID3v1Ext), file) != sizeof (ID3v1Ext)) + return FALSE; - char *tmp = str; - str = str_to_utf8(str); - g_free(tmp); + return ! strncmp (ext->header, "TAG+", 4); +} - return g_strchomp(str); +static bool_t id3v1_can_handle_file (VFSFile * file) +{ + ID3v1Tag tag; + return read_id3v1_tag (file, & tag); } -bool_t id3v1_read_tag (Tuple * tuple, VFSFile * f) +static bool_t combine_string (Tuple * tuple, int field, const char * str1, + int size1, const char * str2, int size2) { - bool_t genre_set = FALSE; + char str[size1 + size2 + 1]; + + memset (str, 0, sizeof str); + strncpy (str, str1, size1); + strncpy (str + strlen (str), str2, size2); + + g_strchomp (str); - if (vfs_fseek(f, -125, SEEK_END)) + if (! str[0]) return FALSE; - char *title = read_char_data(f, 30); - char *artist = read_char_data(f, 30); - char *album = read_char_data(f, 30); - char *year = read_char_data(f, 4); - char *comment = read_char_data(f, 30); - char *genre = read_char_data(f, 1); - char track = 0; + char * utf8 = str_to_utf8 (str, -1); + if (! utf8) + return FALSE; - if (comment && comment[28] == 0 && comment[29] != 0) - track = comment[29]; + tuple_set_str (tuple, field, utf8); - title = convert_to_utf8(title); - artist = convert_to_utf8(artist); - album = convert_to_utf8(album); - comment = convert_to_utf8(comment); + str_unref (utf8); + return TRUE; +} + +static bool_t id3v1_read_tag (Tuple * tuple, VFSFile * file) +{ + ID3v1Tag tag; + ID3v1Ext ext; - if (vfs_fseek(f, -355, SEEK_END)) + if (! read_id3v1_tag (file, & tag)) return FALSE; - char *tag = read_char_data(f, 4); - - if (tag && ! strncmp (tag, "TAG+", 4)) - { - char *ext_title = convert_to_utf8(read_char_data(f, 60)); - char *ext_artist = convert_to_utf8(read_char_data(f, 60)); - char *ext_album = convert_to_utf8(read_char_data(f, 60)); - char *tmp_title = title ? g_strconcat(title, ext_title, NULL) : NULL; - char *tmp_artist = artist ? g_strconcat(artist, ext_artist, NULL) : NULL; - char *tmp_album = album ? g_strconcat(album, ext_album, NULL) : NULL; - g_free(title); - g_free(artist); - g_free(album); - g_free(ext_title); - g_free(ext_artist); - g_free(ext_album); - title = tmp_title; - artist = tmp_artist; - album = tmp_album; - - if (vfs_fseek (f, -170, SEEK_END)) - goto ERR; - - char *ext_genre = convert_to_utf8(read_char_data(f, 30)); - - if (ext_genre) - { - tuple_set_str(tuple, FIELD_GENRE, NULL, ext_genre); - genre_set = TRUE; - g_free(ext_genre); - } - } - - if (title) - tuple_set_str(tuple, FIELD_TITLE, NULL, title); - if (artist) - tuple_set_str(tuple, FIELD_ARTIST, NULL, artist); - if (album) - tuple_set_str(tuple, FIELD_ALBUM, NULL, album); - if (year) - tuple_set_int(tuple, FIELD_YEAR, NULL, atoi(year)); - if (comment) - tuple_set_str(tuple, FIELD_COMMENT, NULL, comment); - if (track) - tuple_set_int(tuple, FIELD_TRACK_NUMBER, NULL, track); - - if (genre && !genre_set) - tuple_set_str(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(*genre)); - - g_free(title); - g_free(artist); - g_free(album); - g_free(year); - g_free(comment); - g_free(genre); + if (! read_id3v1_ext (file, & ext)) + memset (& ext, 0, sizeof (ID3v1Ext)); - return TRUE; + combine_string (tuple, FIELD_TITLE, tag.title, sizeof tag.title, ext.title, sizeof ext.title); + combine_string (tuple, FIELD_ARTIST, tag.artist, sizeof tag.artist, ext.artist, sizeof ext.artist); + combine_string (tuple, FIELD_ALBUM, tag.album, sizeof tag.album, ext.album, sizeof ext.album); + combine_string (tuple, FIELD_COMMENT, tag.comment, sizeof tag.comment, NULL, 0); -ERR: - g_free (title); - g_free (artist); - g_free (album); - g_free (year); - g_free (comment); - g_free (genre); - return FALSE; -} + SNCOPY (year, tag.year, 4); + if (atoi (year)) + tuple_set_int (tuple, FIELD_YEAR, atoi (year)); -static bool_t id3v1_write_tag (const Tuple * tuple, VFSFile * handle) -{ - fprintf (stderr, "Writing ID3v1 tags is not implemented yet, sorry.\n"); - return FALSE; + if (! tag.comment[28] && tag.comment[29]) + tuple_set_int (tuple, FIELD_TRACK_NUMBER, (unsigned char) tag.comment[29]); + + if (! combine_string (tuple, FIELD_GENRE, ext.genre, sizeof ext.genre, NULL, 0)) + tuple_set_str (tuple, FIELD_GENRE, convert_numericgenre_to_text (tag.genre)); + + return TRUE; } tag_module_t id3v1 = { .name = "ID3v1", .can_handle_file = id3v1_can_handle_file, .read_tag = id3v1_read_tag, - .write_tag = id3v1_write_tag, }; diff --git a/src/libaudtag/id3/id3v22.c b/src/libaudtag/id3/id3v22.c index 40cc345..7e886fd 100644 --- a/src/libaudtag/id3/id3v22.c +++ b/src/libaudtag/id3/id3v22.c @@ -1,5 +1,5 @@ /* - * id3v24.c + * id3v22.c * Copyright 2009-2011 Paula Stanciu, Tony Vroon, John Lindgren, * and William Pitcock * @@ -135,7 +135,7 @@ static bool_t read_header (VFSFile * handle, int * version, bool_t * } static bool_t read_frame (VFSFile * handle, int max_size, int version, - bool_t syncsafe, int * frame_size, char * key, unsigned char * * data, int * size) + bool_t syncsafe, int * frame_size, char * key, char * * data, int * size) { ID3v2FrameHeader header; int i; @@ -178,31 +178,6 @@ static bool_t read_frame (VFSFile * handle, int max_size, int version, return TRUE; } -static char * decode_text_frame (const unsigned char * data, int size) -{ - return convert_text ((const char *) data + 1, size - 1, data[0], FALSE, - NULL, NULL); -} - -static bool_t decode_comment_frame (const unsigned char * _data, int size, char * * - lang, char * * type, char * * value) -{ - const char * data = (const char *) _data; - char * pair, * sep; - int converted; - - pair = convert_text (data + 4, size - 4, data[0], FALSE, & converted, NULL); - - if (pair == NULL || (sep = memchr (pair, 0, converted)) == NULL) - return FALSE; - - * lang = g_strndup (data + 1, 3); - * type = g_strdup (pair); - * value = g_strdup (sep + 1); - - g_free (pair); - return TRUE; -} static int get_frame_id (const char * key) { @@ -217,218 +192,6 @@ static int get_frame_id (const char * key) return -1; } -static void associate_string (Tuple * tuple, int field, const char * - customfield, const unsigned char * data, int size) -{ - char * text = decode_text_frame (data, size); - - if (text == NULL || ! text[0]) - { - g_free (text); - return; - } - - if (customfield != NULL) - TAGDBG ("Custom field %s = %s.\n", customfield, text); - else - TAGDBG ("Field %i = %s.\n", field, text); - - tuple_set_str (tuple, field, customfield, text); - g_free (text); -} - -static void associate_int (Tuple * tuple, int field, const char * - customfield, const unsigned char * data, int size) -{ - char * text = decode_text_frame (data, size); - - if (text == NULL || atoi (text) < 1) - { - g_free (text); - return; - } - - if (customfield != NULL) - TAGDBG ("Custom field %s = %s.\n", customfield, text); - else - TAGDBG ("Field %i = %s.\n", field, text); - - tuple_set_int (tuple, field, customfield, atoi (text)); - g_free (text); -} - -static void decode_comment (Tuple * tuple, const unsigned char * data, int size) -{ - char * lang, * type, * value; - - if (! decode_comment_frame (data, size, & lang, & type, & value)) - return; - - TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); - - if (! type[0]) /* blank type == actual comment */ - tuple_set_str (tuple, FIELD_COMMENT, NULL, value); - - g_free (lang); - g_free (type); - g_free (value); -} - -static void decode_txx (Tuple * tuple, const unsigned char * data, int size) -{ - char * text = decode_text_frame (data, size); - - if (text == NULL) - return; - - char *separator = strchr(text, 0); - - if (separator == NULL) - return; - - char * value = separator + 1; - TAGDBG ("TXX: %s = %s.\n", text, value); - tuple_set_str (tuple, -1, text, value); - - g_free (text); -} - -static bool_t decode_rva_block (const unsigned char * * _data, int * _size, int * - channel, int * adjustment, int * adjustment_unit, int * peak, int * - peak_unit) -{ - const unsigned char * data = * _data; - int size = * _size; - int peak_bits; - - if (size < 4) - return FALSE; - - * channel = data[0]; - * adjustment = (char) data[1]; /* first byte is signed */ - * adjustment = (* adjustment << 8) | data[2]; - * adjustment_unit = 512; - peak_bits = data[3]; - - data += 4; - size -= 4; - - TAGDBG ("RVA block: channel = %d, adjustment = %d/%d, peak bits = %d\n", - * channel, * adjustment, * adjustment_unit, peak_bits); - - if (peak_bits > 0 && peak_bits < sizeof (int) * 8) - { - int bytes = (peak_bits + 7) / 8; - int count; - - if (bytes > size) - return FALSE; - - * peak = 0; - * peak_unit = 1 << peak_bits; - - for (count = 0; count < bytes; count ++) - * peak = (* peak << 8) | data[count]; - - data += bytes; - size -= count; - - TAGDBG ("RVA block: peak = %d/%d\n", * peak, * peak_unit); - } - else - { - * peak = 0; - * peak_unit = 0; - } - - * _data = data; - * _size = size; - return TRUE; -} - -static void decode_rva (Tuple * tuple, const unsigned char * data, int size) -{ - const char * domain; - int channel, adjustment, adjustment_unit, peak, peak_unit; - - if (memchr (data, 0, size) == NULL) - return; - - domain = (const char *) data; - - TAGDBG ("RVA domain: %s\n", domain); - - size -= strlen (domain) + 1; - data += strlen (domain) + 1; - - while (size > 0) - { - if (! decode_rva_block (& data, & size, & channel, & adjustment, - & adjustment_unit, & peak, & peak_unit)) - break; - - if (channel != 1) /* specific channel? */ - continue; - - if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT, NULL) == - TUPLE_INT) - adjustment = adjustment * (int64_t) tuple_get_int (tuple, - FIELD_GAIN_GAIN_UNIT, NULL) / adjustment_unit; - else - tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL, - adjustment_unit); - - if (peak_unit) - { - if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT, NULL) == - TUPLE_INT) - peak = peak * (int64_t) tuple_get_int (tuple, - FIELD_GAIN_PEAK_UNIT, NULL) / peak_unit; - else - tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL, - peak_unit); - } - - if (! g_ascii_strcasecmp (domain, "album")) - { - tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL, adjustment); - - if (peak_unit) - tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL, peak); - } - else if (! g_ascii_strcasecmp (domain, "track")) - { - tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL, adjustment); - - if (peak_unit) - tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL, peak); - } - } -} - -static void decode_genre (Tuple * tuple, const unsigned char * data, int size) -{ - int numericgenre; - char * text = decode_text_frame (data, size); - - if (text == NULL) - return; - - if (text[0] == '(') - numericgenre = atoi (text + 1); - else - numericgenre = atoi (text); - - if (numericgenre > 0) - { - tuple_set_str(tuple, FIELD_GENRE, NULL, convert_numericgenre_to_text(numericgenre)); - return; - } - tuple_set_str(tuple, FIELD_GENRE, NULL, text); - g_free (text); - return; -} - static bool_t id3v22_can_handle_file (VFSFile * handle) { int version, header_size, data_size; @@ -457,58 +220,55 @@ bool_t id3v22_read_tag (Tuple * tuple, VFSFile * handle) { int frame_size, size, id; char key[5]; - unsigned char * data; + char * data; if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) - { - TAGDBG("read_frame failed at pos %i\n", pos); - break; - } + { + TAGDBG("read_frame failed at pos %i\n", pos); + break; + } id = get_frame_id (key); switch (id) { case ID3_ALBUM: - associate_string (tuple, FIELD_ALBUM, NULL, data, size); + id3_associate_string (tuple, FIELD_ALBUM, data, size); break; case ID3_TITLE: - associate_string (tuple, FIELD_TITLE, NULL, data, size); + id3_associate_string (tuple, FIELD_TITLE, data, size); break; case ID3_COMPOSER: - associate_string (tuple, FIELD_COMPOSER, NULL, data, size); + id3_associate_string (tuple, FIELD_COMPOSER, data, size); break; case ID3_COPYRIGHT: - associate_string (tuple, FIELD_COPYRIGHT, NULL, data, size); + id3_associate_string (tuple, FIELD_COPYRIGHT, data, size); break; case ID3_DATE: - associate_string (tuple, FIELD_DATE, NULL, data, size); + id3_associate_string (tuple, FIELD_DATE, data, size); break; case ID3_LENGTH: - associate_int (tuple, FIELD_LENGTH, NULL, data, size); + id3_associate_int (tuple, FIELD_LENGTH, data, size); break; case ID3_FUCKO_ARTIST: case ID3_ARTIST: - associate_string (tuple, FIELD_ARTIST, NULL, data, size); + id3_associate_string (tuple, FIELD_ARTIST, data, size); break; case ID3_TRACKNR: - associate_int (tuple, FIELD_TRACK_NUMBER, NULL, data, size); + id3_associate_int (tuple, FIELD_TRACK_NUMBER, data, size); break; case ID3_YEAR: - associate_int (tuple, FIELD_YEAR, NULL, data, size); + id3_associate_int (tuple, FIELD_YEAR, data, size); break; case ID3_GENRE: - decode_genre (tuple, data, size); + id3_decode_genre (tuple, data, size); break; case ID3_COMMENT: - decode_comment (tuple, data, size); - break; - case ID3_TXX: - decode_txx (tuple, data, size); + id3_decode_comment (tuple, data, size); break; case ID3_RVA: - decode_rva (tuple, data, size); + id3_decode_rva (tuple, data, size); break; default: TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); @@ -522,30 +282,9 @@ bool_t id3v22_read_tag (Tuple * tuple, VFSFile * handle) return TRUE; } -static bool_t parse_pic (const unsigned char * data, int size, char * * mime, - int * type, void * * image_data, int * image_size) +static bool_t id3v22_read_image (VFSFile * handle, void * * image_data, int64_t * image_size) { - const unsigned char * sep; - const unsigned char * after; - - if (size < 2 || (sep = memchr (data + 1, 0, size - 2)) == NULL) - return FALSE; - - after = sep + 2; - * mime = g_strdup ((const char *) data + 1); - * type = sep[1]; - * image_data = g_memdup (after, data + size - after); - * image_size = data + size - after; - - TAGDBG ("PIC: mime = %s, type = %d, size = %d.\n", * mime, - * type, * image_size); - return TRUE; -} - -static bool_t id3v22_read_image (VFSFile * handle, void * * image_data, - int64_t * image_size64) -{ - int version, header_size, data_size, parsed, image_size = 0; + int version, header_size, data_size, parsed; bool_t syncsafe; gsize offset; bool_t found = FALSE; @@ -558,8 +297,7 @@ static bool_t id3v22_read_image (VFSFile * handle, void * * image_data, { int frame_size, size, type; char key[5]; - unsigned char * data; - char * mime; + char * data; int frame_length; if (! read_frame (handle, data_size - parsed, version, syncsafe, @@ -568,11 +306,9 @@ static bool_t id3v22_read_image (VFSFile * handle, void * * image_data, frame_length = size; - if (! strcmp (key, "PIC") && parse_pic (data, frame_length, & mime, & type, - image_data, & image_size)) + if (! strcmp (key, "PIC") && id3_decode_picture (data, frame_length, + & type, image_data, image_size)) { - g_free (mime); - if (type == 3) /* album cover */ found = TRUE; else if (type == 0) /* iTunes */ @@ -588,9 +324,6 @@ static bool_t id3v22_read_image (VFSFile * handle, void * * image_data, parsed += frame_size; } - if (found) - * image_size64 = image_size; - return found; } diff --git a/src/libaudtag/id3/id3v24.c b/src/libaudtag/id3/id3v24.c index 15b78bd..fac1a44 100644 --- a/src/libaudtag/id3/id3v24.c +++ b/src/libaudtag/id3/id3v24.c @@ -1,6 +1,6 @@ /* * id3v24.c - * Copyright 2009-2011 Paula Stanciu, Tony Vroon, John Lindgren, + * Copyright 2009-2014 Paula Stanciu, Tony Vroon, John Lindgren, * Mikael Magnusson, and Michał Lipski * * Redistribution and use in source and binary forms, with or without @@ -22,6 +22,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <libaudcore/audstrings.h> @@ -95,7 +96,7 @@ ID3v2FrameHeader; typedef struct { char key[5]; - unsigned char * data; + char * data; int size; } GenericFrame; @@ -197,6 +198,9 @@ static bool_t read_header (VFSFile * handle, int * version, bool_t * if (! validate_header (& footer, TRUE)) return FALSE; + if (vfs_fseek (handle, sizeof (ID3v2Header), SEEK_SET)) + return FALSE; + * footer_size = sizeof (ID3v2Header); } else @@ -263,15 +267,15 @@ static bool_t read_header (VFSFile * handle, int * version, bool_t * return TRUE; } -static int unsyncsafe (unsigned char * data, int size) +static int unsyncsafe (char * data, int size) { - unsigned char * get = data, * set = data; + char * get = data, * set = data; while (size --) { - unsigned char c = * set ++ = * get ++; + char c = * set ++ = * get ++; - if (c == 0xff && size && ! get[0]) + if (c == (char) 0xff && size && ! get[0]) { size --; get ++; @@ -282,7 +286,7 @@ static int unsyncsafe (unsigned char * data, int size) } static bool_t read_frame (VFSFile * handle, int max_size, int version, - bool_t syncsafe, int * frame_size, char * key, unsigned char * * data, int * size) + bool_t syncsafe, int * frame_size, char * key, char * * data, int * size) { ID3v2FrameHeader header; int skip = 0; @@ -339,44 +343,15 @@ static bool_t read_frame (VFSFile * handle, int max_size, int version, return TRUE; } -static char * decode_text_frame (const unsigned char * data, int size) -{ - return convert_text ((const char *) data + 1, size - 1, data[0], FALSE, - NULL, NULL); -} - -static bool_t decode_comment_frame (const unsigned char * _data, int size, char * * - lang, char * * type, char * * value) -{ - const char * data = (const char *) _data; - char * pair, * sep; - int converted; - - pair = convert_text (data + 4, size - 4, data[0], FALSE, & converted, NULL); - - if (pair == NULL || (sep = memchr (pair, 0, converted)) == NULL) - return FALSE; - - * lang = g_strndup (data + 1, 3); - * type = g_strdup (pair); - * value = g_strdup (sep + 1); - - g_free (pair); - return TRUE; -} - static void free_frame (GenericFrame * frame) { g_free (frame->data); - g_free (frame); + g_slice_free (GenericFrame, frame); } static void free_frame_list (GList * list) { - for (GList * node = list; node; node = node->next) - free_frame (node->data); - - g_list_free (list); + g_list_free_full (list, (GDestroyNotify) free_frame); } static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe, @@ -388,7 +363,7 @@ static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe, { int frame_size, size; char key[5]; - unsigned char * data; + char * data; GenericFrame * frame; if (! read_frame (handle, data_size - pos, version, syncsafe, @@ -397,7 +372,7 @@ static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe, pos += frame_size; - frame = g_malloc (sizeof (GenericFrame)); + frame = g_slice_new (GenericFrame); strcpy (frame->key, key); frame->data = data; frame->size = size; @@ -414,23 +389,23 @@ static void read_all_frames (VFSFile * handle, int version, bool_t syncsafe, } } -static bool_t write_frame (VFSFile * handle, GenericFrame * frame, int * - frame_size) +static bool_t write_frame (int fd, GenericFrame * frame, int version, int * frame_size) { TAGDBG ("Writing frame %s, size %d\n", frame->key, frame->size); ID3v2FrameHeader header; memcpy (header.key, frame->key, 4); - header.size = syncsafe32 (frame->size); - header.size = GUINT32_TO_BE (header.size); + + uint32_t size = (version == 3) ? frame->size : syncsafe32 (frame->size); + header.size = GUINT32_TO_BE (size); + header.flags = 0; - if (vfs_fwrite (& header, 1, sizeof (ID3v2FrameHeader), handle) != sizeof - (ID3v2FrameHeader)) + if (write (fd, & header, sizeof (ID3v2FrameHeader)) != sizeof (ID3v2FrameHeader)) return FALSE; - if (vfs_fwrite (frame->data, 1, frame->size, handle) != frame->size) + if (write (fd, frame->data, frame->size) != frame->size) return FALSE; * frame_size = sizeof (ID3v2FrameHeader) + frame->size; @@ -438,7 +413,8 @@ static bool_t write_frame (VFSFile * handle, GenericFrame * frame, int * } typedef struct { - VFSFile * file; + int fd; + int version; int written_size; } WriteState; @@ -449,33 +425,32 @@ static void write_frame_list (void * key, void * list, void * user) for (GList * node = list; node; node = node->next) { int size; - if (write_frame (state->file, node->data, & size)) + if (write_frame (state->fd, node->data, state->version, & size)) state->written_size += size; } } -static int write_all_frames (VFSFile * handle, GHashTable * dict) +static int write_all_frames (int fd, GHashTable * dict, int version) { - WriteState state = {handle, 0}; + WriteState state = {fd, version, 0}; g_hash_table_foreach (dict, write_frame_list, & state); TAGDBG ("Total frame bytes written = %d.\n", state.written_size); return state.written_size; } -static bool_t write_header (VFSFile * handle, int size, bool_t is_footer) +static bool_t write_header (int fd, int version, int size) { ID3v2Header header; - memcpy (header.magic, is_footer ? "3DI" : "ID3", 3); - header.version = 4; + memcpy (header.magic, "ID3", 3); + header.version = version; header.revision = 0; - header.flags = ID3_HEADER_HAS_FOOTER; + header.flags = 0; header.size = syncsafe32 (size); header.size = GUINT32_TO_BE (header.size); - return vfs_fwrite (& header, 1, sizeof (ID3v2Header), handle) == sizeof - (ID3v2Header); + return write (fd, & header, sizeof (ID3v2Header)) == sizeof (ID3v2Header); } static int get_frame_id (const char * key) @@ -491,46 +466,7 @@ static int get_frame_id (const char * key) return -1; } -static void associate_string (Tuple * tuple, int field, const char * - customfield, const unsigned char * data, int size) -{ - char * text = decode_text_frame (data, size); - - if (text == NULL || ! text[0]) - { - g_free (text); - return; - } - - if (customfield != NULL) - TAGDBG ("Custom field %s = %s.\n", customfield, text); - else - TAGDBG ("Field %i = %s.\n", field, text); - - tuple_set_str (tuple, field, customfield, text); - g_free (text); -} - -static void associate_int (Tuple * tuple, int field, const char * - customfield, const unsigned char * data, int size) -{ - char * text = decode_text_frame (data, size); - - if (text == NULL || atoi (text) < 1) - { - g_free (text); - return; - } - - if (customfield != NULL) - TAGDBG ("Custom field %s = %s.\n", customfield, text); - else - TAGDBG ("Field %i = %s.\n", field, text); - - tuple_set_int (tuple, field, customfield, atoi (text)); - g_free (text); -} - +#if 0 static void decode_private_info (Tuple * tuple, const unsigned char * data, int size) { char * text = g_strndup ((const char *) data, size); @@ -572,164 +508,12 @@ static void decode_private_info (Tuple * tuple, const unsigned char * data, int DONE: g_free (text); } - -static void decode_comment (Tuple * tuple, const unsigned char * data, int size) -{ - char * lang, * type, * value; - - if (! decode_comment_frame (data, size, & lang, & type, & value)) - return; - - TAGDBG ("Comment: lang = %s, type = %s, value = %s.\n", lang, type, value); - - if (! type[0]) /* blank type == actual comment */ - tuple_set_str (tuple, FIELD_COMMENT, NULL, value); - - g_free (lang); - g_free (type); - g_free (value); -} - -static bool_t decode_rva2_block (const unsigned char * * _data, int * _size, int * - channel, int * adjustment, int * adjustment_unit, int * peak, int * - peak_unit) -{ - const unsigned char * data = * _data; - int size = * _size; - int peak_bits; - - if (size < 4) - return FALSE; - - * channel = data[0]; - * adjustment = (char) data[1]; /* first byte is signed */ - * adjustment = (* adjustment << 8) | data[2]; - * adjustment_unit = 512; - peak_bits = data[3]; - - data += 4; - size -= 4; - - TAGDBG ("RVA2 block: channel = %d, adjustment = %d/%d, peak bits = %d\n", - * channel, * adjustment, * adjustment_unit, peak_bits); - - if (peak_bits > 0 && peak_bits < sizeof (int) * 8) - { - int bytes = (peak_bits + 7) / 8; - int count; - - if (bytes > size) - return FALSE; - - * peak = 0; - * peak_unit = 1 << peak_bits; - - for (count = 0; count < bytes; count ++) - * peak = (* peak << 8) | data[count]; - - data += bytes; - size -= count; - - TAGDBG ("RVA2 block: peak = %d/%d\n", * peak, * peak_unit); - } - else - { - * peak = 0; - * peak_unit = 0; - } - - * _data = data; - * _size = size; - return TRUE; -} - -static void decode_rva2 (Tuple * tuple, const unsigned char * data, int size) -{ - const char * domain; - int channel, adjustment, adjustment_unit, peak, peak_unit; - - if (memchr (data, 0, size) == NULL) - return; - - domain = (const char *) data; - - TAGDBG ("RVA2 domain: %s\n", domain); - - size -= strlen (domain) + 1; - data += strlen (domain) + 1; - - while (size > 0) - { - if (! decode_rva2_block (& data, & size, & channel, & adjustment, - & adjustment_unit, & peak, & peak_unit)) - break; - - if (channel != 1) /* specific channel? */ - continue; - - if (tuple_get_value_type (tuple, FIELD_GAIN_GAIN_UNIT, NULL) == - TUPLE_INT) - adjustment = adjustment * (int64_t) tuple_get_int (tuple, - FIELD_GAIN_GAIN_UNIT, NULL) / adjustment_unit; - else - tuple_set_int (tuple, FIELD_GAIN_GAIN_UNIT, NULL, - adjustment_unit); - - if (peak_unit) - { - if (tuple_get_value_type (tuple, FIELD_GAIN_PEAK_UNIT, NULL) == - TUPLE_INT) - peak = peak * (int64_t) tuple_get_int (tuple, - FIELD_GAIN_PEAK_UNIT, NULL) / peak_unit; - else - tuple_set_int (tuple, FIELD_GAIN_PEAK_UNIT, NULL, - peak_unit); - } - - if (! g_ascii_strcasecmp (domain, "album")) - { - tuple_set_int (tuple, FIELD_GAIN_ALBUM_GAIN, NULL, adjustment); - - if (peak_unit) - tuple_set_int (tuple, FIELD_GAIN_ALBUM_PEAK, NULL, peak); - } - else if (! g_ascii_strcasecmp (domain, "track")) - { - tuple_set_int (tuple, FIELD_GAIN_TRACK_GAIN, NULL, adjustment); - - if (peak_unit) - tuple_set_int (tuple, FIELD_GAIN_TRACK_PEAK, NULL, peak); - } - } -} - -static void decode_genre (Tuple * tuple, const unsigned char * data, int size) -{ - int numericgenre; - char * text = decode_text_frame (data, size); - - if (text == NULL) - return; - - if (text[0] == '(') - numericgenre = atoi (text + 1); - else - numericgenre = atoi (text); - - if (numericgenre > 0) - tuple_set_str (tuple, FIELD_GENRE, NULL, - convert_numericgenre_to_text (numericgenre)); - else - tuple_set_str (tuple, FIELD_GENRE, NULL, text); - - g_free (text); - return; -} +#endif static GenericFrame * add_generic_frame (int id, int size, GHashTable * dict) { - GenericFrame * frame = g_malloc (sizeof (GenericFrame)); + GenericFrame * frame = g_slice_new (GenericFrame); strcpy (frame->key, id3_frames[id]); frame->data = g_malloc (size); @@ -756,11 +540,18 @@ static void add_text_frame (int id, const char * text, GHashTable * dict) } TAGDBG ("Adding text frame %s = %s.\n", id3_frames[id], text); - int length = strlen (text); - GenericFrame * frame = add_generic_frame (id, length + 1, dict); - frame->data[0] = 3; /* UTF-8 encoding */ - memcpy (frame->data + 1, text, length); + long words; + uint16_t * utf16 = g_utf8_to_utf16 (text, -1, NULL, & words, NULL); + g_return_if_fail (utf16); + + GenericFrame * frame = add_generic_frame (id, 3 + 2 * words, dict); + + frame->data[0] = 1; /* UTF-16 encoding */ + * (uint16_t *) (frame->data + 1) = 0xfeff; /* byte order mark */ + memcpy (frame->data + 3, utf16, 2 * words); + + g_free (utf16); } static void add_comment_frame (const char * text, GHashTable * dict) @@ -772,18 +563,27 @@ static void add_comment_frame (const char * text, GHashTable * dict) } TAGDBG ("Adding comment frame = %s.\n", text); - int length = strlen (text); - GenericFrame * frame = add_generic_frame (ID3_COMMENT, length + 5, dict); - frame->data[0] = 3; /* UTF-8 encoding */ - strcpy ((char *) frame->data + 1, "eng"); /* well, it *might* be English */ - memcpy (frame->data + 5, text, length); + long words; + uint16_t * utf16 = g_utf8_to_utf16 (text, -1, NULL, & words, NULL); + g_return_if_fail (utf16); + + GenericFrame * frame = add_generic_frame (ID3_COMMENT, 10 + 2 * words, dict); + + frame->data[0] = 1; /* UTF-16 encoding */ + memcpy (frame->data + 1, "eng", 3); /* language */ + * (uint16_t *) (frame->data + 4) = 0xfeff; /* byte order mark */ + * (uint16_t *) (frame->data + 6) = 0; /* end of content description */ + * (uint16_t *) (frame->data + 8) = 0xfeff; /* byte order mark */ + memcpy (frame->data + 10, utf16, 2 * words); + + g_free (utf16); } static void add_frameFromTupleStr (const Tuple * tuple, int field, int id3_field, GHashTable * dict) { - char * str = tuple_get_str (tuple, field, NULL); + char * str = tuple_get_str (tuple, field); add_text_frame (id3_field, str, dict); str_unref (str); } @@ -791,14 +591,14 @@ static void add_frameFromTupleStr (const Tuple * tuple, int field, int static void add_frameFromTupleInt (const Tuple * tuple, int field, int id3_field, GHashTable * dict) { - if (tuple_get_value_type (tuple, field, NULL) != TUPLE_INT) + if (tuple_get_value_type (tuple, field) != TUPLE_INT) { remove_frame (id3_field, dict); return; } char scratch[16]; - snprintf (scratch, sizeof scratch, "%d", tuple_get_int (tuple, field, NULL)); + str_itoa (tuple_get_int (tuple, field), scratch, sizeof scratch); add_text_frame (id3_field, scratch, dict); } @@ -827,7 +627,7 @@ static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle) { int frame_size, size, id; char key[5]; - unsigned char * data; + char * data; if (! read_frame (handle, data_size - pos, version, syncsafe, & frame_size, key, & data, & size)) @@ -838,44 +638,46 @@ static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle) switch (id) { case ID3_ALBUM: - associate_string (tuple, FIELD_ALBUM, NULL, data, size); + id3_associate_string (tuple, FIELD_ALBUM, data, size); break; case ID3_TITLE: - associate_string (tuple, FIELD_TITLE, NULL, data, size); + id3_associate_string (tuple, FIELD_TITLE, data, size); break; case ID3_COMPOSER: - associate_string (tuple, FIELD_COMPOSER, NULL, data, size); + id3_associate_string (tuple, FIELD_COMPOSER, data, size); break; case ID3_COPYRIGHT: - associate_string (tuple, FIELD_COPYRIGHT, NULL, data, size); + id3_associate_string (tuple, FIELD_COPYRIGHT, data, size); break; case ID3_DATE: - associate_string (tuple, FIELD_DATE, NULL, data, size); + id3_associate_string (tuple, FIELD_DATE, data, size); break; case ID3_LENGTH: - associate_int (tuple, FIELD_LENGTH, NULL, data, size); + id3_associate_int (tuple, FIELD_LENGTH, data, size); break; case ID3_ARTIST: - associate_string (tuple, FIELD_ARTIST, NULL, data, size); + id3_associate_string (tuple, FIELD_ARTIST, data, size); break; case ID3_TRACKNR: - associate_int (tuple, FIELD_TRACK_NUMBER, NULL, data, size); + id3_associate_int (tuple, FIELD_TRACK_NUMBER, data, size); break; case ID3_YEAR: case ID3_RECORDING_TIME: - associate_int (tuple, FIELD_YEAR, NULL, data, size); + id3_associate_int (tuple, FIELD_YEAR, data, size); break; case ID3_GENRE: - decode_genre (tuple, data, size); + id3_decode_genre (tuple, data, size); break; case ID3_COMMENT: - decode_comment (tuple, data, size); + id3_decode_comment (tuple, data, size); break; +#if 0 case ID3_PRIVATE: decode_private_info (tuple, data, size); break; +#endif case ID3_RVA2: - decode_rva2 (tuple, data, size); + id3_decode_rva (tuple, data, size); break; default: TAGDBG ("Ignoring unsupported ID3 frame %s.\n", key); @@ -889,33 +691,10 @@ static bool_t id3v24_read_tag (Tuple * tuple, VFSFile * handle) return TRUE; } -static bool_t parse_apic (const unsigned char * _data, int size, char * * mime, - int * type, char * * desc, void * * image_data, int * image_size) -{ - const char * data = (const char *) _data; - const char * sep, * after; - - if (size < 2 || (sep = memchr (data + 1, 0, size - 2)) == NULL) - return FALSE; - - if ((* desc = convert_text (sep + 2, data + size - sep - 2, data[0], TRUE, - NULL, & after)) == NULL) - return FALSE; - - * mime = g_strdup (data + 1); - * type = sep[1]; - * image_data = g_memdup (after, data + size - after); - * image_size = data + size - after; - - TAGDBG ("APIC: mime = %s, type = %d, desc = %s, size = %d.\n", * mime, - * type, * desc, * image_size); - return TRUE; -} - static bool_t id3v24_read_image (VFSFile * handle, void * * image_data, - int64_t * image_size64) + int64_t * image_size) { - int version, header_size, data_size, footer_size, parsed, image_size = 0; + int version, header_size, data_size, footer_size, parsed; bool_t syncsafe; int64_t offset; bool_t found = FALSE; @@ -928,19 +707,15 @@ static bool_t id3v24_read_image (VFSFile * handle, void * * image_data, { int frame_size, size, type; char key[5]; - unsigned char * data; - char * mime, * desc; + char * data; if (! read_frame (handle, data_size - parsed, version, syncsafe, & frame_size, key, & data, & size)) break; - if (! strcmp (key, "APIC") && parse_apic (data, size, & mime, & type, - & desc, image_data, & image_size)) + if (! strcmp (key, "APIC") && id3_decode_picture (data, size, & type, + image_data, image_size)) { - g_free (mime); - g_free (desc); - if (type == 3) /* album cover */ found = TRUE; else if (type == 0) /* iTunes */ @@ -956,26 +731,22 @@ static bool_t id3v24_read_image (VFSFile * handle, void * * image_data, parsed += frame_size; } - if (found) - * image_size64 = image_size; - return found; } static bool_t id3v24_write_tag (const Tuple * tuple, VFSFile * f) { - int version, header_size, data_size, footer_size; + int version = 3; + int header_size, data_size, footer_size; bool_t syncsafe; int64_t offset; - if (! read_header (f, & version, & syncsafe, & offset, & header_size, - & data_size, & footer_size)) - return FALSE; - //read all frames into generic frames; GHashTable * dict = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) str_unref, (GDestroyNotify) free_frame_list); - read_all_frames (f, version, syncsafe, data_size, dict); + + if (read_header (f, & version, & syncsafe, & offset, & header_size, & data_size, & footer_size)) + read_all_frames (f, version, syncsafe, data_size, dict); //make the new frames from tuple and replace in the dictionary the old frames with the new ones add_frameFromTupleStr (tuple, FIELD_TITLE, ID3_TITLE, dict); @@ -985,46 +756,50 @@ static bool_t id3v24_write_tag (const Tuple * tuple, VFSFile * f) add_frameFromTupleInt (tuple, FIELD_TRACK_NUMBER, ID3_TRACKNR, dict); add_frameFromTupleStr (tuple, FIELD_GENRE, ID3_GENRE, dict); - char * comment = tuple_get_str (tuple, FIELD_COMMENT, NULL); + char * comment = tuple_get_str (tuple, FIELD_COMMENT); add_comment_frame (comment, dict); str_unref (comment); - if (! offset) - { - if (! cut_beginning_tag (f, header_size + data_size + footer_size)) - goto ERR; - } - else - { - if (offset + header_size + data_size + footer_size != vfs_fsize (f)) - goto ERR; - if (vfs_ftruncate (f, offset)) - goto ERR; - } + /* location and size of non-tag data */ + int64_t mp3_offset = offset ? 0 : header_size + data_size + footer_size; + int64_t mp3_size = offset ? offset : -1; - offset = vfs_fsize (f); + TempFile temp = {0}; + if (! open_temp_file_for (& temp, f)) + goto ERR; - if (offset < 0 || vfs_fseek (f, offset, SEEK_SET) || ! write_header (f, 0, - FALSE)) + /* write empty header (will be overwritten later) */ + if (! write_header (temp.fd, version, 0)) goto ERR; - data_size = write_all_frames (f, dict); + /* write tag data */ + data_size = write_all_frames (temp.fd, dict, version); + + /* copy non-tag data */ + if (! copy_region_to_temp_file (& temp, f, mp3_offset, mp3_size)) + goto ERR; + + /* go back to beginning and write real header */ + if (lseek (temp.fd, 0, SEEK_SET) < 0 || ! write_header (temp.fd, version, data_size)) + goto ERR; - if (! write_header (f, data_size, TRUE) || vfs_fseek (f, offset, SEEK_SET) - || ! write_header (f, data_size, FALSE)) + if (! replace_with_temp_file (& temp, f)) goto ERR; g_hash_table_destroy (dict); + str_unref (temp.name); return TRUE; ERR: g_hash_table_destroy (dict); + str_unref (temp.name); return FALSE; } tag_module_t id3v24 = { .name = "ID3v2.3/4", + .type = TAG_TYPE_ID3V2, .can_handle_file = id3v24_can_handle_file, .read_tag = id3v24_read_tag, .read_image = id3v24_read_image, diff --git a/src/libaudtag/tag_module.c b/src/libaudtag/tag_module.c index 90f6806..560c5b8 100644 --- a/src/libaudtag/tag_module.c +++ b/src/libaudtag/tag_module.c @@ -32,13 +32,12 @@ #include "ape/ape.h" static tag_module_t * const modules[] = {& id3v24, & id3v22, & ape, & id3v1}; -#define N_MODULES (sizeof modules / sizeof modules[0]) tag_module_t * find_tag_module (VFSFile * fd, int new_type) { int i; - for (i = 0; i < N_MODULES; i ++) + for (i = 0; i < ARRAY_LEN (modules); i ++) { if (vfs_fseek(fd, 0, SEEK_SET)) { @@ -56,7 +55,7 @@ tag_module_t * find_tag_module (VFSFile * fd, int new_type) /* No existing tag; see if we can create a new one. */ if (new_type != TAG_TYPE_NONE) { - for (i = 0; i < N_MODULES; i ++) + for (i = 0; i < ARRAY_LEN (modules); i ++) { if (modules[i]->type == new_type) return modules[i]; diff --git a/src/libaudtag/util.c b/src/libaudtag/util.c index 15ffe55..5abd24b 100644 --- a/src/libaudtag/util.c +++ b/src/libaudtag/util.c @@ -1,6 +1,6 @@ /* * util.c - * Copyright 2009-2011 Paula Stanciu, Tony Vroon, and John Lindgren + * Copyright 2009-2014 Paula Stanciu, Tony Vroon, and John Lindgren * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: @@ -17,165 +17,15 @@ * the use of this software. */ -#include <glib.h> #include <stdio.h> +#include <unistd.h> -#include "util.h" -#include <inttypes.h> - -/* convert windows time to unix time */ -time_t unix_time(uint64_t win_time) -{ - uint64_t t = (uint64_t) ((win_time / 10000000LL) - 11644473600LL); - return (time_t) t; -} - -uint16_t get_year(uint64_t win_time) -{ - GDate *d = g_date_new(); - g_date_set_time_t(d, unix_time(win_time)); - uint16_t year = g_date_get_year(d); - g_date_free(d); - return year; -} - -char * read_char_data (VFSFile * file, int size) -{ - char * value = g_malloc (size + 1); - if (vfs_fread (value, 1, size, file) < size) - { - g_free (value); - return NULL; - } - value[size] = 0; - return value; -} - -bool_t write_char_data(VFSFile * f, char * data, size_t i) -{ - return (vfs_fwrite(data, i, 1, f) == i); -} - -uint8_t read_uint8(VFSFile * fd) -{ - uint16_t i; - if (vfs_fread(&i, 1, 1, fd) == 1) - { - return i; - } - return -1; -} - -uint16_t read_LEuint16(VFSFile * fd) -{ - uint16_t a; - if (vfs_fget_le16(&a, fd)) - return a; - else - return -1; -} - -uint16_t read_BEuint16(VFSFile * fd) -{ - uint16_t a; - if (vfs_fget_be16(&a, fd)) - return a; - else - return -1; -} - -uint32_t read_LEuint32(VFSFile * fd) -{ - uint32_t a; - if (vfs_fget_le32(&a, fd)) - return a; - else - return -1; -} - -uint32_t read_BEuint32(VFSFile * fd) -{ - uint32_t a; - if (vfs_fget_be32(&a, fd)) - return a; - else - return -1; -} - -uint64_t read_LEuint64(VFSFile * fd) -{ - uint64_t a; - if (vfs_fget_le64(&a, fd)) - return a; - else - return -1; -} - -uint64_t read_BEuint64(VFSFile * fd) -{ - uint64_t a; - if (vfs_fget_be64(&a, fd)) - return a; - else - return 1; -} - -bool_t write_uint8(VFSFile * fd, uint8_t val) -{ - return (vfs_fwrite(&val, 1, 1, fd) == 1); -} - -bool_t write_LEuint16(VFSFile * fd, uint16_t val) -{ - uint16_t le_val = GUINT32_TO_LE(val); - return (vfs_fwrite(&le_val, 2, 1, fd) == 2); -} - -bool_t write_BEuint32(VFSFile * fd, uint32_t val) -{ - uint32_t be_val = GUINT32_TO_BE(val); - return (vfs_fwrite(&be_val, 4, 1, fd) == 4); -} - -bool_t write_LEuint32(VFSFile * fd, uint32_t val) -{ - uint32_t le_val = GUINT32_TO_LE(val); - return (vfs_fwrite(&le_val, 4, 1, fd) == 4); -} - -bool_t write_LEuint64(VFSFile * fd, uint64_t val) -{ - uint64_t le_val = GUINT64_TO_LE(val); - return (vfs_fwrite(&le_val, 8, 1, fd) == 8); -} - -bool_t cut_beginning_tag (VFSFile * handle, int64_t tag_size) -{ - unsigned char buffer[16384]; - gsize offset = 0, readed; - - if (! tag_size) - return TRUE; - - do - { - if (vfs_fseek (handle, offset + tag_size, SEEK_SET)) - return FALSE; - - readed = vfs_fread (buffer, 1, sizeof buffer, handle); - - if (vfs_fseek (handle, offset, SEEK_SET)) - return FALSE; +#include <glib.h> +#include <glib/gstdio.h> - if (vfs_fwrite (buffer, 1, readed, handle) != readed) - return FALSE; +#include <libaudcore/audstrings.h> - offset += readed; - } - while (readed); - - return vfs_ftruncate (handle, offset) == 0; -} +#include "util.h" const char *convert_numericgenre_to_text(int numericgenre) { @@ -315,12 +165,10 @@ const char *convert_numericgenre_to_text(int numericgenre) int count; - for (count = 0; count < G_N_ELEMENTS(table); count++) + for (count = 0; count < ARRAY_LEN (table); count++) { if (table[count].numericgenre == numericgenre) - { - return table[count].genre; - } + return table[count].genre; } return "Unknown"; @@ -338,3 +186,90 @@ uint32_t syncsafe32 (uint32_t x) 0xfe00000) << 3); } +bool_t open_temp_file_for (TempFile * temp, VFSFile * file) +{ + char * template = filename_build (g_get_tmp_dir (), "audacious-temp-XXXXXX"); + SCOPY (tempname, template); + str_unref (template); + + temp->fd = g_mkstemp (tempname); + if (temp->fd < 0) + return FALSE; + + temp->name = str_get (tempname); + + return TRUE; +} + +bool_t copy_region_to_temp_file (TempFile * temp, VFSFile * file, int64_t offset, int64_t size) +{ + if (vfs_fseek (file, offset, SEEK_SET) < 0) + return FALSE; + + char buf[16384]; + + while (size < 0 || size > 0) + { + int64_t readsize; + + if (size > 0) + { + readsize = MIN (size, sizeof buf); + if (vfs_fread (buf, 1, readsize, file) != readsize) + return FALSE; + + size -= readsize; + } + else + { + /* negative size means copy to EOF */ + readsize = vfs_fread (buf, 1, sizeof buf, file); + if (! readsize) + break; + } + + int64_t written = 0; + while (written < readsize) + { + int64_t writesize = write (temp->fd, buf + written, readsize - written); + if (writesize <= 0) + return FALSE; + + written += writesize; + } + } + + return TRUE; +} + +bool_t replace_with_temp_file (TempFile * temp, VFSFile * file) +{ + if (lseek (temp->fd, 0, SEEK_SET) < 0) + return FALSE; + + if (vfs_fseek (file, 0, SEEK_SET) < 0) + return FALSE; + + if (vfs_ftruncate (file, 0) < 0) + return FALSE; + + char buf[16384]; + + while (1) + { + int64_t readsize = read (temp->fd, buf, sizeof buf); + if (readsize < 0) + return FALSE; + + if (readsize == 0) + break; + + if (vfs_fwrite (buf, 1, readsize, file) != readsize) + return FALSE; + } + + close (temp->fd); + g_unlink (temp->name); + + return TRUE; +} diff --git a/src/libaudtag/util.h b/src/libaudtag/util.h index 27cfd80..3b80ad7 100644 --- a/src/libaudtag/util.h +++ b/src/libaudtag/util.h @@ -18,17 +18,13 @@ */ #ifndef TAGUTIL_H - #define TAGUTIL_H #include <stdint.h> -#include <time.h> +#include <stdio.h> -#include "libaudcore/tuple.h" #include "libaudcore/vfs.h" -#define BROKEN 1 - enum { GENRE_BLUES = 0, GENRE_CLASSIC_ROCK, @@ -161,36 +157,18 @@ extern bool_t tag_verbose; #define TAGDBG(...) do {if (tag_verbose) {printf ("%s:%d [%s]: ", __FILE__, __LINE__, __FUNCTION__); printf (__VA_ARGS__);}} while (0) -time_t unix_time(uint64_t win_time); - -uint16_t get_year(uint64_t win_time); - -char *read_char_data(VFSFile *fd, int size); -bool_t write_char_data(VFSFile *f, char *data, size_t i); - -uint8_t read_uint8(VFSFile *fd); -uint16_t read_LEuint16(VFSFile *fd); -uint16_t read_BEuint16(VFSFile *fd); -uint32_t read_LEuint32(VFSFile *fd); -uint32_t read_BEuint32(VFSFile *fd); -uint64_t read_LEuint64(VFSFile *fd); -uint64_t read_BEuint64(VFSFile *fd); - - -bool_t write_uint8(VFSFile *fd, uint8_t val); -bool_t write_BEuint16(VFSFile *fd, uint16_t val); -bool_t write_LEuint16(VFSFile *fd, uint16_t val); -bool_t write_BEuint32(VFSFile *fd, uint32_t val); -bool_t write_LEuint32(VFSFile *fd, uint32_t val); -bool_t write_BEuint64(VFSFile *fd, uint64_t val); -bool_t write_LEuint64(VFSFile *fd, uint64_t val); - -uint64_t read_LEint64(VFSFile *fd); -bool_t cut_beginning_tag (VFSFile * handle, int64_t tag_size); - const char *convert_numericgenre_to_text(int numericgenre); uint32_t unsyncsafe32 (uint32_t x); uint32_t syncsafe32 (uint32_t x); -#endif +typedef struct { + char * name; + int fd; +} TempFile; + +bool_t open_temp_file_for (TempFile * temp, VFSFile * file); +bool_t copy_region_to_temp_file (TempFile * temp, VFSFile * file, int64_t offset, int64_t size); +bool_t replace_with_temp_file (TempFile * temp, VFSFile * file); + +#endif /* TAGUTIL_H */ -- cgit v1.2.3