Atrinik Client 2.5
gui/mplayer.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00033 #define FILE_MPLAYER_BLACKLIST "mplayer.blacklist"
00034 
00037 #define BLACKLIST_ALL_DELAY 1500
00038 
00040 static uint8 shuffle = 0;
00042 static uint8 *shuffle_blacklist = NULL;
00044 static button_struct button_play, button_shuffle, button_blacklist, button_close, button_help;
00045 
00049 static void list_handle_enter(list_struct *list)
00050 {
00051     sound_start_bg_music(list->text[list->row_selected - 1][0], setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_MUSIC), -1);
00052     sound_map_background(1);
00053     shuffle = 0;
00054 }
00055 
00058 static const char *list_text_color_hook(list_struct *list, const char *default_color, uint32 row, uint32 col)
00059 {
00060     (void) list;
00061     (void) col;
00062 
00063     if (shuffle_blacklist[row])
00064     {
00065         return COLOR_RED;
00066     }
00067 
00068     return default_color;
00069 }
00070 
00075 static void mplayer_do_shuffle(list_struct *list)
00076 {
00077     size_t i;
00078     uint8 found_num = 0;
00079     uint32 *row_ids, row_num, selected;
00080 
00081     /* Calculate whether there are enough non-blacklisted songs to
00082      * shuffle through. */
00083     for (i = 0; i < list->rows - 1 && found_num < 2; i++)
00084     {
00085         if (!shuffle_blacklist[i])
00086         {
00087             found_num++;
00088         }
00089     }
00090 
00091     if (found_num < 2)
00092     {
00093         return;
00094     }
00095 
00096     /* Build a list containing non-blacklisted row IDs. */
00097     row_num = 0;
00098     row_ids = malloc(sizeof(*row_ids) * (list->rows - 1));
00099 
00100     for (i = 0; i < list->rows - 1; i++)
00101     {
00102         if (!shuffle_blacklist[i])
00103         {
00104             row_ids[row_num++] = i;
00105         }
00106     }
00107 
00108     /* Select a row ID at random. */
00109     selected = row_ids[rndm(1, row_num) - 1];
00110     free(row_ids);
00111 
00112     list->row_selected = selected + 1;
00113     list->row_offset = MIN(list->rows - list->max_rows, selected);
00114     sound_start_bg_music(list->text[list->row_selected - 1][0], setting_get_int(OPT_CAT_SOUND, OPT_VOLUME_MUSIC), 0);
00115     cur_widget[MPLAYER_ID]->redraw = 1;
00116 }
00117 
00120 static void mplayer_check_shuffle()
00121 {
00122     if (!sound_playing_music())
00123     {
00124         mplayer_do_shuffle(list_exists(LIST_MPLAYER));
00125     }
00126 }
00127 
00133 static int mplayer_blacklisted(list_struct *list)
00134 {
00135     if (list && shuffle_blacklist && shuffle_blacklist[list->row_selected - 1])
00136     {
00137         return 1;
00138     }
00139 
00140     return 0;
00141 }
00142 
00146 static void mplayer_blacklist_toggle(list_struct *list)
00147 {
00148     if (list && shuffle_blacklist)
00149     {
00150         /* Clear blacklist status. */
00151         if (shuffle_blacklist[list->row_selected - 1])
00152         {
00153             shuffle_blacklist[list->row_selected - 1] = 0;
00154         }
00155         /* Enable blacklist status. */
00156         else
00157         {
00158             shuffle_blacklist[list->row_selected - 1] = 1;
00159 
00160             /* Shuffle mode and we're playing the music we just
00161              * blacklisted, so stop playing it. */
00162             if (shuffle && !strcmp(sound_get_bg_music_basename(), list->text[list->row_selected - 1][0]))
00163             {
00164                 sound_start_bg_music("no_music", 0, 0);
00165             }
00166         }
00167 
00168         cur_widget[MPLAYER_ID]->redraw = 1;
00169     }
00170 }
00171 
00176 static void mplayer_blacklist_mass_toggle(list_struct *list, uint8 state)
00177 {
00178     if (list && shuffle_blacklist)
00179     {
00180         size_t row;
00181 
00182         for (row = 0; row < list->rows - 1; row++)
00183         {
00184             shuffle_blacklist[row] = state;
00185         }
00186 
00187         /* Shuffle mode and we're disabling all, turn off music. */
00188         if (shuffle && state == 1)
00189         {
00190             sound_start_bg_music("no_music", 0, 0);
00191         }
00192 
00193         cur_widget[MPLAYER_ID]->redraw = 1;
00194     }
00195 }
00196 
00200 static void mplayer_blacklist_save(list_struct *list)
00201 {
00202     FILE *fp;
00203     size_t row;
00204 
00205     fp = fopen_wrapper(FILE_MPLAYER_BLACKLIST, "w");
00206 
00207     for (row = 0; row < list->rows; row++)
00208     {
00209         if (shuffle_blacklist[row])
00210         {
00211             fprintf(fp, "%s\n", list->text[row][0]);
00212         }
00213     }
00214 
00215     fclose(fp);
00216 }
00217 
00224 static void mplayer_list_init(list_struct *list, const char *path, uint8 duplicates)
00225 {
00226     DIR *dir;
00227     struct dirent *currentfile;
00228 
00229     /* Read the media directory and add the file names to the list. */
00230     dir = opendir(path);
00231 
00232     if (!dir)
00233     {
00234         return;
00235     }
00236 
00237     while ((currentfile = readdir(dir)))
00238     {
00239         /* Ignore hidden files and files without extension. */
00240         if (currentfile->d_name[0] == '.' || !strchr(currentfile->d_name, '.'))
00241         {
00242             continue;
00243         }
00244 
00245         /* Check for duplicates. */
00246         if (duplicates)
00247         {
00248             uint8 found = 0;
00249             uint32 row;
00250 
00251             for (row = 0; row < list->rows; row++)
00252             {
00253                 if (!strcmp(list->text[row][0], currentfile->d_name))
00254                 {
00255                     found = 1;
00256                     break;
00257                 }
00258             }
00259 
00260             if (found)
00261             {
00262                 continue;
00263             }
00264         }
00265 
00266         list_add(list, list->rows, 0, currentfile->d_name);
00267     }
00268 
00269     closedir(dir);
00270 }
00271 
00275 void widget_show_mplayer(widgetdata *widget)
00276 {
00277     SDL_Rect box, box2;
00278     list_struct *list;
00279     const char *bg_music;
00280     char buf[HUGE_BUF];
00281 
00282     if (!widget->widgetSF)
00283     {
00284         widget->widgetSF = SDL_ConvertSurface(Bitmaps[BITMAP_CONTENT]->bitmap, Bitmaps[BITMAP_CONTENT]->bitmap->format, Bitmaps[BITMAP_CONTENT]->bitmap->flags);
00285     }
00286 
00287     list = list_exists(LIST_MPLAYER);
00288 
00289     /* The list doesn't exist yet, create it. */
00290     if (!list)
00291     {
00292         char version[MAX_BUF];
00293 
00294         /* Create the list and set up settings. */
00295         list = list_create(LIST_MPLAYER, 12, 1, 8);
00296         list->handle_enter_func = list_handle_enter;
00297         list->text_color_hook = list_text_color_hook;
00298         list->surface = widget->widgetSF;
00299         list_scrollbar_enable(list);
00300         list_set_column(list, 0, 130, 7, NULL, -1);
00301         list_set_font(list, FONT_ARIAL10);
00302 
00303         /* Add default media directory songs. */
00304         get_data_dir_file(buf, sizeof(buf), DIRECTORY_MEDIA);
00305         mplayer_list_init(list, buf, 0);
00306 
00307         /* Now add custom ones, but ignore duplicates. */
00308         snprintf(buf, sizeof(buf), "%s/.atrinik/%s/"DIRECTORY_MEDIA, package_get_version_partial(version, sizeof(version)), get_config_dir());
00309         mplayer_list_init(list, buf, 1);
00310 
00311         /* If we added any, sort the list alphabetically and add an entry
00312          * to disable background music. */
00313         if (list->rows)
00314         {
00315             FILE *fp;
00316 
00317             /* Allocate the blacklist. + 1 is for the last entry added
00318              * further down. It is not actually used by the blacklist as
00319              * it's not possible to toggle it on/off using the button, but
00320              * it simplifies other logic checks. */
00321             shuffle_blacklist = calloc(1, sizeof(*shuffle_blacklist) * (list->rows + 1));
00322 
00323             /* Sort the list. */
00324             list_sort(list, LIST_SORT_ALPHA);
00325 
00326             /* Read the blacklist file contents. */
00327             fp = fopen_wrapper(FILE_MPLAYER_BLACKLIST, "r");
00328 
00329             if (fp)
00330             {
00331                 size_t row;
00332 
00333                 while (fgets(buf, sizeof(buf) - 1, fp))
00334                 {
00335                     for (row = 0; row < list->rows; row++)
00336                     {
00337                         if (!strncmp(buf, list->text[row][0], strlen(buf) - 1))
00338                         {
00339                             shuffle_blacklist[row] = 1;
00340                             break;
00341                         }
00342                     }
00343                 }
00344 
00345                 fclose(fp);
00346             }
00347 
00348             list_add(list, list->rows, 0, "Disable music");
00349         }
00350 
00351         list_set_focus(list);
00352 
00353         button_create(&button_play);
00354         button_create(&button_shuffle);
00355         button_create(&button_blacklist);
00356         button_create(&button_help);
00357         button_create(&button_close);
00358         button_blacklist.bitmap = button_help.bitmap = button_close.bitmap = BITMAP_BUTTON_ROUND;
00359         button_blacklist.bitmap_pressed = button_help.bitmap_pressed = button_close.bitmap_pressed = BITMAP_BUTTON_ROUND_DOWN;
00360     }
00361 
00362     if (widget->redraw)
00363     {
00364         _BLTFX bltfx;
00365 
00366         bltfx.surface = widget->widgetSF;
00367         bltfx.flags = 0;
00368         bltfx.alpha = 0;
00369         sprite_blt(Bitmaps[BITMAP_CONTENT], 0, 0, NULL, &bltfx);
00370 
00371         widget->redraw = 0;
00372 
00373         box.h = 0;
00374         box.w = widget->wd;
00375         string_blt(widget->widgetSF, FONT_SERIF12, "Music Player", 0, 3, COLOR_HGOLD, TEXT_ALIGN_CENTER, &box);
00376         list->focus = 1;
00377         list_set_parent(list, widget->x1, widget->y1);
00378         list_show(list, 10, 2);
00379         box.w /= 2;
00380         string_blt(widget->widgetSF, FONT_SANS10, "Currently playing:", widget->wd / 2, 22, COLOR_WHITE, TEXT_ALIGN_CENTER, &box);
00381         box.h = 120;
00382         box.w -= 6;
00383         string_blt(widget->widgetSF, FONT_ARIAL10, "You can use the music player to play your favorite tunes from the game, or play them all one-by-one in random order (shuffle).\n\nNote that if you use the music player, in-game areas won't change your music until you click <b>Stop</b>.", widget->wd / 2, 60, COLOR_WHITE, TEXT_WORD_WRAP | TEXT_MARKUP, &box);
00384     }
00385 
00386     box2.x = widget->x1;
00387     box2.y = widget->y1;
00388     SDL_BlitSurface(widget->widgetSF, NULL, ScreenSurface, &box2);
00389 
00390     bg_music = sound_get_bg_music_basename();
00391     box.h = 0;
00392     box.w = widget->wd / 2;
00393 
00394     /* Store the background music file name in temporary buffer and
00395      * make sure it won't overflow by truncating it if necessary. */
00396     if (bg_music)
00397     {
00398         strncpy(buf, bg_music, sizeof(buf) - 1);
00399         buf[sizeof(buf) - 1] = '\0';
00400         string_truncate_overflow(FONT_SANS11, buf, 150);
00401     }
00402 
00403     /* Show the music that is being played. */
00404     string_blt(ScreenSurface, FONT_SANS11, bg_music ? buf : "No music", widget->x1 + widget->wd / 2 - 5, widget->y1 + 34, COLOR_HGOLD, TEXT_ALIGN_CENTER, &box);
00405 
00406     button_play.x = widget->x1 + 10;
00407     button_play.y = widget->y1 + widget->ht - Bitmaps[BITMAP_BUTTON]->bitmap->h - 4;
00408     button_render(&button_play, sound_map_background(-1) ? "Stop" : "Play");
00409 
00410     button_shuffle.x = widget->x1 + 10 + Bitmaps[BITMAP_BUTTON]->bitmap->w + 5;
00411     button_shuffle.y = widget->y1 + widget->ht - Bitmaps[BITMAP_BUTTON]->bitmap->h - 4;
00412 
00413     if (shuffle)
00414     {
00415         button_shuffle.pressed = 1;
00416     }
00417 
00418     button_render(&button_shuffle, "Shuffle");
00419 
00420     button_blacklist.x = widget->x1 + 10 + Bitmaps[BITMAP_BUTTON]->bitmap->w * 2 + 5 * 2;
00421     button_blacklist.y = widget->y1 + widget->ht - Bitmaps[BITMAP_BUTTON_ROUND]->bitmap->h - 5;
00422     button_blacklist.disabled = list->row_selected == list->rows;
00423 
00424     /* Do mass blacklist status change if the button has been held for
00425      * some time. */
00426     if (button_blacklist.pressed && SDL_GetTicks() - button_blacklist.pressed_ticks > BLACKLIST_ALL_DELAY)
00427     {
00428         mplayer_blacklist_mass_toggle(list, mplayer_blacklisted(list));
00429         mplayer_blacklist_save(list);
00430         button_blacklist.pressed_ticks = SDL_GetTicks();
00431     }
00432 
00433     button_render(&button_blacklist, mplayer_blacklisted(list) ? "+" : "-");
00434 
00435     /* Show close button. */
00436     button_close.x = widget->x1 + widget->wd - Bitmaps[BITMAP_BUTTON_ROUND]->bitmap->w - 4;
00437     button_close.y = widget->y1 + 4;
00438     button_render(&button_close, "X");
00439 
00440     /* Show help button. */
00441     button_help.x = widget->x1 + widget->wd - Bitmaps[BITMAP_BUTTON_ROUND]->bitmap->w * 2 - 4;
00442     button_help.y = widget->y1 + 4;
00443     button_render(&button_help, "?");
00444 }
00445 
00449 void widget_mplayer_background(widgetdata *widget)
00450 {
00451     (void) widget;
00452 
00453     /* If shuffle is enabled, check whether we need to start playing
00454      * another song. */
00455     if (shuffle)
00456     {
00457         mplayer_check_shuffle();
00458     }
00459 }
00460 
00464 void widget_mplayer_deinit(widgetdata *widget)
00465 {
00466     (void) widget;
00467 
00468     free(shuffle_blacklist);
00469     shuffle_blacklist = NULL;
00470 }
00471 
00476 void widget_mplayer_mevent(widgetdata *widget, SDL_Event *event)
00477 {
00478     list_struct *list = list_exists(LIST_MPLAYER);
00479 
00480     /* If the list has handled the mouse event, we need to redraw the
00481      * widget. */
00482     if (list && list_handle_mouse(list, event->motion.x - widget->x1, event->motion.y - widget->y1, event))
00483     {
00484         widget->redraw = 1;
00485     }
00486 
00487     if (button_event(&button_play, event))
00488     {
00489         if (sound_map_background(-1))
00490         {
00491             sound_start_bg_music("no_music", 0, 0);
00492             sound_map_background(0);
00493             shuffle = 0;
00494         }
00495         else
00496         {
00497             list_handle_enter(list);
00498         }
00499     }
00500     else if (button_event(&button_shuffle, event))
00501     {
00502         shuffle = !shuffle;
00503 
00504         if (shuffle)
00505         {
00506             mplayer_do_shuffle(list);
00507             sound_map_background(1);
00508         }
00509         else
00510         {
00511             sound_start_bg_music("no_music", 0, 0);
00512             sound_map_background(0);
00513         }
00514     }
00515     else if (button_event(&button_blacklist, event))
00516     {
00517         /* Toggle the blacklist state of the selected row. */
00518         mplayer_blacklist_toggle(list);
00519         mplayer_blacklist_save(list);
00520     }
00521     else if (button_event(&button_close, event))
00522     {
00523         widget->show = 0;
00524         button_close.pressed = 0;
00525     }
00526     else if (button_event(&button_help, event))
00527     {
00528         show_help("music player");
00529     }
00530 }
00531 
00534 void mplayer_now_playing()
00535 {
00536     char buf[HUGE_BUF];
00537 
00538     if (sound_map_background(-1) && sound_playing_music())
00539     {
00540         snprintf(buf, sizeof(buf), "/me is currently listening to: %s", sound_get_bg_music_basename());
00541         send_command(buf);
00542     }
00543     else
00544     {
00545         draw_info(COLOR_RED, "You are not playing any custom music.");
00546     }
00547 }