diff options
Diffstat (limited to 'src/Application.cpp')
-rw-r--r-- | src/Application.cpp | 5862 |
1 files changed, 5862 insertions, 0 deletions
diff --git a/src/Application.cpp b/src/Application.cpp new file mode 100644 index 0000000000..7ee0a34b07 --- /dev/null +++ b/src/Application.cpp @@ -0,0 +1,5862 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://xbmc.org + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * <http://www.gnu.org/licenses/>. + * + */ + +#include "network/Network.h" +#include "threads/SystemClock.h" +#include "system.h" +#include "Application.h" +#include "interfaces/Builtins.h" +#include "utils/Variant.h" +#include "utils/Splash.h" +#include "LangInfo.h" +#include "utils/Screenshot.h" +#include "Util.h" +#include "URL.h" +#include "guilib/TextureManager.h" +#include "cores/IPlayer.h" +#include "cores/dvdplayer/DVDFileInfo.h" +#include "cores/AudioEngine/AEFactory.h" +#include "cores/AudioEngine/Utils/AEUtil.h" +#include "PlayListPlayer.h" +#include "Autorun.h" +#include "video/Bookmark.h" +#include "network/NetworkServices.h" +#include "guilib/GUIControlProfiler.h" +#include "utils/LangCodeExpander.h" +#include "GUIInfoManager.h" +#include "playlists/PlayListFactory.h" +#include "guilib/GUIFontManager.h" +#include "guilib/GUIColorManager.h" +#include "guilib/StereoscopicsManager.h" +#include "guilib/GUITextLayout.h" +#include "addons/Skin.h" +#include "interfaces/generic/ScriptInvocationManager.h" +#ifdef HAS_PYTHON +#include "interfaces/python/XBPython.h" +#endif +#include "input/ButtonTranslator.h" +#include "guilib/GUIAudioManager.h" +#include "GUIPassword.h" +#include "input/InertialScrollingHandler.h" +#include "ApplicationMessenger.h" +#include "SectionLoader.h" +#include "cores/DllLoader/DllLoaderContainer.h" +#include "GUIUserMessages.h" +#include "filesystem/Directory.h" +#include "filesystem/DirectoryCache.h" +#include "filesystem/StackDirectory.h" +#include "filesystem/SpecialProtocol.h" +#include "filesystem/DllLibCurl.h" +#include "filesystem/MythSession.h" +#include "filesystem/PluginDirectory.h" +#ifdef HAS_FILESYSTEM_SAP +#include "filesystem/SAPDirectory.h" +#endif +#ifdef HAS_FILESYSTEM_HTSP +#include "filesystem/HTSPDirectory.h" +#endif +#include "utils/TuxBoxUtil.h" +#include "utils/SystemInfo.h" +#include "utils/TimeUtils.h" +#include "GUILargeTextureManager.h" +#include "TextureCache.h" +#include "playlists/SmartPlayList.h" +#ifdef HAS_FILESYSTEM_RAR +#include "filesystem/RarManager.h" +#endif +#include "playlists/PlayList.h" +#include "profiles/ProfilesManager.h" +#include "windowing/WindowingFactory.h" +#include "powermanagement/PowerManager.h" +#include "powermanagement/DPMSSupport.h" +#include "settings/SettingAddon.h" +#include "settings/Settings.h" +#include "settings/AdvancedSettings.h" +#include "settings/DisplaySettings.h" +#include "settings/MediaSettings.h" +#include "settings/MediaSourceSettings.h" +#include "settings/SkinSettings.h" +#include "guilib/LocalizeStrings.h" +#include "utils/CPUInfo.h" +#include "utils/RssManager.h" +#include "utils/SeekHandler.h" +#include "view/ViewStateSettings.h" + +#include "input/KeyboardStat.h" +#include "input/XBMC_vkeys.h" +#include "input/MouseStat.h" + +#if SDL_VERSION == 1 +#include <SDL/SDL.h> +#elif SDL_VERSION == 2 +#include <SDL2/SDL.h> +#endif + +#if defined(FILESYSTEM) && !defined(TARGET_POSIX) +#include "filesystem/FileDAAP.h" +#endif +#ifdef HAS_UPNP +#include "network/upnp/UPnP.h" +#include "network/upnp/UPnPSettings.h" +#include "filesystem/UPnPDirectory.h" +#endif +#if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB) +#include "filesystem/SMBDirectory.h" +#endif +#ifdef HAS_FILESYSTEM_NFS +#include "filesystem/NFSFile.h" +#endif +#ifdef HAS_FILESYSTEM_AFP +#include "filesystem/AFPFile.h" +#endif +#ifdef HAS_FILESYSTEM_SFTP +#include "filesystem/SFTPFile.h" +#endif +#include "PartyModeManager.h" +#ifdef HAS_VIDEO_PLAYBACK +#include "cores/VideoRenderers/RenderManager.h" +#endif +#ifdef HAS_KARAOKE +#include "music/karaoke/karaokelyricsmanager.h" +#include "music/karaoke/GUIDialogKaraokeSongSelector.h" +#include "music/karaoke/GUIWindowKaraokeLyrics.h" +#endif +#include "network/Zeroconf.h" +#include "network/ZeroconfBrowser.h" +#ifndef TARGET_POSIX +#include "threads/platform/win/Win32Exception.h" +#endif +#ifdef HAS_EVENT_SERVER +#include "network/EventServer.h" +#endif +#ifdef HAS_DBUS +#include <dbus/dbus.h> +#endif +#ifdef HAS_JSONRPC +#include "interfaces/json-rpc/JSONRPC.h" +#include "network/TCPServer.h" +#endif +#ifdef HAS_AIRPLAY +#include "network/AirPlayServer.h" +#endif +#ifdef HAS_AIRTUNES +#include "network/AirTunesServer.h" +#endif +#include "interfaces/AnnouncementManager.h" +#include "peripherals/Peripherals.h" +#include "peripherals/dialogs/GUIDialogPeripheralManager.h" +#include "peripherals/dialogs/GUIDialogPeripheralSettings.h" +#include "peripherals/devices/PeripheralImon.h" +#include "music/infoscanner/MusicInfoScanner.h" + +// Windows includes +#include "guilib/GUIWindowManager.h" +#include "windows/GUIWindowHome.h" +#include "settings/windows/GUIWindowSettings.h" +#include "windows/GUIWindowFileManager.h" +#include "settings/windows/GUIWindowSettingsCategory.h" +#include "music/windows/GUIWindowMusicPlaylist.h" +#include "music/windows/GUIWindowMusicSongs.h" +#include "music/windows/GUIWindowMusicNav.h" +#include "music/windows/GUIWindowMusicPlaylistEditor.h" +#include "video/windows/GUIWindowVideoPlaylist.h" +#include "music/dialogs/GUIDialogMusicInfo.h" +#include "video/dialogs/GUIDialogVideoInfo.h" +#include "video/windows/GUIWindowVideoNav.h" +#include "profiles/windows/GUIWindowSettingsProfile.h" +#ifdef HAS_GL +#include "rendering/gl/GUIWindowTestPatternGL.h" +#endif +#ifdef HAS_DX +#include "rendering/dx/GUIWindowTestPatternDX.h" +#endif +#include "settings/windows/GUIWindowSettingsScreenCalibration.h" +#include "programs/GUIWindowPrograms.h" +#include "pictures/GUIWindowPictures.h" +#include "windows/GUIWindowWeather.h" +#include "windows/GUIWindowLoginScreen.h" +#include "addons/GUIWindowAddonBrowser.h" +#include "music/windows/GUIWindowVisualisation.h" +#include "windows/GUIWindowDebugInfo.h" +#include "windows/GUIWindowPointer.h" +#include "windows/GUIWindowSystemInfo.h" +#include "windows/GUIWindowScreensaver.h" +#include "windows/GUIWindowScreensaverDim.h" +#include "pictures/GUIWindowSlideShow.h" +#include "windows/GUIWindowStartup.h" +#include "video/windows/GUIWindowFullScreen.h" +#include "video/dialogs/GUIDialogVideoOSD.h" +#include "music/dialogs/GUIDialogMusicOverlay.h" +#include "video/dialogs/GUIDialogVideoOverlay.h" +#include "video/VideoInfoScanner.h" +#include "video/PlayerController.h" + +// Dialog includes +#include "music/dialogs/GUIDialogMusicOSD.h" +#include "music/dialogs/GUIDialogVisualisationPresetList.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "network/GUIDialogNetworkSetup.h" +#include "dialogs/GUIDialogMediaSource.h" +#include "video/dialogs/GUIDialogVideoSettings.h" +#include "video/dialogs/GUIDialogAudioSubtitleSettings.h" +#include "video/dialogs/GUIDialogVideoBookmarks.h" +#include "profiles/dialogs/GUIDialogProfileSettings.h" +#include "profiles/dialogs/GUIDialogLockSettings.h" +#include "settings/dialogs/GUIDialogContentSettings.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogKeyboardGeneric.h" +#include "dialogs/GUIDialogYesNo.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogSeekBar.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogVolumeBar.h" +#include "dialogs/GUIDialogMuteBug.h" +#include "video/dialogs/GUIDialogFileStacking.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogGamepad.h" +#include "dialogs/GUIDialogSubMenu.h" +#include "dialogs/GUIDialogFavourites.h" +#include "dialogs/GUIDialogButtonMenu.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogPlayerControls.h" +#include "music/dialogs/GUIDialogSongInfo.h" +#include "dialogs/GUIDialogSmartPlaylistEditor.h" +#include "dialogs/GUIDialogSmartPlaylistRule.h" +#include "pictures/GUIDialogPictureInfo.h" +#include "addons/GUIDialogAddonSettings.h" +#include "addons/GUIDialogAddonInfo.h" +#ifdef HAS_LINUX_NETWORK +#include "network/GUIDialogAccessPoints.h" +#endif + +/* PVR related include Files */ +#include "pvr/PVRManager.h" +#include "pvr/timers/PVRTimers.h" +#include "pvr/windows/GUIWindowPVRChannels.h" +#include "pvr/windows/GUIWindowPVRRecordings.h" +#include "pvr/windows/GUIWindowPVRGuide.h" +#include "pvr/windows/GUIWindowPVRTimers.h" +#include "pvr/windows/GUIWindowPVRSearch.h" +#include "pvr/dialogs/GUIDialogPVRChannelManager.h" +#include "pvr/dialogs/GUIDialogPVRChannelsOSD.h" +#include "pvr/dialogs/GUIDialogPVRCutterOSD.h" +#include "pvr/dialogs/GUIDialogPVRDirectorOSD.h" +#include "pvr/dialogs/GUIDialogPVRGroupManager.h" +#include "pvr/dialogs/GUIDialogPVRGuideInfo.h" +#include "pvr/dialogs/GUIDialogPVRGuideOSD.h" +#include "pvr/dialogs/GUIDialogPVRGuideSearch.h" +#include "pvr/dialogs/GUIDialogPVRRecordingInfo.h" +#include "pvr/dialogs/GUIDialogPVRTimerSettings.h" + +#include "epg/EpgContainer.h" + +#include "video/dialogs/GUIDialogFullScreenInfo.h" +#include "video/dialogs/GUIDialogTeletext.h" +#include "dialogs/GUIDialogSlider.h" +#include "guilib/GUIControlFactory.h" +#include "dialogs/GUIDialogCache.h" +#include "dialogs/GUIDialogPlayEject.h" +#include "dialogs/GUIDialogMediaFilter.h" +#include "video/dialogs/GUIDialogSubtitles.h" +#include "utils/XMLUtils.h" +#include "addons/AddonInstaller.h" +#include "CompileInfo.h" + +#ifdef HAS_PERFORMANCE_SAMPLE +#include "utils/PerformanceSample.h" +#else +#define MEASURE_FUNCTION +#endif + +#ifdef TARGET_WINDOWS +#include <shlobj.h> +#include "win32util.h" +#endif +#ifdef HAS_XRANDR +#include "windowing/X11/XRandR.h" +#endif + +#ifdef TARGET_DARWIN_OSX +#include "osx/CocoaInterface.h" +#include "osx/XBMCHelper.h" +#endif +#ifdef TARGET_DARWIN +#include "osx/DarwinUtils.h" +#endif + + +#ifdef HAS_DVD_DRIVE +#include <cdio/logging.h> +#endif + +#include "storage/MediaManager.h" +#include "utils/JobManager.h" +#include "utils/SaveFileStateJob.h" +#include "utils/AlarmClock.h" +#include "utils/RssReader.h" +#include "utils/StringUtils.h" +#include "utils/Weather.h" +#include "DatabaseManager.h" + +#ifdef TARGET_POSIX +#include "XHandle.h" +#endif + +#ifdef HAS_LIRC +#include "input/linux/LIRC.h" +#endif +#ifdef HAS_IRSERVERSUITE + #include "input/windows/IRServerSuite.h" +#endif + +#if defined(TARGET_WINDOWS) +#include "input/windows/WINJoystick.h" +#elif defined(HAS_SDL_JOYSTICK) || defined(HAS_EVENT_SERVER) +#include "input/SDLJoystick.h" +#endif + +#if defined(TARGET_ANDROID) +#include "android/activity/XBMCApp.h" +#include "android/activity/AndroidFeatures.h" +#include "android/jni/Build.h" +#endif + +#ifdef TARGET_WINDOWS +#include "utils/Environment.h" +#endif + +#if defined(HAS_LIBAMCODEC) +#include "utils/AMLUtils.h" +#endif + +#include "cores/FFmpeg.h" + +using namespace std; +using namespace ADDON; +using namespace XFILE; +#ifdef HAS_DVD_DRIVE +using namespace MEDIA_DETECT; +#endif +using namespace PLAYLIST; +using namespace VIDEO; +using namespace MUSIC_INFO; +#ifdef HAS_EVENT_SERVER +using namespace EVENTSERVER; +#endif +#ifdef HAS_JSONRPC +using namespace JSONRPC; +#endif +using namespace ANNOUNCEMENT; +using namespace PVR; +using namespace EPG; +using namespace PERIPHERALS; + +using namespace XbmcThreads; + +// uncomment this if you want to use release libs in the debug build. +// Atm this saves you 7 mb of memory +#define USE_RELEASE_LIBS + +#define MAX_FFWD_SPEED 5 + +//extern IDirectSoundRenderer* m_pAudioDecoder; +CApplication::CApplication(void) + : m_pPlayer(new CApplicationPlayer) + , m_itemCurrentFile(new CFileItem) + , m_stackFileItemToUpdate(new CFileItem) + , m_progressTrackingVideoResumeBookmark(*new CBookmark) + , m_progressTrackingItem(new CFileItem) + , m_videoInfoScanner(new CVideoInfoScanner) + , m_musicInfoScanner(new CMusicInfoScanner) + , m_seekHandler(new CSeekHandler) + , m_playerController(new CPlayerController) +{ + m_network = NULL; + TiXmlBase::SetCondenseWhiteSpace(false); + m_bInhibitIdleShutdown = false; + m_bScreenSave = false; + m_dpms = NULL; + m_dpmsIsActive = false; + m_dpmsIsManual = false; + m_iScreenSaveLock = 0; + m_bInitializing = true; + m_eForcedNextPlayer = EPC_NONE; + m_strPlayListFile = ""; + m_nextPlaylistItem = -1; + m_bPlaybackStarting = false; + m_ePlayState = PLAY_STATE_NONE; + m_skinReverting = false; + m_loggingIn = false; + +#ifdef HAS_GLX + XInitThreads(); +#endif + + + /* for now always keep this around */ +#ifdef HAS_KARAOKE + m_pKaraokeMgr = new CKaraokeLyricsManager(); +#endif + m_currentStack = new CFileItemList; + + m_bPresentFrame = false; + m_bPlatformDirectories = true; + + m_bStandalone = false; + m_bEnableLegacyRes = false; + m_bSystemScreenSaverEnable = false; + m_pInertialScrollingHandler = new CInertialScrollingHandler(); +#ifdef HAS_DVD_DRIVE + m_Autorun = new CAutorun(); +#endif + + m_splash = NULL; + m_threadID = 0; + m_progressTrackingPlayCountUpdate = false; + m_currentStackPosition = 0; + m_lastFrameTime = 0; + m_lastRenderTime = 0; + m_bTestMode = false; + + m_muted = false; + m_volumeLevel = VOLUME_MAXIMUM; +} + +CApplication::~CApplication(void) +{ + delete m_musicInfoScanner; + delete m_videoInfoScanner; + delete &m_progressTrackingVideoResumeBookmark; +#ifdef HAS_DVD_DRIVE + delete m_Autorun; +#endif + delete m_currentStack; + +#ifdef HAS_KARAOKE + delete m_pKaraokeMgr; +#endif + + delete m_dpms; + delete m_seekHandler; + delete m_playerController; + delete m_pInertialScrollingHandler; + delete m_pPlayer; +} + +bool CApplication::OnEvent(XBMC_Event& newEvent) +{ + switch(newEvent.type) + { + case XBMC_QUIT: + if (!g_application.m_bStop) + CApplicationMessenger::Get().Quit(); + break; + case XBMC_KEYDOWN: + g_application.OnKey(g_Keyboard.ProcessKeyDown(newEvent.key.keysym)); + break; + case XBMC_KEYUP: + g_Keyboard.ProcessKeyUp(); + break; + case XBMC_MOUSEBUTTONDOWN: + case XBMC_MOUSEBUTTONUP: + case XBMC_MOUSEMOTION: + g_Mouse.HandleEvent(newEvent); + g_application.ProcessMouse(); + break; + case XBMC_VIDEORESIZE: + if (!g_application.m_bInitializing && + !g_advancedSettings.m_fullScreen) + { + g_Windowing.SetWindowResolution(newEvent.resize.w, newEvent.resize.h); + g_graphicsContext.SetVideoResolution(RES_WINDOW, true); + CSettings::Get().SetInt("window.width", newEvent.resize.w); + CSettings::Get().SetInt("window.height", newEvent.resize.h); + CSettings::Get().Save(); + } + break; + case XBMC_VIDEOMOVE: +#ifdef TARGET_WINDOWS + if (g_advancedSettings.m_fullScreen) + { + // when fullscreen, remain fullscreen and resize to the dimensions of the new screen + RESOLUTION newRes = (RESOLUTION) g_Windowing.DesktopResolution(g_Windowing.GetCurrentScreen()); + if (newRes != g_graphicsContext.GetVideoResolution()) + CDisplaySettings::Get().SetCurrentResolution(newRes, true); + } + else +#endif + { + g_Windowing.OnMove(newEvent.move.x, newEvent.move.y); + } + break; + case XBMC_USEREVENT: + CApplicationMessenger::Get().UserEvent(newEvent.user.code); + break; + case XBMC_APPCOMMAND: + return g_application.OnAppCommand(newEvent.appcommand.action); + case XBMC_TOUCH: + { + if (newEvent.touch.action == ACTION_TOUCH_TAP) + { // Send a mouse motion event with no dx,dy for getting the current guiitem selected + g_application.OnAction(CAction(ACTION_MOUSE_MOVE, 0, newEvent.touch.x, newEvent.touch.y, 0, 0)); + } + int actionId = 0; + if (newEvent.touch.action == ACTION_GESTURE_BEGIN || newEvent.touch.action == ACTION_GESTURE_END) + actionId = newEvent.touch.action; + else + { + int iWin = g_application.GetActiveWindowID(); + CButtonTranslator::GetInstance().TranslateTouchAction(iWin, newEvent.touch.action, newEvent.touch.pointers, actionId); + } + + if (actionId <= 0) + return false; + + if ((actionId >= ACTION_TOUCH_TAP && actionId <= ACTION_GESTURE_END) + || (actionId >= ACTION_MOUSE_START && actionId <= ACTION_MOUSE_END) ) + CApplicationMessenger::Get().SendAction(CAction(actionId, 0, newEvent.touch.x, newEvent.touch.y, newEvent.touch.x2, newEvent.touch.y2), WINDOW_INVALID, false); + else + CApplicationMessenger::Get().SendAction(CAction(actionId), WINDOW_INVALID, false); + + // Post an unfocus message for touch device after the action. + if (newEvent.touch.action == ACTION_GESTURE_END || newEvent.touch.action == ACTION_TOUCH_TAP) + { + CGUIMessage msg(GUI_MSG_UNFOCUS_ALL, 0, 0, 0, 0); + CApplicationMessenger::Get().SendGUIMessage(msg); + } + break; + } + case XBMC_SETFOCUS: + // Reset the screensaver + g_application.ResetScreenSaver(); + g_application.WakeUpScreenSaverAndDPMS(); + // Send a mouse motion event with no dx,dy for getting the current guiitem selected + g_application.OnAction(CAction(ACTION_MOUSE_MOVE, 0, static_cast<float>(newEvent.focus.x), static_cast<float>(newEvent.focus.y), 0, 0)); + break; + } + return true; +} + +extern "C" void __stdcall init_emu_environ(); +extern "C" void __stdcall update_emu_environ(); +extern "C" void __stdcall cleanup_emu_environ(); + +// +// Utility function used to copy files from the application bundle +// over to the user data directory in Application Support/Kodi. +// +static void CopyUserDataIfNeeded(const CStdString &strPath, const CStdString &file) +{ + CStdString destPath = URIUtils::AddFileToFolder(strPath, file); + if (!CFile::Exists(destPath)) + { + // need to copy it across + CStdString srcPath = URIUtils::AddFileToFolder("special://xbmc/userdata/", file); + CFile::Copy(srcPath, destPath); + } +} + +void CApplication::Preflight() +{ +#ifdef HAS_DBUS + // call 'dbus_threads_init_default' before any other dbus calls in order to + // avoid race conditions with other threads using dbus connections + dbus_threads_init_default(); +#endif + + // run any platform preflight scripts. +#if defined(TARGET_DARWIN_OSX) + CStdString install_path; + + CUtil::GetHomePath(install_path); + setenv("KODI_HOME", install_path.c_str(), 0); + install_path += "/tools/darwin/runtime/preflight"; + system(install_path.c_str()); +#endif +} + +bool CApplication::Create() +{ +#if defined(HAS_LINUX_NETWORK) + m_network = new CNetworkLinux(); +#elif defined(HAS_WIN32_NETWORK) + m_network = new CNetworkWin32(); +#else + m_network = new CNetwork(); +#endif + + Preflight(); + + for (int i = RES_HDTV_1080i; i <= RES_PAL60_16x9; i++) + { + g_graphicsContext.ResetScreenParameters((RESOLUTION)i); + g_graphicsContext.ResetOverscan((RESOLUTION)i, CDisplaySettings::Get().GetResolutionInfo(i).Overscan); + } + +#ifdef TARGET_POSIX + tzset(); // Initialize timezone information variables +#endif + + // Grab a handle to our thread to be used later in identifying the render thread. + m_threadID = CThread::GetCurrentThreadId(); + +#ifndef TARGET_POSIX + //floating point precision to 24 bits (faster performance) + _controlfp(_PC_24, _MCW_PC); + + /* install win32 exception translator, win32 exceptions + * can now be caught using c++ try catch */ + win32_exception::install_handler(); + +#endif + + // only the InitDirectories* for the current platform should return true + // putting this before the first log entries saves another ifdef for g_advancedSettings.m_logFolder + bool inited = InitDirectoriesLinux(); + if (!inited) + inited = InitDirectoriesOSX(); + if (!inited) + inited = InitDirectoriesWin32(); + + // copy required files + CopyUserDataIfNeeded("special://masterprofile/", "RssFeeds.xml"); + CopyUserDataIfNeeded("special://masterprofile/", "favourites.xml"); + CopyUserDataIfNeeded("special://masterprofile/", "Lircmap.xml"); + + if (!CLog::Init(CSpecialProtocol::TranslatePath(g_advancedSettings.m_logFolder).c_str())) + { + std::string lcAppName = CCompileInfo::GetAppName(); + StringUtils::ToLower(lcAppName); + fprintf(stderr,"Could not init logging classes. Permission errors on ~/.%s (%s)\n", lcAppName.c_str(), + CSpecialProtocol::TranslatePath(g_advancedSettings.m_logFolder).c_str()); + return false; + } + + // Init our DllLoaders emu env + init_emu_environ(); + + CProfilesManager::Get().Load(); + + CLog::Log(LOGNOTICE, "-----------------------------------------------------------------------"); + CLog::Log(LOGNOTICE, "Starting %s (%s). Platform: %s %s %d-bit", g_infoManager.GetAppName().c_str(), g_infoManager.GetVersion().c_str(), + g_sysinfo.GetBuildTargetPlatformName().c_str(), g_sysinfo.GetBuildTargetCpuFamily().c_str(), g_sysinfo.GetXbmcBitness()); + + std::string buildType; +#if defined(_DEBUG) + buildType = "Debug"; +#elif defined(NDEBUG) + buildType = "Release"; +#else + buildType = "Unknown"; +#endif + std::string specialVersion; +#if defined(TARGET_DARWIN_IOS_ATV2) + specialVersion = " (version for AppleTV2)"; +#elif defined(TARGET_RASPBERRY_PI) + specialVersion = " (version for Raspberry Pi)"; +//#elif defined(some_ID) // uncomment for special version/fork +// specialVersion = " (version for XXXX)"; +#endif + CLog::Log(LOGNOTICE, "Using %s %s x%d build%s", buildType.c_str(), g_infoManager.GetAppName().c_str(), g_sysinfo.GetXbmcBitness(), specialVersion.c_str()); + CLog::Log(LOGNOTICE, "%s compiled " __DATE__ " by %s for %s %s %d-bit %s (%s)", g_infoManager.GetAppName().c_str(), g_sysinfo.GetUsedCompilerNameAndVer().c_str(), g_sysinfo.GetBuildTargetPlatformName().c_str(), + g_sysinfo.GetBuildTargetCpuFamily().c_str(), g_sysinfo.GetXbmcBitness(), g_sysinfo.GetBuildTargetPlatformVersionDecoded().c_str(), + g_sysinfo.GetBuildTargetPlatformVersion().c_str()); + + std::string deviceModel(g_sysinfo.GetModelName()); + if (!g_sysinfo.GetManufacturerName().empty()) + deviceModel = g_sysinfo.GetManufacturerName() + " " + (deviceModel.empty() ? std::string("device") : deviceModel); + if (!deviceModel.empty()) + CLog::Log(LOGNOTICE, "Running on %s with %s, kernel: %s %s %d-bit version %s", deviceModel.c_str(), g_sysinfo.GetOsPrettyNameWithVersion().c_str(), + g_sysinfo.GetKernelName().c_str(), g_sysinfo.GetKernelCpuFamily().c_str(), g_sysinfo.GetKernelBitness(), g_sysinfo.GetKernelVersionFull().c_str()); + else + CLog::Log(LOGNOTICE, "Running on %s, kernel: %s %s %d-bit version %s", g_sysinfo.GetOsPrettyNameWithVersion().c_str(), + g_sysinfo.GetKernelName().c_str(), g_sysinfo.GetKernelCpuFamily().c_str(), g_sysinfo.GetKernelBitness(), g_sysinfo.GetKernelVersionFull().c_str()); + +#if defined(TARGET_LINUX) +#if USE_STATIC_FFMPEG + CLog::Log(LOGNOTICE, "FFmpeg statically linked, version: %s", FFMPEG_VERSION); +#else // !USE_STATIC_FFMPEG + CLog::Log(LOGNOTICE, "FFmpeg version: %s", FFMPEG_VERSION); +#endif // !USE_STATIC_FFMPEG + if (!strstr(FFMPEG_VERSION, FFMPEG_VER_SHA)) + { + if (strstr(FFMPEG_VERSION, "xbmc")) + CLog::Log(LOGNOTICE, "WARNING: unknown ffmpeg-xbmc version detected"); + else + CLog::Log(LOGNOTICE, "WARNING: unsupported ffmpeg version detected"); + } +#endif + + std::string cpuModel(g_cpuInfo.getCPUModel()); + if (!cpuModel.empty()) + CLog::Log(LOGNOTICE, "Host CPU: %s, %d core%s available", cpuModel.c_str(), g_cpuInfo.getCPUCount(), (g_cpuInfo.getCPUCount() == 1) ? "" : "s"); + else + CLog::Log(LOGNOTICE, "%d CPU core%s available", g_cpuInfo.getCPUCount(), (g_cpuInfo.getCPUCount() == 1) ? "" : "s"); +#if defined(TARGET_WINDOWS) + CLog::Log(LOGNOTICE, "%s", CWIN32Util::GetResInfoString().c_str()); + CLog::Log(LOGNOTICE, "Running with %s rights", (CWIN32Util::IsCurrentUserLocalAdministrator() == TRUE) ? "administrator" : "restricted"); + CLog::Log(LOGNOTICE, "Aero is %s", (g_sysinfo.IsAeroDisabled() == true) ? "disabled" : "enabled"); +#endif +#if defined(TARGET_ANDROID) + CLog::Log(LOGNOTICE, + "Product: %s, Device: %s, Board: %s - Manufacturer: %s, Brand: %s, Model: %s, Hardware: %s", + CJNIBuild::PRODUCT.c_str(), CJNIBuild::DEVICE.c_str(), CJNIBuild::BOARD.c_str(), + CJNIBuild::MANUFACTURER.c_str(), CJNIBuild::BRAND.c_str(), CJNIBuild::MODEL.c_str(), CJNIBuild::HARDWARE.c_str()); +#endif + +#if defined(__arm__) + if (g_cpuInfo.GetCPUFeatures() & CPU_FEATURE_NEON) + CLog::Log(LOGNOTICE, "ARM Features: Neon enabled"); + else + CLog::Log(LOGNOTICE, "ARM Features: Neon disabled"); +#endif + CSpecialProtocol::LogPaths(); + + CStdString executable = CUtil::ResolveExecutablePath(); + CLog::Log(LOGNOTICE, "The executable running is: %s", executable.c_str()); + CLog::Log(LOGNOTICE, "Local hostname: %s", m_network->GetHostName().c_str()); + std::string lowerAppName = CCompileInfo::GetAppName(); + StringUtils::ToLower(lowerAppName); + CLog::Log(LOGNOTICE, "Log File is located: %s%s.log", g_advancedSettings.m_logFolder.c_str(), lowerAppName.c_str()); + CRegExp::LogCheckUtf8Support(); + CLog::Log(LOGNOTICE, "-----------------------------------------------------------------------"); + + CStdString strExecutablePath; + CUtil::GetHomePath(strExecutablePath); + +#ifdef HAS_XRANDR + g_xrandr.LoadCustomModeLinesToAllOutputs(); +#endif + + // for python scripts that check the OS +#if defined(TARGET_DARWIN) + setenv("OS","OS X",true); +#elif defined(TARGET_POSIX) + setenv("OS","Linux",true); +#elif defined(TARGET_WINDOWS) + CEnvironment::setenv("OS", "win32"); +#endif + + // register ffmpeg lockmanager callback + av_lockmgr_register(&ffmpeg_lockmgr_cb); + // register avcodec + avcodec_register_all(); + // register avformat + av_register_all(); + // register avfilter + avfilter_register_all(); + // set avutil callback + av_log_set_callback(ff_avutil_log); + + g_powerManager.Initialize(); + + // Load the AudioEngine before settings as they need to query the engine + if (!CAEFactory::LoadEngine()) + { + CLog::Log(LOGFATAL, "CApplication::Create: Failed to load an AudioEngine"); + return false; + } + + // Initialize default Settings - don't move + CLog::Log(LOGNOTICE, "load settings..."); + if (!CSettings::Get().Initialize()) + return false; + + g_powerManager.SetDefaults(); + + // load the actual values + if (!CSettings::Get().Load()) + { + CLog::Log(LOGFATAL, "unable to load settings"); + return false; + } + CSettings::Get().SetLoaded(); + + CLog::Log(LOGINFO, "creating subdirectories"); + CLog::Log(LOGINFO, "userdata folder: %s", CProfilesManager::Get().GetProfileUserDataFolder().c_str()); + CLog::Log(LOGINFO, "recording folder: %s", CSettings::Get().GetString("audiocds.recordingpath").c_str()); + CLog::Log(LOGINFO, "screenshots folder: %s", CSettings::Get().GetString("debug.screenshotpath").c_str()); + CDirectory::Create(CProfilesManager::Get().GetUserDataFolder()); + CDirectory::Create(CProfilesManager::Get().GetProfileUserDataFolder()); + CProfilesManager::Get().CreateProfileFolders(); + + update_emu_environ();//apply the GUI settings + + // Load the langinfo to have user charset <-> utf-8 conversion + CStdString strLanguage = CSettings::Get().GetString("locale.language"); + strLanguage[0] = toupper(strLanguage[0]); + + CStdString strLangInfoPath = StringUtils::Format("special://xbmc/language/%s/langinfo.xml", strLanguage.c_str()); + + CLog::Log(LOGINFO, "load language info file: %s", strLangInfoPath.c_str()); + g_langInfo.Load(strLangInfoPath); + g_langInfo.SetAudioLanguage(CSettings::Get().GetString("locale.audiolanguage")); + g_langInfo.SetSubtitleLanguage(CSettings::Get().GetString("locale.subtitlelanguage")); + + CStdString strLanguagePath = "special://xbmc/language/"; + + CLog::Log(LOGINFO, "load %s language file, from path: %s", strLanguage.c_str(), strLanguagePath.c_str()); + if (!g_localizeStrings.Load(strLanguagePath, strLanguage)) + { + CLog::LogF(LOGFATAL, "Failed to load %s language file, from path: %s", strLanguage.c_str(), strLanguagePath.c_str()); + return false; + } + + // start the AudioEngine + if (!CAEFactory::StartEngine()) + { + CLog::Log(LOGFATAL, "CApplication::Create: Failed to start the AudioEngine"); + return false; + } + + // restore AE's previous volume state + SetHardwareVolume(m_volumeLevel); + CAEFactory::SetMute (m_muted); + CAEFactory::SetSoundMode(CSettings::Get().GetInt("audiooutput.guisoundmode")); + + // initialize m_replayGainSettings + m_replayGainSettings.iType = CSettings::Get().GetInt("musicplayer.replaygaintype"); + m_replayGainSettings.iPreAmp = CSettings::Get().GetInt("musicplayer.replaygainpreamp"); + m_replayGainSettings.iNoGainPreAmp = CSettings::Get().GetInt("musicplayer.replaygainnogainpreamp"); + m_replayGainSettings.bAvoidClipping = CSettings::Get().GetBool("musicplayer.replaygainavoidclipping"); + + // initialize the addon database (must be before the addon manager is init'd) + CDatabaseManager::Get().Initialize(true); + +#ifdef HAS_PYTHON + CScriptInvocationManager::Get().RegisterLanguageInvocationHandler(&g_pythonParser, ".py"); +#endif // HAS_PYTHON + + // start-up Addons Framework + // currently bails out if either cpluff Dll is unavailable or system dir can not be scanned + if (!CAddonMgr::Get().Init()) + { + CLog::Log(LOGFATAL, "CApplication::Create: Unable to start CAddonMgr"); + return false; + } +#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) + g_RemoteControl.Initialize(); +#endif + + g_peripherals.Initialise(); + + // Create the Mouse, Keyboard, Remote, and Joystick devices + // Initialize after loading settings to get joystick deadzone setting + g_Mouse.Initialize(); + g_Mouse.SetEnabled(CSettings::Get().GetBool("input.enablemouse")); + + g_Keyboard.Initialize(); + +#if defined(TARGET_DARWIN_OSX) + // Configure and possible manually start the helper. + XBMCHelper::GetInstance().Configure(); +#endif + + CUtil::InitRandomSeed(); + + g_mediaManager.Initialize(); + + m_lastFrameTime = XbmcThreads::SystemClockMillis(); + m_lastRenderTime = m_lastFrameTime; + return true; +} + +bool CApplication::CreateGUI() +{ + m_renderGUI = true; +#ifdef HAS_SDL + CLog::Log(LOGNOTICE, "Setup SDL"); + + /* Clean up on exit, exit on window close and interrupt */ + atexit(SDL_Quit); + + uint32_t sdlFlags = 0; + +#if (defined(HAS_SDL_OPENGL) || (HAS_GLES == 2)) && !defined(HAS_GLX) + sdlFlags |= SDL_INIT_VIDEO; +#endif + +#if defined(HAS_SDL_JOYSTICK) && !defined(TARGET_WINDOWS) + sdlFlags |= SDL_INIT_JOYSTICK; +#endif + + //depending on how it's compiled, SDL periodically calls XResetScreenSaver when it's fullscreen + //this might bring the monitor out of standby, so we have to disable it explicitly + //by passing 0 for overwrite to setsenv, the user can still override this by setting the environment variable +#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) + setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0); +#endif + +#endif // HAS_SDL + +#ifdef TARGET_POSIX + // for nvidia cards - vsync currently ALWAYS enabled. + // the reason is that after screen has been setup changing this env var will make no difference. + setenv("__GL_SYNC_TO_VBLANK", "1", 0); + setenv("__GL_YIELD", "USLEEP", 0); +#endif + + m_bSystemScreenSaverEnable = g_Windowing.IsSystemScreenSaverEnabled(); + g_Windowing.EnableSystemScreenSaver(false); + +#ifdef HAS_SDL + if (SDL_Init(sdlFlags) != 0) + { + CLog::Log(LOGFATAL, "XBAppEx: Unable to initialize SDL: %s", SDL_GetError()); + return false; + } + #if defined(TARGET_DARWIN) + // SDL_Init will install a handler for segfaults, restore the default handler. + signal(SIGSEGV, SIG_DFL); + #endif +#endif + + // Initialize core peripheral port support. Note: If these parameters + // are 0 and NULL, respectively, then the default number and types of + // controllers will be initialized. + if (!g_Windowing.InitWindowSystem()) + { + CLog::Log(LOGFATAL, "CApplication::Create: Unable to init windowing system"); + return false; + } + + // Retrieve the matching resolution based on GUI settings + bool sav_res = false; + CDisplaySettings::Get().SetCurrentResolution(CDisplaySettings::Get().GetDisplayResolution()); + CLog::Log(LOGNOTICE, "Checking resolution %i", CDisplaySettings::Get().GetCurrentResolution()); + if (!g_graphicsContext.IsValidResolution(CDisplaySettings::Get().GetCurrentResolution())) + { + CLog::Log(LOGNOTICE, "Setting safe mode %i", RES_DESKTOP); + // defer saving resolution after window was created + CDisplaySettings::Get().SetCurrentResolution(RES_DESKTOP); + sav_res = true; + } + + // update the window resolution + g_Windowing.SetWindowResolution(CSettings::Get().GetInt("window.width"), CSettings::Get().GetInt("window.height")); + + if (g_advancedSettings.m_startFullScreen && CDisplaySettings::Get().GetCurrentResolution() == RES_WINDOW) + { + // defer saving resolution after window was created + CDisplaySettings::Get().SetCurrentResolution(RES_DESKTOP); + sav_res = true; + } + + if (!g_graphicsContext.IsValidResolution(CDisplaySettings::Get().GetCurrentResolution())) + { + // Oh uh - doesn't look good for starting in their wanted screenmode + CLog::Log(LOGERROR, "The screen resolution requested is not valid, resetting to a valid mode"); + CDisplaySettings::Get().SetCurrentResolution(RES_DESKTOP); + sav_res = true; + } + if (!InitWindow()) + { + return false; + } + + if (sav_res) + CDisplaySettings::Get().SetCurrentResolution(RES_DESKTOP, true); + + if (g_advancedSettings.m_splashImage) + { + CStdString strUserSplash = "special://home/media/Splash.png"; + if (CFile::Exists(strUserSplash)) + { + CLog::Log(LOGINFO, "load user splash image: %s", CSpecialProtocol::TranslatePath(strUserSplash).c_str()); + m_splash = new CSplash(strUserSplash); + } + else + { + CLog::Log(LOGINFO, "load default splash image: %s", CSpecialProtocol::TranslatePath("special://xbmc/media/Splash.png").c_str()); + m_splash = new CSplash("special://xbmc/media/Splash.png"); + } + m_splash->Show(); + } + + // The key mappings may already have been loaded by a peripheral + CLog::Log(LOGINFO, "load keymapping"); + if (!CButtonTranslator::GetInstance().Load()) + return false; + +#ifdef HAS_SDL_JOYSTICK + // Pass the mapping of axis to triggers to g_Joystick + g_Joystick.LoadAxesConfigs(CButtonTranslator::GetInstance().GetAxesConfigs()); +#endif + + RESOLUTION_INFO info = g_graphicsContext.GetResInfo(); + CLog::Log(LOGINFO, "GUI format %ix%i, Display %s", + info.iWidth, + info.iHeight, + info.strMode.c_str()); + g_windowManager.Initialize(); + + return true; +} + +bool CApplication::InitWindow() +{ +#ifdef TARGET_DARWIN_OSX + // force initial window creation to be windowed, if fullscreen, it will switch to it below + // fixes the white screen of death if starting fullscreen and switching to windowed. + bool bFullScreen = false; + if (!g_Windowing.CreateNewWindow(CSysInfo::GetAppName(), bFullScreen, CDisplaySettings::Get().GetResolutionInfo(RES_WINDOW), OnEvent)) + { + CLog::Log(LOGFATAL, "CApplication::Create: Unable to create window"); + return false; + } +#else + bool bFullScreen = CDisplaySettings::Get().GetCurrentResolution() != RES_WINDOW; + if (!g_Windowing.CreateNewWindow(CSysInfo::GetAppName(), bFullScreen, CDisplaySettings::Get().GetCurrentResolutionInfo(), OnEvent)) + { + CLog::Log(LOGFATAL, "CApplication::Create: Unable to create window"); + return false; + } +#endif + + if (!g_Windowing.InitRenderSystem()) + { + CLog::Log(LOGFATAL, "CApplication::Create: Unable to init rendering system"); + return false; + } + // set GUI res and force the clear of the screen + g_graphicsContext.SetVideoResolution(CDisplaySettings::Get().GetCurrentResolution()); + return true; +} + +bool CApplication::DestroyWindow() +{ + return g_Windowing.DestroyWindow(); +} + +bool CApplication::InitDirectoriesLinux() +{ +/* + The following is the directory mapping for Platform Specific Mode: + + special://xbmc/ => [read-only] system directory (/usr/share/kodi) + special://home/ => [read-write] user's directory that will override special://kodi/ system-wide + installations like skins, screensavers, etc. + ($HOME/.kodi) + NOTE: XBMC will look in both special://xbmc/addons and special://home/addons for addons. + special://masterprofile/ => [read-write] userdata of master profile. It will by default be + mapped to special://home/userdata ($HOME/.kodi/userdata) + special://profile/ => [read-write] current profile's userdata directory. + Generally special://masterprofile for the master profile or + special://masterprofile/profiles/<profile_name> for other profiles. + + NOTE: All these root directories are lowercase. Some of the sub-directories + might be mixed case. +*/ + +#if defined(TARGET_POSIX) && !defined(TARGET_DARWIN) + std::string userName; + if (getenv("USER")) + userName = getenv("USER"); + else + userName = "root"; + + std::string userHome; + if (getenv("HOME")) + userHome = getenv("HOME"); + else + userHome = "/root"; + + std::string appBinPath, appPath; + std::string appName = CCompileInfo::GetAppName(); + std::string dotLowerAppName = "." + appName; + StringUtils::ToLower(dotLowerAppName); + const char* envAppHome = "KODI_HOME"; + const char* envAppBinHome = "KODI_BIN_HOME"; + const char* envAppTemp = "KODI_TEMP"; + + + CUtil::GetHomePath(appBinPath, envAppBinHome); + if (getenv(envAppHome)) + appPath = getenv(envAppHome); + else + { + appPath = appBinPath; + /* Check if binaries and arch independent data files are being kept in + * separate locations. */ + if (!CDirectory::Exists(URIUtils::AddFileToFolder(appPath, "language"))) + { + /* Attempt to locate arch independent data files. */ + CUtil::GetHomePath(appPath); + if (!CDirectory::Exists(URIUtils::AddFileToFolder(appPath, "language"))) + { + fprintf(stderr, "Unable to find path to %s data files!\n", appName.c_str()); + exit(1); + } + } + } + + /* Set some environment variables */ + setenv(envAppBinHome, appBinPath.c_str(), 0); + setenv(envAppHome, appPath.c_str(), 0); + + if (m_bPlatformDirectories) + { + // map our special drives + CSpecialProtocol::SetXBMCBinPath(appBinPath); + CSpecialProtocol::SetXBMCPath(appPath); + CSpecialProtocol::SetHomePath(userHome + "/" + dotLowerAppName); + CSpecialProtocol::SetMasterProfilePath(userHome + "/" + dotLowerAppName + "/userdata"); + + CStdString strTempPath = userHome; + strTempPath = URIUtils::AddFileToFolder(strTempPath, dotLowerAppName + "/temp"); + if (getenv(envAppTemp)) + strTempPath = getenv(envAppTemp); + CSpecialProtocol::SetTempPath(strTempPath); + + URIUtils::AddSlashAtEnd(strTempPath); + g_advancedSettings.m_logFolder = strTempPath; + + CreateUserDirs(); + + } + else + { + URIUtils::AddSlashAtEnd(appPath); + g_advancedSettings.m_logFolder = appPath; + + CSpecialProtocol::SetXBMCBinPath(appBinPath); + CSpecialProtocol::SetXBMCPath(appPath); + CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(appPath, "portable_data")); + CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(appPath, "portable_data/userdata")); + + CStdString strTempPath = appPath; + strTempPath = URIUtils::AddFileToFolder(strTempPath, "portable_data/temp"); + if (getenv(envAppTemp)) + strTempPath = getenv(envAppTemp); + CSpecialProtocol::SetTempPath(strTempPath); + CreateUserDirs(); + + URIUtils::AddSlashAtEnd(strTempPath); + g_advancedSettings.m_logFolder = strTempPath; + } + + return true; +#else + return false; +#endif +} + +bool CApplication::InitDirectoriesOSX() +{ +#if defined(TARGET_DARWIN) + CStdString userName; + if (getenv("USER")) + userName = getenv("USER"); + else + userName = "root"; + + std::string userHome; + if (getenv("HOME")) + userHome = getenv("HOME"); + else + userHome = "/root"; + + std::string appPath; + CUtil::GetHomePath(appPath); + setenv("KODI_HOME", appPath.c_str(), 0); + +#if defined(TARGET_DARWIN_IOS) + CStdString fontconfigPath; + fontconfigPath = appPath + "/system/players/dvdplayer/etc/fonts/fonts.conf"; + setenv("FONTCONFIG_FILE", fontconfigPath.c_str(), 0); +#endif + + // setup path to our internal dylibs so loader can find them + CStdString frameworksPath = CUtil::GetFrameworksPath(); + CSpecialProtocol::SetXBMCFrameworksPath(frameworksPath); + + // OSX always runs with m_bPlatformDirectories == true + if (m_bPlatformDirectories) + { + // map our special drives + CSpecialProtocol::SetXBMCBinPath(appPath); + CSpecialProtocol::SetXBMCPath(appPath); + #if defined(TARGET_DARWIN_IOS) + std::string appName = CCompileInfo::GetAppName(); + CSpecialProtocol::SetHomePath(userHome + "/" + CDarwinUtils::GetAppRootFolder() + "/" + appName); + CSpecialProtocol::SetMasterProfilePath(userHome + "/" + CDarwinUtils::GetAppRootFolder() + "/" + appName + "/userdata"); + #else + std::string appName = CCompileInfo::GetAppName(); + CSpecialProtocol::SetHomePath(userHome + "/Library/Application Support/" + appName); + CSpecialProtocol::SetMasterProfilePath(userHome + "/Library/Application Support/" + appName + "/userdata"); + #endif + + std::string dotLowerAppName = "." + appName; + StringUtils::ToLower(dotLowerAppName); + // location for temp files + #if defined(TARGET_DARWIN_IOS) + std::string strTempPath = URIUtils::AddFileToFolder(userHome, std::string(CDarwinUtils::GetAppRootFolder()) + "/" + appName + "/temp"); + #else + std::string strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/"); + CDirectory::Create(strTempPath); + strTempPath = URIUtils::AddFileToFolder(userHome, dotLowerAppName + "/temp"); + #endif + CSpecialProtocol::SetTempPath(strTempPath); + + // xbmc.log file location + #if defined(TARGET_DARWIN_IOS) + strTempPath = userHome + "/" + std::string(CDarwinUtils::GetAppRootFolder()); + #else + strTempPath = userHome + "/Library/Logs"; + #endif + URIUtils::AddSlashAtEnd(strTempPath); + g_advancedSettings.m_logFolder = strTempPath; + + CreateUserDirs(); + } + else + { + URIUtils::AddSlashAtEnd(appPath); + g_advancedSettings.m_logFolder = appPath; + + CSpecialProtocol::SetXBMCBinPath(appPath); + CSpecialProtocol::SetXBMCPath(appPath); + CSpecialProtocol::SetHomePath(URIUtils::AddFileToFolder(appPath, "portable_data")); + CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(appPath, "portable_data/userdata")); + + CStdString strTempPath = URIUtils::AddFileToFolder(appPath, "portable_data/temp"); + CSpecialProtocol::SetTempPath(strTempPath); + + URIUtils::AddSlashAtEnd(strTempPath); + g_advancedSettings.m_logFolder = strTempPath; + } + + return true; +#else + return false; +#endif +} + +bool CApplication::InitDirectoriesWin32() +{ +#ifdef TARGET_WINDOWS + CStdString xbmcPath; + + CUtil::GetHomePath(xbmcPath); + CEnvironment::setenv("KODI_HOME", xbmcPath); + CSpecialProtocol::SetXBMCBinPath(xbmcPath); + CSpecialProtocol::SetXBMCPath(xbmcPath); + + CStdString strWin32UserFolder = CWIN32Util::GetProfilePath(); + + g_advancedSettings.m_logFolder = strWin32UserFolder; + CSpecialProtocol::SetHomePath(strWin32UserFolder); + CSpecialProtocol::SetMasterProfilePath(URIUtils::AddFileToFolder(strWin32UserFolder, "userdata")); + CSpecialProtocol::SetTempPath(URIUtils::AddFileToFolder(strWin32UserFolder,"cache")); + + CEnvironment::setenv("KODI_PROFILE_USERDATA", CSpecialProtocol::TranslatePath("special://masterprofile/")); + + CreateUserDirs(); + + // Expand the DLL search path with our directories + CWIN32Util::ExtendDllPath(); + + return true; +#else + return false; +#endif +} + +void CApplication::CreateUserDirs() +{ + CDirectory::Create("special://home/"); + CDirectory::Create("special://home/addons"); + CDirectory::Create("special://home/addons/packages"); + CDirectory::Create("special://home/media"); + CDirectory::Create("special://home/sounds"); + CDirectory::Create("special://home/system"); + CDirectory::Create("special://masterprofile/"); + CDirectory::Create("special://temp/"); + CDirectory::Create("special://temp/temp"); // temp directory for python and dllGetTempPathA +} + +bool CApplication::Initialize() +{ +#if defined(HAS_DVD_DRIVE) && !defined(TARGET_WINDOWS) // somehow this throws an "unresolved external symbol" on win32 + // turn off cdio logging + cdio_loglevel_default = CDIO_LOG_ERROR; +#endif + +#ifdef TARGET_POSIX // TODO: Win32 has no special://home/ mapping by default, so we + // must create these here. Ideally this should be using special://home/ and + // be platform agnostic (i.e. unify the InitDirectories*() functions) + if (!m_bPlatformDirectories) +#endif + { + CDirectory::Create("special://xbmc/language"); + CDirectory::Create("special://xbmc/addons"); + CDirectory::Create("special://xbmc/sounds"); + } + + // Load curl so curl_global_init gets called before any service threads + // are started. Unloading will have no effect as curl is never fully unloaded. + // To quote man curl_global_init: + // "This function is not thread safe. You must not call it when any other + // thread in the program (i.e. a thread sharing the same memory) is running. + // This doesn't just mean no other thread that is using libcurl. Because + // curl_global_init() calls functions of other libraries that are similarly + // thread unsafe, it could conflict with any other thread that + // uses these other libraries." + g_curlInterface.Load(); + g_curlInterface.Unload(); + + // initialize (and update as needed) our databases + CDatabaseManager::Get().Initialize(); + + StartServices(); + + // Init DPMS, before creating the corresponding setting control. + m_dpms = new DPMSSupport(); + if (g_windowManager.Initialized()) + { + CSettings::Get().GetSetting("powermanagement.displaysoff")->SetRequirementsMet(m_dpms->IsSupported()); + + g_windowManager.Add(new CGUIWindowHome); + g_windowManager.Add(new CGUIWindowPrograms); + g_windowManager.Add(new CGUIWindowPictures); + g_windowManager.Add(new CGUIWindowFileManager); + g_windowManager.Add(new CGUIWindowSettings); + g_windowManager.Add(new CGUIWindowSystemInfo); +#ifdef HAS_GL + g_windowManager.Add(new CGUIWindowTestPatternGL); +#endif +#ifdef HAS_DX + g_windowManager.Add(new CGUIWindowTestPatternDX); +#endif + g_windowManager.Add(new CGUIWindowSettingsScreenCalibration); + g_windowManager.Add(new CGUIWindowSettingsCategory); + g_windowManager.Add(new CGUIWindowVideoNav); + g_windowManager.Add(new CGUIWindowVideoPlaylist); + g_windowManager.Add(new CGUIWindowLoginScreen); + g_windowManager.Add(new CGUIWindowSettingsProfile); + g_windowManager.Add(new CGUIWindow(WINDOW_SKIN_SETTINGS, "SkinSettings.xml")); + g_windowManager.Add(new CGUIWindowAddonBrowser); + g_windowManager.Add(new CGUIWindowScreensaverDim); + g_windowManager.Add(new CGUIWindowDebugInfo); + g_windowManager.Add(new CGUIWindowPointer); + g_windowManager.Add(new CGUIDialogYesNo); + g_windowManager.Add(new CGUIDialogProgress); + g_windowManager.Add(new CGUIDialogExtendedProgressBar); + g_windowManager.Add(new CGUIDialogKeyboardGeneric); + g_windowManager.Add(new CGUIDialogVolumeBar); + g_windowManager.Add(new CGUIDialogSeekBar); + g_windowManager.Add(new CGUIDialogSubMenu); + g_windowManager.Add(new CGUIDialogContextMenu); + g_windowManager.Add(new CGUIDialogKaiToast); + g_windowManager.Add(new CGUIDialogNumeric); + g_windowManager.Add(new CGUIDialogGamepad); + g_windowManager.Add(new CGUIDialogButtonMenu); + g_windowManager.Add(new CGUIDialogMuteBug); + g_windowManager.Add(new CGUIDialogPlayerControls); +#ifdef HAS_KARAOKE + g_windowManager.Add(new CGUIDialogKaraokeSongSelectorSmall); + g_windowManager.Add(new CGUIDialogKaraokeSongSelectorLarge); +#endif + g_windowManager.Add(new CGUIDialogSlider); + g_windowManager.Add(new CGUIDialogMusicOSD); + g_windowManager.Add(new CGUIDialogVisualisationPresetList); + g_windowManager.Add(new CGUIDialogVideoSettings); + g_windowManager.Add(new CGUIDialogAudioSubtitleSettings); + g_windowManager.Add(new CGUIDialogVideoBookmarks); + // Don't add the filebrowser dialog - it's created and added when it's needed + g_windowManager.Add(new CGUIDialogNetworkSetup); + g_windowManager.Add(new CGUIDialogMediaSource); + g_windowManager.Add(new CGUIDialogProfileSettings); + g_windowManager.Add(new CGUIDialogFavourites); + g_windowManager.Add(new CGUIDialogSongInfo); + g_windowManager.Add(new CGUIDialogSmartPlaylistEditor); + g_windowManager.Add(new CGUIDialogSmartPlaylistRule); + g_windowManager.Add(new CGUIDialogBusy); + g_windowManager.Add(new CGUIDialogPictureInfo); + g_windowManager.Add(new CGUIDialogAddonInfo); + g_windowManager.Add(new CGUIDialogAddonSettings); +#ifdef HAS_LINUX_NETWORK + g_windowManager.Add(new CGUIDialogAccessPoints); +#endif + + g_windowManager.Add(new CGUIDialogLockSettings); + + g_windowManager.Add(new CGUIDialogContentSettings); + + g_windowManager.Add(new CGUIDialogPlayEject); + + g_windowManager.Add(new CGUIDialogPeripheralManager); + g_windowManager.Add(new CGUIDialogPeripheralSettings); + + g_windowManager.Add(new CGUIDialogMediaFilter); + g_windowManager.Add(new CGUIDialogSubtitles); + + g_windowManager.Add(new CGUIWindowMusicPlayList); + g_windowManager.Add(new CGUIWindowMusicSongs); + g_windowManager.Add(new CGUIWindowMusicNav); + g_windowManager.Add(new CGUIWindowMusicPlaylistEditor); + + /* Load PVR related Windows and Dialogs */ + g_windowManager.Add(new CGUIDialogTeletext); + g_windowManager.Add(new CGUIWindowPVRChannels(false)); + g_windowManager.Add(new CGUIWindowPVRRecordings(false)); + g_windowManager.Add(new CGUIWindowPVRGuide(false)); + g_windowManager.Add(new CGUIWindowPVRTimers(false)); + g_windowManager.Add(new CGUIWindowPVRSearch(false)); + g_windowManager.Add(new CGUIWindowPVRChannels(true)); + g_windowManager.Add(new CGUIWindowPVRRecordings(true)); + g_windowManager.Add(new CGUIWindowPVRGuide(true)); + g_windowManager.Add(new CGUIWindowPVRTimers(true)); + g_windowManager.Add(new CGUIWindowPVRSearch(true)); + g_windowManager.Add(new CGUIDialogPVRGuideInfo); + g_windowManager.Add(new CGUIDialogPVRRecordingInfo); + g_windowManager.Add(new CGUIDialogPVRTimerSettings); + g_windowManager.Add(new CGUIDialogPVRGroupManager); + g_windowManager.Add(new CGUIDialogPVRChannelManager); + g_windowManager.Add(new CGUIDialogPVRGuideSearch); + g_windowManager.Add(new CGUIDialogPVRChannelsOSD); + g_windowManager.Add(new CGUIDialogPVRGuideOSD); + g_windowManager.Add(new CGUIDialogPVRDirectorOSD); + g_windowManager.Add(new CGUIDialogPVRCutterOSD); + + g_windowManager.Add(new CGUIDialogSelect); + g_windowManager.Add(new CGUIDialogMusicInfo); + g_windowManager.Add(new CGUIDialogOK); + g_windowManager.Add(new CGUIDialogVideoInfo); + g_windowManager.Add(new CGUIDialogTextViewer); + g_windowManager.Add(new CGUIWindowFullScreen); + g_windowManager.Add(new CGUIWindowVisualisation); + g_windowManager.Add(new CGUIWindowSlideShow); + g_windowManager.Add(new CGUIDialogFileStacking); +#ifdef HAS_KARAOKE + g_windowManager.Add(new CGUIWindowKaraokeLyrics); +#endif + + g_windowManager.Add(new CGUIDialogVideoOSD); + g_windowManager.Add(new CGUIDialogMusicOverlay); + g_windowManager.Add(new CGUIDialogVideoOverlay); + g_windowManager.Add(new CGUIWindowScreensaver); + g_windowManager.Add(new CGUIWindowWeather); + g_windowManager.Add(new CGUIWindowStartup); + + /* window id's 3000 - 3100 are reserved for python */ + + // Make sure we have at least the default skin + string defaultSkin = ((const CSettingString*)CSettings::Get().GetSetting("lookandfeel.skin"))->GetDefault(); + if (!LoadSkin(CSettings::Get().GetString("lookandfeel.skin")) && !LoadSkin(defaultSkin)) + { + CLog::Log(LOGERROR, "Default skin '%s' not found! Terminating..", defaultSkin.c_str()); + return false; + } + + if (g_advancedSettings.m_splashImage) + SAFE_DELETE(m_splash); + + if (CSettings::Get().GetBool("masterlock.startuplock") && + CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && + !CProfilesManager::Get().GetMasterProfile().getLockCode().empty()) + { + g_passwordManager.CheckStartUpLock(); + } + + // check if we should use the login screen + if (CProfilesManager::Get().UsingLoginScreen()) + g_windowManager.ActivateWindow(WINDOW_LOGIN_SCREEN); + else + { +#ifdef HAS_JSONRPC + CJSONRPC::Initialize(); +#endif + ADDON::CAddonMgr::Get().StartServices(false); + + // let's start the PVR manager and decide if the PVR manager handle the startup window activation + if (!StartPVRManager()) + g_windowManager.ActivateWindow(g_SkinInfo->GetFirstWindow()); + + CStereoscopicsManager::Get().Initialize(); + } + + } + else //No GUI Created + { +#ifdef HAS_JSONRPC + CJSONRPC::Initialize(); +#endif + ADDON::CAddonMgr::Get().StartServices(false); + } + + g_sysinfo.Refresh(); + + CLog::Log(LOGINFO, "removing tempfiles"); + CUtil::RemoveTempFiles(); + + if (!CProfilesManager::Get().UsingLoginScreen()) + { + UpdateLibraries(); + SetLoggingIn(true); + } + + m_slowTimer.StartZero(); + + CAddonMgr::Get().StartServices(true); + + CLog::Log(LOGNOTICE, "initialize done"); + + m_bInitializing = false; + + // reset our screensaver (starts timers etc.) + ResetScreenSaver(); + +#ifdef HAS_SDL_JOYSTICK + g_Joystick.SetEnabled(CSettings::Get().GetBool("input.enablejoystick") && + CPeripheralImon::GetCountOfImonsConflictWithDInput() == 0 ); +#endif + + // show info dialog about moved configuration files if needed + ShowAppMigrationMessage(); + + return true; +} + +bool CApplication::StartServer(enum ESERVERS eServer, bool bStart, bool bWait/* = false*/) +{ + bool ret = false; + switch(eServer) + { + case ES_WEBSERVER: + // the callback will take care of starting/stopping webserver + ret = CSettings::Get().SetBool("services.webserver", bStart); + break; + + case ES_AIRPLAYSERVER: + // the callback will take care of starting/stopping airplay + ret = CSettings::Get().SetBool("services.airplay", bStart); + break; + + case ES_JSONRPCSERVER: + // the callback will take care of starting/stopping jsonrpc server + ret = CSettings::Get().SetBool("services.esenabled", bStart); + break; + + case ES_UPNPSERVER: + // the callback will take care of starting/stopping upnp server + ret = CSettings::Get().SetBool("services.upnpserver", bStart); + break; + + case ES_UPNPRENDERER: + // the callback will take care of starting/stopping upnp renderer + ret = CSettings::Get().SetBool("services.upnprenderer", bStart); + break; + + case ES_EVENTSERVER: + // the callback will take care of starting/stopping event server + ret = CSettings::Get().SetBool("services.esenabled", bStart); + break; + + case ES_ZEROCONF: + // the callback will take care of starting/stopping zeroconf + ret = CSettings::Get().SetBool("services.zeroconf", bStart); + break; + + default: + ret = false; + break; + } + CSettings::Get().Save(); + + return ret; +} + +bool CApplication::StartPVRManager() +{ + if (!CSettings::Get().GetBool("pvrmanager.enabled")) + return false; + + int firstWindowId = 0; + if (g_PVRManager.IsPVRWindow(g_SkinInfo->GetStartWindow())) + firstWindowId = g_SkinInfo->GetFirstWindow(); + + g_PVRManager.Start(true, firstWindowId); + + return (firstWindowId > 0); +} + +void CApplication::StopPVRManager() +{ + CLog::Log(LOGINFO, "stopping PVRManager"); + if (g_PVRManager.IsPlaying()) + StopPlaying(); + g_PVRManager.Stop(); + g_EpgContainer.Stop(); +} + +void CApplication::StartServices() +{ +#if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE) + // Start Thread for DVD Mediatype detection + CLog::Log(LOGNOTICE, "start dvd mediatype detection"); + m_DetectDVDType.Create(false, THREAD_MINSTACKSIZE); +#endif +} + +void CApplication::StopServices() +{ + m_network->NetworkMessage(CNetwork::SERVICES_DOWN, 0); + +#if !defined(TARGET_WINDOWS) && defined(HAS_DVD_DRIVE) + CLog::Log(LOGNOTICE, "stop dvd detect media"); + m_DetectDVDType.StopThread(); +#endif + + g_peripherals.Clear(); +} + +void CApplication::OnSettingChanged(const CSetting *setting) +{ + if (setting == NULL) + return; + + const std::string &settingId = setting->GetId(); + if (settingId == "lookandfeel.skin" || + settingId == "lookandfeel.font" || + settingId == "lookandfeel.skincolors") + { + // if the skin changes and the current theme is not the default one, reset + // the theme to the default value (which will also change lookandfeel.skincolors + // which in turn will reload the skin. Similarly, if the current skin font is not + // the default, reset it as well. + if (settingId == "lookandfeel.skin" && CSettings::Get().GetString("lookandfeel.skintheme") != "SKINDEFAULT") + CSettings::Get().SetString("lookandfeel.skintheme", "SKINDEFAULT"); + else if (settingId == "lookandfeel.skin" && CSettings::Get().GetString("lookandfeel.font") != "Default") + CSettings::Get().SetString("lookandfeel.font", "Default"); + else + { + std::string builtin("ReloadSkin"); + if (settingId == "lookandfeel.skin" && !m_skinReverting) + builtin += "(confirm)"; + CApplicationMessenger::Get().ExecBuiltIn(builtin); + } + } + else if (settingId == "lookandfeel.skintheme") + { + // also set the default color theme + CStdString colorTheme = ((CSettingString*)setting)->GetValue(); + URIUtils::RemoveExtension(colorTheme); + if (StringUtils::EqualsNoCase(colorTheme, "Textures")) + colorTheme = "defaults"; + + // check if we have to change the skin color + // if yes, it will trigger a call to ReloadSkin() in + // it's OnSettingChanged() callback + // if no we have to call ReloadSkin() ourselves + if (!StringUtils::EqualsNoCase(colorTheme, CSettings::Get().GetString("lookandfeel.skincolors"))) + CSettings::Get().SetString("lookandfeel.skincolors", colorTheme); + else + CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin"); + } + else if (settingId == "lookandfeel.skinzoom") + { + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_WINDOW_RESIZE); + g_windowManager.SendThreadMessage(msg); + } + else if (StringUtils::StartsWithNoCase(settingId, "audiooutput.")) + { + // AE is master of audio settings and needs to be informed first + CAEFactory::OnSettingsChange(settingId); + + if (settingId == "audiooutput.guisoundmode") + { + CAEFactory::SetSoundMode(((CSettingInt*)setting)->GetValue()); + } + // this tells player whether to open an audio stream passthrough or PCM + // if this is changed, audio stream has to be reopened + else if (settingId == "audiooutput.passthrough") + { + CApplicationMessenger::Get().MediaRestart(false); + } + } + else if (StringUtils::EqualsNoCase(settingId, "musicplayer.replaygaintype")) + m_replayGainSettings.iType = ((CSettingInt*)setting)->GetValue(); + else if (StringUtils::EqualsNoCase(settingId, "musicplayer.replaygainpreamp")) + m_replayGainSettings.iPreAmp = ((CSettingInt*)setting)->GetValue(); + else if (StringUtils::EqualsNoCase(settingId, "musicplayer.replaygainnogainpreamp")) + m_replayGainSettings.iNoGainPreAmp = ((CSettingInt*)setting)->GetValue(); + else if (StringUtils::EqualsNoCase(settingId, "musicplayer.replaygainavoidclipping")) + m_replayGainSettings.bAvoidClipping = ((CSettingBool*)setting)->GetValue(); +} + +void CApplication::OnSettingAction(const CSetting *setting) +{ + if (setting == NULL) + return; + + const std::string &settingId = setting->GetId(); + if (settingId == "lookandfeel.skinsettings") + g_windowManager.ActivateWindow(WINDOW_SKIN_SETTINGS); + else if (settingId == "screensaver.preview") + ActivateScreenSaver(true); + else if (settingId == "screensaver.settings") + { + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(CSettings::Get().GetString("screensaver.mode"), addon, ADDON_SCREENSAVER)) + CGUIDialogAddonSettings::ShowAndGetInput(addon); + } + else if (settingId == "audiocds.settings") + { + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(CSettings::Get().GetString("audiocds.encoder"), addon, ADDON_AUDIOENCODER)) + CGUIDialogAddonSettings::ShowAndGetInput(addon); + } + else if (settingId == "videoscreen.guicalibration") + g_windowManager.ActivateWindow(WINDOW_SCREEN_CALIBRATION); + else if (settingId == "videoscreen.testpattern") + g_windowManager.ActivateWindow(WINDOW_TEST_PATTERN); +} + +bool CApplication::OnSettingUpdate(CSetting* &setting, const char *oldSettingId, const TiXmlNode *oldSettingNode) +{ + if (setting == NULL) + return false; + + const std::string &settingId = setting->GetId(); + if (settingId == "audiooutput.channels") + { + // check if this is an update from Eden + if (oldSettingId != NULL && oldSettingNode != NULL && + StringUtils::EqualsNoCase(oldSettingId, "audiooutput.channellayout")) + { + bool ret = false; + CSettingInt* channels = (CSettingInt*)setting; + if (channels->FromString(oldSettingNode->FirstChild()->ValueStr()) && channels->GetValue() < AE_CH_LAYOUT_MAX - 1) + ret = channels->SetValue(channels->GetValue() + 1); + + // let's just reset the audiodevice settings as well + std::string audiodevice = CSettings::Get().GetString("audiooutput.audiodevice"); + CAEFactory::VerifyOutputDevice(audiodevice, false); + ret |= CSettings::Get().SetString("audiooutput.audiodevice", audiodevice.c_str()); + + return ret; + } + } + else if (settingId == "screensaver.mode") + { + CSettingString *screensaverMode = (CSettingString*)setting; + // we no longer ship the built-in slideshow screensaver, replace it if it's still in use + if (StringUtils::EqualsNoCase(screensaverMode->GetValue(), "screensaver.xbmc.builtin.slideshow")) + return screensaverMode->SetValue("screensaver.xbmc.builtin.dim"); + } + else if (settingId == "scrapers.musicvideosdefault") + { + CSettingAddon *musicvideoScraper = (CSettingAddon*)setting; + if (StringUtils::EqualsNoCase(musicvideoScraper->GetValue(), "metadata.musicvideos.last.fm")) + { + musicvideoScraper->Reset(); + return true; + } + } +#if defined(HAS_LIBAMCODEC) + else if (settingId == "videoplayer.useamcodec") + { + // Do not permit amcodec to be used on non-aml platforms. + // The setting will be hidden but the default value is true, + // so change it to false. + if (!aml_present()) + { + CSettingBool *useamcodec = (CSettingBool*)setting; + return useamcodec->SetValue(false); + } + } +#endif +#if defined(TARGET_ANDROID) + else if (settingId == "videoplayer.usemediacodec") + { + // Do not permit MediaCodec to be used Android platforms that do not have it. + // The setting will be hidden but the default value is true, + // so change it to false. + if (CAndroidFeatures::GetVersion() < 16) + { + CSettingBool *usemediacodec = (CSettingBool*)setting; + return usemediacodec->SetValue(false); + } + } + else if (settingId == "videoplayer.usestagefright") + { + CSettingBool *usestagefright = (CSettingBool*)setting; + return usestagefright->SetValue(false); + } +#endif +#if defined(TARGET_DARWIN_OSX) + else if (settingId == "audiooutput.audiodevice") + { + CSettingString *audioDevice = (CSettingString*)setting; + // Gotham and older didn't enumerate audio devices per stream on osx + // add stream0 per default which should be ok for all old settings. + if (!StringUtils::EqualsNoCase(audioDevice->GetValue(), "DARWINOSX:default") && + StringUtils::FindWords(audioDevice->GetValue().c_str(), ":stream") == std::string::npos) + { + std::string newSetting = audioDevice->GetValue(); + newSetting += ":stream0"; + return audioDevice->SetValue(newSetting); + } + } +#endif + + return false; +} + +bool CApplication::OnSettingsSaving() const +{ + // don't save settings when we're busy stopping the application + // a lot of screens try to save settings on deinit and deinit is + // called for every screen when the application is stopping + if (m_bStop) + return false; + + return true; +} + +void CApplication::ReloadSkin(bool confirm/*=false*/) +{ + std::string oldSkin = g_SkinInfo ? g_SkinInfo->ID() : ""; + + CGUIMessage msg(GUI_MSG_LOAD_SKIN, -1, g_windowManager.GetActiveWindow()); + g_windowManager.SendMessage(msg); + + string newSkin = CSettings::Get().GetString("lookandfeel.skin"); + if (LoadSkin(newSkin)) + { + /* The Reset() or SetString() below will cause recursion, so the m_skinReverting boolean is set so as to not prompt the + user as to whether they want to keep the current skin. */ + if (confirm && !m_skinReverting) + { + bool cancelled; + if (!CGUIDialogYesNo::ShowAndGetInput(13123, 13111, -1, -1, -1, -1, cancelled, 10000)) + { + m_skinReverting = true; + if (oldSkin.empty()) + CSettings::Get().GetSetting("lookandfeel.skin")->Reset(); + else + CSettings::Get().SetString("lookandfeel.skin", oldSkin); + } + } + } + else + { + // skin failed to load - we revert to the default only if we didn't fail loading the default + string defaultSkin = ((CSettingString*)CSettings::Get().GetSetting("lookandfeel.skin"))->GetDefault(); + if (newSkin != defaultSkin) + { + m_skinReverting = true; + CSettings::Get().GetSetting("lookandfeel.skin")->Reset(); + CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(24102), g_localizeStrings.Get(24103)); + } + } + m_skinReverting = false; +} + +bool CApplication::Load(const TiXmlNode *settings) +{ + if (settings == NULL) + return false; + + const TiXmlElement *audioElement = settings->FirstChildElement("audio"); + if (audioElement != NULL) + { + XMLUtils::GetBoolean(audioElement, "mute", m_muted); + if (!XMLUtils::GetFloat(audioElement, "fvolumelevel", m_volumeLevel, VOLUME_MINIMUM, VOLUME_MAXIMUM)) + m_volumeLevel = VOLUME_MAXIMUM; + } + + return true; +} + +bool CApplication::Save(TiXmlNode *settings) const +{ + if (settings == NULL) + return false; + + TiXmlElement volumeNode("audio"); + TiXmlNode *audioNode = settings->InsertEndChild(volumeNode); + if (audioNode == NULL) + return false; + + XMLUtils::SetBoolean(audioNode, "mute", m_muted); + XMLUtils::SetFloat(audioNode, "fvolumelevel", m_volumeLevel); + + return true; +} + +bool CApplication::LoadSkin(const CStdString& skinID) +{ + AddonPtr addon; + if (CAddonMgr::Get().GetAddon(skinID, addon, ADDON_SKIN)) + { + if (LoadSkin(boost::dynamic_pointer_cast<ADDON::CSkinInfo>(addon))) + return true; + } + CLog::Log(LOGERROR, "failed to load requested skin '%s'", skinID.c_str()); + return false; +} + +bool CApplication::LoadSkin(const SkinPtr& skin) +{ + if (!skin) + return false; + + skin->Start(); + if (!skin->HasSkinFile("Home.xml")) + return false; + + bool bPreviousPlayingState=false; + bool bPreviousRenderingState=false; + if (g_application.m_pPlayer->IsPlayingVideo()) + { + bPreviousPlayingState = !g_application.m_pPlayer->IsPausedPlayback(); + if (bPreviousPlayingState) + g_application.m_pPlayer->Pause(); +#ifdef HAS_VIDEO_PLAYBACK + if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) + { + g_windowManager.ActivateWindow(WINDOW_HOME); + bPreviousRenderingState = true; + } +#endif + } + // close the music and video overlays (they're re-opened automatically later) + CSingleLock lock(g_graphicsContext); + + // save the current window details and focused control + int currentWindow = g_windowManager.GetActiveWindow(); + int iCtrlID = -1; + CGUIWindow* pWindow = g_windowManager.GetWindow(currentWindow); + if (pWindow) + iCtrlID = pWindow->GetFocusedControlID(); + vector<int> currentModelessWindows; + g_windowManager.GetActiveModelessWindows(currentModelessWindows); + + UnloadSkin(); + + CLog::Log(LOGINFO, " load skin from: %s (version: %s)", skin->Path().c_str(), skin->Version().asString().c_str()); + g_SkinInfo = skin; + g_SkinInfo->Start(); + + CLog::Log(LOGINFO, " load fonts for skin..."); + g_graphicsContext.SetMediaDir(skin->Path()); + g_directoryCache.ClearSubPaths(skin->Path()); + + g_colorManager.Load(CSettings::Get().GetString("lookandfeel.skincolors")); + + g_fontManager.LoadFonts(CSettings::Get().GetString("lookandfeel.font")); + + // load in the skin strings + CStdString langPath = URIUtils::AddFileToFolder(skin->Path(), "language"); + URIUtils::AddSlashAtEnd(langPath); + + g_localizeStrings.LoadSkinStrings(langPath, CSettings::Get().GetString("locale.language")); + + g_SkinInfo->LoadIncludes(); + + int64_t start; + start = CurrentHostCounter(); + + CLog::Log(LOGINFO, " load new skin..."); + + // Load the user windows + LoadUserWindows(); + + int64_t end, freq; + end = CurrentHostCounter(); + freq = CurrentHostFrequency(); + CLog::Log(LOGDEBUG,"Load Skin XML: %.2fms", 1000.f * (end - start) / freq); + + CLog::Log(LOGINFO, " initialize new skin..."); + g_windowManager.AddMsgTarget(this); + g_windowManager.AddMsgTarget(&g_playlistPlayer); + g_windowManager.AddMsgTarget(&g_infoManager); + g_windowManager.AddMsgTarget(&g_fontManager); + g_windowManager.AddMsgTarget(&CStereoscopicsManager::Get()); + g_windowManager.SetCallback(*this); + g_windowManager.Initialize(); + CTextureCache::Get().Initialize(); + g_audioManager.Enable(true); + g_audioManager.Load(); + + if (g_SkinInfo->HasSkinFile("DialogFullScreenInfo.xml")) + g_windowManager.Add(new CGUIDialogFullScreenInfo); + + { // we can't register visible condition in dialog's ctor because infomanager is cleared when unloading skin + CGUIDialog *overlay = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_OVERLAY); + if (overlay) overlay->SetVisibleCondition("skin.hasvideooverlay"); + overlay = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_OVERLAY); + if (overlay) overlay->SetVisibleCondition("skin.hasmusicoverlay"); + } + + CLog::Log(LOGINFO, " skin loaded..."); + + // leave the graphics lock + lock.Leave(); + + // restore windows + if (currentWindow != WINDOW_INVALID) + { + g_windowManager.ActivateWindow(currentWindow); + for (unsigned int i = 0; i < currentModelessWindows.size(); i++) + { + CGUIDialog *dialog = (CGUIDialog *)g_windowManager.GetWindow(currentModelessWindows[i]); + if (dialog) dialog->Show(); + } + if (iCtrlID != -1) + { + pWindow = g_windowManager.GetWindow(currentWindow); + if (pWindow && pWindow->HasSaveLastControl()) + { + CGUIMessage msg(GUI_MSG_SETFOCUS, currentWindow, iCtrlID, 0); + pWindow->OnMessage(msg); + } + } + } + + if (g_application.m_pPlayer->IsPlayingVideo()) + { + if (bPreviousPlayingState) + g_application.m_pPlayer->Pause(); + if (bPreviousRenderingState) + g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO); + } + return true; +} + +void CApplication::UnloadSkin(bool forReload /* = false */) +{ + CLog::Log(LOGINFO, "Unloading old skin %s...", forReload ? "for reload " : ""); + + g_audioManager.Enable(false); + + g_windowManager.DeInitialize(); + CTextureCache::Get().Deinitialize(); + + // remove the skin-dependent window + g_windowManager.Delete(WINDOW_DIALOG_FULLSCREEN_INFO); + + g_TextureManager.Cleanup(); + g_largeTextureManager.CleanupUnusedImages(true); + + g_fontManager.Clear(); + + g_colorManager.Clear(); + + g_infoManager.Clear(); + +// The g_SkinInfo boost shared_ptr ought to be reset here +// but there are too many places it's used without checking for NULL +// and as a result a race condition on exit can cause a crash. +} + +bool CApplication::LoadUserWindows() +{ + // Start from wherever home.xml is + std::vector<std::string> vecSkinPath; + g_SkinInfo->GetSkinPaths(vecSkinPath); + for (unsigned int i = 0;i < vecSkinPath.size();++i) + { + CLog::Log(LOGINFO, "Loading user windows, path %s", vecSkinPath[i].c_str()); + CFileItemList items; + if (CDirectory::GetDirectory(vecSkinPath[i], items, ".xml", DIR_FLAG_NO_FILE_DIRS)) + { + for (int i = 0; i < items.Size(); ++i) + { + if (items[i]->m_bIsFolder) + continue; + CStdString skinFile = URIUtils::GetFileName(items[i]->GetPath()); + if (StringUtils::StartsWithNoCase(skinFile, "custom")) + { + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(items[i]->GetPath())) + { + CLog::Log(LOGERROR, "unable to load: %s, Line %d\n%s", items[i]->GetPath().c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc()); + continue; + } + + // Root element should be <window> + TiXmlElement* pRootElement = xmlDoc.RootElement(); + CStdString strValue = pRootElement->Value(); + if (!strValue.Equals("window")) + { + CLog::Log(LOGERROR, "file: %s doesnt contain <window>", skinFile.c_str()); + continue; + } + + // Read the <type> element to get the window type to create + // If no type is specified, create a CGUIWindow as default + CGUIWindow* pWindow = NULL; + CStdString strType; + if (pRootElement->Attribute("type")) + strType = pRootElement->Attribute("type"); + else + { + const TiXmlNode *pType = pRootElement->FirstChild("type"); + if (pType && pType->FirstChild()) + strType = pType->FirstChild()->Value(); + } + int id = WINDOW_INVALID; + if (!pRootElement->Attribute("id", &id)) + { + const TiXmlNode *pType = pRootElement->FirstChild("id"); + if (pType && pType->FirstChild()) + id = atol(pType->FirstChild()->Value()); + } + CStdString visibleCondition; + CGUIControlFactory::GetConditionalVisibility(pRootElement, visibleCondition); + + if (strType.Equals("dialog")) + pWindow = new CGUIDialog(id + WINDOW_HOME, skinFile); + else if (strType.Equals("submenu")) + pWindow = new CGUIDialogSubMenu(id + WINDOW_HOME, skinFile); + else if (strType.Equals("buttonmenu")) + pWindow = new CGUIDialogButtonMenu(id + WINDOW_HOME, skinFile); + else + pWindow = new CGUIWindow(id + WINDOW_HOME, skinFile); + + // Check to make sure the pointer isn't still null + if (pWindow == NULL) + { + CLog::Log(LOGERROR, "Out of memory / Failed to create new object in LoadUserWindows"); + return false; + } + if (id == WINDOW_INVALID || g_windowManager.GetWindow(WINDOW_HOME + id)) + { + delete pWindow; + continue; + } + pWindow->SetVisibleCondition(visibleCondition); + pWindow->SetLoadType(CGUIWindow::KEEP_IN_MEMORY); + g_windowManager.AddCustomWindow(pWindow); + } + } + } + } + return true; +} + +bool CApplication::RenderNoPresent() +{ + MEASURE_FUNCTION; + +// DXMERGE: This may have been important? +// g_graphicsContext.AcquireCurrentContext(); + + g_graphicsContext.Lock(); + + // dont show GUI when playing full screen video + if (g_graphicsContext.IsFullScreenVideo()) + { + g_graphicsContext.SetRenderingResolution(g_graphicsContext.GetVideoResolution(), false); + g_renderManager.Render(true, 0, 255); + + // close window overlays + CGUIDialog *overlay = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_OVERLAY); + if (overlay) overlay->Close(true); + overlay = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_MUSIC_OVERLAY); + if (overlay) overlay->Close(true); + + } + + bool hasRendered = g_windowManager.Render(); + + g_graphicsContext.Unlock(); + + return hasRendered; +} + +float CApplication::GetDimScreenSaverLevel() const +{ + if (!m_bScreenSave || !m_screenSaver || + (m_screenSaver->ID() != "screensaver.xbmc.builtin.dim" && + m_screenSaver->ID() != "screensaver.xbmc.builtin.black" && + !m_screenSaver->ID().empty())) + return 0; + + if (!m_screenSaver->GetSetting("level").empty()) + return 100.0f - (float)atof(m_screenSaver->GetSetting("level").c_str()); + return 100.0f; +} + +void CApplication::Render() +{ + // do not render if we are stopped or in background + if (m_bStop) + return; + + MEASURE_FUNCTION; + + int vsync_mode = CSettings::Get().GetInt("videoscreen.vsync"); + + bool hasRendered = false; + bool limitFrames = false; + unsigned int singleFrameTime = 10; // default limit 100 fps + + { + // Less fps in DPMS + bool lowfps = m_dpmsIsActive || g_Windowing.EnableFrameLimiter(); + // Whether externalplayer is playing and we're unfocused + bool extPlayerActive = m_pPlayer->GetCurrentPlayer() == EPC_EXTPLAYER && m_pPlayer->IsPlaying() && !m_AppFocused; + + m_bPresentFrame = false; + if (!extPlayerActive && g_graphicsContext.IsFullScreenVideo() && !m_pPlayer->IsPausedPlayback() && g_renderManager.RendererHandlesPresent()) + { + m_bPresentFrame = g_renderManager.FrameWait(100); + hasRendered = true; + } + else + { + // engage the frame limiter as needed + limitFrames = lowfps || extPlayerActive; + // DXMERGE - we checked for g_videoConfig.GetVSyncMode() before this + // perhaps allowing it to be set differently than the UI option?? + if (vsync_mode == VSYNC_DISABLED || vsync_mode == VSYNC_VIDEO) + limitFrames = true; // not using vsync. + else if ((g_infoManager.GetFPS() > g_graphicsContext.GetFPS() + 10) && g_infoManager.GetFPS() > 1000 / singleFrameTime) + limitFrames = true; // using vsync, but it isn't working. + + if (limitFrames) + { + if (extPlayerActive) + { + ResetScreenSaver(); // Prevent screensaver dimming the screen + singleFrameTime = 1000; // 1 fps, high wakeup latency but v.low CPU usage + } + else if (lowfps) + singleFrameTime = 200; // 5 fps, <=200 ms latency to wake up + } + + } + } + + CSingleLock lock(g_graphicsContext); + g_infoManager.UpdateFPS(); + + if (g_graphicsContext.IsFullScreenVideo() && m_pPlayer->IsPlaying() && vsync_mode == VSYNC_VIDEO) + g_Windowing.SetVSync(true); + else if (vsync_mode == VSYNC_ALWAYS) + g_Windowing.SetVSync(true); + else if (vsync_mode != VSYNC_DRIVER) + g_Windowing.SetVSync(false); + + if (m_bPresentFrame && m_pPlayer->IsPlaying() && !m_pPlayer->IsPaused()) + ResetScreenSaver(); + + if(!g_Windowing.BeginRender()) + return; + + g_renderManager.FrameMove(); + + CDirtyRegionList dirtyRegions = g_windowManager.GetDirty(); + if(g_graphicsContext.GetStereoMode()) + { + g_graphicsContext.SetStereoView(RENDER_STEREO_VIEW_LEFT); + if(RenderNoPresent()) + hasRendered = true; + + if(g_graphicsContext.GetStereoMode() != RENDER_STEREO_MODE_MONO) + { + g_graphicsContext.SetStereoView(RENDER_STEREO_VIEW_RIGHT); + if(RenderNoPresent()) + hasRendered = true; + } + g_graphicsContext.SetStereoView(RENDER_STEREO_VIEW_OFF); + } + else + { + if(RenderNoPresent()) + hasRendered = true; + } + + g_renderManager.FrameFinish(); + + g_Windowing.EndRender(); + + // execute post rendering actions (finalize window closing) + g_windowManager.AfterRender(); + + // reset our info cache - we do this at the end of Render so that it is + // fresh for the next process(), or after a windowclose animation (where process() + // isn't called) + g_infoManager.ResetCache(); + lock.Leave(); + + unsigned int now = XbmcThreads::SystemClockMillis(); + if (hasRendered) + m_lastRenderTime = now; + + //when nothing has been rendered for m_guiDirtyRegionNoFlipTimeout milliseconds, + //we don't call g_graphicsContext.Flip() anymore, this saves gpu and cpu usage + bool flip; + if (g_advancedSettings.m_guiDirtyRegionNoFlipTimeout >= 0) + flip = hasRendered || (now - m_lastRenderTime) < (unsigned int)g_advancedSettings.m_guiDirtyRegionNoFlipTimeout; + else + flip = true; + + //fps limiter, make sure each frame lasts at least singleFrameTime milliseconds + if (limitFrames || !flip) + { + if (!limitFrames) + singleFrameTime = 40; //if not flipping, loop at 25 fps + + unsigned int frameTime = now - m_lastFrameTime; + if (frameTime < singleFrameTime) + Sleep(singleFrameTime - frameTime); + } + + if (flip) + g_graphicsContext.Flip(dirtyRegions); + + m_lastFrameTime = XbmcThreads::SystemClockMillis(); + CTimeUtils::UpdateFrameTime(flip); + + g_renderManager.UpdateResolution(); + g_renderManager.ManageCaptures(); +} + +void CApplication::SetStandAlone(bool value) +{ + g_advancedSettings.m_handleMounting = m_bStandalone = value; +} + +// OnKey() translates the key into a CAction which is sent on to our Window Manager. +// The window manager will return true if the event is processed, false otherwise. +// If not already processed, this routine handles global keypresses. It returns +// true if the key has been processed, false otherwise. + +bool CApplication::OnKey(const CKey& key) +{ + + // Turn the mouse off, as we've just got a keypress from controller or remote + g_Mouse.SetActive(false); + + // get the current active window + int iWin = GetActiveWindowID(); + + // this will be checked for certain keycodes that need + // special handling if the screensaver is active + CAction action = CButtonTranslator::GetInstance().GetAction(iWin, key); + + // a key has been pressed. + // reset Idle Timer + m_idleTimer.StartZero(); + bool processKey = AlwaysProcess(action); + + if (StringUtils::StartsWithNoCase(action.GetName(),"CECToggleState") || StringUtils::StartsWithNoCase(action.GetName(),"CECStandby")) + { + // do not wake up the screensaver right after switching off the playing device + if (StringUtils::StartsWithNoCase(action.GetName(),"CECToggleState")) + { + CLog::LogF(LOGDEBUG, "action %s [%d], toggling state of playing device", action.GetName().c_str(), action.GetID()); + if (!CApplicationMessenger::Get().CECToggleState()) + return true; + } + else + { + CApplicationMessenger::Get().CECStandby(); + return true; + } + } + + ResetScreenSaver(); + + // allow some keys to be processed while the screensaver is active + if (WakeUpScreenSaverAndDPMS(processKey) && !processKey) + { + CLog::LogF(LOGDEBUG, "%s pressed, screen saver/dpms woken up", g_Keyboard.GetKeyName((int) key.GetButtonCode()).c_str()); + return true; + } + + if (iWin != WINDOW_FULLSCREEN_VIDEO) + { + // current active window isnt the fullscreen window + // just use corresponding section from keymap.xml + // to map key->action + + // first determine if we should use keyboard input directly + bool useKeyboard = key.FromKeyboard() && (iWin == WINDOW_DIALOG_KEYBOARD || iWin == WINDOW_DIALOG_NUMERIC); + CGUIWindow *window = g_windowManager.GetWindow(iWin); + if (window) + { + CGUIControl *control = window->GetFocusedControl(); + if (control) + { + // If this is an edit control set usekeyboard to true. This causes the + // keypress to be processed directly not through the key mappings. + if (control->GetControlType() == CGUIControl::GUICONTROL_EDIT) + useKeyboard = true; + + // If the key pressed is shift-A to shift-Z set usekeyboard to true. + // This causes the keypress to be used for list navigation. + if (control->IsContainer() && key.GetModifiers() == CKey::MODIFIER_SHIFT && key.GetVKey() >= XBMCVK_A && key.GetVKey() <= XBMCVK_Z) + useKeyboard = true; + } + } + if (useKeyboard) + { + // use the virtualkeyboard section of the keymap, and send keyboard-specific or navigation + // actions through if that's what they are + CAction action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key); + if (!(action.GetID() == ACTION_MOVE_LEFT || + action.GetID() == ACTION_MOVE_RIGHT || + action.GetID() == ACTION_MOVE_UP || + action.GetID() == ACTION_MOVE_DOWN || + action.GetID() == ACTION_SELECT_ITEM || + action.GetID() == ACTION_ENTER || + action.GetID() == ACTION_PREVIOUS_MENU || + action.GetID() == ACTION_NAV_BACK)) + { + // the action isn't plain navigation - check for a keyboard-specific keymap + action = CButtonTranslator::GetInstance().GetAction(WINDOW_DIALOG_KEYBOARD, key, false); + if (!(action.GetID() >= REMOTE_0 && action.GetID() <= REMOTE_9) || + action.GetID() == ACTION_BACKSPACE || + action.GetID() == ACTION_SHIFT || + action.GetID() == ACTION_SYMBOLS || + action.GetID() == ACTION_CURSOR_LEFT || + action.GetID() == ACTION_CURSOR_RIGHT) + action = CAction(0); // don't bother with this action + } + // else pass the keys through directly + if (!action.GetID()) + { + if (key.GetFromService()) + action = CAction(key.GetButtonCode() != KEY_INVALID ? key.GetButtonCode() : 0, key.GetUnicode()); + else + { + // Check for paste keypress +#ifdef TARGET_WINDOWS + // In Windows paste is ctrl-V + if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_CTRL) +#elif defined(TARGET_LINUX) + // In Linux paste is ctrl-V + if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_CTRL) +#elif defined(TARGET_DARWIN_OSX) + // In OSX paste is cmd-V + if (key.GetVKey() == XBMCVK_V && key.GetModifiers() == CKey::MODIFIER_META) +#else + // Placeholder for other operating systems + if (false) +#endif + action = CAction(ACTION_PASTE); + // If the unicode is non-zero the keypress is a non-printing character + else if (key.GetUnicode()) + action = CAction(key.GetAscii() | KEY_ASCII, key.GetUnicode()); + // The keypress is a non-printing character + else + action = CAction(key.GetVKey() | KEY_VKEY); + } + } + + CLog::LogF(LOGDEBUG, "%s pressed, trying keyboard action %x", g_Keyboard.GetKeyName((int) key.GetButtonCode()).c_str(), action.GetID()); + + if (OnAction(action)) + return true; + // failed to handle the keyboard action, drop down through to standard action + } + if (key.GetFromService()) + { + if (key.GetButtonCode() != KEY_INVALID) + action = CButtonTranslator::GetInstance().GetAction(iWin, key); + } + else + action = CButtonTranslator::GetInstance().GetAction(iWin, key); + } + if (!key.IsAnalogButton()) + CLog::LogF(LOGDEBUG, "%s pressed, action is %s", g_Keyboard.GetKeyName((int) key.GetButtonCode()).c_str(), action.GetName().c_str()); + + return ExecuteInputAction(action); +} + +// OnAppCommand is called in response to a XBMC_APPCOMMAND event. +// This needs to return true if it processed the appcommand or false if it didn't +bool CApplication::OnAppCommand(const CAction &action) +{ + // Reset the screen saver + ResetScreenSaver(); + + // If we were currently in the screen saver wake up and don't process the appcommand + if (WakeUpScreenSaverAndDPMS()) + return true; + + // The action ID is the APPCOMMAND code. We need to retrieve the action + // associated with this appcommand from the mapping table. + uint32_t appcmd = action.GetID(); + CKey key(appcmd | KEY_APPCOMMAND, (unsigned int) 0); + int iWin = g_windowManager.GetActiveWindow() & WINDOW_ID_MASK; + CAction appcmdaction = CButtonTranslator::GetInstance().GetAction(iWin, key); + + // If we couldn't find an action return false to indicate we have not + // handled this appcommand + if (!appcmdaction.GetID()) + { + CLog::LogF(LOGDEBUG, "unknown appcommand %d", appcmd); + return false; + } + + // Process the appcommand + CLog::LogF(LOGDEBUG, "appcommand %d, trying action %s", appcmd, appcmdaction.GetName().c_str()); + OnAction(appcmdaction); + + // Always return true regardless of whether the action succeeded or not. + // This stops Windows handling the appcommand itself. + return true; +} + +bool CApplication::OnAction(const CAction &action) +{ + // special case for switching between GUI & fullscreen mode. + if (action.GetID() == ACTION_SHOW_GUI) + { // Switch to fullscreen mode if we can + if (SwitchToFullScreen()) + { + m_navigationTimer.StartZero(); + return true; + } + } + + if (action.GetID() == ACTION_TOGGLE_FULLSCREEN) + { + g_graphicsContext.ToggleFullScreenRoot(); + return true; + } + + if (action.IsMouse()) + g_Mouse.SetActive(true); + + + if (action.GetID() == ACTION_CREATE_EPISODE_BOOKMARK) + { + CGUIDialogVideoBookmarks::OnAddEpisodeBookmark(); + } + if (action.GetID() == ACTION_CREATE_BOOKMARK) + { + CGUIDialogVideoBookmarks::OnAddBookmark(); + } + + // The action PLAYPAUSE behaves as ACTION_PAUSE if we are currently + // playing or ACTION_PLAYER_PLAY if we are seeking (FF/RW) or not playing. + if (action.GetID() == ACTION_PLAYER_PLAYPAUSE) + { + if (m_pPlayer->IsPlaying() && m_pPlayer->GetPlaySpeed() == 1) + return OnAction(CAction(ACTION_PAUSE)); + else + return OnAction(CAction(ACTION_PLAYER_PLAY)); + } + + //if the action would start or stop inertial scrolling + //by gesture - bypass the normal OnAction handler of current window + if( !m_pInertialScrollingHandler->CheckForInertialScrolling(&action) ) + { + // in normal case + // just pass the action to the current window and let it handle it + if (g_windowManager.OnAction(action)) + { + m_navigationTimer.StartZero(); + return true; + } + } + + // handle extra global presses + + // screenshot : take a screenshot :) + if (action.GetID() == ACTION_TAKE_SCREENSHOT) + { + CScreenShot::TakeScreenshot(); + return true; + } + // built in functions : execute the built-in + if (action.GetID() == ACTION_BUILT_IN_FUNCTION) + { + CBuiltins::Execute(action.GetName()); + m_navigationTimer.StartZero(); + return true; + } + + // reload keymaps + if (action.GetID() == ACTION_RELOAD_KEYMAPS) + { + CButtonTranslator::GetInstance().Clear(); + CButtonTranslator::GetInstance().Load(); + } + + // show info : Shows the current video or song information + if (action.GetID() == ACTION_SHOW_INFO) + { + g_infoManager.ToggleShowInfo(); + return true; + } + + // codec info : Shows the current song, video or picture codec information + if (action.GetID() == ACTION_SHOW_CODEC) + { + g_infoManager.ToggleShowCodec(); + return true; + } + + if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_pPlayer->IsPlayingAudio()) + { + const CMusicInfoTag *tag = g_infoManager.GetCurrentSongTag(); + if (tag) + { + *m_itemCurrentFile->GetMusicInfoTag() = *tag; + char rating = tag->GetRating(); + bool needsUpdate(false); + if (rating > '0' && action.GetID() == ACTION_DECREASE_RATING) + { + m_itemCurrentFile->GetMusicInfoTag()->SetRating(rating - 1); + needsUpdate = true; + } + else if (rating < '5' && action.GetID() == ACTION_INCREASE_RATING) + { + m_itemCurrentFile->GetMusicInfoTag()->SetRating(rating + 1); + needsUpdate = true; + } + if (needsUpdate) + { + CMusicDatabase db; + if (db.Open()) // OpenForWrite() ? + { + db.SetSongRating(m_itemCurrentFile->GetPath(), m_itemCurrentFile->GetMusicInfoTag()->GetRating()); + db.Close(); + } + // send a message to all windows to tell them to update the fileitem (eg playlistplayer, media windows) + CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile); + g_windowManager.SendMessage(msg); + } + } + return true; + } + + // Now check with the playlist player if action can be handled. + // In case of the action PREV_ITEM, we only allow the playlist player to take it if we're less than 3 seconds into playback. + if (!(action.GetID() == ACTION_PREV_ITEM && m_pPlayer->CanSeek() && GetTime() > 3) ) + { + if (g_playlistPlayer.OnAction(action)) + return true; + } + + // Now check with the player if action can be handled. + if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO || + (g_windowManager.GetActiveWindow() == WINDOW_DIALOG_VIDEO_OSD && (action.GetID() == ACTION_NEXT_ITEM || action.GetID() == ACTION_PREV_ITEM || action.GetID() == ACTION_CHANNEL_UP || action.GetID() == ACTION_CHANNEL_DOWN)) || + action.GetID() == ACTION_STOP) + { + if (m_pPlayer->OnAction(action)) + return true; + // Player ignored action; popup the OSD + if ((action.GetID() == ACTION_MOUSE_MOVE && (action.GetAmount(2) || action.GetAmount(3))) // filter "false" mouse move from touch + || action.GetID() == ACTION_MOUSE_LEFT_CLICK) + CApplicationMessenger::Get().SendAction(CAction(ACTION_TRIGGER_OSD), WINDOW_INVALID, false); + } + + // stop : stops playing current audio song + if (action.GetID() == ACTION_STOP) + { + StopPlaying(); + return true; + } + + // In case the playlist player nor the player didn't handle PREV_ITEM, because we are past the 3 secs limit. + // If so, we just jump to the start of the track. + if (action.GetID() == ACTION_PREV_ITEM && m_pPlayer->CanSeek()) + { + SeekTime(0); + m_pPlayer->SetPlaySpeed(1, g_application.m_muted); + return true; + } + + // forward action to g_PVRManager and break if it was able to handle it + if (g_PVRManager.OnAction(action)) + return true; + + // forward action to graphic context and see if it can handle it + if (CStereoscopicsManager::Get().OnAction(action)) + return true; + + if (m_pPlayer->IsPlaying()) + { + // forward channel switches to the player - he knows what to do + if (action.GetID() == ACTION_CHANNEL_UP || action.GetID() == ACTION_CHANNEL_DOWN) + { + m_pPlayer->OnAction(action); + return true; + } + + // pause : toggle pause action + if (action.GetID() == ACTION_PAUSE) + { + m_pPlayer->Pause(); + // go back to normal play speed on unpause + if (!m_pPlayer->IsPaused() && m_pPlayer->GetPlaySpeed() != 1) + m_pPlayer->SetPlaySpeed(1, g_application.m_muted); + + #ifdef HAS_KARAOKE + m_pKaraokeMgr->SetPaused( m_pPlayer->IsPaused() ); +#endif + g_audioManager.Enable(m_pPlayer->IsPaused()); + return true; + } + // play: unpause or set playspeed back to normal + if (action.GetID() == ACTION_PLAYER_PLAY) + { + // if currently paused - unpause + if (m_pPlayer->IsPaused()) + return OnAction(CAction(ACTION_PAUSE)); + // if we do a FF/RW then go back to normal speed + if (m_pPlayer->GetPlaySpeed() != 1) + m_pPlayer->SetPlaySpeed(1, g_application.m_muted); + return true; + } + if (!m_pPlayer->IsPaused()) + { + if (action.GetID() == ACTION_PLAYER_FORWARD || action.GetID() == ACTION_PLAYER_REWIND) + { + int iPlaySpeed = m_pPlayer->GetPlaySpeed(); + if (action.GetID() == ACTION_PLAYER_REWIND && iPlaySpeed == 1) // Enables Rewinding + iPlaySpeed *= -2; + else if (action.GetID() == ACTION_PLAYER_REWIND && iPlaySpeed > 1) //goes down a notch if you're FFing + iPlaySpeed /= 2; + else if (action.GetID() == ACTION_PLAYER_FORWARD && iPlaySpeed < 1) //goes up a notch if you're RWing + iPlaySpeed /= 2; + else + iPlaySpeed *= 2; + + if (action.GetID() == ACTION_PLAYER_FORWARD && iPlaySpeed == -1) //sets iSpeed back to 1 if -1 (didn't plan for a -1) + iPlaySpeed = 1; + if (iPlaySpeed > 32 || iPlaySpeed < -32) + iPlaySpeed = 1; + + m_pPlayer->SetPlaySpeed(iPlaySpeed, g_application.m_muted); + return true; + } + else if ((action.GetAmount() || m_pPlayer->GetPlaySpeed() != 1) && (action.GetID() == ACTION_ANALOG_REWIND || action.GetID() == ACTION_ANALOG_FORWARD)) + { + // calculate the speed based on the amount the button is held down + int iPower = (int)(action.GetAmount() * MAX_FFWD_SPEED + 0.5f); + // returns 0 -> MAX_FFWD_SPEED + int iSpeed = 1 << iPower; + if (iSpeed != 1 && action.GetID() == ACTION_ANALOG_REWIND) + iSpeed = -iSpeed; + g_application.m_pPlayer->SetPlaySpeed(iSpeed, g_application.m_muted); + if (iSpeed == 1) + CLog::Log(LOGDEBUG,"Resetting playspeed"); + return true; + } + } + // allow play to unpause + else + { + if (action.GetID() == ACTION_PLAYER_PLAY) + { + // unpause, and set the playspeed back to normal + m_pPlayer->Pause(); + g_audioManager.Enable(m_pPlayer->IsPaused()); + + g_application.m_pPlayer->SetPlaySpeed(1, g_application.m_muted); + return true; + } + } + + // record current file + if (action.GetID() == ACTION_RECORD) + { + if (m_pPlayer->CanRecord()) + m_pPlayer->Record(!m_pPlayer->IsRecording()); + } + + if (m_playerController->OnAction(action)) + return true; + } + + + if (action.GetID() == ACTION_SWITCH_PLAYER) + { + if(m_pPlayer->IsPlaying()) + { + VECPLAYERCORES cores; + CFileItem item(*m_itemCurrentFile.get()); + CPlayerCoreFactory::Get().GetPlayers(item, cores); + PLAYERCOREID core = CPlayerCoreFactory::Get().SelectPlayerDialog(cores); + if(core != EPC_NONE) + { + g_application.m_eForcedNextPlayer = core; + item.m_lStartOffset = (int)(GetTime() * 75); + PlayFile(item, true); + } + } + else + { + VECPLAYERCORES cores; + CPlayerCoreFactory::Get().GetRemotePlayers(cores); + PLAYERCOREID core = CPlayerCoreFactory::Get().SelectPlayerDialog(cores); + if(core != EPC_NONE) + { + CFileItem item; + g_application.m_eForcedNextPlayer = core; + PlayFile(item, false); + } + } + } + + if (g_peripherals.OnAction(action)) + return true; + + if (action.GetID() == ACTION_MUTE) + { + ToggleMute(); + return true; + } + + if (action.GetID() == ACTION_TOGGLE_DIGITAL_ANALOG) + { + bool passthrough = CSettings::Get().GetBool("audiooutput.passthrough"); + CSettings::Get().SetBool("audiooutput.passthrough", !passthrough); + + if (g_windowManager.GetActiveWindow() == WINDOW_SETTINGS_SYSTEM) + { + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0,0,WINDOW_INVALID,g_windowManager.GetActiveWindow()); + g_windowManager.SendMessage(msg); + } + return true; + } + + // Check for global volume control + if (action.GetAmount() && (action.GetID() == ACTION_VOLUME_UP || action.GetID() == ACTION_VOLUME_DOWN)) + { + if (!m_pPlayer->IsPassthrough()) + { + if (m_muted) + UnMute(); + float volume = m_volumeLevel; +// Android has steps based on the max available volume level +#if defined(TARGET_ANDROID) + float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / CXBMCApp::GetMaxSystemVolume(); +#else + float step = (VOLUME_MAXIMUM - VOLUME_MINIMUM) / VOLUME_CONTROL_STEPS; + + if (action.GetRepeat()) + step *= action.GetRepeat() * 50; // 50 fps +#endif + if (action.GetID() == ACTION_VOLUME_UP) + volume += (float)fabs(action.GetAmount()) * action.GetAmount() * step; + else + volume -= (float)fabs(action.GetAmount()) * action.GetAmount() * step; + SetVolume(volume, false); + } + // show visual feedback of volume change... + ShowVolumeBar(&action); + return true; + } + // Check for global seek control + if (m_pPlayer->IsPlaying() && action.GetAmount() && (action.GetID() == ACTION_ANALOG_SEEK_FORWARD || action.GetID() == ACTION_ANALOG_SEEK_BACK)) + { + if (!m_pPlayer->CanSeek()) return false; + m_seekHandler->Seek(action.GetID() == ACTION_ANALOG_SEEK_FORWARD, action.GetAmount(), action.GetRepeat()); + return true; + } + if (action.GetID() == ACTION_GUIPROFILE_BEGIN) + { + CGUIControlProfiler::Instance().SetOutputFile(CSpecialProtocol::TranslatePath("special://home/guiprofiler.xml")); + CGUIControlProfiler::Instance().Start(); + return true; + } + if (action.GetID() == ACTION_SHOW_PLAYLIST) + { + int iPlaylist = g_playlistPlayer.GetCurrentPlaylist(); + if (iPlaylist == PLAYLIST_VIDEO && g_windowManager.GetActiveWindow() != WINDOW_VIDEO_PLAYLIST) + g_windowManager.ActivateWindow(WINDOW_VIDEO_PLAYLIST); + else if (iPlaylist == PLAYLIST_MUSIC && g_windowManager.GetActiveWindow() != WINDOW_MUSIC_PLAYLIST) + g_windowManager.ActivateWindow(WINDOW_MUSIC_PLAYLIST); + return true; + } + return false; +} + +void CApplication::FrameMove(bool processEvents, bool processGUI) +{ + MEASURE_FUNCTION; + + if (processEvents) + { + // currently we calculate the repeat time (ie time from last similar keypress) just global as fps + float frameTime = m_frameTime.GetElapsedSeconds(); + m_frameTime.StartZero(); + // never set a frametime less than 2 fps to avoid problems when debuggin and on breaks + if( frameTime > 0.5 ) frameTime = 0.5; + + if (processGUI && m_renderGUI) + { + g_graphicsContext.Lock(); + // check if there are notifications to display + CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); + if (toast && toast->DoWork()) + { + if (!toast->IsDialogRunning()) + { + toast->Show(); + } + } + g_graphicsContext.Unlock(); + } + CWinEvents::MessagePump(); + +#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) + // Read the input from a remote + g_RemoteControl.Update(); +#endif + + // process input actions + ProcessRemote(frameTime); + ProcessGamepad(frameTime); + ProcessEventServer(frameTime); + ProcessPeripherals(frameTime); + if (processGUI && m_renderGUI) + { + m_pInertialScrollingHandler->ProcessInertialScroll(frameTime); + m_seekHandler->Process(); + } + } + if (processGUI && m_renderGUI) + { + if (!m_bStop) + g_windowManager.Process(CTimeUtils::GetFrameTime()); + g_windowManager.FrameMove(); + } +} + +bool CApplication::ProcessGamepad(float frameTime) +{ +#ifdef HAS_SDL_JOYSTICK + if (!m_AppFocused) + return false; + + int iWin = GetActiveWindowID(); + int keymapId, joyId; + g_Joystick.Update(); + std::string joyName; + if (g_Joystick.GetButton(joyName, joyId)) + { + // reset Idle Timer + m_idleTimer.StartZero(); + + ResetScreenSaver(); + if (WakeUpScreenSaverAndDPMS()) + { + g_Joystick.Reset(); + return true; + } + + int actionID; + CStdString actionName; + bool fullrange; + keymapId = joyId + 1; + if (CButtonTranslator::GetInstance().TranslateJoystickString(iWin, joyName, keymapId, JACTIVE_BUTTON, actionID, actionName, fullrange)) + { + CAction action(actionID, 1.0f, 0.0f, actionName); + g_Mouse.SetActive(false); + return ExecuteInputAction(action); + } + } + if (g_Joystick.GetAxis(joyName, joyId)) + { + keymapId = joyId + 1; + if (g_Joystick.GetAmount(joyName, joyId) < 0) + { + keymapId = -keymapId; + } + + int actionID; + CStdString actionName; + bool fullrange; + if (CButtonTranslator::GetInstance().TranslateJoystickString(iWin, joyName, keymapId, JACTIVE_AXIS, actionID, actionName, fullrange)) + { + ResetScreenSaver(); + if (WakeUpScreenSaverAndDPMS()) + { + return true; + } + + float amount = g_Joystick.GetAmount(joyName, joyId); + CAction action(actionID, fullrange ? (amount + 1.0f)/2.0f : fabs(amount), 0.0f, actionName); + g_Mouse.SetActive(false); + return ExecuteInputAction(action); + } + } + int position = 0; + if (g_Joystick.GetHat(joyName, joyId, position)) + { + keymapId = joyId + 1; + // reset Idle Timer + m_idleTimer.StartZero(); + + ResetScreenSaver(); + if (WakeUpScreenSaverAndDPMS()) + { + g_Joystick.Reset(); + return true; + } + + int actionID; + CStdString actionName; + bool fullrange; + + keymapId = position << 16 | keymapId; + + if (keymapId && CButtonTranslator::GetInstance().TranslateJoystickString(iWin, joyName, keymapId, JACTIVE_HAT, actionID, actionName, fullrange)) + { + CAction action(actionID, 1.0f, 0.0f, actionName); + g_Mouse.SetActive(false); + return ExecuteInputAction(action); + } + } +#endif + return false; +} + +bool CApplication::ProcessRemote(float frameTime) +{ +#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) + if (g_RemoteControl.GetButton()) + { + CKey key(g_RemoteControl.GetButton(), g_RemoteControl.GetHoldTime()); + g_RemoteControl.Reset(); + return OnKey(key); + } +#endif + return false; +} + +bool CApplication::ProcessPeripherals(float frameTime) +{ + CKey key; + if (g_peripherals.GetNextKeypress(frameTime, key)) + return OnKey(key); + return false; +} + +bool CApplication::ProcessMouse() +{ + MEASURE_FUNCTION; + + if (!g_Mouse.IsActive() || !m_AppFocused) + return false; + + // Get the mouse command ID + uint32_t mousekey = g_Mouse.GetKey(); + if (mousekey == KEY_MOUSE_NOOP) + return true; + + // Reset the screensaver and idle timers + m_idleTimer.StartZero(); + ResetScreenSaver(); + if (WakeUpScreenSaverAndDPMS()) + return true; + + // Retrieve the corresponding action + int iWin = GetActiveWindowID(); + CKey key(mousekey, (unsigned int) 0); + CAction mouseaction = CButtonTranslator::GetInstance().GetAction(iWin, key); + + // Deactivate mouse if non-mouse action + if (!mouseaction.IsMouse()) + g_Mouse.SetActive(false); + + // Consume ACTION_NOOP. + // Some views or dialogs gets closed after any ACTION and + // a sensitive mouse might cause problems. + if (mouseaction.GetID() == ACTION_NOOP) + return false; + + // If we couldn't find an action return false to indicate we have not + // handled this mouse action + if (!mouseaction.GetID()) + { + CLog::LogF(LOGDEBUG, "unknown mouse command %d", mousekey); + return false; + } + + // Log mouse actions except for move and noop + if (mouseaction.GetID() != ACTION_MOUSE_MOVE && mouseaction.GetID() != ACTION_NOOP) + CLog::LogF(LOGDEBUG, "trying mouse action %s", mouseaction.GetName().c_str()); + + // The action might not be a mouse action. For example wheel moves might + // be mapped to volume up/down in mouse.xml. In this case we do not want + // the mouse position saved in the action. + if (!mouseaction.IsMouse()) + return OnAction(mouseaction); + + // This is a mouse action so we need to record the mouse position + return OnAction(CAction(mouseaction.GetID(), + g_Mouse.GetHold(MOUSE_LEFT_BUTTON), + (float)g_Mouse.GetX(), + (float)g_Mouse.GetY(), + (float)g_Mouse.GetDX(), + (float)g_Mouse.GetDY(), + mouseaction.GetName())); +} + +bool CApplication::ProcessEventServer(float frameTime) +{ +#ifdef HAS_EVENT_SERVER + CEventServer* es = CEventServer::GetInstance(); + if (!es || !es->Running() || es->GetNumberOfClients()==0) + return false; + + // process any queued up actions + if (es->ExecuteNextAction()) + { + // reset idle timers + m_idleTimer.StartZero(); + ResetScreenSaver(); + WakeUpScreenSaverAndDPMS(); + } + + // now handle any buttons or axis + std::string joystickName; + bool isAxis = false; + float fAmount = 0.0; + + // es->ExecuteNextAction() invalidates the ref to the CEventServer instance + // when the action exits XBMC + es = CEventServer::GetInstance(); + if (!es || !es->Running() || es->GetNumberOfClients()==0) + return false; + unsigned int wKeyID = es->GetButtonCode(joystickName, isAxis, fAmount); + + if (wKeyID) + { + if (joystickName.length() > 0) + { + if (isAxis == true) + { + if (fabs(fAmount) >= 0.08) + m_lastAxisMap[joystickName][wKeyID] = fAmount; + else + m_lastAxisMap[joystickName].erase(wKeyID); + } + + return ProcessJoystickEvent(joystickName, wKeyID, isAxis ? JACTIVE_AXIS : JACTIVE_BUTTON, fAmount); + } + else + { + CKey key; + if (wKeyID & ES_FLAG_UNICODE) + { + key = CKey((uint8_t)0, wKeyID & ~ES_FLAG_UNICODE, 0, 0, 0); + return OnKey(key); + } + + if(wKeyID == KEY_BUTTON_LEFT_ANALOG_TRIGGER) + key = CKey(wKeyID, (BYTE)(255*fAmount), 0, 0.0, 0.0, 0.0, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_RIGHT_ANALOG_TRIGGER) + key = CKey(wKeyID, 0, (BYTE)(255*fAmount), 0.0, 0.0, 0.0, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_LEFT) + key = CKey(wKeyID, 0, 0, -fAmount, 0.0, 0.0, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_RIGHT) + key = CKey(wKeyID, 0, 0, fAmount, 0.0, 0.0, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_UP) + key = CKey(wKeyID, 0, 0, 0.0, fAmount, 0.0, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_LEFT_THUMB_STICK_DOWN) + key = CKey(wKeyID, 0, 0, 0.0, -fAmount, 0.0, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_LEFT) + key = CKey(wKeyID, 0, 0, 0.0, 0.0, -fAmount, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT) + key = CKey(wKeyID, 0, 0, 0.0, 0.0, fAmount, 0.0, frameTime); + else if(wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_UP) + key = CKey(wKeyID, 0, 0, 0.0, 0.0, 0.0, fAmount, frameTime); + else if(wKeyID == KEY_BUTTON_RIGHT_THUMB_STICK_DOWN) + key = CKey(wKeyID, 0, 0, 0.0, 0.0, 0.0, -fAmount, frameTime); + else + key = CKey(wKeyID); + key.SetFromService(true); + return OnKey(key); + } + } + + if (m_lastAxisMap.size() > 0) + { + // Process all the stored axis. + for (map<std::string, map<int, float> >::iterator iter = m_lastAxisMap.begin(); iter != m_lastAxisMap.end(); ++iter) + { + for (map<int, float>::iterator iterAxis = (*iter).second.begin(); iterAxis != (*iter).second.end(); ++iterAxis) + ProcessJoystickEvent((*iter).first, (*iterAxis).first, JACTIVE_AXIS, (*iterAxis).second); + } + } + + { + CPoint pos; + if (es->GetMousePos(pos.x, pos.y) && g_Mouse.IsEnabled()) + { + XBMC_Event newEvent; + newEvent.type = XBMC_MOUSEMOTION; + newEvent.motion.xrel = 0; + newEvent.motion.yrel = 0; + newEvent.motion.state = 0; + newEvent.motion.which = 0x10; // just a different value to distinguish between mouse and event client device. + newEvent.motion.x = (uint16_t)pos.x; + newEvent.motion.y = (uint16_t)pos.y; + OnEvent(newEvent); // had to call this to update g_Mouse position + return OnAction(CAction(ACTION_MOUSE_MOVE, pos.x, pos.y)); + } + } +#endif + return false; +} + +bool CApplication::ProcessJoystickEvent(const std::string& joystickName, int wKeyID, short inputType, float fAmount, unsigned int holdTime /*=0*/) +{ +#if defined(HAS_EVENT_SERVER) + m_idleTimer.StartZero(); + + // Make sure to reset screen saver, mouse. + ResetScreenSaver(); + if (WakeUpScreenSaverAndDPMS()) + return true; + + g_Mouse.SetActive(false); + + int iWin = GetActiveWindowID(); + int actionID; + CStdString actionName; + bool fullRange = false; + + // Translate using regular joystick translator. + if (CButtonTranslator::GetInstance().TranslateJoystickString(iWin, joystickName, wKeyID, inputType, actionID, actionName, fullRange)) + return ExecuteInputAction( CAction(actionID, fAmount, 0.0f, actionName, holdTime) ); + else + CLog::Log(LOGDEBUG, "ERROR mapping joystick action. Joystick: %s %i",joystickName.c_str(), wKeyID); +#endif + + return false; +} + +bool CApplication::ExecuteInputAction(const CAction &action) +{ + bool bResult = false; + + // play sound before the action unless the button is held, + // where we execute after the action as held actions aren't fired every time. + if(action.GetHoldTime()) + { + bResult = OnAction(action); + if(bResult) + g_audioManager.PlayActionSound(action); + } + else + { + g_audioManager.PlayActionSound(action); + bResult = OnAction(action); + } + return bResult; +} + +int CApplication::GetActiveWindowID(void) +{ + // Get the currently active window + int iWin = g_windowManager.GetActiveWindow() & WINDOW_ID_MASK; + + // If there is a dialog active get the dialog id instead + if (g_windowManager.HasModalDialog()) + iWin = g_windowManager.GetTopMostModalDialogID() & WINDOW_ID_MASK; + + // If the window is FullScreenVideo check for special cases + if (iWin == WINDOW_FULLSCREEN_VIDEO) + { + // check if we're in a DVD menu + if(g_application.m_pPlayer->IsInMenu()) + iWin = WINDOW_VIDEO_MENU; + // check for LiveTV and switch to it's virtual window + else if (g_PVRManager.IsStarted() && g_application.CurrentFileItem().HasPVRChannelInfoTag()) + iWin = WINDOW_FULLSCREEN_LIVETV; + } + // special casing for PVR radio + if (iWin == WINDOW_VISUALISATION && g_PVRManager.IsStarted() && g_application.CurrentFileItem().HasPVRChannelInfoTag()) + iWin = WINDOW_FULLSCREEN_RADIO; + + // Return the window id + return iWin; +} + +bool CApplication::Cleanup() +{ + try + { + g_windowManager.Delete(WINDOW_MUSIC_PLAYLIST); + g_windowManager.Delete(WINDOW_MUSIC_PLAYLIST_EDITOR); + g_windowManager.Delete(WINDOW_MUSIC_FILES); + g_windowManager.Delete(WINDOW_MUSIC_NAV); + g_windowManager.Delete(WINDOW_DIALOG_MUSIC_INFO); + g_windowManager.Delete(WINDOW_DIALOG_VIDEO_INFO); + g_windowManager.Delete(WINDOW_VIDEO_FILES); + g_windowManager.Delete(WINDOW_VIDEO_PLAYLIST); + g_windowManager.Delete(WINDOW_VIDEO_NAV); + g_windowManager.Delete(WINDOW_FILES); + g_windowManager.Delete(WINDOW_DIALOG_YES_NO); + g_windowManager.Delete(WINDOW_DIALOG_PROGRESS); + g_windowManager.Delete(WINDOW_DIALOG_NUMERIC); + g_windowManager.Delete(WINDOW_DIALOG_GAMEPAD); + g_windowManager.Delete(WINDOW_DIALOG_SUB_MENU); + g_windowManager.Delete(WINDOW_DIALOG_BUTTON_MENU); + g_windowManager.Delete(WINDOW_DIALOG_CONTEXT_MENU); + g_windowManager.Delete(WINDOW_DIALOG_PLAYER_CONTROLS); + g_windowManager.Delete(WINDOW_DIALOG_KARAOKE_SONGSELECT); + g_windowManager.Delete(WINDOW_DIALOG_KARAOKE_SELECTOR); + g_windowManager.Delete(WINDOW_DIALOG_MUSIC_OSD); + g_windowManager.Delete(WINDOW_DIALOG_VIS_PRESET_LIST); + g_windowManager.Delete(WINDOW_DIALOG_SELECT); + g_windowManager.Delete(WINDOW_DIALOG_OK); + g_windowManager.Delete(WINDOW_DIALOG_FILESTACKING); + g_windowManager.Delete(WINDOW_DIALOG_KEYBOARD); + g_windowManager.Delete(WINDOW_FULLSCREEN_VIDEO); + g_windowManager.Delete(WINDOW_DIALOG_PROFILE_SETTINGS); + g_windowManager.Delete(WINDOW_DIALOG_LOCK_SETTINGS); + g_windowManager.Delete(WINDOW_DIALOG_NETWORK_SETUP); + g_windowManager.Delete(WINDOW_DIALOG_MEDIA_SOURCE); + g_windowManager.Delete(WINDOW_DIALOG_VIDEO_OSD_SETTINGS); + g_windowManager.Delete(WINDOW_DIALOG_AUDIO_OSD_SETTINGS); + g_windowManager.Delete(WINDOW_DIALOG_VIDEO_BOOKMARKS); + g_windowManager.Delete(WINDOW_DIALOG_CONTENT_SETTINGS); + g_windowManager.Delete(WINDOW_DIALOG_FAVOURITES); + g_windowManager.Delete(WINDOW_DIALOG_SONG_INFO); + g_windowManager.Delete(WINDOW_DIALOG_SMART_PLAYLIST_EDITOR); + g_windowManager.Delete(WINDOW_DIALOG_SMART_PLAYLIST_RULE); + g_windowManager.Delete(WINDOW_DIALOG_BUSY); + g_windowManager.Delete(WINDOW_DIALOG_PICTURE_INFO); + g_windowManager.Delete(WINDOW_DIALOG_ADDON_INFO); + g_windowManager.Delete(WINDOW_DIALOG_ADDON_SETTINGS); + g_windowManager.Delete(WINDOW_DIALOG_ACCESS_POINTS); + g_windowManager.Delete(WINDOW_DIALOG_SLIDER); + g_windowManager.Delete(WINDOW_DIALOG_MEDIA_FILTER); + g_windowManager.Delete(WINDOW_DIALOG_SUBTITLES); + + /* Delete PVR related windows and dialogs */ + g_windowManager.Delete(WINDOW_TV_CHANNELS); + g_windowManager.Delete(WINDOW_TV_RECORDINGS); + g_windowManager.Delete(WINDOW_TV_GUIDE); + g_windowManager.Delete(WINDOW_TV_TIMERS); + g_windowManager.Delete(WINDOW_TV_SEARCH); + g_windowManager.Delete(WINDOW_RADIO_CHANNELS); + g_windowManager.Delete(WINDOW_RADIO_RECORDINGS); + g_windowManager.Delete(WINDOW_RADIO_GUIDE); + g_windowManager.Delete(WINDOW_RADIO_TIMERS); + g_windowManager.Delete(WINDOW_RADIO_SEARCH); + g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_INFO); + g_windowManager.Delete(WINDOW_DIALOG_PVR_RECORDING_INFO); + g_windowManager.Delete(WINDOW_DIALOG_PVR_TIMER_SETTING); + g_windowManager.Delete(WINDOW_DIALOG_PVR_GROUP_MANAGER); + g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_MANAGER); + g_windowManager.Delete(WINDOW_DIALOG_PVR_GUIDE_SEARCH); + g_windowManager.Delete(WINDOW_DIALOG_PVR_CHANNEL_SCAN); + g_windowManager.Delete(WINDOW_DIALOG_PVR_UPDATE_PROGRESS); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CHANNELS); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_GUIDE); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_DIRECTOR); + g_windowManager.Delete(WINDOW_DIALOG_PVR_OSD_CUTTER); + g_windowManager.Delete(WINDOW_DIALOG_OSD_TELETEXT); + + g_windowManager.Delete(WINDOW_DIALOG_TEXT_VIEWER); + g_windowManager.Delete(WINDOW_DIALOG_PLAY_EJECT); + g_windowManager.Delete(WINDOW_STARTUP_ANIM); + g_windowManager.Delete(WINDOW_LOGIN_SCREEN); + g_windowManager.Delete(WINDOW_VISUALISATION); + g_windowManager.Delete(WINDOW_KARAOKELYRICS); + g_windowManager.Delete(WINDOW_SETTINGS_MENU); + g_windowManager.Delete(WINDOW_SETTINGS_PROFILES); + g_windowManager.Delete(WINDOW_SETTINGS_MYPICTURES); // all the settings categories + g_windowManager.Delete(WINDOW_TEST_PATTERN); + g_windowManager.Delete(WINDOW_SCREEN_CALIBRATION); + g_windowManager.Delete(WINDOW_SYSTEM_INFORMATION); + g_windowManager.Delete(WINDOW_SCREENSAVER); + g_windowManager.Delete(WINDOW_DIALOG_VIDEO_OSD); + g_windowManager.Delete(WINDOW_DIALOG_MUSIC_OVERLAY); + g_windowManager.Delete(WINDOW_DIALOG_VIDEO_OVERLAY); + g_windowManager.Delete(WINDOW_SLIDESHOW); + g_windowManager.Delete(WINDOW_ADDON_BROWSER); + g_windowManager.Delete(WINDOW_SKIN_SETTINGS); + + g_windowManager.Delete(WINDOW_HOME); + g_windowManager.Delete(WINDOW_PROGRAMS); + g_windowManager.Delete(WINDOW_PICTURES); + g_windowManager.Delete(WINDOW_WEATHER); + + g_windowManager.Delete(WINDOW_SETTINGS_MYPICTURES); + g_windowManager.Remove(WINDOW_SETTINGS_MYPROGRAMS); + g_windowManager.Remove(WINDOW_SETTINGS_MYWEATHER); + g_windowManager.Remove(WINDOW_SETTINGS_MYMUSIC); + g_windowManager.Remove(WINDOW_SETTINGS_SYSTEM); + g_windowManager.Remove(WINDOW_SETTINGS_MYVIDEOS); + g_windowManager.Remove(WINDOW_SETTINGS_SERVICE); + g_windowManager.Remove(WINDOW_SETTINGS_APPEARANCE); + g_windowManager.Remove(WINDOW_SETTINGS_MYPVR); + g_windowManager.Remove(WINDOW_DIALOG_KAI_TOAST); + + g_windowManager.Remove(WINDOW_DIALOG_SEEK_BAR); + g_windowManager.Remove(WINDOW_DIALOG_VOLUME_BAR); + + CAddonMgr::Get().DeInit(); + +#if defined(HAS_LIRC) || defined(HAS_IRSERVERSUITE) + CLog::Log(LOGNOTICE, "closing down remote control service"); + g_RemoteControl.Disconnect(); +#endif + + CLog::Log(LOGNOTICE, "unload sections"); + +#ifdef HAS_PERFORMANCE_SAMPLE + CLog::Log(LOGNOTICE, "performance statistics"); + m_perfStats.DumpStats(); +#endif + + // Shutdown as much as possible of the + // application, to reduce the leaks dumped + // to the vc output window before calling + // _CrtDumpMemoryLeaks(). Most of the leaks + // shown are no real leaks, as parts of the app + // are still allocated. + + g_localizeStrings.Clear(); + g_LangCodeExpander.Clear(); + g_charsetConverter.clear(); + g_directoryCache.Clear(); + CButtonTranslator::GetInstance().Clear(); +#ifdef HAS_EVENT_SERVER + CEventServer::RemoveInstance(); +#endif + DllLoaderContainer::Clear(); + g_playlistPlayer.Clear(); + CSettings::Get().Uninitialize(); + g_advancedSettings.Clear(); + +#ifdef TARGET_POSIX + CXHandle::DumpObjectTracker(); + +#ifdef HAS_DVD_DRIVE + CLibcdio::ReleaseInstance(); +#endif +#endif +#if defined(TARGET_ANDROID) + // enable for all platforms once it's safe + g_sectionLoader.UnloadAll(); +#endif +#ifdef _CRTDBG_MAP_ALLOC + _CrtDumpMemoryLeaks(); + while(1); // execution ends +#endif + + delete m_network; + m_network = NULL; + + return true; + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in CApplication::Cleanup()"); + return false; + } +} + +void CApplication::Stop(int exitCode) +{ + try + { + CVariant vExitCode(CVariant::VariantTypeObject); + vExitCode["exitcode"] = exitCode; + CAnnouncementManager::Get().Announce(System, "xbmc", "OnQuit", vExitCode); + + SaveFileState(true); + + g_alarmClock.StopThread(); + + if( m_bSystemScreenSaverEnable ) + g_Windowing.EnableSystemScreenSaver(true); + + CLog::Log(LOGNOTICE, "Storing total System Uptime"); + g_sysinfo.SetTotalUptime(g_sysinfo.GetTotalUptime() + (int)(CTimeUtils::GetFrameTime() / 60000)); + + // Update the settings information (volume, uptime etc. need saving) + if (CFile::Exists(CProfilesManager::Get().GetSettingsFile())) + { + CLog::Log(LOGNOTICE, "Saving settings"); + CSettings::Get().Save(); + } + else + CLog::Log(LOGNOTICE, "Not saving settings (settings.xml is not present)"); + + m_bStop = true; + m_AppFocused = false; + m_ExitCode = exitCode; + CLog::Log(LOGNOTICE, "stop all"); + + // cancel any jobs from the jobmanager + CJobManager::GetInstance().CancelJobs(); + + // stop scanning before we kill the network and so on + if (m_musicInfoScanner->IsScanning()) + m_musicInfoScanner->Stop(); + + if (m_videoInfoScanner->IsScanning()) + m_videoInfoScanner->Stop(); + + CApplicationMessenger::Get().Cleanup(); + + CLog::Log(LOGNOTICE, "stop player"); + m_pPlayer->ClosePlayer(); + + CAnnouncementManager::Get().Deinitialize(); + + StopPVRManager(); + StopServices(); + //Sleep(5000); + +#if HAS_FILESYTEM_DAAP + CLog::Log(LOGNOTICE, "stop daap clients"); + g_DaapClient.Release(); +#endif +#ifdef HAS_FILESYSTEM_SAP + CLog::Log(LOGNOTICE, "stop sap announcement listener"); + g_sapsessions.StopThread(); +#endif +#ifdef HAS_ZEROCONF + if(CZeroconfBrowser::IsInstantiated()) + { + CLog::Log(LOGNOTICE, "stop zeroconf browser"); + CZeroconfBrowser::GetInstance()->Stop(); + CZeroconfBrowser::ReleaseInstance(); + } +#endif + + CLog::Log(LOGNOTICE, "clean cached files!"); +#ifdef HAS_FILESYSTEM_RAR + g_RarManager.ClearCache(true); +#endif + +#ifdef HAS_FILESYSTEM_SFTP + CSFTPSessionManager::DisconnectAllSessions(); +#endif + +#if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB) + smb.Deinit(); +#endif + + CLog::Log(LOGNOTICE, "unload skin"); + UnloadSkin(); + +#if defined(TARGET_DARWIN_OSX) + if (XBMCHelper::GetInstance().IsAlwaysOn() == false) + XBMCHelper::GetInstance().Stop(); +#endif + + g_mediaManager.Stop(); + + // Stop services before unloading Python + CAddonMgr::Get().StopServices(false); + + // stop all remaining scripts; must be done after skin has been unloaded, + // not before some windows still need it when deinitializing during skin + // unloading + CScriptInvocationManager::Get().Uninitialize(); + + g_Windowing.DestroyRenderSystem(); + g_Windowing.DestroyWindow(); + g_Windowing.DestroyWindowSystem(); + + // shutdown the AudioEngine + CAEFactory::Shutdown(); + CAEFactory::UnLoadEngine(); + + // unregister ffmpeg lock manager call back + av_lockmgr_register(NULL); + + CLog::Log(LOGNOTICE, "stopped"); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in CApplication::Stop()"); + } + + // we may not get to finish the run cycle but exit immediately after a call to g_application.Stop() + // so we may never get to Destroy() in CXBApplicationEx::Run(), we call it here. + Destroy(); + cleanup_emu_environ(); + + Sleep(200); +} + +bool CApplication::PlayMedia(const CFileItem& item, int iPlaylist) +{ + //If item is a plugin, expand out now and run ourselves again + if (item.IsPlugin()) + { + CFileItem item_new(item); + if (XFILE::CPluginDirectory::GetPluginResult(item.GetPath(), item_new)) + return PlayMedia(item_new, iPlaylist); + return false; + } + if (item.IsSmartPlayList()) + { + CFileItemList items; + CUtil::GetRecursiveListing(item.GetPath(), items, "", DIR_FLAG_NO_FILE_DIRS); + if (items.Size()) + { + CSmartPlaylist smartpl; + //get name and type of smartplaylist, this will always succeed as GetDirectory also did this. + smartpl.OpenAndReadName(item.GetURL()); + CPlayList playlist; + playlist.Add(items); + return ProcessAndStartPlaylist(smartpl.GetName(), playlist, (smartpl.GetType() == "songs" || smartpl.GetType() == "albums") ? PLAYLIST_MUSIC:PLAYLIST_VIDEO); + } + } + else if (item.IsPlayList() || item.IsInternetStream()) + { + CGUIDialogCache* dlgCache = new CGUIDialogCache(5000, g_localizeStrings.Get(10214), item.GetLabel()); + + //is or could be a playlist + auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(item)); + bool gotPlayList = (pPlayList.get() && pPlayList->Load(item.GetPath())); + + if (dlgCache) + { + dlgCache->Close(); + if (dlgCache->IsCanceled()) + return true; + } + + if (gotPlayList) + { + + if (iPlaylist != PLAYLIST_NONE) + { + int track=0; + if (item.HasProperty("playlist_starting_track")) + track = (int)item.GetProperty("playlist_starting_track").asInteger(); + return ProcessAndStartPlaylist(item.GetPath(), *pPlayList, iPlaylist, track); + } + else + { + CLog::Log(LOGWARNING, "CApplication::PlayMedia called to play a playlist %s but no idea which playlist to use, playing first item", item.GetPath().c_str()); + if(pPlayList->size()) + return PlayFile(*(*pPlayList)[0], false) == PLAYBACK_OK; + } + } + } + else if (item.IsPVR()) + { + return g_PVRManager.PlayMedia(item); + } + + //nothing special just play + return PlayFile(item, false) == PLAYBACK_OK; +} + +// PlayStack() +// For playing a multi-file video. Particularly inefficient +// on startup, as we are required to calculate the length +// of each video, so we open + close each one in turn. +// A faster calculation of video time would improve this +// substantially. +// return value: same with PlayFile() +PlayBackRet CApplication::PlayStack(const CFileItem& item, bool bRestart) +{ + if (!item.IsStack()) + return PLAYBACK_FAIL; + + CVideoDatabase dbs; + + // case 1: stacked ISOs + if (CFileItem(CStackDirectory::GetFirstStackedFile(item.GetPath()),false).IsDiscImage()) + { + CStackDirectory dir; + CFileItemList movieList; + dir.GetDirectory(item.GetURL(), movieList); + + // first assume values passed to the stack + int selectedFile = item.m_lStartPartNumber; + int startoffset = item.m_lStartOffset; + + // check if we instructed the stack to resume from default + if (startoffset == STARTOFFSET_RESUME) // selected file is not specified, pick the 'last' resume point + { + if (dbs.Open()) + { + CBookmark bookmark; + CStdString path = item.GetPath(); + if (item.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString())) + path = item.GetProperty("original_listitem_url").asString(); + if( dbs.GetResumeBookMark(path, bookmark) ) + { + startoffset = (int)(bookmark.timeInSeconds*75); + selectedFile = bookmark.partNumber; + } + dbs.Close(); + } + else + CLog::LogF(LOGERROR, "Cannot open VideoDatabase"); + } + + // make sure that the selected part is within the boundaries + if (selectedFile <= 0) + { + CLog::LogF(LOGWARNING, "Selected part %d out of range, playing part 1", selectedFile); + selectedFile = 1; + } + else if (selectedFile > movieList.Size()) + { + CLog::LogF(LOGWARNING, "Selected part %d out of range, playing part %d", selectedFile, movieList.Size()); + selectedFile = movieList.Size(); + } + + // set startoffset in movieitem, track stack item for updating purposes, and finally play disc part + movieList[selectedFile - 1]->m_lStartOffset = startoffset > 0 ? STARTOFFSET_RESUME : 0; + movieList[selectedFile - 1]->SetProperty("stackFileItemToUpdate", true); + *m_stackFileItemToUpdate = item; + return PlayFile(*(movieList[selectedFile - 1])); + } + // case 2: all other stacks + else + { + LoadVideoSettings(item.GetPath()); + + // see if we have the info in the database + // TODO: If user changes the time speed (FPS via framerate conversion stuff) + // then these times will be wrong. + // Also, this is really just a hack for the slow load up times we have + // A much better solution is a fast reader of FPS and fileLength + // that we can use on a file to get it's time. + vector<int> times; + bool haveTimes(false); + CVideoDatabase dbs; + if (dbs.Open()) + { + haveTimes = dbs.GetStackTimes(item.GetPath(), times); + dbs.Close(); + } + + + // calculate the total time of the stack + CStackDirectory dir; + dir.GetDirectory(item.GetURL(), *m_currentStack); + long totalTime = 0; + for (int i = 0; i < m_currentStack->Size(); i++) + { + if (haveTimes) + (*m_currentStack)[i]->m_lEndOffset = times[i]; + else + { + int duration; + if (!CDVDFileInfo::GetFileDuration((*m_currentStack)[i]->GetPath(), duration)) + { + m_currentStack->Clear(); + return PLAYBACK_FAIL; + } + totalTime += duration / 1000; + (*m_currentStack)[i]->m_lEndOffset = totalTime; + times.push_back(totalTime); + } + } + + double seconds = item.m_lStartOffset / 75.0; + + if (!haveTimes || item.m_lStartOffset == STARTOFFSET_RESUME ) + { // have our times now, so update the dB + if (dbs.Open()) + { + if( !haveTimes ) + dbs.SetStackTimes(item.GetPath(), times); + + if( item.m_lStartOffset == STARTOFFSET_RESUME ) + { + // can only resume seek here, not dvdstate + CBookmark bookmark; + CStdString path = item.GetPath(); + if (item.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString())) + path = item.GetProperty("original_listitem_url").asString(); + if( dbs.GetResumeBookMark(path, bookmark) ) + seconds = bookmark.timeInSeconds; + else + seconds = 0.0f; + } + dbs.Close(); + } + } + + *m_itemCurrentFile = item; + m_currentStackPosition = 0; + m_pPlayer->ResetPlayer(); // must be reset on initial play otherwise last player will be used + + if (seconds > 0) + { + // work out where to seek to + for (int i = 0; i < m_currentStack->Size(); i++) + { + if (seconds < (*m_currentStack)[i]->m_lEndOffset) + { + CFileItem item(*(*m_currentStack)[i]); + long start = (i > 0) ? (*m_currentStack)[i-1]->m_lEndOffset : 0; + item.m_lStartOffset = (long)(seconds - start) * 75; + m_currentStackPosition = i; + return PlayFile(item, true); + } + } + } + + return PlayFile(*(*m_currentStack)[0], true); + } + return PLAYBACK_FAIL; +} + +PlayBackRet CApplication::PlayFile(const CFileItem& item, bool bRestart) +{ + // Ensure the MIME type has been retrieved for http:// and shout:// streams + if (item.GetMimeType().empty()) + const_cast<CFileItem&>(item).FillInMimeType(); + + if (!bRestart) + { + SaveFileState(true); + + // Switch to default options + CMediaSettings::Get().GetCurrentVideoSettings() = CMediaSettings::Get().GetDefaultVideoSettings(); + // see if we have saved options in the database + + m_pPlayer->SetPlaySpeed(1, g_application.m_muted); + m_pPlayer->m_iPlaySpeed = 1; // Reset both CApp's & Player's speed else we'll get confused + + *m_itemCurrentFile = item; + m_nextPlaylistItem = -1; + m_currentStackPosition = 0; + m_currentStack->Clear(); + + if (item.IsVideo()) + CUtil::ClearSubtitles(); + } + + if (item.IsDiscStub()) + { +#ifdef HAS_DVD_DRIVE + // Display the Play Eject dialog if there is any optical disc drive + if (g_mediaManager.HasOpticalDrive()) + { + if (CGUIDialogPlayEject::ShowAndGetInput(item)) + // PlayDiscAskResume takes path to disc. No parameter means default DVD drive. + // Can't do better as CGUIDialogPlayEject calls CMediaManager::IsDiscInDrive, which assumes default DVD drive anyway + return MEDIA_DETECT::CAutorun::PlayDiscAskResume() ? PLAYBACK_OK : PLAYBACK_FAIL; + } + else +#endif + CGUIDialogOK::ShowAndGetInput(435, 0, 436, 0); + + return PLAYBACK_OK; + } + + if (item.IsPlayList()) + return PLAYBACK_FAIL; + + if (item.IsPlugin()) + { // we modify the item so that it becomes a real URL + CFileItem item_new(item); + if (XFILE::CPluginDirectory::GetPluginResult(item.GetPath(), item_new)) + return PlayFile(item_new, false); + return PLAYBACK_FAIL; + } + +#ifdef HAS_UPNP + if (URIUtils::IsUPnP(item.GetPath())) + { + CFileItem item_new(item); + if (XFILE::CUPnPDirectory::GetResource(item.GetURL(), item_new)) + return PlayFile(item_new, false); + return PLAYBACK_FAIL; + } +#endif + + // if we have a stacked set of files, we need to setup our stack routines for + // "seamless" seeking and total time of the movie etc. + // will recall with restart set to true + if (item.IsStack()) + return PlayStack(item, bRestart); + + //Is TuxBox, this should probably be moved to CTuxBoxFile + if(item.IsTuxBox()) + { + CLog::LogF(LOGDEBUG, "TuxBox URL Detected %s",item.GetPath().c_str()); + + if(g_tuxboxService.IsRunning()) + g_tuxboxService.Stop(); + + PlayBackRet ret = PLAYBACK_FAIL; + CFileItem item_new; + if(g_tuxbox.CreateNewItem(item, item_new)) + { + + // Make sure it doesn't have a player + // so we actually select one normally + m_pPlayer->ResetPlayer(); + + // keep the tuxbox:// url as playing url + // and give the new url to the player + ret = PlayFile(item_new, true); + if(ret == PLAYBACK_OK) + { + if(!g_tuxboxService.IsRunning()) + g_tuxboxService.Start(); + } + } + return ret; + } + + CPlayerOptions options; + + if( item.HasProperty("StartPercent") ) + { + double fallback = 0.0f; + if(item.GetProperty("StartPercent").isString()) + fallback = (double)atof(item.GetProperty("StartPercent").asString().c_str()); + options.startpercent = item.GetProperty("StartPercent").asDouble(fallback); + } + + PLAYERCOREID eNewCore = EPC_NONE; + if( bRestart ) + { + // have to be set here due to playstack using this for starting the file + options.starttime = item.m_lStartOffset / 75.0; + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0 && m_itemCurrentFile->m_lStartOffset != 0) + m_itemCurrentFile->m_lStartOffset = STARTOFFSET_RESUME; // to force fullscreen switching + + if( m_eForcedNextPlayer != EPC_NONE ) + eNewCore = m_eForcedNextPlayer; + else if( m_pPlayer->GetCurrentPlayer() == EPC_NONE ) + eNewCore = CPlayerCoreFactory::Get().GetDefaultPlayer(item); + else + eNewCore = m_pPlayer->GetCurrentPlayer(); + } + else + { + options.starttime = item.m_lStartOffset / 75.0; + LoadVideoSettings(item.GetPath()); + + if (item.IsVideo()) + { + // open the d/b and retrieve the bookmarks for the current movie + CVideoDatabase dbs; + dbs.Open(); + + if( item.m_lStartOffset == STARTOFFSET_RESUME ) + { + options.starttime = 0.0f; + CBookmark bookmark; + CStdString path = item.GetPath(); + if (item.HasVideoInfoTag() && StringUtils::StartsWith(item.GetVideoInfoTag()->m_strFileNameAndPath, "removable://")) + path = item.GetVideoInfoTag()->m_strFileNameAndPath; + else if (item.HasProperty("original_listitem_url") && URIUtils::IsPlugin(item.GetProperty("original_listitem_url").asString())) + path = item.GetProperty("original_listitem_url").asString(); + if(dbs.GetResumeBookMark(path, bookmark)) + { + options.starttime = bookmark.timeInSeconds; + options.state = bookmark.playerState; + } + /* + override with information from the actual item if available. We do this as the VFS (eg plugins) + may set the resume point to override whatever XBMC has stored, yet we ignore it until now so that, + should the playerState be required, it is fetched from the database. + See the note in CGUIWindowVideoBase::ShowResumeMenu. + */ + if (item.IsResumePointSet()) + options.starttime = item.GetCurrentResumeTime(); + } + else if (item.HasVideoInfoTag()) + { + const CVideoInfoTag *tag = item.GetVideoInfoTag(); + + if (tag->m_iBookmarkId != -1 && tag->m_iBookmarkId != 0) + { + CBookmark bookmark; + dbs.GetBookMarkForEpisode(*tag, bookmark); + options.starttime = bookmark.timeInSeconds; + options.state = bookmark.playerState; + } + } + + dbs.Close(); + } + + if (m_eForcedNextPlayer != EPC_NONE) + eNewCore = m_eForcedNextPlayer; + else + eNewCore = CPlayerCoreFactory::Get().GetDefaultPlayer(item); + } + + // this really aught to be inside !bRestart, but since PlayStack + // uses that to init playback, we have to keep it outside + int playlist = g_playlistPlayer.GetCurrentPlaylist(); + if (item.IsVideo() && playlist == PLAYLIST_VIDEO && g_playlistPlayer.GetPlaylist(playlist).size() > 1) + { // playing from a playlist by the looks + // don't switch to fullscreen if we are not playing the first item... + options.fullscreen = !g_playlistPlayer.HasPlayedFirstFile() && g_advancedSettings.m_fullScreenOnMovieStart && !CMediaSettings::Get().DoesVideoStartWindowed(); + } + else if(m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + { + // TODO - this will fail if user seeks back to first file in stack + if(m_currentStackPosition == 0 || m_itemCurrentFile->m_lStartOffset == STARTOFFSET_RESUME) + options.fullscreen = g_advancedSettings.m_fullScreenOnMovieStart && !CMediaSettings::Get().DoesVideoStartWindowed(); + else + options.fullscreen = false; + // reset this so we don't think we are resuming on seek + m_itemCurrentFile->m_lStartOffset = 0; + } + else + options.fullscreen = g_advancedSettings.m_fullScreenOnMovieStart && !CMediaSettings::Get().DoesVideoStartWindowed(); + + // reset VideoStartWindowed as it's a temp setting + CMediaSettings::Get().SetVideoStartWindowed(false); + +#ifdef HAS_KARAOKE + //We have to stop parsing a cdg before mplayer is deallocated + // WHY do we have to do this???? + if (m_pKaraokeMgr) + m_pKaraokeMgr->Stop(); +#endif + + { + CSingleLock lock(m_playStateMutex); + // tell system we are starting a file + m_bPlaybackStarting = true; + + // for playing a new item, previous playing item's callback may already + // pushed some delay message into the threadmessage list, they are not + // expected be processed after or during the new item playback starting. + // so we clean up previous playing item's playback callback delay messages here. + int previousMsgsIgnoredByNewPlaying[] = { + GUI_MSG_PLAYBACK_STARTED, + GUI_MSG_PLAYBACK_ENDED, + GUI_MSG_PLAYBACK_STOPPED, + GUI_MSG_PLAYLIST_CHANGED, + GUI_MSG_PLAYLISTPLAYER_STOPPED, + GUI_MSG_PLAYLISTPLAYER_STARTED, + GUI_MSG_PLAYLISTPLAYER_CHANGED, + GUI_MSG_QUEUE_NEXT_ITEM, + 0 + }; + int dMsgCount = g_windowManager.RemoveThreadMessageByMessageIds(&previousMsgsIgnoredByNewPlaying[0]); + if (dMsgCount > 0) + CLog::LogF(LOGDEBUG,"Ignored %d playback thread messages", dMsgCount); + } + + // We should restart the player, unless the previous and next tracks are using + // one of the players that allows gapless playback (paplayer, dvdplayer) + m_pPlayer->ClosePlayerGapless(eNewCore); + + // now reset play state to starting, since we already stopped the previous playing item if there is. + // and from now there should be no playback callback from previous playing item be called. + m_ePlayState = PLAY_STATE_STARTING; + + m_pPlayer->CreatePlayer(eNewCore, *this); + + PlayBackRet iResult; + if (m_pPlayer->HasPlayer()) + { + /* When playing video pause any low priority jobs, they will be unpaused when playback stops. + * This should speed up player startup for files on internet filesystems (eg. webdav) and + * increase performance on low powered systems (Atom/ARM). + */ + if (item.IsVideo()) + { + CJobManager::GetInstance().PauseJobs(); + } + + // don't hold graphicscontext here since player + // may wait on another thread, that requires gfx + CSingleExit ex(g_graphicsContext); + + iResult = m_pPlayer->OpenFile(item, options); + } + else + { + CLog::Log(LOGERROR, "Error creating player for item %s (File doesn't exist?)", item.GetPath().c_str()); + iResult = PLAYBACK_FAIL; + } + + if(iResult == PLAYBACK_OK) + { + if (m_pPlayer->GetPlaySpeed() != 1) + { + int iSpeed = m_pPlayer->GetPlaySpeed(); + m_pPlayer->m_iPlaySpeed = 1; + m_pPlayer->SetPlaySpeed(iSpeed, g_application.m_muted); + } + + // if player has volume control, set it. + if (m_pPlayer->ControlsVolume()) + { + m_pPlayer->SetVolume(m_volumeLevel); + m_pPlayer->SetMute(m_muted); + } + + if( m_pPlayer->IsPlayingAudio() ) + { + if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) + g_windowManager.ActivateWindow(WINDOW_VISUALISATION); + } + +#ifdef HAS_VIDEO_PLAYBACK + else if( m_pPlayer->IsPlayingVideo() ) + { + if (g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION) + g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO); + + // if player didn't manange to switch to fullscreen by itself do it here + if( options.fullscreen && g_renderManager.IsStarted() + && g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO ) + SwitchToFullScreen(); + } +#endif + else + { + if (g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION + || g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) + g_windowManager.PreviousWindow(); + + } + +#if !defined(TARGET_POSIX) + g_audioManager.Enable(false); +#endif + + if (item.HasPVRChannelInfoTag()) + g_playlistPlayer.SetCurrentPlaylist(PLAYLIST_NONE); + } + + CSingleLock lock(m_playStateMutex); + m_bPlaybackStarting = false; + + if (iResult == PLAYBACK_OK) + { + // play state: none, starting; playing; stopped; ended. + // last 3 states are set by playback callback, they are all ignored during starting, + // but we recorded the state, here we can make up the callback for the state. + CLog::LogF(LOGDEBUG,"OpenFile succeed, play state %d", m_ePlayState); + switch (m_ePlayState) + { + case PLAY_STATE_PLAYING: + OnPlayBackStarted(); + break; + // FIXME: it seems no meaning to callback started here if there was an started callback + // before this stopped/ended callback we recorded. if we callback started here + // first, it will delay send OnPlay announce, but then we callback stopped/ended + // which will send OnStop announce at once, so currently, just call stopped/ended. + case PLAY_STATE_ENDED: + OnPlayBackEnded(); + break; + case PLAY_STATE_STOPPED: + OnPlayBackStopped(); + break; + case PLAY_STATE_STARTING: + // neither started nor stopped/ended callback be called, that means the item still + // not started, we need not make up any callback, just leave this and + // let the player callback do its work. + break; + default: + break; + } + } + else if (iResult == PLAYBACK_FAIL) + { + // we send this if it isn't playlistplayer that is doing this + int next = g_playlistPlayer.GetNextSong(); + int size = g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()).size(); + if(next < 0 + || next >= size) + OnPlayBackStopped(); + m_ePlayState = PLAY_STATE_NONE; + } + + return iResult; +} + +void CApplication::OnPlayBackEnded() +{ + CSingleLock lock(m_playStateMutex); + CLog::LogF(LOGDEBUG,"play state was %d, starting %d", m_ePlayState, m_bPlaybackStarting); + m_ePlayState = PLAY_STATE_ENDED; + if(m_bPlaybackStarting) + return; + + // informs python script currently running playback has ended + // (does nothing if python is not loaded) +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackEnded(); +#endif + + CVariant data(CVariant::VariantTypeObject); + data["end"] = true; + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnStop", m_itemCurrentFile, data); + + CGUIMessage msg(GUI_MSG_PLAYBACK_ENDED, 0, 0); + g_windowManager.SendThreadMessage(msg); +} + +void CApplication::OnPlayBackStarted() +{ + CSingleLock lock(m_playStateMutex); + CLog::LogF(LOGDEBUG,"play state was %d, starting %d", m_ePlayState, m_bPlaybackStarting); + m_ePlayState = PLAY_STATE_PLAYING; + if(m_bPlaybackStarting) + return; + +#ifdef HAS_PYTHON + // informs python script currently running playback has started + // (does nothing if python is not loaded) + g_pythonParser.OnPlayBackStarted(); +#endif + + CGUIMessage msg(GUI_MSG_PLAYBACK_STARTED, 0, 0); + g_windowManager.SendThreadMessage(msg); +} + +void CApplication::OnQueueNextItem() +{ + CSingleLock lock(m_playStateMutex); + CLog::LogF(LOGDEBUG,"play state was %d, starting %d", m_ePlayState, m_bPlaybackStarting); + if(m_bPlaybackStarting) + return; + // informs python script currently running that we are requesting the next track + // (does nothing if python is not loaded) +#ifdef HAS_PYTHON + g_pythonParser.OnQueueNextItem(); // currently unimplemented +#endif + + CGUIMessage msg(GUI_MSG_QUEUE_NEXT_ITEM, 0, 0); + g_windowManager.SendThreadMessage(msg); +} + +void CApplication::OnPlayBackStopped() +{ + CSingleLock lock(m_playStateMutex); + CLog::LogF(LOGDEBUG, "play state was %d, starting %d", m_ePlayState, m_bPlaybackStarting); + m_ePlayState = PLAY_STATE_STOPPED; + if(m_bPlaybackStarting) + return; + + // informs python script currently running playback has ended + // (does nothing if python is not loaded) +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackStopped(); +#endif + + CVariant data(CVariant::VariantTypeObject); + data["end"] = false; + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnStop", m_itemCurrentFile, data); + + CGUIMessage msg( GUI_MSG_PLAYBACK_STOPPED, 0, 0 ); + g_windowManager.SendThreadMessage(msg); +} + +void CApplication::OnPlayBackPaused() +{ +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackPaused(); +#endif + + CVariant param; + param["player"]["speed"] = 0; + param["player"]["playerid"] = g_playlistPlayer.GetCurrentPlaylist(); + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnPause", m_itemCurrentFile, param); +} + +void CApplication::OnPlayBackResumed() +{ +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackResumed(); +#endif + + CVariant param; + param["player"]["speed"] = 1; + param["player"]["playerid"] = g_playlistPlayer.GetCurrentPlaylist(); + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnPlay", m_itemCurrentFile, param); +} + +void CApplication::OnPlayBackSpeedChanged(int iSpeed) +{ +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackSpeedChanged(iSpeed); +#endif + + CVariant param; + param["player"]["speed"] = iSpeed; + param["player"]["playerid"] = g_playlistPlayer.GetCurrentPlaylist(); + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnSpeedChanged", m_itemCurrentFile, param); +} + +void CApplication::OnPlayBackSeek(int iTime, int seekOffset) +{ +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackSeek(iTime, seekOffset); +#endif + + CVariant param; + CJSONUtils::MillisecondsToTimeObject(iTime, param["player"]["time"]); + CJSONUtils::MillisecondsToTimeObject(seekOffset, param["player"]["seekoffset"]);; + param["player"]["playerid"] = g_playlistPlayer.GetCurrentPlaylist(); + param["player"]["speed"] = m_pPlayer->GetPlaySpeed(); + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnSeek", m_itemCurrentFile, param); + g_infoManager.SetDisplayAfterSeek(2500, seekOffset); +} + +void CApplication::OnPlayBackSeekChapter(int iChapter) +{ +#ifdef HAS_PYTHON + g_pythonParser.OnPlayBackSeekChapter(iChapter); +#endif +} + +bool CApplication::IsPlayingFullScreenVideo() const +{ + return m_pPlayer->IsPlayingVideo() && g_graphicsContext.IsFullScreenVideo(); +} + +bool CApplication::IsFullScreen() +{ + return IsPlayingFullScreenVideo() || + (g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION) || + g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW; +} + +void CApplication::SaveFileState(bool bForeground /* = false */) +{ + if (!CProfilesManager::Get().GetCurrentProfile().canWriteDatabases()) + return; + + CJob* job = new CSaveFileStateJob(*m_progressTrackingItem, + *m_stackFileItemToUpdate, + m_progressTrackingVideoResumeBookmark, + m_progressTrackingPlayCountUpdate, + CMediaSettings::Get().GetCurrentVideoSettings()); + + if (bForeground) + { + // Run job in the foreground to make sure it finishes + job->DoWork(); + delete job; + } + else + CJobManager::GetInstance().AddJob(job, NULL, CJob::PRIORITY_NORMAL); +} + +void CApplication::UpdateFileState() +{ + // Did the file change? + if (m_progressTrackingItem->GetPath() != "" && m_progressTrackingItem->GetPath() != CurrentFile()) + { + // Ignore for PVR channels, PerformChannelSwitch takes care of this. + // Also ignore playlists as correct video settings have already been saved in PlayFile() - we're causing off-by-1 errors here. + if (!m_progressTrackingItem->IsPVRChannel() && g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_NONE) + SaveFileState(); + + // Reset tracking item + m_progressTrackingItem->Reset(); + } + else + { + if (m_pPlayer->IsPlaying()) + { + if (m_progressTrackingItem->GetPath() == "") + { + // Init some stuff + *m_progressTrackingItem = CurrentFileItem(); + m_progressTrackingPlayCountUpdate = false; + } + + if ((m_progressTrackingItem->IsAudio() && g_advancedSettings.m_audioPlayCountMinimumPercent > 0 && + GetPercentage() >= g_advancedSettings.m_audioPlayCountMinimumPercent) || + (m_progressTrackingItem->IsVideo() && g_advancedSettings.m_videoPlayCountMinimumPercent > 0 && + GetPercentage() >= g_advancedSettings.m_videoPlayCountMinimumPercent)) + { + m_progressTrackingPlayCountUpdate = true; + } + + // Check whether we're *really* playing video else we may race when getting eg. stream details + if (m_pPlayer->IsPlayingVideo()) + { + /* Always update streamdetails, except for DVDs where we only update + streamdetails if title length > 15m (Should yield more correct info) */ + if (!(m_progressTrackingItem->IsDiscImage() || m_progressTrackingItem->IsDVDFile()) || m_pPlayer->GetTotalTime() > 15*60*1000) + { + CStreamDetails details; + // Update with stream details from player, if any + if (m_pPlayer->GetStreamDetails(details)) + m_progressTrackingItem->GetVideoInfoTag()->m_streamDetails = details; + + if (m_progressTrackingItem->IsStack()) + m_progressTrackingItem->GetVideoInfoTag()->m_streamDetails.SetVideoDuration(0, (int)GetTotalTime()); // Overwrite with CApp's totaltime as it takes into account total stack time + } + + // Update bookmark for save + m_progressTrackingVideoResumeBookmark.player = CPlayerCoreFactory::Get().GetPlayerName(m_pPlayer->GetCurrentPlayer()); + m_progressTrackingVideoResumeBookmark.playerState = m_pPlayer->GetPlayerState(); + m_progressTrackingVideoResumeBookmark.thumbNailImage.clear(); + + if (g_advancedSettings.m_videoIgnorePercentAtEnd > 0 && + GetTotalTime() - GetTime() < 0.01f * g_advancedSettings.m_videoIgnorePercentAtEnd * GetTotalTime()) + { + // Delete the bookmark + m_progressTrackingVideoResumeBookmark.timeInSeconds = -1.0f; + } + else + if (GetTime() > g_advancedSettings.m_videoIgnoreSecondsAtStart) + { + // Update the bookmark + m_progressTrackingVideoResumeBookmark.timeInSeconds = GetTime(); + m_progressTrackingVideoResumeBookmark.totalTimeInSeconds = GetTotalTime(); + } + else + { + // Do nothing + m_progressTrackingVideoResumeBookmark.timeInSeconds = 0.0f; + } + } + } + } +} + +void CApplication::LoadVideoSettings(const std::string &path) +{ + CVideoDatabase dbs; + if (dbs.Open()) + { + CLog::Log(LOGDEBUG, "Loading settings for %s", path.c_str()); + + // Load stored settings if they exist, otherwise use default + if (!dbs.GetVideoSettings(path, CMediaSettings::Get().GetCurrentVideoSettings())) + CMediaSettings::Get().GetCurrentVideoSettings() = CMediaSettings::Get().GetDefaultVideoSettings(); + + dbs.Close(); + } +} + +void CApplication::StopPlaying() +{ + int iWin = g_windowManager.GetActiveWindow(); + if ( m_pPlayer->IsPlaying() ) + { +#ifdef HAS_KARAOKE + if( m_pKaraokeMgr ) + m_pKaraokeMgr->Stop(); +#endif + + m_pPlayer->CloseFile(); + + // turn off visualisation window when stopping + if ((iWin == WINDOW_VISUALISATION + || iWin == WINDOW_FULLSCREEN_VIDEO) + && !m_bStop) + g_windowManager.PreviousWindow(); + + g_partyModeManager.Disable(); + } +} + +void CApplication::ResetSystemIdleTimer() +{ + // reset system idle timer + m_idleTimer.StartZero(); +} + +void CApplication::ResetScreenSaver() +{ + // reset our timers + m_shutdownTimer.StartZero(); + + // screen saver timer is reset only if we're not already in screensaver or + // DPMS mode + if ((!m_bScreenSave && m_iScreenSaveLock == 0) && !m_dpmsIsActive) + ResetScreenSaverTimer(); +} + +void CApplication::ResetScreenSaverTimer() +{ + m_screenSaverTimer.StartZero(); +} + +void CApplication::StopScreenSaverTimer() +{ + m_screenSaverTimer.Stop(); +} + +bool CApplication::ToggleDPMS(bool manual) +{ + if (manual || (m_dpmsIsManual == manual)) + { + if (m_dpmsIsActive) + { + m_dpmsIsActive = false; + m_dpmsIsManual = false; + CAnnouncementManager::Get().Announce(GUI, "xbmc", "OnDPMSDeactivated"); + return m_dpms->DisablePowerSaving(); + } + else + { + if (m_dpms->EnablePowerSaving(m_dpms->GetSupportedModes()[0])) + { + m_dpmsIsActive = true; + m_dpmsIsManual = manual; + CAnnouncementManager::Get().Announce(GUI, "xbmc", "OnDPMSActivated"); + return true; + } + } + } + return false; +} + +bool CApplication::WakeUpScreenSaverAndDPMS(bool bPowerOffKeyPressed /* = false */) +{ + bool result; + + // First reset DPMS, if active + if (m_dpmsIsActive) + { + if (m_dpmsIsManual) + return false; + // TODO: if screensaver lock is specified but screensaver is not active + // (DPMS came first), activate screensaver now. + ToggleDPMS(false); + ResetScreenSaverTimer(); + result = !m_bScreenSave || WakeUpScreenSaver(bPowerOffKeyPressed); + } + else + result = WakeUpScreenSaver(bPowerOffKeyPressed); + + if(result) + { + // allow listeners to ignore the deactivation if it preceeds a powerdown/suspend etc + CVariant data(CVariant::VariantTypeObject); + data["shuttingdown"] = bPowerOffKeyPressed; + CAnnouncementManager::Get().Announce(GUI, "xbmc", "OnScreensaverDeactivated", data); + } + + return result; +} + +bool CApplication::WakeUpScreenSaver(bool bPowerOffKeyPressed /* = false */) +{ + if (m_iScreenSaveLock == 2) + return false; + + // if Screen saver is active + if (m_bScreenSave && m_screenSaver) + { + if (m_iScreenSaveLock == 0) + if (CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE && + (CProfilesManager::Get().UsingLoginScreen() || CSettings::Get().GetBool("masterlock.startuplock")) && + CProfilesManager::Get().GetCurrentProfile().getLockMode() != LOCK_MODE_EVERYONE && + m_screenSaver->ID() != "screensaver.xbmc.builtin.dim" && m_screenSaver->ID() != "screensaver.xbmc.builtin.black" && !m_screenSaver->ID().empty() && m_screenSaver->ID() != "visualization") + { + m_iScreenSaveLock = 2; + CGUIMessage msg(GUI_MSG_CHECK_LOCK,0,0); + + CGUIWindow* pWindow = g_windowManager.GetWindow(WINDOW_SCREENSAVER); + if (pWindow) + pWindow->OnMessage(msg); + } + if (m_iScreenSaveLock == -1) + { + m_iScreenSaveLock = 0; + return true; + } + + // disable screensaver + m_bScreenSave = false; + m_iScreenSaveLock = 0; + ResetScreenSaverTimer(); + + if (m_screenSaver->ID() == "visualization") + { + // we can just continue as usual from vis mode + return false; + } + else if (m_screenSaver->ID() == "screensaver.xbmc.builtin.dim" || m_screenSaver->ID() == "screensaver.xbmc.builtin.black" || m_screenSaver->ID().empty()) + return true; + else if (!m_screenSaver->ID().empty()) + { // we're in screensaver window + if (g_windowManager.GetActiveWindow() == WINDOW_SCREENSAVER) + g_windowManager.PreviousWindow(); // show the previous window + if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) + CApplicationMessenger::Get().SendAction(CAction(ACTION_STOP), WINDOW_SLIDESHOW); + } + return true; + } + else + return false; +} + +void CApplication::CheckScreenSaverAndDPMS() +{ + if (!m_dpmsIsActive) + g_Windowing.ResetOSScreensaver(); + + bool maybeScreensaver = + !m_dpmsIsActive && !m_bScreenSave + && !CSettings::Get().GetString("screensaver.mode").empty(); + bool maybeDPMS = + !m_dpmsIsActive && m_dpms->IsSupported() + && CSettings::Get().GetInt("powermanagement.displaysoff") > 0; + + // Has the screen saver window become active? + if (maybeScreensaver && g_windowManager.IsWindowActive(WINDOW_SCREENSAVER)) + { + m_bScreenSave = true; + maybeScreensaver = false; + } + + if (m_bScreenSave && m_pPlayer->IsPlayingVideo() && !m_pPlayer->IsPaused()) + { + WakeUpScreenSaverAndDPMS(); + return; + } + + if (!maybeScreensaver && !maybeDPMS) return; // Nothing to do. + + // See if we need to reset timer. + // * Are we playing a video and it is not paused? + if ((m_pPlayer->IsPlayingVideo() && !m_pPlayer->IsPaused()) + // * Are we playing some music in fullscreen vis? + || (m_pPlayer->IsPlayingAudio() && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION + && !CSettings::Get().GetString("musicplayer.visualisation").empty())) + { + ResetScreenSaverTimer(); + return; + } + + float elapsed = m_screenSaverTimer.IsRunning() ? m_screenSaverTimer.GetElapsedSeconds() : 0.f; + + // DPMS has priority (it makes the screensaver not needed) + if (maybeDPMS + && elapsed > CSettings::Get().GetInt("powermanagement.displaysoff") * 60) + { + ToggleDPMS(false); + WakeUpScreenSaver(); + } + else if (maybeScreensaver + && elapsed > CSettings::Get().GetInt("screensaver.time") * 60) + { + ActivateScreenSaver(); + } +} + +// activate the screensaver. +// if forceType is true, we ignore the various conditions that can alter +// the type of screensaver displayed +void CApplication::ActivateScreenSaver(bool forceType /*= false */) +{ + if (m_pPlayer->IsPlayingAudio() && CSettings::Get().GetBool("screensaver.usemusicvisinstead") && !CSettings::Get().GetString("musicplayer.visualisation").empty()) + { // just activate the visualisation if user toggled the usemusicvisinstead option + g_windowManager.ActivateWindow(WINDOW_VISUALISATION); + return; + } + + m_bScreenSave = true; + + // Get Screensaver Mode + m_screenSaver.reset(); + if (!CAddonMgr::Get().GetAddon(CSettings::Get().GetString("screensaver.mode"), m_screenSaver)) + m_screenSaver.reset(new CScreenSaver("")); + + CAnnouncementManager::Get().Announce(GUI, "xbmc", "OnScreensaverActivated"); + + // disable screensaver lock from the login screen + m_iScreenSaveLock = g_windowManager.GetActiveWindow() == WINDOW_LOGIN_SCREEN ? 1 : 0; + if (!forceType) + { + // set to Dim in the case of a dialog on screen or playing video + if (g_windowManager.HasModalDialog() || (m_pPlayer->IsPlayingVideo() && CSettings::Get().GetBool("screensaver.usedimonpause")) || g_PVRManager.IsRunningChannelScan()) + { + if (!CAddonMgr::Get().GetAddon("screensaver.xbmc.builtin.dim", m_screenSaver)) + m_screenSaver.reset(new CScreenSaver("")); + } + } + if (m_screenSaver->ID() == "screensaver.xbmc.builtin.dim" || m_screenSaver->ID().empty()) + return; + else if (m_screenSaver->ID() == "screensaver.xbmc.builtin.black") + return; + else if (!m_screenSaver->ID().empty()) + g_windowManager.ActivateWindow(WINDOW_SCREENSAVER); +} + +void CApplication::CheckShutdown() +{ + // first check if we should reset the timer + if (m_bInhibitIdleShutdown + || m_pPlayer->IsPlaying() || m_pPlayer->IsPausedPlayback() // is something playing? + || m_musicInfoScanner->IsScanning() + || m_videoInfoScanner->IsScanning() + || g_windowManager.IsWindowActive(WINDOW_DIALOG_PROGRESS) // progress dialog is onscreen + || (CSettings::Get().GetBool("pvrmanager.enabled") && !g_PVRManager.IsIdle())) + { + m_shutdownTimer.StartZero(); + return; + } + + float elapsed = m_shutdownTimer.IsRunning() ? m_shutdownTimer.GetElapsedSeconds() : 0.f; + if ( elapsed > CSettings::Get().GetInt("powermanagement.shutdowntime") * 60 ) + { + // Since it is a sleep instead of a shutdown, let's set everything to reset when we wake up. + m_shutdownTimer.Stop(); + + // Sleep the box + CApplicationMessenger::Get().Shutdown(); + } +} + +void CApplication::InhibitIdleShutdown(bool inhibit) +{ + m_bInhibitIdleShutdown = inhibit; +} + +bool CApplication::IsIdleShutdownInhibited() const +{ + return m_bInhibitIdleShutdown; +} + +bool CApplication::OnMessage(CGUIMessage& message) +{ + switch ( message.GetMessage() ) + { + case GUI_MSG_NOTIFY_ALL: + { + if (message.GetParam1()==GUI_MSG_REMOVED_MEDIA) + { + // Update general playlist: Remove DVD playlist items + int nRemoved = g_playlistPlayer.RemoveDVDItems(); + if ( nRemoved > 0 ) + { + CGUIMessage msg( GUI_MSG_PLAYLIST_CHANGED, 0, 0 ); + g_windowManager.SendMessage( msg ); + } + // stop the file if it's on dvd (will set the resume point etc) + if (m_itemCurrentFile->IsOnDVD()) + StopPlaying(); + } + } + break; + + case GUI_MSG_PLAYBACK_STARTED: + { +#ifdef TARGET_DARWIN + CDarwinUtils::SetScheduling(message.GetMessage()); +#endif + // reset the seek handler + m_seekHandler->Reset(); + CPlayList playList = g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()); + + // Update our infoManager with the new details etc. + if (m_nextPlaylistItem >= 0) + { + // playing an item which is not in the list - player might be stopped already + // so do nothing + if (playList.size() <= m_nextPlaylistItem) + return true; + + // we've started a previously queued item + CFileItemPtr item = playList[m_nextPlaylistItem]; + // update the playlist manager + int currentSong = g_playlistPlayer.GetCurrentSong(); + int param = ((currentSong & 0xffff) << 16) | (m_nextPlaylistItem & 0xffff); + CGUIMessage msg(GUI_MSG_PLAYLISTPLAYER_CHANGED, 0, 0, g_playlistPlayer.GetCurrentPlaylist(), param, item); + g_windowManager.SendThreadMessage(msg); + g_playlistPlayer.SetCurrentSong(m_nextPlaylistItem); + *m_itemCurrentFile = *item; + } + g_infoManager.SetCurrentItem(*m_itemCurrentFile); + g_partyModeManager.OnSongChange(true); + + CVariant param; + param["player"]["speed"] = 1; + param["player"]["playerid"] = g_playlistPlayer.GetCurrentPlaylist(); + CAnnouncementManager::Get().Announce(Player, "xbmc", "OnPlay", m_itemCurrentFile, param); + + if (m_pPlayer->IsPlayingAudio()) + { + // Start our cdg parser as appropriate +#ifdef HAS_KARAOKE + if (m_pKaraokeMgr && CSettings::Get().GetBool("karaoke.enabled") && !m_itemCurrentFile->IsInternetStream()) + { + m_pKaraokeMgr->Stop(); + if (m_itemCurrentFile->IsMusicDb()) + { + if (!m_itemCurrentFile->HasMusicInfoTag() || !m_itemCurrentFile->GetMusicInfoTag()->Loaded()) + { + IMusicInfoTagLoader* tagloader = CMusicInfoTagLoaderFactory::CreateLoader(m_itemCurrentFile->GetPath()); + tagloader->Load(m_itemCurrentFile->GetPath(),*m_itemCurrentFile->GetMusicInfoTag()); + delete tagloader; + } + m_pKaraokeMgr->Start(m_itemCurrentFile->GetMusicInfoTag()->GetURL()); + } + else + m_pKaraokeMgr->Start(m_itemCurrentFile->GetPath()); + } +#endif + } + + return true; + } + break; + + case GUI_MSG_QUEUE_NEXT_ITEM: + { + // Check to see if our playlist player has a new item for us, + // and if so, we check whether our current player wants the file + int iNext = g_playlistPlayer.GetNextSong(); + CPlayList& playlist = g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist()); + if (iNext < 0 || iNext >= playlist.size()) + { + m_pPlayer->OnNothingToQueueNotify(); + return true; // nothing to do + } + + // ok, grab the next song + CFileItem file(*playlist[iNext]); + // handle plugin:// + CURL url(file.GetPath()); + if (url.IsProtocol("plugin")) + XFILE::CPluginDirectory::GetPluginResult(url.Get(), file); + +#ifdef HAS_UPNP + if (URIUtils::IsUPnP(file.GetPath())) + { + if (!XFILE::CUPnPDirectory::GetResource(file.GetURL(), file)) + return true; + } +#endif + + // ok - send the file to the player, if it accepts it + if (m_pPlayer->QueueNextFile(file)) + { + // player accepted the next file + m_nextPlaylistItem = iNext; + } + else + { + /* Player didn't accept next file: *ALWAYS* advance playlist in this case so the player can + queue the next (if it wants to) and it doesn't keep looping on this song */ + g_playlistPlayer.SetCurrentSong(iNext); + } + + return true; + } + break; + + case GUI_MSG_PLAYBACK_STOPPED: + case GUI_MSG_PLAYBACK_ENDED: + case GUI_MSG_PLAYLISTPLAYER_STOPPED: + { +#ifdef HAS_KARAOKE + if (m_pKaraokeMgr ) + m_pKaraokeMgr->Stop(); +#endif +#ifdef TARGET_DARWIN + CDarwinUtils::SetScheduling(message.GetMessage()); +#endif + // first check if we still have items in the stack to play + if (message.GetMessage() == GUI_MSG_PLAYBACK_ENDED) + { + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0 && m_currentStackPosition < m_currentStack->Size() - 1) + { // just play the next item in the stack + PlayFile(*(*m_currentStack)[++m_currentStackPosition], true); + return true; + } + } + + // In case playback ended due to user eg. skipping over the end, clear + // our resume bookmark here + if (message.GetMessage() == GUI_MSG_PLAYBACK_ENDED && m_progressTrackingPlayCountUpdate && g_advancedSettings.m_videoIgnorePercentAtEnd > 0) + { + // Delete the bookmark + m_progressTrackingVideoResumeBookmark.timeInSeconds = -1.0f; + } + + // reset the current playing file + m_itemCurrentFile->Reset(); + g_infoManager.ResetCurrentItem(); + m_currentStack->Clear(); + + if (message.GetMessage() == GUI_MSG_PLAYBACK_ENDED) + { + g_playlistPlayer.PlayNext(1, true); + } + else + { + // reset any forced player + m_eForcedNextPlayer = EPC_NONE; + + m_pPlayer->ClosePlayer(); + + // Reset playspeed + m_pPlayer->m_iPlaySpeed = 1; + } + + if (!m_pPlayer->IsPlaying()) + { + g_audioManager.Enable(true); + } + + if (!m_pPlayer->IsPlayingVideo()) + { + if(g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO) + { + g_windowManager.PreviousWindow(); + } + else + { + CSingleLock lock(g_graphicsContext); + // resets to res_desktop or look&feel resolution (including refreshrate) + g_graphicsContext.SetFullScreenVideo(false); + } + } + + if (!m_pPlayer->IsPlayingAudio() && g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_NONE && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION) + { + CSettings::Get().Save(); // save vis settings + WakeUpScreenSaverAndDPMS(); + g_windowManager.PreviousWindow(); + } + + // DVD ejected while playing in vis ? + if (!m_pPlayer->IsPlayingAudio() && (m_itemCurrentFile->IsCDDA() || m_itemCurrentFile->IsOnDVD()) && !g_mediaManager.IsDiscInDrive() && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION) + { + // yes, disable vis + CSettings::Get().Save(); // save vis settings + WakeUpScreenSaverAndDPMS(); + g_windowManager.PreviousWindow(); + } + + if (IsEnableTestMode()) + CApplicationMessenger::Get().Quit(); + return true; + } + break; + + case GUI_MSG_PLAYLISTPLAYER_STARTED: + case GUI_MSG_PLAYLISTPLAYER_CHANGED: + { + return true; + } + break; + case GUI_MSG_FULLSCREEN: + { // Switch to fullscreen, if we can + SwitchToFullScreen(); + return true; + } + break; + case GUI_MSG_EXECUTE: + if (message.GetNumStringParams()) + return ExecuteXBMCAction(message.GetStringParam()); + break; + } + return false; +} + +bool CApplication::ExecuteXBMCAction(std::string actionStr) +{ + // see if it is a user set string + + //We don't know if there is unsecure information in this yet, so we + //postpone any logging + const std::string in_actionStr(actionStr); + actionStr = CGUIInfoLabel::GetLabel(actionStr); + + // user has asked for something to be executed + if (CBuiltins::HasCommand(actionStr)) + CBuiltins::Execute(actionStr); + else + { + // try translating the action from our ButtonTranslator + int actionID; + if (CButtonTranslator::TranslateActionString(actionStr.c_str(), actionID)) + { + OnAction(CAction(actionID)); + return true; + } + CFileItem item(actionStr, false); +#ifdef HAS_PYTHON + if (item.IsPythonScript()) + { // a python script + CScriptInvocationManager::Get().Execute(item.GetPath()); + } + else +#endif + if (item.IsAudio() || item.IsVideo()) + { // an audio or video file + PlayFile(item); + } + else + { + //At this point we have given up to translate, so even though + //there may be insecure information, we log it. + CLog::LogF(LOGDEBUG,"Tried translating, but failed to understand %s", in_actionStr.c_str()); + return false; + } + } + return true; +} + +// inform the user that the configuration data has moved from old XBMC location +// to new Kodi location - if applicable +void CApplication::ShowAppMigrationMessage() +{ + // .kodi_migration_complete will be created from the installer/packaging + // once an old XBMC configuration was moved to the new Kodi location + // if this is the case show the migration info to the user once which + // tells him to have a look into the wiki where the move of configuration + // is further explained. + if (CFile::Exists("special://home/.kodi_data_was_migrated") && + !CFile::Exists("special://home/.kodi_migration_info_shown")) + { + CGUIDialogOK::ShowAndGetInput(24128, 0, 24129, 0); + CFile tmpFile; + // create the file which will prevent this dialog from appearing in the future + tmpFile.OpenForWrite("special://home/.kodi_migration_info_shown"); + tmpFile.Close(); + } +} + +void CApplication::Process() +{ + MEASURE_FUNCTION; + + // dispatch the messages generated by python or other threads to the current window + g_windowManager.DispatchThreadMessages(); + + // process messages which have to be send to the gui + // (this can only be done after g_windowManager.Render()) + CApplicationMessenger::Get().ProcessWindowMessages(); + + if (m_loggingIn) + { + m_loggingIn = false; + + // autoexec.py - profile + CStdString strAutoExecPy = CSpecialProtocol::TranslatePath("special://profile/autoexec.py"); + + if (XFILE::CFile::Exists(strAutoExecPy)) + CScriptInvocationManager::Get().Execute(strAutoExecPy); + else + CLog::Log(LOGDEBUG, "no profile autoexec.py (%s) found, skipping", strAutoExecPy.c_str()); + } + + // handle any active scripts + CScriptInvocationManager::Get().Process(); + + // process messages, even if a movie is playing + CApplicationMessenger::Get().ProcessMessages(); + if (g_application.m_bStop) return; //we're done, everything has been unloaded + + // check how far we are through playing the current item + // and do anything that needs doing (playcount updates etc) + CheckPlayingProgress(); + + // update sound + m_pPlayer->DoAudioWork(); + + // do any processing that isn't needed on each run + if( m_slowTimer.GetElapsedMilliseconds() > 500 ) + { + m_slowTimer.Reset(); + ProcessSlow(); + } + + g_cpuInfo.getUsedPercentage(); // must call it to recalculate pct values +} + +// We get called every 500ms +void CApplication::ProcessSlow() +{ + g_powerManager.ProcessEvents(); + +#if defined(TARGET_DARWIN_OSX) + // There is an issue on OS X that several system services ask the cursor to become visible + // during their startup routines. Given that we can't control this, we hack it in by + // forcing the + if (g_Windowing.IsFullScreen()) + { // SDL thinks it's hidden + Cocoa_HideMouse(); + } +#endif + + // Temporarely pause pausable jobs when viewing video/picture + int currentWindow = g_windowManager.GetActiveWindow(); + if (CurrentFileItem().IsVideo() || CurrentFileItem().IsPicture() || currentWindow == WINDOW_FULLSCREEN_VIDEO || currentWindow == WINDOW_SLIDESHOW) + { + CJobManager::GetInstance().PauseJobs(); + } + else + { + CJobManager::GetInstance().UnPauseJobs(); + } + + // Store our file state for use on close() + UpdateFileState(); + + // Check if we need to activate the screensaver / DPMS. + CheckScreenSaverAndDPMS(); + + // Check if we need to shutdown (if enabled). +#if defined(TARGET_DARWIN) + if (CSettings::Get().GetInt("powermanagement.shutdowntime") && g_advancedSettings.m_fullScreen) +#else + if (CSettings::Get().GetInt("powermanagement.shutdowntime")) +#endif + { + CheckShutdown(); + } + + // check if we should restart the player + CheckDelayedPlayerRestart(); + + // check if we can unload any unreferenced dlls or sections + if (!m_pPlayer->IsPlayingVideo()) + CSectionLoader::UnloadDelayed(); + + // check for any idle curl connections + g_curlInterface.CheckIdle(); + + // check for any idle myth sessions + CMythSession::CheckIdle(); + +#ifdef HAS_FILESYSTEM_HTSP + // check for any idle htsp sessions + HTSP::CHTSPDirectorySession::CheckIdle(); +#endif + +#ifdef HAS_KARAOKE + if ( m_pKaraokeMgr ) + m_pKaraokeMgr->ProcessSlow(); +#endif + + if (!m_pPlayer->IsPlayingVideo()) + g_largeTextureManager.CleanupUnusedImages(); + + g_TextureManager.FreeUnusedTextures(5000); + +#ifdef HAS_DVD_DRIVE + // checks whats in the DVD drive and tries to autostart the content (xbox games, dvd, cdda, avi files...) + if (!m_pPlayer->IsPlayingVideo()) + m_Autorun->HandleAutorun(); +#endif + + // update upnp server/renderer states +#ifdef HAS_UPNP + if(UPNP::CUPnP::IsInstantiated()) + UPNP::CUPnP::GetInstance()->UpdateState(); +#endif + +#if defined(TARGET_POSIX) && defined(HAS_FILESYSTEM_SMB) + smb.CheckIfIdle(); +#endif + +#ifdef HAS_FILESYSTEM_NFS + gNfsConnection.CheckIfIdle(); +#endif + +#ifdef HAS_FILESYSTEM_AFP + gAfpConnection.CheckIfIdle(); +#endif + +#ifdef HAS_FILESYSTEM_SFTP + CSFTPSessionManager::ClearOutIdleSessions(); +#endif + + g_mediaManager.ProcessEvents(); + +#ifdef HAS_LIRC + if (g_RemoteControl.IsInUse() && !g_RemoteControl.IsInitialized()) + g_RemoteControl.Initialize(); +#endif + + if (!m_pPlayer->IsPlayingVideo() && + CSettings::Get().GetInt("general.addonupdates") != AUTO_UPDATES_NEVER) + CAddonInstaller::Get().UpdateRepos(); + + CAEFactory::GarbageCollect(); + + // if we don't render the gui there's no reason to start the screensaver. + // that way the screensaver won't kick in if we maximize the XBMC window + // after the screensaver start time. + if(!m_renderGUI) + ResetScreenSaverTimer(); +} + +// Global Idle Time in Seconds +// idle time will be resetet if on any OnKey() +// int return: system Idle time in seconds! 0 is no idle! +int CApplication::GlobalIdleTime() +{ + if(!m_idleTimer.IsRunning()) + m_idleTimer.StartZero(); + return (int)m_idleTimer.GetElapsedSeconds(); +} + +float CApplication::NavigationIdleTime() +{ + if (!m_navigationTimer.IsRunning()) + m_navigationTimer.StartZero(); + return m_navigationTimer.GetElapsedSeconds(); +} + +void CApplication::DelayedPlayerRestart() +{ + m_restartPlayerTimer.StartZero(); +} + +void CApplication::CheckDelayedPlayerRestart() +{ + if (m_restartPlayerTimer.GetElapsedSeconds() > 3) + { + m_restartPlayerTimer.Stop(); + m_restartPlayerTimer.Reset(); + Restart(true); + } +} + +void CApplication::Restart(bool bSamePosition) +{ + // this function gets called when the user changes a setting (like noninterleaved) + // and which means we gotta close & reopen the current playing file + + // first check if we're playing a file + if ( !m_pPlayer->IsPlayingVideo() && !m_pPlayer->IsPlayingAudio()) + return ; + + if( !m_pPlayer->HasPlayer() ) + return ; + + SaveFileState(); + + // do we want to return to the current position in the file + if (false == bSamePosition) + { + // no, then just reopen the file and start at the beginning + PlayFile(*m_itemCurrentFile, true); + return ; + } + + // else get current position + double time = GetTime(); + + // get player state, needed for dvd's + CStdString state = m_pPlayer->GetPlayerState(); + + // set the requested starttime + m_itemCurrentFile->m_lStartOffset = (long)(time * 75.0); + + // reopen the file + if ( PlayFile(*m_itemCurrentFile, true) == PLAYBACK_OK ) + m_pPlayer->SetPlayerState(state); +} + +const std::string& CApplication::CurrentFile() +{ + return m_itemCurrentFile->GetPath(); +} + +CFileItem& CApplication::CurrentFileItem() +{ + return *m_itemCurrentFile; +} + +CFileItem& CApplication::CurrentUnstackedItem() +{ + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + return *(*m_currentStack)[m_currentStackPosition]; + else + return *m_itemCurrentFile; +} + +void CApplication::ShowVolumeBar(const CAction *action) +{ + CGUIDialog *volumeBar = (CGUIDialog *)g_windowManager.GetWindow(WINDOW_DIALOG_VOLUME_BAR); + if (volumeBar) + { + volumeBar->Show(); + if (action) + volumeBar->OnAction(*action); + } +} + +bool CApplication::IsMuted() const +{ + if (g_peripherals.IsMuted()) + return true; + return CAEFactory::IsMuted(); +} + +void CApplication::ToggleMute(void) +{ + if (m_muted) + UnMute(); + else + Mute(); +} + +void CApplication::SetMute(bool mute) +{ + if (m_muted != mute) + { + ToggleMute(); + m_muted = mute; + } +} + +void CApplication::Mute() +{ + if (g_peripherals.Mute()) + return; + + CAEFactory::SetMute(true); + m_muted = true; + VolumeChanged(); +} + +void CApplication::UnMute() +{ + if (g_peripherals.UnMute()) + return; + + CAEFactory::SetMute(false); + m_muted = false; + VolumeChanged(); +} + +void CApplication::SetVolume(float iValue, bool isPercentage/*=true*/) +{ + float hardwareVolume = iValue; + + if(isPercentage) + hardwareVolume /= 100.0f; + + SetHardwareVolume(hardwareVolume); + VolumeChanged(); +} + +void CApplication::SetHardwareVolume(float hardwareVolume) +{ + hardwareVolume = std::max(VOLUME_MINIMUM, std::min(VOLUME_MAXIMUM, hardwareVolume)); + m_volumeLevel = hardwareVolume; + + CAEFactory::SetVolume(hardwareVolume); +} + +float CApplication::GetVolume(bool percentage /* = true */) const +{ + if (percentage) + { + // converts the hardware volume to a percentage + return m_volumeLevel * 100.0f; + } + + return m_volumeLevel; +} + +void CApplication::VolumeChanged() const +{ + CVariant data(CVariant::VariantTypeObject); + data["volume"] = GetVolume(); + data["muted"] = m_muted; + CAnnouncementManager::Get().Announce(Application, "xbmc", "OnVolumeChanged", data); + + // if player has volume control, set it. + if (m_pPlayer->ControlsVolume()) + { + m_pPlayer->SetVolume(m_volumeLevel); + m_pPlayer->SetMute(m_muted); + } +} + +int CApplication::GetSubtitleDelay() const +{ + // converts subtitle delay to a percentage + return int(((float)(CMediaSettings::Get().GetCurrentVideoSettings().m_SubtitleDelay + g_advancedSettings.m_videoSubsDelayRange)) / (2 * g_advancedSettings.m_videoSubsDelayRange)*100.0f + 0.5f); +} + +int CApplication::GetAudioDelay() const +{ + // converts audio delay to a percentage + return int(((float)(CMediaSettings::Get().GetCurrentVideoSettings().m_AudioDelay + g_advancedSettings.m_videoAudioDelayRange)) / (2 * g_advancedSettings.m_videoAudioDelayRange)*100.0f + 0.5f); +} + +// Returns the total time in seconds of the current media. Fractional +// portions of a second are possible - but not necessarily supported by the +// player class. This returns a double to be consistent with GetTime() and +// SeekTime(). +double CApplication::GetTotalTime() const +{ + double rc = 0.0; + + if (m_pPlayer->IsPlaying()) + { + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + rc = (*m_currentStack)[m_currentStack->Size() - 1]->m_lEndOffset; + else + rc = static_cast<double>(m_pPlayer->GetTotalTime() * 0.001f); + } + + return rc; +} + +void CApplication::StopShutdownTimer() +{ + m_shutdownTimer.Stop(); +} + +void CApplication::ResetShutdownTimers() +{ + // reset system shutdown timer + m_shutdownTimer.StartZero(); + + // delete custom shutdown timer + if (g_alarmClock.HasAlarm("shutdowntimer")) + g_alarmClock.Stop("shutdowntimer", true); +} + +// Returns the current time in seconds of the currently playing media. +// Fractional portions of a second are possible. This returns a double to +// be consistent with GetTotalTime() and SeekTime(). +double CApplication::GetTime() const +{ + double rc = 0.0; + + if (m_pPlayer->IsPlaying()) + { + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + { + long startOfCurrentFile = (m_currentStackPosition > 0) ? (*m_currentStack)[m_currentStackPosition-1]->m_lEndOffset : 0; + rc = (double)startOfCurrentFile + m_pPlayer->GetTime() * 0.001; + } + else + rc = static_cast<double>(m_pPlayer->GetTime() * 0.001f); + } + + return rc; +} + +// Sets the current position of the currently playing media to the specified +// time in seconds. Fractional portions of a second are valid. The passed +// time is the time offset from the beginning of the file as opposed to a +// delta from the current position. This method accepts a double to be +// consistent with GetTime() and GetTotalTime(). +void CApplication::SeekTime( double dTime ) +{ + if (m_pPlayer->IsPlaying() && (dTime >= 0.0)) + { + if (!m_pPlayer->CanSeek()) return; + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + { + // find the item in the stack we are seeking to, and load the new + // file if necessary, and calculate the correct seek within the new + // file. Otherwise, just fall through to the usual routine if the + // time is higher than our total time. + for (int i = 0; i < m_currentStack->Size(); i++) + { + if ((*m_currentStack)[i]->m_lEndOffset > dTime) + { + long startOfNewFile = (i > 0) ? (*m_currentStack)[i-1]->m_lEndOffset : 0; + if (m_currentStackPosition == i) + m_pPlayer->SeekTime((int64_t)((dTime - startOfNewFile) * 1000.0)); + else + { // seeking to a new file + m_currentStackPosition = i; + CFileItem item(*(*m_currentStack)[i]); + item.m_lStartOffset = (long)((dTime - startOfNewFile) * 75.0); + // don't just call "PlayFile" here, as we are quite likely called from the + // player thread, so we won't be able to delete ourselves. + CApplicationMessenger::Get().PlayFile(item, true); + } + return; + } + } + } + // convert to milliseconds and perform seek + m_pPlayer->SeekTime( static_cast<int64_t>( dTime * 1000.0 ) ); + } +} + +float CApplication::GetPercentage() const +{ + if (m_pPlayer->IsPlaying()) + { + if (m_pPlayer->GetTotalTime() == 0 && m_pPlayer->IsPlayingAudio() && m_itemCurrentFile->HasMusicInfoTag()) + { + const CMusicInfoTag& tag = *m_itemCurrentFile->GetMusicInfoTag(); + if (tag.GetDuration() > 0) + return (float)(GetTime() / tag.GetDuration() * 100); + } + + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + { + double totalTime = GetTotalTime(); + if (totalTime > 0.0f) + return (float)(GetTime() / totalTime * 100); + } + else + return m_pPlayer->GetPercentage(); + } + return 0.0f; +} + +float CApplication::GetCachePercentage() const +{ + if (m_pPlayer->IsPlaying()) + { + // Note that the player returns a relative cache percentage and we want an absolute percentage + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + { + float stackedTotalTime = (float) GetTotalTime(); + // We need to take into account the stack's total time vs. currently playing file's total time + if (stackedTotalTime > 0.0f) + return min( 100.0f, GetPercentage() + (m_pPlayer->GetCachePercentage() * m_pPlayer->GetTotalTime() * 0.001f / stackedTotalTime ) ); + } + else + return min( 100.0f, m_pPlayer->GetPercentage() + m_pPlayer->GetCachePercentage() ); + } + return 0.0f; +} + +void CApplication::SeekPercentage(float percent) +{ + if (m_pPlayer->IsPlaying() && (percent >= 0.0)) + { + if (!m_pPlayer->CanSeek()) return; + if (m_itemCurrentFile->IsStack() && m_currentStack->Size() > 0) + SeekTime(percent * 0.01 * GetTotalTime()); + else + m_pPlayer->SeekPercentage(percent); + } +} + +// SwitchToFullScreen() returns true if a switch is made, else returns false +bool CApplication::SwitchToFullScreen() +{ + // if playing from the video info window, close it first! + if (g_windowManager.HasModalDialog() && g_windowManager.GetTopMostModalDialogID() == WINDOW_DIALOG_VIDEO_INFO) + { + CGUIDialogVideoInfo* pDialog = (CGUIDialogVideoInfo*)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_INFO); + if (pDialog) pDialog->Close(true); + } + + // don't switch if there is a dialog on screen or the slideshow is active + if (/*g_windowManager.HasModalDialog() ||*/ g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW) + return false; + + // See if we're playing a video, and are in GUI mode + if ( m_pPlayer->IsPlayingVideo() && g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO) + { + // then switch to fullscreen mode + g_windowManager.ActivateWindow(WINDOW_FULLSCREEN_VIDEO); + return true; + } + // special case for switching between GUI & visualisation mode. (only if we're playing an audio song) + if (m_pPlayer->IsPlayingAudio() && g_windowManager.GetActiveWindow() != WINDOW_VISUALISATION) + { // then switch to visualisation + g_windowManager.ActivateWindow(WINDOW_VISUALISATION); + return true; + } + return false; +} + +void CApplication::Minimize() +{ + g_Windowing.Minimize(); +} + +PLAYERCOREID CApplication::GetCurrentPlayer() +{ + return m_pPlayer->GetCurrentPlayer(); +} + +void CApplication::UpdateLibraries() +{ + if (CSettings::Get().GetBool("videolibrary.updateonstartup")) + { + CLog::LogF(LOGNOTICE, "Starting video library startup scan"); + StartVideoScan("", !CSettings::Get().GetBool("videolibrary.backgroundupdate")); + } + + if (CSettings::Get().GetBool("musiclibrary.updateonstartup")) + { + CLog::LogF(LOGNOTICE, "Starting music library startup scan"); + StartMusicScan("", !CSettings::Get().GetBool("musiclibrary.backgroundupdate")); + } +} + +bool CApplication::IsVideoScanning() const +{ + return m_videoInfoScanner->IsScanning(); +} + +bool CApplication::IsMusicScanning() const +{ + return m_musicInfoScanner->IsScanning(); +} + +void CApplication::StopVideoScan() +{ + if (m_videoInfoScanner->IsScanning()) + m_videoInfoScanner->Stop(); +} + +void CApplication::StopMusicScan() +{ + if (m_musicInfoScanner->IsScanning()) + m_musicInfoScanner->Stop(); +} + +void CApplication::StartVideoCleanup(bool userInitiated /* = true */) +{ + if (m_videoInfoScanner->IsScanning()) + return; + + if (userInitiated) + m_videoInfoScanner->CleanDatabase(NULL, NULL, true); + else + { + m_videoInfoScanner->ShowDialog(false); + m_videoInfoScanner->StartCleanDatabase(); + } +} + +void CApplication::StartVideoScan(const CStdString &strDirectory, bool userInitiated /* = true */, bool scanAll /* = false */) +{ + if (m_videoInfoScanner->IsScanning()) + return; + + m_videoInfoScanner->ShowDialog(userInitiated); + + m_videoInfoScanner->Start(strDirectory,scanAll); +} + +void CApplication::StartMusicCleanup(bool userInitiated /* = true */) +{ + if (m_musicInfoScanner->IsScanning()) + return; + + if (userInitiated) + m_musicInfoScanner->CleanDatabase(true); + else + { + m_musicInfoScanner->ShowDialog(false); + m_musicInfoScanner->StartCleanDatabase(); + } +} + +void CApplication::StartMusicScan(const CStdString &strDirectory, bool userInitiated /* = true */, int flags /* = 0 */) +{ + if (m_musicInfoScanner->IsScanning()) + return; + + if (!flags) + { // setup default flags + if (CSettings::Get().GetBool("musiclibrary.downloadinfo")) + flags |= CMusicInfoScanner::SCAN_ONLINE; + if (!userInitiated || CSettings::Get().GetBool("musiclibrary.backgroundupdate")) + flags |= CMusicInfoScanner::SCAN_BACKGROUND; + } + + if (!(flags & CMusicInfoScanner::SCAN_BACKGROUND)) + m_musicInfoScanner->ShowDialog(true); + + m_musicInfoScanner->Start(strDirectory, flags); +} + +void CApplication::StartMusicAlbumScan(const CStdString& strDirectory, + bool refresh) +{ + if (m_musicInfoScanner->IsScanning()) + return; + + m_musicInfoScanner->ShowDialog(true); + + m_musicInfoScanner->FetchAlbumInfo(strDirectory,refresh); +} + +void CApplication::StartMusicArtistScan(const CStdString& strDirectory, + bool refresh) +{ + if (m_musicInfoScanner->IsScanning()) + return; + + m_musicInfoScanner->ShowDialog(true); + + m_musicInfoScanner->FetchArtistInfo(strDirectory,refresh); +} + +void CApplication::CheckPlayingProgress() +{ + // check if we haven't rewound past the start of the file + if (m_pPlayer->IsPlaying()) + { + int iSpeed = g_application.m_pPlayer->GetPlaySpeed(); + if (iSpeed < 1) + { + iSpeed *= -1; + int iPower = 0; + while (iSpeed != 1) + { + iSpeed >>= 1; + iPower++; + } + if (g_infoManager.GetPlayTime() / 1000 < iPower) + { + g_application.m_pPlayer->SetPlaySpeed(1, g_application.m_muted); + g_application.SeekTime(0); + } + } + } +} + +bool CApplication::ProcessAndStartPlaylist(const CStdString& strPlayList, CPlayList& playlist, int iPlaylist, int track) +{ + CLog::Log(LOGDEBUG,"CApplication::ProcessAndStartPlaylist(%s, %i)",strPlayList.c_str(), iPlaylist); + + // initial exit conditions + // no songs in playlist just return + if (playlist.size() == 0) + return false; + + // illegal playlist + if (iPlaylist < PLAYLIST_MUSIC || iPlaylist > PLAYLIST_VIDEO) + return false; + + // setup correct playlist + g_playlistPlayer.ClearPlaylist(iPlaylist); + + // if the playlist contains an internet stream, this file will be used + // to generate a thumbnail for musicplayer.cover + g_application.m_strPlayListFile = strPlayList; + + // add the items to the playlist player + g_playlistPlayer.Add(iPlaylist, playlist); + + // if we have a playlist + if (g_playlistPlayer.GetPlaylist(iPlaylist).size()) + { + // start playing it + g_playlistPlayer.SetCurrentPlaylist(iPlaylist); + g_playlistPlayer.Reset(); + g_playlistPlayer.Play(track); + return true; + } + return false; +} + +bool CApplication::AlwaysProcess(const CAction& action) +{ + // check if this button is mapped to a built-in function + if (!action.GetName().empty()) + { + CStdString builtInFunction; + vector<string> params; + CUtil::SplitExecFunction(action.GetName(), builtInFunction, params); + StringUtils::ToLower(builtInFunction); + + // should this button be handled normally or just cancel the screensaver? + if ( builtInFunction.Equals("powerdown") + || builtInFunction.Equals("reboot") + || builtInFunction.Equals("restart") + || builtInFunction.Equals("restartapp") + || builtInFunction.Equals("suspend") + || builtInFunction.Equals("hibernate") + || builtInFunction.Equals("quit") + || builtInFunction.Equals("shutdown")) + { + return true; + } + } + + return false; +} + +bool CApplication::IsCurrentThread() const +{ + return CThread::IsCurrentThread(m_threadID); +} + +void CApplication::SetRenderGUI(bool renderGUI) +{ + if (renderGUI && ! m_renderGUI) + g_windowManager.MarkDirty(); + m_renderGUI = renderGUI; +} + +CNetwork& CApplication::getNetwork() +{ + return *m_network; +} +#ifdef HAS_PERFORMANCE_SAMPLE +CPerformanceStats &CApplication::GetPerformanceStats() +{ + return m_perfStats; +} +#endif + +bool CApplication::SetLanguage(const CStdString &strLanguage) +{ + CStdString strPreviousLanguage = CSettings::Get().GetString("locale.language"); + if (strLanguage != strPreviousLanguage) + { + CStdString strLangInfoPath = StringUtils::Format("special://xbmc/language/%s/langinfo.xml", strLanguage.c_str()); + if (!g_langInfo.Load(strLangInfoPath)) + return false; + + CSettings::Get().SetString("locale.language", strLanguage); + + if (!g_localizeStrings.Load("special://xbmc/language/", strLanguage)) + return false; + + // also tell our weather and skin to reload as these are localized + g_weatherManager.Refresh(); + g_PVRManager.LocalizationChanged(); + ReloadSkin(); + } + + return true; +} + +void CApplication::CloseNetworkShares() +{ + CLog::Log(LOGDEBUG,"CApplication::CloseNetworkShares: Closing all network shares"); + +#if defined(HAS_FILESYSTEM_SMB) && !defined(TARGET_WINDOWS) + smb.Deinit(); +#endif + +#ifdef HAS_FILESYSTEM_NFS + gNfsConnection.Deinit(); +#endif + +#ifdef HAS_FILESYSTEM_AFP + gAfpConnection.Deinit(); +#endif + +#ifdef HAS_FILESYSTEM_SFTP + CSFTPSessionManager::DisconnectAllSessions(); +#endif +} |