/* -*- linux-c -*- Copyright (C) 2008-2010 Jeremy Evans This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id$ */ #include #include "config.h" #include "decoder/file_decoder.h" #ifdef HAVE_LUA #include #include #include #include #ifdef LUA_HEADER_lua5_1 #include #include #include #else #include #include #include #endif /* LUA_HEADER_DIR */ #include "metadata.h" #include "options.h" #include "playlist.h" #define AQUALUNG_LUA_APPLICATION_TITLE_FUNCTION "application_title" #define AQUALUNG_LUA_TITLE_FORMAT_FUNCTION "playlist_title" #define AQUALUNG_LUA_METADATA_FUNCTION "m" #define AQUALUNG_LUA_FILEINFO_FUNCTION "i" #define FILEINFO_TYPE_FILENAME 0x1 #define FILEINFO_TYPE_SAMPLE_RATE 0x2 #define FILEINFO_TYPE_CHANNELS 0x3 #define FILEINFO_TYPE_BPS 0x4 #define FILEINFO_TYPE_SAMPLES 0x5 #define FILEINFO_TYPE_VOLADJ_DB 0x6 #define FILEINFO_TYPE_VOLADJ_LIN 0x7 #define FILEINFO_TYPE_STREAM 0x8 #define FILEINFO_TYPE_MONO 0x9 extern options_t options; extern char current_file[MAXLEN]; static lua_State * L = NULL; static GtkWidget * l_playlist_menu_entry = NULL; static GtkWidget * l_playlist_menu = NULL; static GtkWidget * l_playlist_menu_sep = NULL; static GtkWidget * l_playlist_menu_reload = NULL; static GMutex * l_mutex = NULL; static const char l_cur_fdec = 'l'; static const char l_cur_menu = 'm'; static const char AQUALUNG_LUA_MAIN_TABLE[] = "Aqualung"; static const char AQUALUNG_LUA_API[] = " \ Aqualung = {raw_playlist_menu={}, has_playlist_menu=false, \ remote_commands={}, keybindings={main={}, playlist={}, store={}}, \ hooks={track_change={}}} \ \ \ function add_playlist_menu_command(path, fn) \ Aqualung.raw_playlist_menu[path] = fn \ end \ \ function add_remote_command(name, fn) \ Aqualung.remote_commands[name] = fn \ end \ \ function add_main_keybinding(name, fn) \ Aqualung.keybindings['main'][name] = fn \ end \ \ function add_playlist_keybinding(name, fn) \ Aqualung.keybindings['playlist'][name] = fn \ end \ \ function add_store_keybinding(name, fn) \ Aqualung.keybindings['store'][name] = fn \ end \ \ function add_hook(type, fn) \ table.insert(Aqualung.hooks[type], fn) \ end \ \ \ function Aqualung.run_hooks(type) \ for i,v in ipairs(Aqualung.hooks[type]) do \ v() \ end \ end \ \ function Aqualung.run_remote_command(name) \ Aqualung.remote_commands[name]() \ end \ \ function Aqualung.run_keybinding(window, name, control, alt, super) \ if alt then \ name = 'alt-' .. name \ end \ if control then \ name = 'control-' .. name \ end \ if super then \ name = 'super-' .. name \ end \ Aqualung.keybindings[window][name]() \ end \ \ function Aqualung.process_playlist_menu() \ for path, fn in pairs(Aqualung.raw_playlist_menu) do \ if type(Aqualung.playlist_menu) ~= 'table' then \ Aqualung.has_playlist_menu = true \ Aqualung.playlist_menu = {} \ end \ local t0 = Aqualung.playlist_menu \ local t1 = Aqualung.playlist_menu \ local p = nil \ for part in string.gmatch(path, '[^/]+') do \ p = part \ t1[p] = t1[p] or {} \ t0 = t1 \ t1 = t1[p] \ end \ t0[p] = fn \ end \ return Aqualung.has_playlist_menu \ end \ \ function Aqualung.build_playlist_menu(gtk_menu) \ Aqualung.build_menu(gtk_menu, Aqualung.add_playlist_menu_command, nil, Aqualung.playlist_menu) \ end \ \ function Aqualung.build_menu(gtk_menu, fn, root, menu) \ for name, submenu_or_fn in pairs(menu) do \ local path = name \ if root then \ path = root .. '/' .. path \ end \ local typ = type(submenu_or_fn) \ if typ == 'table' then \ Aqualung.build_menu(Aqualung.add_submenu(gtk_menu, name), fn, path, submenu_or_fn) \ elseif typ == 'function' then \ fn(gtk_menu, name, path) \ end \ end \ end \ "; /* Glib hash tables are abused as we store ints instead of pointers for the values */ static GHashTable * metadata_type_hash = NULL; static GHashTable * fileinfo_type_hash = NULL; void add_custom_commands_to_playlist_menu(void); void setup_extended_title_formatting(void); int metadata_type_int(char * type_string) { return (int)(long)g_hash_table_lookup(metadata_type_hash, type_string); } int fileinfo_type_int(char * type_string) { return (int)(long)g_hash_table_lookup(fileinfo_type_hash, type_string); } /* Caller is responsible for freeing returned string */ char * metadata_value_all(metadata_t * meta, int type) { int l = 0; int t = 0; char * news; char * s = NULL; char * tmp_str; meta_frame_t * mframe = NULL; while ((mframe = metadata_pref_frame_by_type(meta, type, mframe)) != NULL) { if (mframe->field_val != NULL) { tmp_str = mframe->field_val; l = strlen(tmp_str); } else { if ((l = asprintf(&tmp_str, "%f", mframe->float_val)) == -1) { tmp_str = NULL; goto metadata_value_all_cleanup; } l += 1; } if (s == NULL) { t = l + 1; if ((s = (char *)malloc(t)) == NULL) { goto metadata_value_all_cleanup; } s[0] = '\0'; } else { t += l + 3; if ((news = (char *)realloc(s, t)) == NULL) { goto metadata_value_all_cleanup; } s = news; strncat(s, ", ", 2); } strncat(s, tmp_str, l); if (mframe->field_val == NULL) { free(tmp_str); } } return s; metadata_value_all_cleanup: free(s); if (mframe->field_val == NULL) { free(tmp_str); } return NULL; } static file_decoder_t * l_get_cur_fdec(lua_State * L) { file_decoder_t * fdec; lua_pushlightuserdata(L, (void *)&l_cur_fdec); lua_gettable(L, LUA_REGISTRYINDEX); fdec = (file_decoder_t *)lua_touserdata(L, -1); if (fdec == NULL) { luaL_error(L, "m or l can only be called inside playlist_title or application_title"); } return fdec; } static int l_metadata_value(lua_State * L) { char * s = NULL; int i; file_decoder_t * fdec; s = (char *)lua_tostring(L, 1); if((i = metadata_type_int(s)) == 0){ luaL_error(L, "invalid metadata field: %s", s); } lua_pop(L, 1); fdec = l_get_cur_fdec(L); if ((fdec->meta != NULL) && \ ((s = metadata_value_all(fdec->meta, i)) != NULL)) { lua_pushstring(L, s); free(s); } else { lua_pushstring(L, ""); } return 1; } static int l_fileinfo_value(lua_State * L) { char * s = NULL; int i; file_decoder_t * fdec; s = (char *)lua_tostring(L, 1); if((i = fileinfo_type_int(s)) == 0){ luaL_error(L, "invalid fileinfo field: %s", s); } lua_pop(L, 1); fdec = l_get_cur_fdec(L); switch (i) { case FILEINFO_TYPE_FILENAME: lua_pushstring(L, fdec->filename); break; case FILEINFO_TYPE_SAMPLE_RATE: lua_pushinteger(L, fdec->fileinfo.sample_rate); break; case FILEINFO_TYPE_CHANNELS: lua_pushinteger(L, fdec->fileinfo.channels); break; case FILEINFO_TYPE_BPS: lua_pushinteger(L, fdec->fileinfo.bps); break; case FILEINFO_TYPE_SAMPLES: lua_pushinteger(L, fdec->fileinfo.total_samples); break; case FILEINFO_TYPE_VOLADJ_DB: lua_pushnumber(L, fdec->voladj_db); break; case FILEINFO_TYPE_VOLADJ_LIN: lua_pushnumber(L, fdec->voladj_lin); break; case FILEINFO_TYPE_STREAM: lua_pushboolean(L, fdec->is_stream); break; case FILEINFO_TYPE_MONO: lua_pushboolean(L, fdec->fileinfo.is_mono); break; default: luaL_error(L, "fileinfo type not handled: %d", i); break; } return 1; } static int l_add_submenu(lua_State * L) { char * name = NULL; GtkWidget * menu; GtkWidget * entry; GtkWidget * submenu; menu = (GtkWidget *)lua_touserdata(L, 1); name = (char *)lua_tostring(L, 2); if (name == NULL || menu == NULL) { luaL_error(L, "Aqualung.add_submenu arguments: first argument must be userdata, and second must be a string"); } entry = gtk_menu_item_new_with_label(name); gtk_menu_shell_append(GTK_MENU_SHELL(menu), entry); submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(entry), submenu); gtk_widget_show(entry); lua_pushlightuserdata(L, (void *)submenu); return 1; } int l_selected_files_foreach(playlist_t * pl, GtkTreeIter * iter, void * user_data) { playlist_data_t * data; gtk_tree_model_get(GTK_TREE_MODEL(pl->store), iter, PL_COL_DATA, &data, -1); if (data->file != NULL) { ++(*(int *)user_data); lua_pushinteger(L, *(int *)user_data); lua_pushstring(L, data->file); lua_settable(L, -3); } return 0; } int l_selected_files(lua_State * L) { int i = 0; playlist_t * pl; lua_newtable(L); if ((pl = playlist_get_current()) != NULL) { playlist_foreach_selected(pl, l_selected_files_foreach, &i); } return 1; } int l_current_file(lua_State * L) { if (current_file[0] == '\0') { lua_pushnil(L); } else { lua_pushstring(L, current_file); } return 1; } void custom_playlist_menu_cb(gpointer path) { int error = 0; g_mutex_lock(l_mutex); lua_getglobal(L, AQUALUNG_LUA_MAIN_TABLE); lua_getfield(L, -1, "raw_playlist_menu"); lua_getfield(L, -1, (const char *)path); error = lua_pcall(L, 0, 0, 0); if (error) { fprintf(stderr, "Error: in callback function for menu command %s: %s\n", (char *)path, lua_tostring(L, -1)); lua_pop(L, 1); } g_mutex_unlock(l_mutex); } static int l_add_playlist_menu_command(lua_State * L) { int num_chars; char * name = NULL; char * path = NULL; char * lua_path = NULL; GtkWidget * entry; GtkWidget * menu; menu = (GtkWidget *)lua_touserdata(L, 1); name = (char *)lua_tostring(L, 2); path = (char *)lua_tostring(L, 3); if (name == NULL || menu == NULL || path == NULL) { luaL_error(L, "Aqualung.add_playlist_menu_command arguments: first argument must be userdata, and second and third must be strings"); } /* Copy the string to a Lua userdata for a constant valid address and automatic cleanup */ num_chars = strlen(path); lua_path = lua_newuserdata(L, num_chars + 1); strncpy(lua_path, path, num_chars); entry = gtk_menu_item_new_with_label(name); gtk_menu_shell_append(GTK_MENU_SHELL(menu), entry); g_signal_connect_swapped(G_OBJECT(entry), "activate", G_CALLBACK(custom_playlist_menu_cb), (gpointer)path); gtk_widget_show(entry); return 0; } void reload_lua_cb(gpointer data) { setup_extended_title_formatting(); } void setup_extended_title_formatting(void) { int error; options.use_ext_title_format = options.ext_title_format_file[0] != '\0'; if (l_playlist_menu_entry == NULL) { l_playlist_menu_sep = gtk_separator_menu_item_new(); l_playlist_menu_reload = gtk_menu_item_new_with_label("Reload Lua interpreter"); l_playlist_menu_entry = gtk_menu_item_new_with_label("Custom Commands"); g_signal_connect_swapped(G_OBJECT(l_playlist_menu_reload), "activate", G_CALLBACK(reload_lua_cb), NULL); } gtk_widget_hide(l_playlist_menu_sep); gtk_widget_hide(l_playlist_menu_reload); gtk_widget_hide(l_playlist_menu_entry); if (l_mutex == NULL) { if (!options.use_ext_title_format) { /* Save memory if extension file is never used */ return; } l_mutex = g_mutex_new(); } g_mutex_lock(l_mutex); if (metadata_type_hash == NULL) { metadata_type_hash = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(metadata_type_hash, "title", (gpointer)META_FIELD_TITLE); g_hash_table_insert(metadata_type_hash, "artist", (gpointer)META_FIELD_ARTIST); g_hash_table_insert(metadata_type_hash, "album", (gpointer)META_FIELD_ALBUM); g_hash_table_insert(metadata_type_hash, "date", (gpointer)META_FIELD_DATE); g_hash_table_insert(metadata_type_hash, "genre", (gpointer)META_FIELD_GENRE); g_hash_table_insert(metadata_type_hash, "trackno", (gpointer)META_FIELD_TRACKNO); g_hash_table_insert(metadata_type_hash, "comment", (gpointer)META_FIELD_COMMENT); g_hash_table_insert(metadata_type_hash, "disc", (gpointer)META_FIELD_DISC); g_hash_table_insert(metadata_type_hash, "performer", (gpointer)META_FIELD_PERFORMER); g_hash_table_insert(metadata_type_hash, "description", (gpointer)META_FIELD_DESCRIPTION); g_hash_table_insert(metadata_type_hash, "organization", (gpointer)META_FIELD_ORGANIZATION); g_hash_table_insert(metadata_type_hash, "location", (gpointer)META_FIELD_LOCATION); g_hash_table_insert(metadata_type_hash, "contact", (gpointer)META_FIELD_CONTACT); g_hash_table_insert(metadata_type_hash, "license", (gpointer)META_FIELD_LICENSE); g_hash_table_insert(metadata_type_hash, "copyright", (gpointer)META_FIELD_COPYRIGHT); g_hash_table_insert(metadata_type_hash, "isrc", (gpointer)META_FIELD_ISRC); g_hash_table_insert(metadata_type_hash, "version", (gpointer)META_FIELD_VERSION); g_hash_table_insert(metadata_type_hash, "subtitle", (gpointer)META_FIELD_SUBTITLE); g_hash_table_insert(metadata_type_hash, "debut_album", (gpointer)META_FIELD_DEBUT_ALBUM); g_hash_table_insert(metadata_type_hash, "publisher", (gpointer)META_FIELD_PUBLISHER); g_hash_table_insert(metadata_type_hash, "conductor", (gpointer)META_FIELD_CONDUCTOR); g_hash_table_insert(metadata_type_hash, "composer", (gpointer)META_FIELD_COMPOSER); g_hash_table_insert(metadata_type_hash, "publication_right", (gpointer)META_FIELD_PRIGHT); g_hash_table_insert(metadata_type_hash, "file", (gpointer)META_FIELD_FILE); g_hash_table_insert(metadata_type_hash, "ean_upc", (gpointer)META_FIELD_EAN_UPC); g_hash_table_insert(metadata_type_hash, "isbn", (gpointer)META_FIELD_ISBN); g_hash_table_insert(metadata_type_hash, "catalog", (gpointer)META_FIELD_CATALOG); g_hash_table_insert(metadata_type_hash, "label_code", (gpointer)META_FIELD_LC); g_hash_table_insert(metadata_type_hash, "record_date", (gpointer)META_FIELD_RECORD_DATE); g_hash_table_insert(metadata_type_hash, "record_location", (gpointer)META_FIELD_RECORD_LOC); g_hash_table_insert(metadata_type_hash, "media", (gpointer)META_FIELD_MEDIA); g_hash_table_insert(metadata_type_hash, "index", (gpointer)META_FIELD_INDEX); g_hash_table_insert(metadata_type_hash, "related", (gpointer)META_FIELD_RELATED); g_hash_table_insert(metadata_type_hash, "abstract", (gpointer)META_FIELD_ABSTRACT); g_hash_table_insert(metadata_type_hash, "language", (gpointer)META_FIELD_LANGUAGE); g_hash_table_insert(metadata_type_hash, "bibliography", (gpointer)META_FIELD_BIBLIOGRAPHY); g_hash_table_insert(metadata_type_hash, "introplay", (gpointer)META_FIELD_INTROPLAY); g_hash_table_insert(metadata_type_hash, "bpm", (gpointer)META_FIELD_TBPM); g_hash_table_insert(metadata_type_hash, "encoding_time", (gpointer)META_FIELD_TDEN); g_hash_table_insert(metadata_type_hash, "playlist_delay", (gpointer)META_FIELD_TDLY); g_hash_table_insert(metadata_type_hash, "original_release_time", (gpointer)META_FIELD_TDOR); g_hash_table_insert(metadata_type_hash, "release_time", (gpointer)META_FIELD_TDRL); g_hash_table_insert(metadata_type_hash, "tagging_time", (gpointer)META_FIELD_TDTG); g_hash_table_insert(metadata_type_hash, "encoded_by", (gpointer)META_FIELD_TENC); g_hash_table_insert(metadata_type_hash, "lyricist", (gpointer)META_FIELD_T_E_X_T); g_hash_table_insert(metadata_type_hash, "filetype", (gpointer)META_FIELD_TFLT); g_hash_table_insert(metadata_type_hash, "involved_people", (gpointer)META_FIELD_TIPL); g_hash_table_insert(metadata_type_hash, "content_group", (gpointer)META_FIELD_TIT1); g_hash_table_insert(metadata_type_hash, "initial_key", (gpointer)META_FIELD_TKEY); g_hash_table_insert(metadata_type_hash, "length", (gpointer)META_FIELD_TLEN); g_hash_table_insert(metadata_type_hash, "musician_credits", (gpointer)META_FIELD_TMCL); g_hash_table_insert(metadata_type_hash, "mood", (gpointer)META_FIELD_TMOO); g_hash_table_insert(metadata_type_hash, "original_album", (gpointer)META_FIELD_TOAL); g_hash_table_insert(metadata_type_hash, "original_filename", (gpointer)META_FIELD_TOFN); g_hash_table_insert(metadata_type_hash, "original_lyricist", (gpointer)META_FIELD_TOLY); g_hash_table_insert(metadata_type_hash, "original_artist", (gpointer)META_FIELD_TOPE); g_hash_table_insert(metadata_type_hash, "file_owner", (gpointer)META_FIELD_TOWN); g_hash_table_insert(metadata_type_hash, "band", (gpointer)META_FIELD_TPE2); g_hash_table_insert(metadata_type_hash, "remixed", (gpointer)META_FIELD_TPE4); g_hash_table_insert(metadata_type_hash, "set_part", (gpointer)META_FIELD_TPOS); g_hash_table_insert(metadata_type_hash, "produced", (gpointer)META_FIELD_TPRO); g_hash_table_insert(metadata_type_hash, "station_name", (gpointer)META_FIELD_TRSN); g_hash_table_insert(metadata_type_hash, "station_owner", (gpointer)META_FIELD_TRSO); g_hash_table_insert(metadata_type_hash, "album_order", (gpointer)META_FIELD_TSOA); g_hash_table_insert(metadata_type_hash, "performer_order", (gpointer)META_FIELD_TSOP); g_hash_table_insert(metadata_type_hash, "title_order", (gpointer)META_FIELD_TSOT); g_hash_table_insert(metadata_type_hash, "software", (gpointer)META_FIELD_TSSE); g_hash_table_insert(metadata_type_hash, "set_subtitle", (gpointer)META_FIELD_TSST); g_hash_table_insert(metadata_type_hash, "user_text", (gpointer)META_FIELD_TXXX); g_hash_table_insert(metadata_type_hash, "commercial_info", (gpointer)META_FIELD_WCOM); g_hash_table_insert(metadata_type_hash, "legal_info", (gpointer)META_FIELD_WCOP); g_hash_table_insert(metadata_type_hash, "file_website", (gpointer)META_FIELD_WOAF); g_hash_table_insert(metadata_type_hash, "artist_website", (gpointer)META_FIELD_WOAR); g_hash_table_insert(metadata_type_hash, "source_website", (gpointer)META_FIELD_WOAS); g_hash_table_insert(metadata_type_hash, "station_website", (gpointer)META_FIELD_WORS); g_hash_table_insert(metadata_type_hash, "payment", (gpointer)META_FIELD_WPAY); g_hash_table_insert(metadata_type_hash, "publisher_website", (gpointer)META_FIELD_WPUB); g_hash_table_insert(metadata_type_hash, "user_url", (gpointer)META_FIELD_WXXX); g_hash_table_insert(metadata_type_hash, "vendor", (gpointer)META_FIELD_VENDOR); g_hash_table_insert(metadata_type_hash, "rg_loudness", (gpointer)META_FIELD_RG_REFLOUDNESS); g_hash_table_insert(metadata_type_hash, "track_gain", (gpointer)META_FIELD_RG_TRACK_GAIN); g_hash_table_insert(metadata_type_hash, "track_peak", (gpointer)META_FIELD_RG_TRACK_PEAK); g_hash_table_insert(metadata_type_hash, "album_gain", (gpointer)META_FIELD_RG_ALBUM_GAIN); g_hash_table_insert(metadata_type_hash, "album_peak", (gpointer)META_FIELD_RG_ALBUM_PEAK); g_hash_table_insert(metadata_type_hash, "icy_name", (gpointer)META_FIELD_ICY_NAME); g_hash_table_insert(metadata_type_hash, "icy_descr", (gpointer)META_FIELD_ICY_DESCR); g_hash_table_insert(metadata_type_hash, "icy_genre", (gpointer)META_FIELD_ICY_GENRE); g_hash_table_insert(metadata_type_hash, "rva", (gpointer)META_FIELD_RVA2); fileinfo_type_hash = g_hash_table_new(g_str_hash, g_str_equal); g_hash_table_insert(fileinfo_type_hash, "filename", (gpointer)FILEINFO_TYPE_FILENAME); g_hash_table_insert(fileinfo_type_hash, "sample_rate", (gpointer)FILEINFO_TYPE_SAMPLE_RATE); g_hash_table_insert(fileinfo_type_hash, "channels", (gpointer)FILEINFO_TYPE_CHANNELS); g_hash_table_insert(fileinfo_type_hash, "bps", (gpointer)FILEINFO_TYPE_BPS); g_hash_table_insert(fileinfo_type_hash, "samples", (gpointer)FILEINFO_TYPE_SAMPLES); g_hash_table_insert(fileinfo_type_hash, "voladj_db", (gpointer)FILEINFO_TYPE_VOLADJ_DB); g_hash_table_insert(fileinfo_type_hash, "voladj_lin", (gpointer)FILEINFO_TYPE_VOLADJ_LIN); g_hash_table_insert(fileinfo_type_hash, "stream", (gpointer)FILEINFO_TYPE_STREAM); g_hash_table_insert(fileinfo_type_hash, "mono", (gpointer)FILEINFO_TYPE_MONO); } if (l_playlist_menu != NULL) { gtk_widget_destroy(l_playlist_menu); l_playlist_menu = NULL; } if (L != NULL) { lua_close(L); L = NULL; } if(!options.use_ext_title_format) { g_mutex_unlock(l_mutex); return; } L = lua_open(); luaL_openlibs(L); error = luaL_dostring(L, AQUALUNG_LUA_API); if (error) { fprintf(stderr, "Programmer Error: in AQUALUNG_LUA_API: %s\n", lua_tostring(L, -1)); exit(1); } lua_getglobal(L, AQUALUNG_LUA_MAIN_TABLE); lua_pushstring(L, "add_submenu"); lua_pushcfunction(L, l_add_submenu); lua_settable(L, 1); lua_pushstring(L, "add_playlist_menu_command"); lua_pushcfunction(L, l_add_playlist_menu_command); lua_settable(L, 1); lua_pushcfunction(L, l_metadata_value); lua_setglobal(L, AQUALUNG_LUA_METADATA_FUNCTION); lua_pushcfunction(L, l_fileinfo_value); lua_setglobal(L, AQUALUNG_LUA_FILEINFO_FUNCTION); lua_pushcfunction(L, l_selected_files); lua_setglobal(L, "selected_files"); lua_pushcfunction(L, l_current_file); lua_setglobal(L, "current_file"); error = luaL_dofile(L, options.ext_title_format_file); if (error) { fprintf(stderr, "Error: while loading your .lua extension file (%s): %s\n", options.ext_title_format_file, lua_tostring(L, -1)); options.use_ext_title_format = 0; lua_pop(L, 1); } add_custom_commands_to_playlist_menu(); gtk_widget_show(l_playlist_menu_sep); gtk_widget_show(l_playlist_menu_reload); g_mutex_unlock(l_mutex); } char * l_title_format(const char * function_name, file_decoder_t * fdec) { int error; char * s = NULL; if (options.use_ext_title_format) { g_mutex_lock(l_mutex); /* Set current file decoder in Lua registry */ lua_pushlightuserdata(L, (void *)&l_cur_fdec); lua_pushlightuserdata(L, (void *)fdec); lua_settable(L, LUA_REGISTRYINDEX); lua_getglobal(L, function_name); error = lua_pcall(L, 0, 1, 0); if (error) { fprintf(stderr, "Error: while executing function %s in your .lua extension file (%s): %s\n", function_name, options.ext_title_format_file, lua_tostring(L, -1)); } else { s = strdup(lua_tostring(L, -1)); } lua_pop(L, 1); /* Remove current file decoder in Lua registry * Necessary so that future calls to m and l in * Lua don't try to use pointers that are no longer * valid. */ lua_pushlightuserdata(L, (void *)&l_cur_fdec); lua_pushlightuserdata(L, NULL); lua_settable(L, LUA_REGISTRYINDEX); g_mutex_unlock(l_mutex); } return s; } char * extended_title_format(file_decoder_t * fdec) { return l_title_format(AQUALUNG_LUA_TITLE_FORMAT_FUNCTION, fdec); } char * application_title_format(file_decoder_t * fdec) { char * s = NULL; if ((s = l_title_format(AQUALUNG_LUA_APPLICATION_TITLE_FUNCTION, fdec)) == NULL) { s = l_title_format(AQUALUNG_LUA_TITLE_FORMAT_FUNCTION, fdec); } return s; } void add_custom_commands_to_playlist_menu(void) { int error; lua_getglobal(L, AQUALUNG_LUA_MAIN_TABLE); lua_getfield(L, 1, "process_playlist_menu"); error = lua_pcall(L, 0, 1, 0); if (error) { fprintf(stderr, "Error: in Aqualung.process_playlist_menu: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); } else if(lua_toboolean(L, -1)) { lua_pop(L, 1); l_playlist_menu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(l_playlist_menu_entry), l_playlist_menu); lua_getfield(L, 1, "build_playlist_menu"); lua_pushlightuserdata(L, (void *)l_playlist_menu); error = lua_pcall(L, 1, 0, 0); if (error) { fprintf(stderr, "Error: in Aqualung.build_playlist_menu: %s\n", lua_tostring(L, -1)); lua_pop(L, 1); } else { gtk_widget_show(l_playlist_menu_entry); } } } void add_custom_command_menu_to_playlist_menu(GtkWidget * menu) { gtk_menu_shell_append(GTK_MENU_SHELL(menu), l_playlist_menu_sep); gtk_menu_shell_append(GTK_MENU_SHELL(menu), l_playlist_menu_reload); gtk_menu_shell_append(GTK_MENU_SHELL(menu), l_playlist_menu_entry); } void run_custom_remote_command(char * command) { int error; if (options.use_ext_title_format) { g_mutex_lock(l_mutex); lua_getglobal(L, AQUALUNG_LUA_MAIN_TABLE); lua_getfield(L, 1, "run_remote_command"); lua_pushstring(L, command); error = lua_pcall(L, 1, 0, 0); if (error) { fprintf(stderr, "Error: while running remote command %s: %s\n", command, lua_tostring(L, -1)); lua_pop(L, 1); } g_mutex_unlock(l_mutex); } } void run_custom_keybinding(char * window, char * keyname, guint state) { int error; if (options.use_ext_title_format) { g_mutex_lock(l_mutex); lua_getglobal(L, AQUALUNG_LUA_MAIN_TABLE); lua_getfield(L, 1, "run_keybinding"); lua_pushstring(L, window); lua_pushstring(L, keyname); lua_pushboolean(L, state & GDK_CONTROL_MASK); lua_pushboolean(L, state & GDK_MOD1_MASK); lua_pushboolean(L, state & GDK_MOD4_MASK); error = lua_pcall(L, 5, 0, 0); if (error) { fprintf(stderr, "Error: while running keybinding for key %s with state 0x%x: %s\n", keyname, state, lua_tostring(L, -1)); lua_pop(L, 1); } g_mutex_unlock(l_mutex); } } void run_custom_main_keybinding(char * keyname, guint state){ run_custom_keybinding("main", keyname, state); } void run_custom_playlist_keybinding(char * keyname, guint state){ run_custom_keybinding("playlist", keyname, state); } void run_custom_store_keybinding(char * keyname, guint state){ run_custom_keybinding("store", keyname, state); } void run_hooks(char * type) { int error; if (options.use_ext_title_format) { g_mutex_lock(l_mutex); lua_getglobal(L, AQUALUNG_LUA_MAIN_TABLE); lua_getfield(L, 1, "run_hooks"); lua_pushstring(L, type); error = lua_pcall(L, 1, 0, 0); if (error) { fprintf(stderr, "Error: while running %s hooks: %s\n", type, lua_tostring(L, -1)); lua_pop(L, 1); } g_mutex_unlock(l_mutex); } } #else void setup_extended_title_formatting(void){} void add_custom_command_menu_to_playlist_menu(GtkWidget* menu){} void run_hooks(char * type){} void run_custom_remote_command(char * command){} void run_custom_main_keybinding(char * keyname, guint state){} void run_custom_playlist_keybinding(char * keyname, guint state){} void run_custom_store_keybinding(char * keyname, guint state){} char * extended_title_format(file_decoder_t * fdec){return NULL;} char * application_title_format(file_decoder_t * fdec){return NULL;} #endif /* HAVE_LUA */