1 /* -*- mode: C; c-basic-offset: 2; indent-tabs-mode: nil; -*- */ 2 /* 3 * Copyright (C) 2009-2011 Tiger Soldier 4 * 5 * This file is part of OSD Lyrics. 6 * OSD Lyrics is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * OSD Lyrics is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with OSD Lyrics. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 #include <stdio.h> 20 #include <time.h> 21 #include <sys/types.h> 22 #include <sys/stat.h> 23 #include <unistd.h> 24 #include <signal.h> 25 #include <pwd.h> 26 #include "config.h" 27 #include "ol_lrc.h" 28 #include "ol_player.h" 29 #include "ol_utils.h" 30 #include "ol_lrc_fetch.h" 31 #include "ol_lrc_fetch_ui.h" 32 #include "ol_trayicon.h" 33 #include "ol_intl.h" 34 #include "ol_config.h" 35 #include "ol_display_module.h" 36 #include "ol_keybindings.h" 37 #include "ol_lrc_fetch_module.h" 38 #include "ol_lyric_manage.h" 39 #include "ol_stock.h" 40 #include "ol_app.h" 41 #include "ol_notify.h" 42 #include "ol_lrclib.h" 43 #include "ol_debug.h" 44 #include "ol_singleton.h" 45 #include "ol_player_chooser.h" 46 47 #define REFRESH_INTERVAL 100 48 #define INFO_INTERVAL 500 49 #define TIMEOUT_WAIT_LAUNCH 5000 50 #define LRCDB_FILENAME "lrc.db" 51 52 gboolean _arg_debug_cb (const gchar *option_name, 53 const gchar *value, 54 gpointer data, 55 GError **error); 56 static gboolean _arg_version; 57 58 static GOptionEntry cmdargs[] = 59 { 60 { "debug", 'd', G_OPTION_FLAG_OPTIONAL_ARG, G_OPTION_ARG_CALLBACK, _arg_debug_cb, 61 N_ ("The level of debug messages to log, can be 'none', 'error', 'debug', or 'info'"), "level" }, 62 { "version", 'v', 0, G_OPTION_ARG_NONE, &_arg_version, 63 N_ ("Show version information"), NULL}, 64 { NULL } 65 }; 66 67 static guint refresh_source = 0; 68 static guint info_timer = 0; 69 static guint _lost_action_delay_timer = 0; 70 static struct OlPlayer *player = NULL; 71 static OlMusicInfo music_info = {0}; 72 static gchar *previous_title = NULL; 73 static gchar *previous_artist = NULL; 74 static gchar *previous_uri = NULL; 75 static enum OlPlayerStatus previous_status = OL_PLAYER_UNKNOWN; 76 static gint previous_duration = 0; 77 static gint previous_position = -1; 78 static struct OlLrc *lrc_file = NULL; 79 static char *display_mode = NULL; 80 static struct OlDisplayModule *module = NULL; 81 static int search_id = -1; 82 static enum _PlayerLostAction { 83 ACTION_NONE = 0, 84 ACTION_LAUNCH_DEFAULT, 85 ACTION_CHOOSE_PLAYER, 86 ACTION_CHOOSE_PLAYER_DISCONNECTED, 87 ACTION_WAIT_LAUNCH, 88 ACTION_QUIT, 89 } player_lost_action = ACTION_LAUNCH_DEFAULT; 90 static OlPlayerChooser *player_chooser = NULL; 91 92 static void _initialize (int argc, char **argv); 93 static gint _refresh_music_info (gpointer data); 94 static gint _refresh_player_info (gpointer data); 95 static void _start_refresh_music_info (void); 96 static void _stop_refresh_music_info (void); 97 static void _start_refresh_player_info (void); 98 static void _wait_for_player_launch (void); 99 static void _set_player_lost_action (enum _PlayerLostAction action); 100 static void _set_player_lost_action_delay (enum _PlayerLostAction action, 101 guint delay_ms); 102 static void _player_lost_cb (void); 103 static void _player_chooser_response_cb (GtkDialog *dialog, 104 gint response_id, 105 gpointer user_data); 106 static void _check_music_change (); 107 static void _on_music_changed (void); 108 static gboolean _check_lyric_file (void); 109 static void _update_player_status (enum OlPlayerStatus status); 110 static gboolean _get_active_player (void); 111 static void _search_callback (struct OlLrcFetchResult *result, 112 void *userdata); 113 static void _download_callback (struct OlLrcDownloadResult *result); 114 static void _on_config_changed (OlConfig *config, 115 gchar *group, 116 gchar *name, 117 gpointer userdata); 118 119 static void 120 _on_config_changed (OlConfig *config, 121 gchar *group, 122 gchar *name, 123 gpointer userdata) 124 { 125 if (module != NULL && strcmp (name, "display-mode") == 0) 126 { 127 char *mode = ol_config_get_string (config, group, name); 128 if (display_mode == NULL || 129 ol_stricmp (mode, display_mode, -1) != 0) 130 { 131 if (display_mode != NULL) 132 g_free (display_mode); 133 display_mode = g_strdup (mode); 134 ol_display_module_free (module); 135 module = ol_display_module_new (display_mode); 136 ol_display_module_set_music_info (module, &music_info); 137 ol_display_module_set_duration (module, previous_duration); 138 ol_display_module_set_lrc (module, lrc_file); 139 ol_display_module_set_player (module, player); 140 ol_display_module_set_status (module, previous_status); 141 } 142 g_free (mode); 143 } 144 } 145 146 static void 147 _download_callback (struct OlLrcDownloadResult *result) 148 { 149 ol_log_func (); 150 if (result->filepath != NULL) 151 ol_app_assign_lrcfile (result->info, result->filepath, TRUE); 152 else 153 ol_display_module_download_fail_message (module, _("Download failed")); 154 } 155 156 static void 157 _search_msg_callback (int _search_id, 158 enum OlLrcSearchMsgType msg_type, 159 const char *message, 160 void *userdata) 161 { 162 ol_assert (_search_id == search_id); 163 switch (msg_type) 164 { 165 case OL_LRC_SEARCH_MSG_ENGINE: 166 if (module != NULL) 167 { 168 char *msg = g_strdup_printf (_("Searching lyrics from %s"), _(message)); 169 ol_display_module_search_fail_message (module, msg); 170 g_free (msg); 171 } 172 break; 173 } 174 } 175 176 177 static void 178 _search_callback (struct OlLrcFetchResult *result, 179 void *userdata) 180 { 181 ol_log_func (); 182 ol_assert (result != NULL); 183 ol_assert (result->engine != NULL); 184 ol_assert (result->id == search_id); 185 search_id = -1; 186 if (result->count > 0 && result->candidates != 0) 187 { 188 char *filename = ol_lyric_download_path (&result->info); 189 if (filename == NULL) 190 { 191 ol_display_module_download_fail_message (module, _("Cannot create the lyric directory")); 192 } 193 else 194 { 195 if (module != NULL) { 196 ol_display_module_clear_message (module); 197 } 198 ol_lrc_fetch_ui_show (result->engine, result->candidates, result->count, 199 &result->info, 200 filename); 201 g_free (filename); 202 } 203 } 204 else 205 { 206 if (module != NULL) 207 ol_display_module_search_fail_message (module, _("Lyrics not found")); 208 } 209 } 210 211 gboolean 212 ol_app_download_lyric (OlMusicInfo *music_info) 213 { 214 ol_log_func (); 215 if (search_id > 0) 216 ol_lrc_fetch_cancel_search (search_id); 217 OlConfig *config = ol_config_get_instance (); 218 char **engine_list = ol_config_get_str_list (config, 219 "Download", 220 "download-engine", 221 NULL); 222 search_id = ol_lrc_fetch_begin_search (engine_list, 223 music_info, 224 _search_msg_callback, 225 _search_callback, 226 NULL); 227 g_strfreev (engine_list); 228 return TRUE; 229 } 230 231 struct OlLrc * 232 ol_app_get_current_lyric () 233 { 234 ol_log_func (); 235 return lrc_file; 236 } 237 238 gboolean 239 ol_app_assign_lrcfile (const OlMusicInfo *info, 240 const char *filepath, 241 gboolean update) 242 { 243 ol_log_func (); 244 ol_assert_ret (info != NULL, FALSE); 245 ol_assert_ret (filepath == NULL || ol_path_is_file (filepath), FALSE); 246 if (update) 247 { 248 ol_lrclib_assign_lyric (info, filepath); 249 } 250 if (ol_music_info_equal (&music_info, info)) 251 { 252 if (lrc_file != NULL) 253 { 254 ol_lrc_free (lrc_file); 255 lrc_file = NULL; 256 } 257 if (filepath != NULL) 258 lrc_file = ol_lrc_new (filepath); 259 if (module != NULL) 260 ol_display_module_set_lrc (module, lrc_file); 261 } 262 return TRUE; 263 } 264 265 static gboolean 266 _check_lyric_file () 267 { 268 ol_log_func (); 269 gboolean ret = TRUE; 270 char *filename = NULL; 271 int code = ol_lrclib_find (&music_info, &filename); 272 if (code == 0) 273 filename = ol_lyric_find (&music_info); 274 275 if (filename != NULL) 276 { 277 ret = ol_app_assign_lrcfile (&music_info, filename, code == 0); 278 g_free (filename); 279 } 280 else 281 { 282 ol_debugf("filename;%s\n", filename); 283 if (code == 0) ret = FALSE; 284 } 285 return ret; 286 } 287 288 static void 289 _on_music_changed () 290 { 291 ol_log_func (); 292 if (module != NULL) 293 { 294 ol_display_module_set_music_info (module, &music_info); 295 ol_display_module_set_duration (module, previous_duration); 296 ol_display_module_set_lrc (module, NULL); 297 } 298 if (!_check_lyric_file () && 299 !ol_is_string_empty (ol_music_info_get_title (&music_info))) 300 ol_app_download_lyric (&music_info); 301 OlConfig *config = ol_config_get_instance (); 302 if (ol_config_get_bool (config, "General", "notify-music")) 303 ol_notify_music_change (&music_info, ol_player_get_icon_path (player)); 304 } 305 306 static void 307 _normalize_music_info (OlMusicInfo *music_info) 308 { 309 if (ol_is_string_empty (ol_music_info_get_title (music_info)) && 310 ! ol_is_string_empty (ol_music_info_get_uri (music_info))) 311 { 312 const char *uri = ol_music_info_get_uri (music_info); 313 char *path = NULL; 314 if (uri[0] == '/') 315 { 316 path = g_strdup (uri); 317 } 318 else 319 { 320 GError *err = NULL; 321 path = g_filename_from_uri (uri, NULL, &err); 322 if (path == NULL) 323 { 324 ol_debugf ("Convert uri failed: %s\n", err->message); 325 g_error_free (err); 326 } 327 } 328 if (path != NULL) 329 { 330 char *basename = g_path_get_basename (path); 331 char *mainname = NULL; 332 ol_path_splitext (basename, &mainname, NULL); 333 if (mainname != NULL) 334 { 335 ol_music_info_set_title (music_info, mainname); 336 g_free (mainname); 337 } 338 g_free (basename); 339 g_free (path); 340 } 341 } 342 } 343 344 static void 345 _check_music_change () 346 { 347 ol_log_func (); 348 /* checks whether the music has been changed */ 349 gboolean changed = FALSE; 350 /* compares the previous title with current title */ 351 if (player && !ol_player_get_music_info (player, &music_info)) 352 { 353 player = NULL; 354 } 355 else 356 { 357 _normalize_music_info (&music_info); 358 } 359 gint duration = 0; 360 if (player && !ol_player_get_music_length (player, &duration)) 361 { 362 player = NULL; 363 } 364 if (!ol_streq (music_info.title, previous_title)) 365 changed = TRUE; 366 ol_strptrcpy (&previous_title, music_info.title); 367 /* compares the previous artist with current */ 368 if (!ol_streq (music_info.artist, previous_artist)) 369 changed = TRUE; 370 ol_strptrcpy (&previous_artist, music_info.artist); 371 if (!ol_streq (music_info.uri, previous_uri)) 372 changed = TRUE; 373 ol_strptrcpy (&previous_uri, music_info.uri); 374 /* compares the previous duration */ 375 /* FIXME: because the a of banshee, some lyrics may return different 376 duration for the same song when plays to different position, so the 377 comparison is commented out temporarily */ 378 /* if (previous_duration != duration) */ 379 /* { */ 380 /* ol_debugf ("change6:%d-%d\n", previous_duration, duration); */ 381 /* changed = TRUE; */ 382 /* } */ 383 if (previous_duration != duration) 384 { 385 previous_duration = duration; 386 if (module != NULL) 387 ol_display_module_set_duration (module, duration); 388 } 389 if (changed) 390 { 391 _on_music_changed (); 392 } 393 } 394 395 static void 396 _update_player_status (enum OlPlayerStatus status) 397 { 398 ol_log_func (); 399 if (previous_status != status) 400 { 401 previous_status = status; 402 if (module != NULL) 403 { 404 ol_display_module_set_status (module, status); 405 } 406 ol_trayicon_status_changed (status); 407 } 408 } 409 410 static gint 411 _refresh_player_info (gpointer data) 412 { 413 ol_log_func (); 414 if (player != NULL) 415 { 416 if (ol_player_get_capacity (player) & OL_PLAYER_STATUS) 417 _update_player_status (ol_player_get_status (player)); 418 _check_music_change (); 419 } 420 else 421 { 422 if (_get_active_player ()) 423 _start_refresh_music_info (); 424 } 425 return TRUE; 426 } 427 428 static void 429 _set_player_lost_action (enum _PlayerLostAction action) 430 { 431 if (_lost_action_delay_timer) 432 { 433 g_source_remove (_lost_action_delay_timer); 434 _lost_action_delay_timer = 0; 435 } 436 player_lost_action = action; 437 } 438 439 static gboolean 440 _player_lost_action_delay_cb (gpointer userdata) 441 { 442 enum _PlayerLostAction action = GPOINTER_TO_INT (userdata); 443 _set_player_lost_action (action); 444 return FALSE; 445 } 446 447 static void 448 _set_player_lost_action_delay (enum _PlayerLostAction action, 449 guint delay_seconds) 450 { 451 if (_lost_action_delay_timer) 452 { 453 g_source_remove (_lost_action_delay_timer); 454 } 455 _lost_action_delay_timer = g_timeout_add_seconds (delay_seconds, 456 _player_lost_action_delay_cb, 457 GINT_TO_POINTER ((int) action)); 458 } 459 460 static gboolean 461 _player_launch_timeout (gpointer userdata) 462 { 463 if (player_lost_action == ACTION_WAIT_LAUNCH) 464 _set_player_lost_action (ACTION_CHOOSE_PLAYER); 465 return FALSE; 466 } 467 468 static void 469 _wait_for_player_launch (void) 470 { 471 _set_player_lost_action (ACTION_WAIT_LAUNCH); 472 g_timeout_add (TIMEOUT_WAIT_LAUNCH, _player_launch_timeout, NULL); 473 } 474 475 static void 476 _player_chooser_response_cb (GtkDialog *dialog, 477 gint response_id, 478 gpointer user_data) 479 { 480 ol_assert (GTK_IS_DIALOG (dialog)); 481 switch (response_id) 482 { 483 case OL_PLAYER_CHOOSER_RESPONSE_LAUNCH: 484 _wait_for_player_launch (); 485 break; 486 case GTK_RESPONSE_DELETE_EVENT: 487 case GTK_RESPONSE_CLOSE: 488 gtk_widget_hide (GTK_WIDGET (dialog)); 489 if (player == NULL) 490 gtk_main_quit (); 491 break; 492 default: 493 ol_errorf ("Unknown response id: %d\n", response_id); 494 } 495 } 496 497 static void 498 _player_lost_cb (void) 499 { 500 if (_lost_action_delay_timer > 0) 501 { 502 g_source_remove (_lost_action_delay_timer); 503 _lost_action_delay_timer = 0; 504 } 505 ol_music_info_clear (&music_info); 506 if (module != NULL) 507 { 508 ol_display_module_free (module); 509 module = NULL; 510 } 511 switch (player_lost_action) 512 { 513 case ACTION_LAUNCH_DEFAULT: 514 case ACTION_CHOOSE_PLAYER: 515 case ACTION_CHOOSE_PLAYER_DISCONNECTED: 516 { 517 if (player_lost_action == ACTION_LAUNCH_DEFAULT) 518 { 519 OlConfig *config = ol_config_get_instance (); 520 char *player_cmd = ol_config_get_string (config, 521 "General", 522 "startup-player"); 523 if (!ol_is_string_empty (player_cmd)) 524 { 525 ol_debugf ("Running %s\n", player_cmd); 526 ol_launch_app (player_cmd); 527 _wait_for_player_launch (); 528 g_free (player_cmd); 529 break; 530 } 531 else 532 { 533 g_free (player_cmd); 534 } 535 } 536 if (!player_chooser) 537 { 538 GList *supported_players = ol_player_get_support_players (); 539 player_chooser = OL_PLAYER_CHOOSER (ol_player_chooser_new (supported_players)); 540 g_signal_connect (player_chooser, 541 "response", 542 G_CALLBACK (_player_chooser_response_cb), 543 NULL); 544 ol_player_chooser_set_info_by_state (player_chooser, 545 OL_PLAYER_CHOOSER_STATE_NO_PLAYER); 546 } 547 else 548 { 549 if (player_lost_action == ACTION_CHOOSE_PLAYER_DISCONNECTED) 550 ol_player_chooser_set_info_by_state (player_chooser, 551 OL_PLAYER_CHOOSER_STATE_DISCONNECTED); 552 else 553 ol_player_chooser_set_info_by_state (player_chooser, 554 OL_PLAYER_CHOOSER_STATE_LAUNCH_FAIL); 555 } 556 gtk_widget_show (GTK_WIDGET (player_chooser)); 557 _set_player_lost_action (ACTION_NONE); 558 break; 559 } 560 case ACTION_QUIT: 561 printf (_("Player is not running. OSD Lyrics exits now.\n")); 562 gtk_main_quit (); 563 break; 564 default: 565 break; 566 } 567 } 568 569 static void 570 _player_connected_cb (void) 571 { 572 if (player_chooser != NULL && 573 gtk_widget_get_visible (GTK_WIDGET (player_chooser))) 574 ol_player_chooser_set_info_by_state (player_chooser, 575 OL_PLAYER_CHOOSER_STATE_CONNECTED); 576 if (!module) 577 { 578 /* Initialize display modules */ 579 OlConfig *config = ol_config_get_instance (); 580 ol_display_module_init (); 581 display_mode = ol_config_get_string (config, "General", "display-mode"); 582 module = ol_display_module_new (display_mode); 583 } 584 /* If the player lost in 1 minute, it is possible that the player crashed or 585 something bad happen to the D-Bus connection. In this case, we should let 586 our use to choose another player rather than simply exit */ 587 _set_player_lost_action (ACTION_CHOOSE_PLAYER_DISCONNECTED); 588 _set_player_lost_action_delay (ACTION_QUIT, 60); 589 } 590 591 static gboolean 592 _get_active_player (void) 593 { 594 ol_log_func (); 595 player = ol_player_get_active_player (); 596 if (player == NULL) 597 { 598 _player_lost_cb (); 599 } 600 else 601 { 602 _player_connected_cb (); 603 } 604 if (module != NULL) 605 ol_display_module_set_player (module, player); 606 return player != NULL; 607 } 608 609 static gint 610 _refresh_music_info (gpointer data) 611 { 612 ol_log_func (); 613 if (player == NULL && !_get_active_player ()) 614 { 615 _stop_refresh_music_info (); 616 return FALSE; 617 } 618 gint time = 0; 619 if (player && !ol_player_get_played_time (player, &time)) 620 { 621 player = NULL; 622 } 623 if (previous_position < 0 || time < previous_position || 624 previous_title == NULL) 625 _check_music_change (); 626 previous_position = time; 627 if (player == NULL) 628 { 629 previous_position = -1; 630 return TRUE; 631 } 632 if (module != NULL) 633 ol_display_module_set_played_time (module, time); 634 return TRUE; 635 } 636 637 struct OlPlayer* 638 ol_app_get_player () 639 { 640 return player; 641 } 642 643 OlMusicInfo* 644 ol_app_get_current_music () 645 { 646 return &music_info; 647 } 648 649 void 650 ol_app_adjust_lyric_offset (int offset_ms) 651 { 652 ol_log_func (); 653 struct OlLrc *lrc = ol_app_get_current_lyric (); 654 if (lrc == NULL) 655 return; 656 int old_offset = ol_lrc_get_offset (lrc); 657 int new_offset = old_offset - offset_ms; 658 ol_lrc_set_offset (lrc, new_offset); 659 } 660 661 static void 662 _parse_cmd_args (int *argc, char ***argv) 663 { 664 ol_log_func (); 665 GError *error = NULL; 666 GOptionContext *context; 667 668 context = g_option_context_new ("- Display your lyrics"); 669 g_option_context_add_main_entries (context, cmdargs, PACKAGE); 670 g_option_context_add_group (context, gtk_get_option_group (TRUE)); 671 if (!g_option_context_parse (context, argc, argv, &error)) 672 { 673 ol_errorf ("option parsing failed: %s\n", error->message); 674 } 675 if (_arg_version) 676 { 677 printf ("%s %s\n", PROGRAM_NAME, VERSION); 678 exit (0); 679 } 680 g_option_context_free (context); 681 } 682 683 gboolean 684 _arg_debug_cb (const gchar *option_name, 685 const gchar *value, 686 gpointer data, 687 GError **error) 688 { 689 if (value == NULL) 690 value = "debug"; 691 if (strcmp (value, "none") == 0) 692 { 693 ol_log_set_level (OL_LOG_NONE); 694 } 695 else if (strcmp (value, "error") == 0) 696 { 697 ol_log_set_level (OL_ERROR); 698 } 699 else if (strcmp (value, "debug") == 0) 700 { 701 ol_log_set_level (OL_DEBUG); 702 } 703 else if (strcmp (value, "info") == 0) 704 { 705 ol_log_set_level (OL_INFO); 706 } 707 else 708 { 709 g_set_error_literal (error, g_quark_from_string (PACKAGE_NAME), 1, 710 N_ ("debug level should be one of ``none'', ``error'', ``debug'', or ``info''")); 711 return FALSE; 712 } 713 return TRUE; 714 } 715 716 static void 717 _initialize (int argc, char **argv) 718 { 719 ol_log_func (); 720 #if ENABLE_NLS 721 /* Set the text message domain. */ 722 bindtextdomain (PACKAGE, LOCALEDIR); 723 bind_textdomain_codeset(PACKAGE, "UTF-8"); 724 /* textdomain (PACKAGE); */ 725 #endif 726 727 g_thread_init(NULL); 728 gtk_init (&argc, &argv); 729 _parse_cmd_args (&argc, &argv); 730 g_set_prgname (_(PROGRAM_NAME)); 731 if (ol_is_running ()) 732 { 733 printf ("%s\n", _("Another OSD Lyrics is running, exit.")); 734 exit (0); 735 } 736 ol_stock_init (); 737 ol_player_init (); 738 OlConfig *config = ol_config_get_instance (); 739 g_signal_connect (config, "changed", 740 G_CALLBACK (_on_config_changed), 741 NULL); 742 ol_trayicon_inital (); 743 ol_notify_init (); 744 ol_keybinding_init (); 745 ol_lrc_fetch_module_init (); 746 char *lrcdb_file = g_strdup_printf ("%s/%s/%s", 747 g_get_user_config_dir (), 748 PACKAGE_NAME, 749 LRCDB_FILENAME); 750 if (!ol_lrclib_init (lrcdb_file)) 751 { 752 ol_error ("Initialize lrclib failed"); 753 } 754 g_free (lrcdb_file); 755 ol_lrc_fetch_add_async_download_callback (_download_callback); 756 _start_refresh_player_info (); 757 } 758 759 static void 760 _start_refresh_player_info (void) 761 { 762 if (info_timer == 0) 763 info_timer = g_timeout_add (INFO_INTERVAL, _refresh_player_info, NULL); 764 } 765 766 static void 767 _start_refresh_music_info (void) 768 { 769 if (refresh_source == 0) 770 refresh_source = g_timeout_add (REFRESH_INTERVAL, _refresh_music_info, NULL); 771 } 772 773 static void 774 _stop_refresh_music_info (void) 775 { 776 if (refresh_source > 0) 777 { 778 g_source_remove (refresh_source); 779 refresh_source = 0; 780 } 781 } 782 783 int 784 main (int argc, char **argv) 785 { 786 _initialize (argc, argv); 787 gtk_main (); 788 ol_player_unload (); 789 ol_notify_unload (); 790 if (module != NULL) 791 { 792 ol_display_module_free (module); 793 module = NULL; 794 } 795 if (display_mode != NULL) 796 { 797 g_free (display_mode); 798 display_mode = NULL; 799 } 800 if (player_chooser != NULL) 801 { 802 gtk_widget_destroy (GTK_WIDGET (player_chooser)); 803 player_chooser = NULL; 804 } 805 ol_display_module_unload (); 806 ol_trayicon_free (); 807 ol_lrclib_unload (); 808 ol_config_unload (); 809 ol_lrc_fetch_module_unload (); 810 return 0; 811 } 812