1 /*******************************************************************************
2 * Goggles Music Manager *
3 ********************************************************************************
4 * Copyright (C) 2006-2021 by Sander Jansen. All Rights Reserved *
5 * --- *
6 * This program 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 * This program 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 this program. If not, see http://www.gnu.org/licenses. *
18 ********************************************************************************/
19 #include <unistd.h>
20 #include <signal.h>
21 #include <sys/wait.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <sys/fcntl.h>
25
26 #include "gmdefs.h"
27
28 #include <xincs.h>
29 #include <X11/XF86keysym.h>
30
31
32 #include "gmutils.h"
33
34 #include "GMSession.h"
35
36 #include "GMCover.h"
37 #include "GMTrack.h"
38 #include "GMTrackDatabase.h"
39
40 #include "GMCoverManager.h"
41
42 #ifdef HAVE_DBUS
43 #include "GMDBus.h"
44 #include "GMNotifyDaemon.h"
45 #include "GMSettingsDaemon.h"
46 #include "GMMediaPlayerService.h"
47 #endif
48
49 #include <FXPNGIcon.h>
50 #include "GMApp.h"
51 #include "GMTrackList.h"
52 #include "GMTag.h"
53 #include "GMFilename.h"
54
55 #include "GMTaskManager.h"
56 #include "GMList.h"
57 #include "GMTrackView.h"
58 #include "GMSourceView.h"
59 #include "GMSource.h"
60 #include "GMDatabaseSource.h"
61 #include "GMStreamSource.h"
62 #include "GMPlayListSource.h"
63 #include "GMPodcastSource.h"
64
65 #include "GMFilter.h"
66 #include "GMFilterSource.h"
67
68
69 #include "GMPlayQueue.h"
70 #include "GMLocalSource.h"
71
72 #include "GMIconTheme.h"
73 #include "GMPlayerManager.h"
74 #include "GMTrayIcon.h"
75
76 #include "GMWindow.h"
77 #include "GMRemote.h"
78
79 #include "GMCoverCache.h"
80 #include "GMAudioPlayer.h"
81
82 #include "GMAudioScrobbler.h"
83
84
85
86 #if APPLICATION_BETA_DB > 0
87 #define DATABASE_FILENAME "goggles_beta.db"
88 #else
89 #define DATABASE_FILENAME "goggles.db"
90 #endif
91
92
93 enum {
94 FIFO_STATUS_ERROR = 0,
95 FIFO_STATUS_OWNER = 1,
96 FIFO_STATUS_EXISTS = 2
97 };
98
99 FXDEFMAP(GMPlayerManager) GMPlayerManagerMap[]={
100 FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY,GMPlayerManager::onUpdTrackDisplay),
101 FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_SLEEP_TIMER,GMPlayerManager::onCmdSleepTimer),
102 FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_PLAY_NOTIFY,GMPlayerManager::onPlayNotify),
103 FXMAPFUNC(SEL_IO_READ,GMPlayerManager::ID_DDE_MESSAGE,GMPlayerManager::onDDEMessage),
104 FXMAPFUNC(SEL_CLOSE,GMPlayerManager::ID_WINDOW,GMPlayerManager::onCmdCloseWindow),
105 FXMAPFUNC(SEL_SIGNAL,GMPlayerManager::ID_CHILD,GMPlayerManager::onCmdChild),
106
107 FXMAPFUNC(SEL_COMMAND,GMPlayerManager::ID_SCROBBLER,GMPlayerManager::onScrobblerError),
108 FXMAPFUNC(SEL_OPENED,GMPlayerManager::ID_SCROBBLER,GMPlayerManager::onScrobblerOpen),
109
110 #ifdef HAVE_DBUS
111 FXMAPFUNC(SEL_KEYPRESS,GMPlayerManager::ID_GNOME_SETTINGS_DAEMON,GMPlayerManager::onCmdSettingsDaemon),
112 #endif
113 FXMAPFUNC(SEL_PLAYER_BOS,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerBOS),
114 FXMAPFUNC(SEL_PLAYER_EOS,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerEOS),
115 FXMAPFUNC(SEL_PLAYER_TIME,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerTime),
116 FXMAPFUNC(SEL_PLAYER_STATE,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerState),
117 FXMAPFUNC(SEL_PLAYER_META,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerMeta),
118 FXMAPFUNC(SEL_PLAYER_ERROR,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerError),
119 FXMAPFUNC(SEL_PLAYER_VOLUME,GMPlayerManager::ID_AUDIO_PLAYER,GMPlayerManager::onPlayerVolume),
120
121 FXMAPFUNC(SEL_COMMAND,GMPlayerManager::ID_CANCEL_TASK,GMPlayerManager::onCancelTask),
122
123 FXMAPFUNC(SEL_TASK_STATUS,GMPlayerManager::ID_TASKMANAGER,GMPlayerManager::onTaskManagerStatus),
124 FXMAPFUNC(SEL_TASK_IDLE,GMPlayerManager::ID_TASKMANAGER,GMPlayerManager::onTaskManagerIdle),
125 FXMAPFUNC(SEL_TASK_RUNNING,GMPlayerManager::ID_TASKMANAGER,GMPlayerManager::onTaskManagerRunning),
126
127 FXMAPFUNC(SEL_TIMEOUT,GMPlayerManager::ID_TASKMANAGER_SHUTDOWN,GMPlayerManager::onTaskManagerShutdown),
128
129 FXMAPFUNC(SEL_TASK_COMPLETED,GMPlayerManager::ID_IMPORT_TASK,GMPlayerManager::onImportTaskCompleted),
130 FXMAPFUNC(SEL_TASK_CANCELLED,GMPlayerManager::ID_IMPORT_TASK,GMPlayerManager::onImportTaskCompleted),
131 #ifdef HAVE_SESSION
132 FXMAPFUNC(SEL_SESSION_CLOSED,GMPlayerManager::ID_SESSION_MANAGER,GMPlayerManager::onCmdQuit)
133 #endif
134 };
135
FXIMPLEMENT(GMPlayerManager,FXObject,GMPlayerManagerMap,ARRAYNUMBER (GMPlayerManagerMap))136 FXIMPLEMENT(GMPlayerManager,FXObject,GMPlayerManagerMap,ARRAYNUMBER(GMPlayerManagerMap))
137
138 #ifdef HAVE_DBUS
139
140 DBusHandlerResult dbus_systembus_filter(DBusConnection *,DBusMessage * msg,void * data){
141 FXTRACE((80,"-----dbus_systembus_filter-------\n"));
142 FXTRACE((80,"path: %s\n",dbus_message_get_path(msg)));
143 FXTRACE((80,"member: \"%s\"\n",dbus_message_get_member(msg)));
144 FXTRACE((80,"interface: %s\n",dbus_message_get_interface(msg)));
145 FXTRACE((80,"sender: %s\n",dbus_message_get_sender(msg)));
146
147 GMPlayerManager * p = static_cast<GMPlayerManager*>(data);
148 if (dbus_message_has_path(msg,"/org/freedesktop/NetworkManager")){
149 if (dbus_message_is_signal(msg,"org.freedesktop.NetworkManager","StateChanged") ||
150 dbus_message_is_signal(msg,"org.freedesktop.NetworkManager","StateChange")) {
151
152 FXuint state=0;
153 enum {
154 /*
155 // Network Manager 0.7 / 0.8
156
157 NM7_STATE_UNKNOWN = 0,
158 NM7_STATE_ASLEEP = 1,
159 NM7_STATE_CONNECTING = 2,
160 NM7_STATE_CONNECTED = 3,
161 NM7_STATE_DISCONNECTED = 4,
162
163 // Network Manager 0.9
164 NM9_STATE_UNKNOWN = 0,
165 NM9_STATE_ASLEEP = 10,
166 NM9_STATE_DISCONNECTED = 20,
167 NM9_STATE_DISCONNECTING = 30,
168 NM9_STATE_CONNECTING = 40,
169 NM9_STATE_CONNECTED_LOCAL = 50,
170 NM9_STATE_CONNECTED_SITE = 60,
171 NM9_STATE_CONNECTED_GLOBAL = 70,
172
173 */
174 NM7_STATE_CONNECTED = 3,
175 NM9_STATE_CONNECTED_GLOBAL = 70,
176 };
177
178 if (dbus_message_get_args(msg,nullptr,DBUS_TYPE_UINT32,&state,DBUS_TYPE_INVALID)) {
179 if (p->getAudioScrobbler() && (state==NM7_STATE_CONNECTED ||
180 state==NM9_STATE_CONNECTED_GLOBAL))
181 p->getAudioScrobbler()->nudge();
182 }
183 return DBUS_HANDLER_RESULT_HANDLED;
184 }
185 }
186 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
187 }
188 #endif
189
190
191
onPlayNotify(FXObject *,FXSelector,void *)192 long GMPlayerManager::onPlayNotify(FXObject*,FXSelector,void*){
193
194 if (!trackinfo.title.empty() && !trackinfo.artist.empty()) {
195
196 display_track_notification();
197
198 if (scrobbler) scrobbler->nowplaying(trackinfo);
199 }
200 return 0;
201 }
202
onUpdTrackDisplay(FXObject *,FXSelector,void *)203 long GMPlayerManager::onUpdTrackDisplay(FXObject*,FXSelector,void*){
204 //fxmessage("onUpdTrackDisplay\n");
205 update_track_display();
206 getTrackView()->showCurrent();
207 return 0;
208 }
209
210
211 /* Stop Playback! */
onCmdSleepTimer(FXObject *,FXSelector,void *)212 long GMPlayerManager::onCmdSleepTimer(FXObject*,FXSelector,void*){
213 if (playing()) {
214 stop();
215 }
216 return 1;
217 }
218
219
onDDEMessage(FXObject *,FXSelector,void *)220 long GMPlayerManager::onDDEMessage(FXObject*,FXSelector,void*){
221 FXString cmd;
222 FXchar buffer[1024];
223 int nread=fifo.readBlock(buffer,1024);
224 if (nread>0) {
225 cmd=FXString(buffer,nread);
226 cmd.trim();
227 if (cmd=="--previous") cmd_prev();
228 else if (cmd=="--play") cmd_play();
229 else if (cmd=="--play-pause") cmd_playpause();
230 else if (cmd=="--pause") cmd_pause();
231 else if (cmd=="--next") cmd_next();
232 else if (cmd=="--stop") cmd_stop();
233 else if (cmd=="--toggle-shown") cmd_toggle_shown();
234 else if (cmd=="--now-playing") display_track_notification();
235 else if (cmd=="--raise") cmd_raise();
236 else if (cmd=="--update-podcasts") cmd_update_podcasts();
237 else {
238 if (gm_is_local_file(cmd)) {
239 if (!FXPath::isAbsolute(cmd)) {
240 cmd=FXPath::absolute(cmd);
241 }
242 }
243 if (!cmd.empty())
244 open(cmd);
245 }
246 }
247 return 1;
248 }
249
250
251
252 GMPlayerManager * GMPlayerManager::myself = nullptr;
253
instance()254 GMPlayerManager* GMPlayerManager::instance() {
255 return myself;
256 }
257
258
259 /// Constructor
GMPlayerManager()260 GMPlayerManager::GMPlayerManager() {
261 FXASSERT(myself==NULL);
262 myself=this;
263 }
264
265
266 /// Destructor
~GMPlayerManager()267 GMPlayerManager::~GMPlayerManager() {
268
269 /// Remove Signal Handlers
270 if (application) {
271 application->removeSignal(SIGINT);
272 application->removeSignal(SIGQUIT);
273 application->removeSignal(SIGTERM);
274 application->removeSignal(SIGHUP);
275 application->removeSignal(SIGPIPE);
276 application->removeSignal(SIGCHLD);
277 }
278
279 /// Cleanup fifo crap
280 if (fifo.isOpen())
281 fifo.close();
282 if (!fifofilename.empty())
283 FXFile::remove(fifofilename);
284
285 delete scrobbler;
286
287 delete database;
288
289 #ifdef HAVE_DBUS
290 delete sessionbus;
291 delete systembus;
292 #endif
293
294 delete player;
295 delete taskmanager;
296
297 myself=nullptr;
298
299 #ifdef HAVE_SESSION
300 delete session;
301 #endif
302 delete application;
303 }
304
305
306
307
init_fifo(int & argc,char ** argv)308 FXint GMPlayerManager::init_fifo(int& argc,char** argv){
309 FXString fifodir = FXSystem::getHomeDirectory() + PATHSEPSTRING + ".goggles";
310 FXStat info;
311
312 if ( (!FXStat::exists(fifodir) && !FXDir::create(fifodir) ) || !FXStat::isDirectory(fifodir) ) {
313 FXMessageBox::error(application,MBOX_OK,"Goggles Music Manager",fxtrformat("Unable to create directory %s\n"),fifodir.text());
314 return FIFO_STATUS_ERROR;
315 }
316
317 fifofilename = fifodir + PATHSEPSTRING + "gmm.dde";
318
319 /// Find existing fifo
320 if (FXStat::statFile(fifofilename,info)) {
321
322 /// File exists, but it's not a fifo... try removing it
323 if (!info.isFifo() && !FXFile::remove(fifofilename)) {
324 fifofilename=FXString::null;
325 return FIFO_STATUS_ERROR;
326 }
327
328 if (fifo.open(fifofilename,FXIO::WriteOnly|FXIO::NonBlocking)){
329 FXString commandline;
330
331 for (FXint i=1;i<argc;i++){
332 commandline+=argv[i];
333 }
334
335 /// Try raising the window
336 if (commandline.empty())
337 commandline="--raise";
338
339 /// if write failed or command line was empty, user probably tries to start up normally
340 /// and fifo may be stale
341 if (fifo.writeBlock(commandline.text(),commandline.length())) {
342 fifofilename=FXString::null;
343 return FIFO_STATUS_EXISTS;
344 }
345
346 /// Most likely left behind by crashed music manager
347 fifo.close();
348 }
349
350 if (!FXFile::remove(fifofilename))
351 return FIFO_STATUS_OWNER;
352 }
353
354 /// Create the fifo
355 if (mkfifo(fifofilename.text(),S_IWUSR|S_IRUSR)!=0){
356 fifofilename=FXString::null;
357 return FIFO_STATUS_OWNER;
358 }
359
360 /// Try open the fifo
361 fifo.open(fifofilename,FXIO::Reading|FXIO::WriteOnly,FXIO::OwnerWrite);
362
363 /// Close fifo on execute or remove fifo if we couldn't open it...
364 if (fifo.isOpen()) {
365 ap_set_closeonexec(fifo.handle());
366 //fcntl(fifo.handle(),F_SETFD,FD_CLOEXEC);
367 }
368 else {
369 FXFile::remove(fifofilename);
370 fifofilename=FXString::null;
371 }
372
373 return FIFO_STATUS_OWNER;
374 }
375
376
377
378
init_sources()379 FXbool GMPlayerManager::init_sources() {
380
381 // Main Database
382 database = new GMTrackDatabase;
383 covermanager = new GMCoverManager;
384
385 // Make sure we can open it.
386 if (!init_database(database)) {
387 return false;
388 }
389
390 /// Create the main database source
391 sources.append(new GMDatabaseSource(database));
392
393 /// Load User Queries
394 GMFilterSource::init(database,sources);
395
396 /// Create Play List Sources
397 FXIntList playlists;
398 if (database->listPlaylists(playlists)) {
399 for (FXint i=0;i<playlists.no();i++) {
400 sources.append(new GMPlayListSource(database,playlists[i]));
401 }
402 }
403
404 /// Init Play Queue
405 if (preferences.play_from_queue) {
406 queue = new GMPlayQueue(database);
407 sources.append(queue);
408 }
409
410 /// Internet Streams
411 sources.append(new GMStreamSource(database));
412
413 /// File System
414 sources.append(new GMLocalSource());
415
416 /// Podcast Source
417 podcast = new GMPodcastSource(database);
418 sources.append(podcast);
419
420 /// Load Settings
421 for (FXint i=0;i<sources.no();i++) {
422 sources[i]->load(application->reg());
423 }
424
425 return true;
426 }
427
428
getDatabaseSource() const429 GMDatabaseSource * GMPlayerManager::getDatabaseSource() const {
430 return dynamic_cast<GMDatabaseSource*>(sources[0]);
431 }
432
433
434
setPlayQueue(FXbool enable)435 void GMPlayerManager::setPlayQueue(FXbool enable) {
436 preferences.play_from_queue=enable;
437 if (enable) {
438 if (!queue) {
439 queue = new GMPlayQueue(database);
440 sources.append(queue);
441 GMPlayerManager::instance()->getSourceView()->refresh();
442 }
443 }
444 else {
445 if (queue) {
446 removeSource(queue);
447 queue=nullptr;
448 }
449 }
450 }
451
452
removeSource(GMSource * src)453 void GMPlayerManager::removeSource(GMSource * src) {
454
455 sources.remove(src);
456
457 GMPlayerManager::instance()->getSourceView()->refresh();
458
459 if (application->hasTimeout(src,GMSource::ID_TRACK_PLAYED))
460 application->removeTimeout(src,GMSource::ID_TRACK_PLAYED);
461
462 if (src==source) {
463 source->resetCurrent();
464 source=nullptr;
465 }
466
467 delete src;
468 }
469
470
471
init_window(FXbool wizard)472 void GMPlayerManager::init_window(FXbool wizard) {
473 const FXint argc = application->getArgc();
474 const FXchar *const * argv = application->getArgv();
475
476 /// Create Main Window
477 mainwindow = new GMWindow(application,this,ID_WINDOW);
478 mainwindow->create();
479
480 // Register Global Hotkeys
481 register_global_hotkeys();
482
483 /// Handle interrupt to save stuff nicely
484 application->addSignal(SIGINT,mainwindow,GMWindow::ID_QUIT);
485 application->addSignal(SIGQUIT,mainwindow,GMWindow::ID_QUIT);
486 application->addSignal(SIGTERM,mainwindow,GMWindow::ID_QUIT);
487 application->addSignal(SIGHUP,mainwindow,GMWindow::ID_QUIT);
488 application->addSignal(SIGPIPE,mainwindow,GMWindow::ID_QUIT);
489
490 /// Create Tooltip Window
491 FXToolTip * tooltip = new FXToolTip(application,TOOLTIP_VARIABLE);
492 tooltip->create();
493 ewmh_change_window_type(tooltip,WINDOWTYPE_TOOLTIP);
494
495 if (database->isEmpty() && wizard) {
496 cleanSourceSettings();
497 mainwindow->init(SHOW_WIZARD);
498 }
499 else {
500 FXbool start_as_tray=false;
501 if (preferences.gui_tray_icon_disabled==false){
502 for(FXint i=1;i<argc;i++) {
503 if (comparecase(argv[i],"--tray")==0){
504 start_as_tray=true;
505 preferences.gui_tray_icon=true;
506 break;
507 }
508 }
509 }
510 if (start_as_tray)
511 mainwindow->init(SHOW_TRAY);
512 else
513 mainwindow->init(SHOW_NORMAL);
514 }
515 }
516
517
get_cmdline_url(int & argc,char ** argv)518 static FXString get_cmdline_url(int& argc,char** argv) {
519 FXString url;
520 for (FXint i=1;i<argc;i++) {
521 if (argv[i][0]!='-') {
522 url=argv[i];
523 }
524 }
525 return url;
526 }
527
get_cmdline_update_podcasts(int & argc,char ** argv)528 static FXbool get_cmdline_update_podcasts(int & argc, char ** argv){
529 for (FXint i=1;i<argc;i++) {
530 if (compare(argv[i],"--update-podcasts")==0)
531 return true;
532 }
533 return false;
534 }
535
536
init_configuration()537 void GMPlayerManager::init_configuration() {
538 FXString xdg_config_home = GMApp::getConfigDirectory(true);
539 FXString xdg_data_home = GMApp::getDataDirectory(true);
540
541 /// Check if we need to migrate old files to new directory.
542 FXString newbase = xdg_data_home+PATHSEPSTRING;
543 FXString oldbase = FXSystem::getHomeDirectory()+PATHSEPSTRING ".goggles" PATHSEPSTRING;
544
545 if ( (FXStat::exists(newbase+DATABASE_FILENAME)==false) && FXStat::exists(oldbase+DATABASE_FILENAME) ) {
546
547
548 /// Move database (for now disabled untill we have a upgrade path)
549 /// FXFile::moveFiles(oldbase+DATABASE_FILENAME,newbase+DATABASE_FILENAME);
550 /// FXFile::moveFiles(oldbase+"scrobbler.cache",newbase+"scrobbler.cache");
551
552 newbase = xdg_config_home+PATHSEPSTRING;
553
554 /// Let's move
555 FXString oldconfig = FXSystem::getHomeDirectory()+PATHSEPSTRING ".foxrc" PATHSEPSTRING "musicmanager";
556
557 /// Copy Settings
558 if (FXStat::exists(oldconfig))
559 FXFile::copyFiles(oldconfig,newbase+"settings.rc");
560
561 ///FIXME enable this when we release 0.12. Remove the old config directory.
562 //FXFile::removeFiles(FXSystem::getHomeDirectory()+PATHSEPSTRING+".goggles",true);
563 }
564 }
565
566
567
568 #ifdef HAVE_DBUS
init_dbus(int & argc,char ** argv)569 FXbool GMPlayerManager::init_dbus(int & argc,char**argv) {
570
571 sessionbus=new GMDBus();
572 systembus =new GMDBus();
573
574 FXASSERT(sessionbus);
575 FXASSERT(systembus);
576
577 if (!sessionbus->open(DBUS_BUS_SESSION) || !sessionbus->connected()) {
578 FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager",fxtr("Session bus not available. All features requiring dbus are disabled."));
579 delete sessionbus;
580 sessionbus=nullptr;
581 }
582 else {
583
584 mpris2=new GMMediaPlayerService2(sessionbus);
585 FXint result = mpris2->create();
586 switch(result) {
587 case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
588 // success
589 break;
590 case DBUS_REQUEST_NAME_REPLY_EXISTS:
591 if (argc>1 && strlen(argv[1])>0)
592 mpris2->request(argv[1]);
593 delete mpris2;
594 return false;
595 break;
596 default:
597 FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager",fxtr("Session Bus not available. All features requiring sessionbus are disabled."));
598 delete mpris2;
599 mpris2=nullptr;
600 delete sessionbus;
601 sessionbus=nullptr;
602 return true;
603 break;
604 }
605
606 if (!systembus->open(DBUS_BUS_SYSTEM) || !systembus->connected()) {
607 delete systembus;
608 systembus=nullptr;
609 }
610
611 if (systembus && !dbus_connection_add_filter(systembus->connection(),dbus_systembus_filter,this,nullptr)){
612 delete systembus;
613 systembus=nullptr;
614 }
615
616 if (systembus) {
617 DBusError error;
618 dbus_error_init(&error);
619 dbus_bus_add_match(systembus->connection(),"type='signal',path='/org/freedesktop/NetworkManager',interface='org.freedesktop.NetworkManager',member='StateChanged'",&error);
620 if (dbus_error_is_set(&error)) {
621 dbus_error_free(&error);
622 delete systembus;
623 systembus=nullptr;
624 }
625 }
626
627 if (systembus) {
628 DBusError error;
629 dbus_error_init(&error);
630 dbus_bus_add_match(systembus->connection(),"type='signal',path='/org/freedesktop/NetworkManager',interface='org.freedesktop.NetworkManager',member='StateChange'",&error);
631 if (dbus_error_is_set(&error)) {
632 dbus_error_free(&error);
633 delete systembus;
634 systembus=nullptr;
635 }
636 }
637 }
638 return true;
639 }
640
641 #endif
642
643
644
run(int & argc,char ** argv)645 FXint GMPlayerManager::run(int& argc,char** argv) {
646 FXint result;
647
648 /// Initialize pre-thread libraries.
649 GMTag::init();
650
651 // Initialize Crypto Support
652 if (!ap_init_crypto()) {
653 fxwarning("gogglesmm: failed to initialize ssl support\n");
654 return 1;
655 }
656
657 /// Setup and migrate old config files.
658 init_configuration();
659
660 /// Create Application so we can do things like popup dialogs and such
661 application = new GMApp();
662 application->init(argc,argv);
663 application->create();
664
665 /// Keep track of child processes
666 application->addSignal(SIGCHLD,this,GMPlayerManager::ID_CHILD);
667
668 /// Make sure we're threadsafe
669 if (GMDatabase::threadsafe()==0) {
670 FXMessageBox::error(application,MBOX_OK,"Goggles Music Manager",
671 fxtrformat("A non threadsafe version of SQLite (%s) is being used.\n"
672 "Goggles Music Manager requires a threadsafe SQLite.\n"
673 "Please upgrade your SQLite installation."),GMDatabase::version());
674 return 1;
675 }
676
677 /// Give warning when PNG is not compiled in...
678 if (FXPNGIcon::supported==false) {
679 FXMessageBox::warning(application,MBOX_OK,"Goggles Music Manager",
680 fxtr("For some reason the FOX library was compiled without PNG support.\n"
681 "In order to show all icons, Goggles Music Manager requires PNG\n"
682 "support in the FOX library. If you've compiled FOX yourself, most\n"
683 "likely the libpng header files were not installed on your system."));
684 }
685
686 taskmanager = new GMTaskManager(this,ID_TASKMANAGER);
687
688 #ifdef HAVE_DBUS
689 /// Connect to the dbus...
690 if (!init_dbus(argc,argv))
691 return 0;
692
693 /// Fallback to fifo method to check for existing instance
694 if (sessionbus==nullptr) {
695 result = init_fifo(argc,argv);
696 if (result==FIFO_STATUS_ERROR)
697 return 1;
698 else if (result==FIFO_STATUS_EXISTS)
699 return 0;
700 }
701
702 #else
703 result = init_fifo(argc,argv);
704 if (result==FIFO_STATUS_ERROR)
705 return 1;
706 else if (result==FIFO_STATUS_EXISTS)
707 return 0;
708 #endif
709
710
711 /// Load Application Preferences
712 preferences.load(application->reg());
713
714 /// Check for overrides on the command line
715 preferences.parseCommandLine(argc,argv);
716
717 /// Open Database and initialize all sources.
718 if (!init_sources())
719 return false;
720
721 /// Everything opened succesfully... now create the GUI
722 player = new GMAudioPlayer(application,this,ID_AUDIO_PLAYER);
723
724 /// Open Audio Device if needed.
725 player->init();
726 player->loadSettings();
727
728 /// FIXME move to player->loadSettings
729 switch(preferences.play_replaygain){
730 case 0: player->setReplayGain(ReplayGainOff); break;
731 case 1: player->setReplayGain(ReplayGainTrack); break;
732 case 2: player->setReplayGain(ReplayGainAlbum); break;
733 }
734
735 if (preferences.play_crossfade)
736 player->setCrossFade(preferences.play_crossfade_duration);
737
738
739 /// Receive events from fifo
740 if (fifo.isOpen()) {
741 application->addInput(this,GMPlayerManager::ID_DDE_MESSAGE,fifo.handle(),INPUT_READ);
742 }
743
744 FXString url = get_cmdline_url(argc,argv);
745
746 /// Show user interface
747 init_window(url.empty());
748
749 #ifdef HAVE_DBUS
750 if (sessionbus) {
751 /// Integrate Dbus into FOX Event Loop
752 GMDBus::initEventLoop();
753 }
754 #endif
755
756 /// Start Services
757 scrobbler = new GMAudioScrobbler(this,ID_SCROBBLER);
758 #ifdef HAVE_DBUS
759 if (sessionbus) {
760 notifydaemon = new GMNotifyDaemon(sessionbus);
761
762 // KDE5 comes with mpris plugin on the toolbar, no need for
763 // tray icon
764 if (gm_desktop_session()==DESKTOP_SESSION_KDE_PLASMA) {
765 preferences.gui_tray_icon_disabled=false;
766 }
767
768 /// Grab Media Player Keys
769 gsd = new GMSettingsDaemon(sessionbus,this,ID_GNOME_SETTINGS_DAEMON);
770 gsd->GrabMediaPlayerKeys("gogglesmm");
771
772 update_mpris();
773
774 notifydaemon->init();
775 }
776 #endif
777
778 #ifdef HAVE_SESSION
779 /// Connect to the session manager
780 session = new GMSession(application,this,GMPlayerManager::ID_SESSION_MANAGER);
781 session->init(argc,argv);
782 #endif
783
784 /// Open url from command line
785 if (!url.empty())
786 open(url);
787
788 #ifndef HAVE_DBUS
789 update_tray_icon();
790 #endif
791
792 // Update Podcasts on startup if desired.
793 if (get_cmdline_update_podcasts(argc,argv))
794 cmd_update_podcasts();
795
796
797 /// Run the application
798 return application->run();
799 }
800
801
exit()802 void GMPlayerManager::exit() {
803
804 player->saveSettings();
805 player->stop();
806 player->exit();
807
808 /// Save settings
809 for (FXint i=0;i<sources.no();i++)
810 sources[i]->save(application->reg());
811
812 application->removeTimeout(this,GMPlayerManager::ID_UPDATE_TRACK_DISPLAY);
813
814 application->removeTimeout(this,GMPlayerManager::ID_SLEEP_TIMER);
815
816 application->removeTimeout(this,GMPlayerManager::ID_PLAY_NOTIFY);
817
818 application->removeTimeout(this,GMPlayerManager::ID_TASKMANAGER_SHUTDOWN);
819
820 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
821
822 preferences.save(application->reg());
823
824 if (scrobbler) scrobbler->shutdown();
825 if (taskmanager) taskmanager->shutdown();
826
827 #ifdef HAVE_DBUS
828 if (sessionbus) {
829
830 gsd->ReleaseMediaPlayerKeys("gogglesmm");
831 delete gsd;
832 gsd=nullptr;
833
834 if (notifydaemon) {
835 notifydaemon->close();
836 delete notifydaemon;
837 notifydaemon=nullptr;
838 }
839
840 if (mpris1) delete mpris1;
841 if (mpris2) delete mpris2;
842 }
843 if (systembus) {
844 dbus_connection_remove_filter(systembus->connection(),dbus_systembus_filter,this);
845 }
846 #endif
847
848 // Destroy shared datastructures between playlists
849 getDatabaseSource()->shutdown();
850
851 /// Delete Sources
852 for (FXint i=0;i<sources.no();i++)
853 delete sources[i];
854
855 delete covermanager;
856
857 ap_free_crypto();
858
859 application->exit(0);
860 }
861
862 #ifdef HAVE_DBUS
update_mpris()863 void GMPlayerManager::update_mpris() {
864 if (mpris1) {
865 if (!sessionbus || !preferences.dbus_mpris1){
866 delete mpris1;
867 mpris1=nullptr;
868 }
869 }
870
871 if (!mpris1 && preferences.dbus_mpris1 && sessionbus)
872 mpris1=new GMMediaPlayerService1(sessionbus);
873 }
874 #endif
875
876
877
update_tray_icon()878 void GMPlayerManager::update_tray_icon() {
879 if (trayicon){
880 if (!preferences.gui_tray_icon || preferences.gui_tray_icon_disabled) {
881 delete trayicon;
882 trayicon=nullptr;
883 }
884 }
885 else {
886 if (preferences.gui_tray_icon && !preferences.gui_tray_icon_disabled) {
887 trayicon = new GMTrayIcon(application);
888 trayicon->create();
889 }
890 }
891 }
892
893
getTrackView() const894 GMTrackView * GMPlayerManager::getTrackView() const {
895 return mainwindow->trackview;
896 }
897
getSourceView() const898 GMSourceView * GMPlayerManager::getSourceView() const {
899 return mainwindow->sourceview;
900 }
901
getDatabaseFilename() const902 FXString GMPlayerManager::getDatabaseFilename() const {
903 return GMApp::getDataDirectory() + PATHSEPSTRING + DATABASE_FILENAME;
904 }
905
init_database(GMTrackDatabase * db)906 FXbool GMPlayerManager::init_database(GMTrackDatabase * db){
907 FXString databasefilename = getDatabaseFilename();
908
909 if (FXStat::exists(databasefilename) && (!FXStat::isWritable(databasefilename) || !FXStat::isReadable(databasefilename)))
910 return false;
911
912 /// Init Database
913 if (!db->init(databasefilename)) {
914 return false;
915 }
916
917 return true;
918 }
919
hasSourceWithKey(const FXString & key) const920 FXbool GMPlayerManager::hasSourceWithKey(const FXString & key) const{
921 for (FXint i=0;i<sources.no();i++){
922 if (key==sources[i]->settingKey())
923 return true;
924 }
925 return false;
926 }
927
928
cleanSourceSettings()929 void GMPlayerManager::cleanSourceSettings() {
930 FXint s;
931 FXStringList keys;
932
933 for (s=0;s<application->reg().no();s++){
934 if (!application->reg().empty(s) && comparecase(application->reg().key(s),"database",8)==0){
935 if (!hasSourceWithKey(application->reg().key(s))) {
936 keys.append(application->reg().key(s));
937 }
938 }
939 }
940
941 for (s=0;s<keys.no();s++){
942 application->reg().deleteSection(keys[s]);
943 }
944 }
945
removePlayListSources()946 void GMPlayerManager::removePlayListSources(){
947 for (FXint i=sources.no()-1;i>=0;i--){
948 if (sources[i]->getType()==SOURCE_DATABASE_PLAYLIST) {
949 FXApp::instance()->reg().deleteSection(sources[i]->settingKey().text());
950 GMSource * src = sources[i];
951 sources.erase(i);
952 delete src;
953 }
954 }
955 }
956
957
open(const FXString & url)958 void GMPlayerManager::open(const FXString & url) {
959 FXint id;
960
961 if (source) {
962 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
963 source->resetCurrent();
964 source=nullptr;
965 }
966
967 trackinfo.url = url;
968 if (gm_is_local_file(url) && sources[0]->hasTrack(url,id)) {
969 source = sources[0];
970 trackinfoset = source->getTrack(trackinfo);
971 sources[0]->setCurrentTrack(id);
972 }
973 else {
974 trackinfoset=false;
975 getTrackView()->setActive(-1);
976 }
977
978 player->open(url,true);
979 }
980
981
982
playItem(FXuint whence)983 void GMPlayerManager::playItem(FXuint whence) {
984 FXint track=-1;
985
986 // Any scheduled stops should be cancelled
987 scheduled_stop = false;
988
989 /// Remove Current Timeout
990 if (source) {
991 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
992 source->resetCurrent();
993 source=nullptr;
994 }
995
996 if (queue) {
997 switch(whence) {
998 case TRACK_CURRENT : track = queue->getCurrent();
999 if (track==-1 && queue->canPlaySource(getTrackView()->getSource())) {
1000 track = getTrackView()->getCurrent();
1001 }
1002 break;
1003 case TRACK_NEXT : track = queue->getNext(); break;
1004 default : FXASSERT(0); break;
1005 };
1006 if (track!=-1) {
1007 source = queue;
1008 }
1009 }
1010 else {
1011 if (track==-1) {
1012 switch(whence) {
1013 case TRACK_CURRENT : track = getTrackView()->getCurrent(); break;
1014 case TRACK_NEXT : track = getTrackView()->getNext(true); break;
1015 case TRACK_PREVIOUS: track = getTrackView()->getPrevious(); break;
1016 default : FXASSERT(0); break;
1017 };
1018 if (track!=-1) {
1019 source = getTrackView()->getSource();
1020 getTrackView()->setActive(track);
1021 }
1022 }
1023 }
1024
1025 if (source) {
1026 trackinfoset = source->getTrack(trackinfo);
1027 player->open(trackinfo.url,true);
1028 }
1029 else {
1030 player->stop();
1031 }
1032 }
1033
1034
1035
1036
stop(FXbool)1037 void GMPlayerManager::stop(FXbool /*force_close*/) {
1038
1039 // Any scheduled stops should be cancelled
1040 scheduled_stop = false;
1041
1042 /// Reset Source
1043 if (source) {
1044 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
1045 source->resetCurrent();
1046 source=nullptr;
1047 }
1048
1049 player->stop();
1050
1051 // We don't call reset_track_display() here, since we'll do that when we get the
1052 // PLAYER_STATE_STOPPED signal from the audio player.
1053 }
1054
1055
1056
seekTime(FXint time)1057 void GMPlayerManager::seekTime(FXint time) {
1058 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
1059 player->setPosition(time);
1060 has_seeked=true;
1061 }
1062
seek(FXdouble pos)1063 void GMPlayerManager::seek(FXdouble pos) {
1064 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
1065 player->seek(pos);
1066 has_seeked=true;
1067 }
1068
1069
pause()1070 void GMPlayerManager::pause() {
1071
1072 // Any scheduled stops should be cancelled
1073 scheduled_stop = false;
1074
1075 player->pause();
1076
1077 if (application->hasTimeout(source,GMSource::ID_TRACK_PLAYED)) {
1078 count_track_remaining = application->remainingTimeout(source,GMSource::ID_TRACK_PLAYED);
1079 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
1080 }
1081 else {
1082 count_track_remaining=0;
1083 }
1084 }
1085
unpause()1086 void GMPlayerManager::unpause() {
1087 player->pause();
1088 }
1089
playlist_empty()1090 FXbool GMPlayerManager::playlist_empty() {
1091 if (getTrackView()->hasTracks()) return false;
1092 if (preferences.play_repeat!=REPEAT_OFF) return false;
1093 if (getTrackView()->getNext()!=-1) return false;
1094 return true;
1095 }
1096
notify_playback_finished()1097 void GMPlayerManager::notify_playback_finished() {
1098 /*
1099 The current track is still playing (and about to be finished) so don't call reset_track_display if
1100 there is nothing to play anymore. It will eventually be called by the PLAYER_STATE_STOPPED signal.
1101 */
1102 FXString errormsg;
1103 FXString filename;
1104 FXint track=-1;
1105
1106 // Check whether playback should be stopped and reset flag
1107 FXbool stop_playback = scheduled_stop;
1108 scheduled_stop=false;
1109
1110 if (queue) {
1111
1112 /// Reset Source
1113 if (source!=queue) {
1114 source->resetCurrent();
1115 source=nullptr;
1116 }
1117
1118 /// Nothing else to do, mark current as played
1119 if (stop_playback) {
1120 queue->getNext();
1121 return;
1122 }
1123
1124 //FIXME handle stop_playback
1125 track = queue->getNext();
1126 if (track==-1) {
1127 source = nullptr;
1128
1129 if (getTrackView()->getSource()==queue)
1130 getTrackView()->refresh();
1131
1132 //reset_track_display();
1133 return;
1134 }
1135 else {
1136 source = queue;
1137 }
1138 trackinfoset = queue->getTrack(trackinfo);
1139 }
1140 else {
1141
1142 /// Don't play anything if we didn't play anything from the library
1143 if (source==nullptr)
1144 return;
1145
1146 /// Can we just start playback without user interaction
1147 if (!getTrackView()->getSource()->autoPlay() || stop_playback) {
1148
1149 /// Reset Source
1150 if (source) {
1151 source->resetCurrent();
1152 source=nullptr;
1153 }
1154
1155 if(preferences.play_repeat!=REPEAT_TRACK) {
1156 track = getTrackView()->getNext();
1157 if (track!=-1) getTrackView()->setCurrent(track);
1158 }
1159 return;
1160 }
1161
1162 if (source) {
1163 source->resetCurrent();
1164 source=nullptr;
1165 }
1166
1167 if (preferences.play_repeat==REPEAT_TRACK)
1168 track = getTrackView()->getActive();
1169 else
1170 track = getTrackView()->getNext();
1171
1172 if (track==-1) {
1173 //reset_track_display();
1174 return;
1175 }
1176
1177 // FIXME only does source->markCurrent
1178 getTrackView()->setActive(track,false);
1179
1180 source = getTrackView()->getSource();
1181 trackinfoset = source->getTrack(trackinfo);
1182 }
1183 player->open(trackinfo.url,false);
1184 }
1185
playing() const1186 FXbool GMPlayerManager::playing() const {
1187 return player->playing() ;
1188 }
1189
1190
reset_track_display()1191 void GMPlayerManager::reset_track_display() {
1192 FXTRACE((51,"GMPlayerManager::reset_track_display()\n"));
1193
1194 /// Reset the cover
1195 covermanager->clear();
1196
1197 /// Reset Main Window
1198 mainwindow->reset();
1199
1200 if (trayicon) trayicon->reset();
1201
1202 /// Reset Active Track
1203 getTrackView()->setActive(-1);
1204
1205 /// Remove Notify
1206 application->removeTimeout(this,ID_PLAY_NOTIFY);
1207
1208 #ifdef HAVE_DBUS
1209 if (notifydaemon && preferences.dbus_notify_daemon)
1210 notifydaemon->reset();
1211 #endif
1212
1213 /// Update View in queue play.
1214 if (queue) {
1215 getSourceView()->refresh(queue);
1216 if (getTrackView()->getSource()==queue) {
1217 getTrackView()->refresh();
1218 }
1219 }
1220
1221 /// Schedule a GUI update
1222 application->refresh();
1223 }
1224
1225
setStatus(const FXString & text)1226 void GMPlayerManager::setStatus(const FXString & text){
1227 mainwindow->statusbar->getStatusLine()->setNormalText(text);
1228 }
1229
update_cover_display()1230 void GMPlayerManager::update_cover_display() {
1231 if (playing()) {
1232
1233 if (preferences.gui_show_playing_albumcover && covermanager->getCover()==nullptr) {
1234
1235 covermanager->load(trackinfo.url);
1236
1237 mainwindow->update_meta_display();
1238
1239 mainwindow->update_cover_display();
1240
1241 }
1242
1243 else if (preferences.gui_show_playing_albumcover==false && covermanager->getCover()) {
1244
1245 covermanager->clear();
1246
1247 mainwindow->update_meta_display();
1248
1249 mainwindow->update_cover_display();
1250
1251 }
1252
1253 else {
1254 mainwindow->update_meta_display();
1255 }
1256 }
1257 else if (covermanager->getCover()) {
1258
1259 covermanager->clear();
1260
1261 mainwindow->update_meta_display();
1262
1263 mainwindow->update_cover_display();
1264 }
1265 }
1266
update_track_display(FXbool notify)1267 void GMPlayerManager::update_track_display(FXbool notify) {
1268 FXTRACE((51,"GMPlayerManager::update_track_display()\n"));
1269
1270 /// If track information is not set, we need to get the latest from the player.
1271 if (source) {
1272 FXint time = (FXint) (((double)trackinfo.time) * 0.80);
1273 if (time <= 5) {
1274 application->removeTimeout(source,GMSource::ID_TRACK_PLAYED);
1275 source->handle(this,FXSEL(SEL_TIMEOUT,GMSource::ID_TRACK_PLAYED),nullptr);
1276 }
1277 else {
1278 count_track_remaining=0;
1279 application->addTimeout(source,GMSource::ID_TRACK_PLAYED,TIME_SEC(time));
1280 }
1281 }
1282
1283 if (preferences.gui_show_playing_albumcover)
1284 covermanager->load(trackinfo.url);
1285
1286 mainwindow->display(trackinfo);
1287
1288 if (notify) application->addTimeout(this,ID_PLAY_NOTIFY,500_ms);
1289
1290 if (queue) {
1291 getSourceView()->refresh(queue);
1292 if (getTrackView()->getSource()==queue)
1293 getTrackView()->refresh();
1294 getTrackView()->showCurrent();
1295 }
1296 }
1297
1298
volume() const1299 FXint GMPlayerManager::volume() const{
1300 return player->getVolume();
1301 }
1302
volume(FXint l)1303 void GMPlayerManager::volume(FXint l) {
1304 player->volume((FXfloat)l/100.0f);
1305 }
1306
can_stop() const1307 FXbool GMPlayerManager::can_stop() const {
1308 return (player->playing() || player->pausing());
1309 }
1310
can_play() const1311 FXbool GMPlayerManager::can_play() const {
1312 return !player->playing() && ((queue==nullptr && getTrackView()->hasTracks()) ||
1313 (queue && (queue->getNumTracks() || (getTrackView()->hasTracks() && getPlayQueue()->canPlaySource(getTrackView()->getSource())))));
1314 }
1315
can_pause() const1316 FXbool GMPlayerManager::can_pause() const {
1317 return (player->playing() && !player->pausing());
1318 }
1319
can_unpause() const1320 FXbool GMPlayerManager::can_unpause() const {
1321 return player->pausing();
1322 }
1323
can_next() const1324 FXbool GMPlayerManager::can_next() const {
1325 if (player->playing() && !player->pausing()) {
1326 if (( queue && queue->getNumTracks()>0) || getTrackView()->getNumTracks()>1)
1327 return true;
1328 }
1329 return false;
1330 }
1331
can_prev() const1332 FXbool GMPlayerManager::can_prev() const {
1333 if (player->playing() && !player->pausing() && queue==nullptr) {
1334 return (getTrackView()->getNumTracks()>1);
1335 }
1336 return false;
1337 }
1338
setSleepTimer(FXlong ns)1339 void GMPlayerManager::setSleepTimer(FXlong ns) {
1340 if (ns==0)
1341 application->removeTimeout(this,GMPlayerManager::ID_SLEEP_TIMER);
1342 else
1343 application->addTimeout(this,GMPlayerManager::ID_SLEEP_TIMER,ns);
1344 }
1345
hasSleepTimer()1346 FXbool GMPlayerManager::hasSleepTimer() {
1347 return application->hasTimeout(this,GMPlayerManager::ID_SLEEP_TIMER);
1348 }
1349
show_message(const FXchar * title,const FXchar * msg)1350 void GMPlayerManager::show_message(const FXchar * title,const FXchar * msg){
1351 if (application->getActiveWindow() && application->getActiveWindow()->shown()) {
1352 FXMessageBox::error(application->getActiveWindow(),MBOX_OK,title,"%s",msg);
1353 }
1354 else {
1355 if (mainwindow) {
1356 if (mainwindow->shown())
1357 FXMessageBox::error(mainwindow,MBOX_OK,title,"%s",msg);
1358 else if (mainwindow->getRemote())
1359 FXMessageBox::error(mainwindow->getRemote(),MBOX_OK,title,"%s",msg);
1360 else
1361 FXMessageBox::error(application,MBOX_OK,title,"%s",msg);
1362 }
1363 else {
1364 FXMessageBox::error(application,MBOX_OK,title,"%s",msg);
1365 }
1366 }
1367 }
1368
1369
onCmdCloseWindow(FXObject * sender,FXSelector,void *)1370 long GMPlayerManager::onCmdCloseWindow(FXObject*sender,FXSelector,void*){
1371 FXWindow * window = static_cast<FXWindow*>(sender);
1372 if (getPreferences().gui_hide_player_when_close && !getPreferences().gui_tray_icon_disabled) {
1373 window->hide();
1374 }
1375 else {
1376 getMainWindow()->handle(this,FXSEL(SEL_COMMAND,GMWindow::ID_QUIT),nullptr);
1377 }
1378 return 1;
1379 }
1380
onCmdQuit(FXObject *,FXSelector,void *)1381 long GMPlayerManager::onCmdQuit(FXObject*,FXSelector,void*){
1382 getMainWindow()->handle(this,FXSEL(SEL_COMMAND,GMWindow::ID_QUIT),nullptr);
1383 return 1;
1384 }
1385
1386
onCmdChild(FXObject *,FXSelector,void *)1387 long GMPlayerManager::onCmdChild(FXObject*,FXSelector,void*){
1388 FXint pid;
1389 FXint status;
1390 while(1) {
1391 pid = waitpid(-1,&status,WNOHANG);
1392 if (pid>0) {
1393 continue;
1394 }
1395 break;
1396 }
1397 return 1;
1398 }
1399
onScrobblerError(FXObject *,FXSelector,void * ptr)1400 long GMPlayerManager::onScrobblerError(FXObject*,FXSelector,void*ptr){
1401 show_message(fxtr("Last.FM Error"),(const FXchar*)ptr);
1402 return 1;
1403 }
1404
onScrobblerOpen(FXObject *,FXSelector,void * ptr)1405 long GMPlayerManager::onScrobblerOpen(FXObject*,FXSelector,void*ptr){
1406 gm_open_browser((const FXchar*)ptr);
1407 return 1;
1408 }
1409
1410
1411
onImportTaskCompleted(FXObject *,FXSelector sel,void * ptr)1412 long GMPlayerManager::onImportTaskCompleted(FXObject*,FXSelector sel,void*ptr){
1413 GMTask * task = *static_cast<GMTask**>(ptr);
1414 if (FXSELTYPE(sel)==SEL_TASK_COMPLETED) {
1415 database->initArtistLookup();
1416 getDatabaseSource()->updateCovers();
1417 GMSource * src = getTrackView()->getSource();
1418 if (src) {
1419 switch(src->getType()){
1420 case SOURCE_DATABASE:
1421 case SOURCE_DATABASE_FILTER:
1422 case SOURCE_DATABASE_PLAYLIST: getTrackView()->refresh(); break;
1423 default: break;
1424 }
1425 }
1426 }
1427 delete task;
1428 return 0;
1429 }
1430
1431
1432
runTask(GMTask * task)1433 void GMPlayerManager::runTask(GMTask * task) {
1434 application->removeTimeout(this,GMPlayerManager::ID_TASKMANAGER_SHUTDOWN);
1435 taskmanager->run(task);
1436 }
1437
onTaskManagerIdle(FXObject *,FXSelector,void *)1438 long GMPlayerManager::onTaskManagerIdle(FXObject*,FXSelector,void*){
1439 mainwindow->setStatus(FXString::null);
1440 GM_DEBUG_PRINT("Schedule taskmanager shutdown in 30s\n");
1441 application->addTimeout(this,GMPlayerManager::ID_TASKMANAGER_SHUTDOWN,30_s);
1442 return 0;
1443 }
1444
onTaskManagerRunning(FXObject *,FXSelector,void *)1445 long GMPlayerManager::onTaskManagerRunning(FXObject*,FXSelector,void*){
1446 GM_DEBUG_PRINT("Taskmanager running\n");
1447 application->removeTimeout(this,GMPlayerManager::ID_TASKMANAGER_SHUTDOWN);
1448 return 0;
1449 }
1450
1451
onTaskManagerShutdown(FXObject *,FXSelector,void *)1452 long GMPlayerManager::onTaskManagerShutdown(FXObject*,FXSelector,void*){
1453 GM_DEBUG_PRINT("Shutdown taskmanager now\n");
1454 taskmanager->shutdown();
1455 return 0;
1456 }
1457
1458
onTaskManagerStatus(FXObject *,FXSelector,void * ptr)1459 long GMPlayerManager::onTaskManagerStatus(FXObject*,FXSelector,void*ptr){
1460 FXchar * msg = (FXchar*)ptr;
1461 mainwindow->setStatus(msg);
1462 return 0;
1463 }
1464
onCancelTask(FXObject *,FXSelector,void *)1465 long GMPlayerManager::onCancelTask(FXObject*,FXSelector,void*){
1466 taskmanager->cancelTask();
1467 return 0;
1468 }
1469
1470
1471
1472
cmd_play()1473 void GMPlayerManager::cmd_play(){
1474 if (can_unpause())
1475 unpause();
1476 else if (can_play())
1477 playItem(TRACK_CURRENT);
1478 }
1479
1480
cmd_playpause()1481 void GMPlayerManager::cmd_playpause(){
1482 if (can_pause())
1483 pause();
1484 else if (can_unpause())
1485 unpause();
1486 else if (can_play())
1487 playItem(TRACK_CURRENT);
1488 }
1489
1490
cmd_pause()1491 void GMPlayerManager::cmd_pause(){
1492 if (can_pause())
1493 pause();
1494 }
1495
cmd_schedule_stop()1496 void GMPlayerManager::cmd_schedule_stop(){
1497 if (scheduled_stop==false) {
1498 if (can_stop()) {
1499 GM_DEBUG_PRINT("enable scheduled stop\n");
1500 scheduled_stop=true;
1501 }
1502 }
1503 else {
1504 GM_DEBUG_PRINT("disable scheduled stop\n");
1505 scheduled_stop=false;
1506 }
1507 }
1508
has_scheduled_stop() const1509 FXbool GMPlayerManager::has_scheduled_stop() const {
1510 if (can_stop() && scheduled_stop)
1511 return true;
1512 else
1513 return false;
1514 }
1515
cmd_stop()1516 void GMPlayerManager::cmd_stop(){
1517 if (can_stop())
1518 stop();
1519 }
1520
cmd_next()1521 void GMPlayerManager::cmd_next(){
1522 if (can_next())
1523 playItem(TRACK_NEXT);
1524 }
cmd_prev()1525 void GMPlayerManager::cmd_prev(){
1526 if (can_prev())
1527 playItem(TRACK_PREVIOUS);
1528 }
1529
cmd_raise()1530 void GMPlayerManager::cmd_raise() {
1531 getMainWindow()->raiseWindow();
1532 }
1533
cmd_toggle_shown()1534 void GMPlayerManager::cmd_toggle_shown(){
1535 getMainWindow()->toggleShown();
1536 }
1537
cmd_update_podcasts()1538 void GMPlayerManager::cmd_update_podcasts(){
1539 FXASSERT(podcast);
1540 podcast->refreshFeeds();
1541 }
1542
1543
1544
display_track_notification()1545 void GMPlayerManager::display_track_notification() {
1546 #ifdef DEBUG
1547 fxmessage("Track Change Notification\n");
1548 fxmessage("\t Title: %s\n",trackinfo.title.text());
1549 fxmessage("\tArtist: %s\n",trackinfo.artist.text());
1550 fxmessage("\t Album: %s\n",trackinfo.album.text());
1551 #endif
1552 #ifdef HAVE_DBUS
1553 if (sessionbus) {
1554 if (preferences.dbus_notify_daemon && notifydaemon) {
1555 notifydaemon->notify_track_change(trackinfo);
1556 }
1557 if (mpris1) mpris1->notify_track_change(trackinfo);
1558 if (mpris2) mpris2->notify_track_change(trackinfo);
1559 }
1560 #endif
1561 }
1562
1563
1564 #ifdef HAVE_DBUS
onCmdSettingsDaemon(FXObject *,FXSelector,void * ptr)1565 long GMPlayerManager::onCmdSettingsDaemon(FXObject*,FXSelector,void*ptr){
1566 const FXchar * cmd = (const FXchar*)ptr;
1567 if (comparecase(cmd,"play")==0) cmd_playpause();
1568 else if (comparecase(cmd,"pause")==0) cmd_playpause();
1569 else if (comparecase(cmd,"stop")==0) cmd_stop();
1570 else if (comparecase(cmd,"previous")==0) cmd_prev();
1571 else if (comparecase(cmd,"next")==0) cmd_next();
1572 else {
1573 GM_DEBUG_PRINT("Unknown or unhandled key press: %s\n",cmd);
1574 }
1575 return 1;
1576 }
1577 #endif
1578
1579
1580 // Perhaps should do something else...
xregisterhotkeys(Display * dpy,XErrorEvent * eev)1581 static int xregisterhotkeys(Display* dpy,XErrorEvent* eev){
1582 char buf[256];
1583
1584 if(eev->error_code==BadAccess && eev->request_code==33) return 0;
1585
1586 // A BadWindow due to X_SendEvent is likely due to XDND
1587 if(eev->error_code==BadWindow && eev->request_code==25) return 0;
1588
1589 // WM_TAKE_FOCUS causes sporadic errors for X_SetInputFocus
1590 if(eev->request_code==42) return 0;
1591
1592 // Get error codes
1593 XGetErrorText(dpy,eev->error_code,buf,sizeof(buf));
1594
1595 // Print out meaningful warning
1596 fxwarning("gogglesmm X Error: code %d major %d minor %d: %s.\n",eev->error_code,eev->request_code,eev->minor_code,buf);
1597 return 1;
1598 }
1599
1600
register_global_hotkeys()1601 void GMPlayerManager::register_global_hotkeys() {
1602 Window root = application->getRootWindow()->id();
1603 Display * display = (Display*) application->getDisplay();
1604 KeyCode keycode;
1605
1606 XErrorHandler previous = XSetErrorHandler(xregisterhotkeys);
1607
1608 /// Only register hotkeys on the rootwindow.
1609 #ifdef XF86XK_AudioPlay
1610 keycode = XKeysymToKeycode(display,XF86XK_AudioPlay);
1611 if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1612 #endif
1613 #ifdef XF86XK_AudioPause
1614 keycode = XKeysymToKeycode(display,XF86XK_AudioPause);
1615 if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1616 #endif
1617 #ifdef XF86XK_AudioStop
1618 keycode = XKeysymToKeycode(display,XF86XK_AudioStop);
1619 if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1620 #endif
1621 #ifdef XF86XK_AudioNext
1622 keycode = XKeysymToKeycode(display,XF86XK_AudioNext);
1623 if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1624 #endif
1625 #ifdef XF86XK_AudioPrev
1626 keycode = XKeysymToKeycode(display,XF86XK_AudioPrev);
1627 if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1628 #endif
1629 //#ifdef XF86XK_AudioMute
1630 // keycode = XKeysymToKeycode(display,XF86XK_AudioMute);
1631 // if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1632 //#endif
1633 //#ifdef XF86XK_AudioLowerVolume
1634 // keycode = XKeysymToKeycode(display,XF86XK_AudioLowerVolume);
1635 // if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1636 //#endif
1637 //#ifdef XF86XK_AudioRaiseVolume
1638 // keycode = XKeysymToKeycode(display,XF86XK_AudioRaiseVolume);
1639 // if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1640 //#endif
1641 //#ifdef XF86XK_AudioRepeat
1642 // keycode = XKeysymToKeycode(display,XF86XK_AudioRepeat);
1643 // if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1644 //#endif
1645 //#ifdef XF86XK_AudioRandomPlay
1646 // keycode = XKeysymToKeycode(display,XF86XK_AudioRandomPlay);
1647 // if (keycode) XGrabKey(display,keycode,AnyModifier,root,False,GrabModeAsync,GrabModeAsync);
1648 //#endif
1649
1650 XSync (display,False);
1651 XSetErrorHandler(previous);
1652 }
1653
handle_global_hotkeys(FXuint code)1654 FXbool GMPlayerManager::handle_global_hotkeys(FXuint code) {
1655 switch(code) {
1656 // case XF86XK_AudioMute : break;
1657 // case XF86XK_AudioRaiseVolume : break;
1658 // case XF86XK_AudioLowerVolume : break;
1659 #ifdef XF86XK_AudioPlay
1660 case XF86XK_AudioPlay : cmd_playpause(); break;
1661 #endif
1662 #ifdef XF86XK_AudioPause
1663 case XF86XK_AudioPause : cmd_playpause(); break;
1664 #endif
1665 #ifdef XF86XK_AudioStop
1666 case XF86XK_AudioStop : cmd_stop(); break;
1667 #endif
1668 #ifdef XF86XK_AudioPrev
1669 case XF86XK_AudioPrev : cmd_prev(); break;
1670 #endif
1671 #ifdef XF86XK_AudioNext
1672 case XF86XK_AudioNext : cmd_next(); break;
1673 #endif
1674 // case XF86XK_AudioRepeat : break;
1675 // case XF86XK_AudioRandomPlay : break;
1676 default : return false; break;
1677 }
1678 return true;
1679 }
1680
1681 //#ifndef HAVE_XINE_LIB
onPlayerBOS(FXObject *,FXSelector,void *)1682 long GMPlayerManager::onPlayerBOS(FXObject*,FXSelector,void*){
1683 GM_DEBUG_PRINT("[player] bos\n");
1684 update_track_display();
1685 getTrackView()->showCurrent();
1686 return 1;
1687 }
1688
onPlayerEOS(FXObject *,FXSelector,void *)1689 long GMPlayerManager::onPlayerEOS(FXObject*,FXSelector,void*){
1690 GM_DEBUG_PRINT("[player] eos\n");
1691 notify_playback_finished();
1692 return 1;
1693 }
1694
1695
onPlayerTime(FXObject *,FXSelector,void * ptr)1696 long GMPlayerManager::onPlayerTime(FXObject*,FXSelector,void* ptr){
1697 const PlaybackTime * tm = static_cast<const PlaybackTime*>(ptr);
1698
1699 TrackTime tm_progress;
1700 TrackTime tm_remaining;
1701
1702 FXint time,pos=0;
1703
1704 /// Time progressed
1705 time = tm->position;
1706 tm_progress.hours = (FXint) floor((double)time/3600.0);
1707 time -= (3600*tm_progress.hours);
1708 tm_progress.minutes = (FXint) floor((double)time/60.0);
1709 time -= (FXint) (60*tm_progress.minutes);
1710 tm_progress.seconds = (FXint) floor((double)time);
1711
1712
1713 /// Time Remaining
1714 if (tm->length) {
1715 time = (tm->length-tm->position);
1716 tm_remaining.hours = (FXint) floor((double)time/3600.0);
1717 time -= (3600*tm_remaining.hours);
1718 tm_remaining.minutes = (FXint) floor((double)time/60.0);
1719 time -= (FXint) (60*tm_remaining.minutes);
1720 tm_remaining.seconds = (FXint) floor((double)time);
1721
1722 pos = (FXint)(100000.0 * ( (double)tm->position / (double)tm->length));
1723 }
1724
1725 mainwindow->update_time(tm_progress,tm_remaining,pos,true,true);
1726
1727 #ifdef HAVE_DBUS
1728 if (has_seeked) {
1729 if (mpris2) mpris2->notify_seek(tm->position);
1730 has_seeked=false;
1731 }
1732 #endif
1733 return 1;
1734 }
1735
onPlayerState(FXObject *,FXSelector,void * ptr)1736 long GMPlayerManager::onPlayerState(FXObject*,FXSelector,void* ptr){
1737 FXint state = (FXint)(FXival)ptr;
1738 switch(state){
1739 case PLAYER_STOPPED: GM_DEBUG_PRINT("[player] stopped\n"); reset_track_display(); break;
1740 case PLAYER_PLAYING: break;
1741 case PLAYER_PAUSING: break;
1742 }
1743 #ifdef HAVE_DBUS
1744 if (mpris1) mpris1->notify_status_change();
1745 if (mpris2) mpris2->notify_status_change();
1746 #endif
1747 return 1;
1748 }
1749
onPlayerVolume(FXObject *,FXSelector,void * ptr)1750 long GMPlayerManager::onPlayerVolume(FXObject*,FXSelector,void* ptr){
1751 getMainWindow()->update_volume_display((FXint)(FXival)ptr);
1752 #ifdef HAVE_DBUS
1753 if (mpris2) mpris2->notify_volume((FXint)(FXival)ptr);
1754 #endif
1755 return 1;
1756 }
1757
onPlayerMeta(FXObject *,FXSelector,void * ptr)1758 long GMPlayerManager::onPlayerMeta(FXObject*,FXSelector,void* ptr){
1759 GMTrack * track = static_cast<GMTrack*>(ptr);
1760 GM_DEBUG_PRINT("[player] meta\n");
1761 if (trackinfoset==false) {
1762 trackinfo.title.adopt(track->title);
1763 trackinfo.artist.adopt(track->artist);
1764 trackinfo.album.adopt(track->album);
1765 update_track_display();
1766 }
1767 return 1;
1768 }
1769
1770
onPlayerError(FXObject *,FXSelector,void * ptr)1771 long GMPlayerManager::onPlayerError(FXObject*,FXSelector,void*ptr){
1772 FXString * msg = (FXString*)ptr;
1773 show_message(fxtr("Playback Error"),msg->text());
1774 return 1;
1775 }
1776
1777
createPlaylist(const FXString & name)1778 FXint GMPlayerManager::createPlaylist(const FXString & name) {
1779 FXint playlist=-1;
1780 if (database->insertPlaylist(name,playlist)) {
1781 insertSource(new GMPlayListSource(database,playlist));
1782 getSourceView()->refresh();
1783 }
1784 return playlist;
1785 }
1786
getMainWindowId() const1787 FXuint GMPlayerManager::getMainWindowId() const {
1788 return mainwindow->id();
1789 }
1790
1791
1792