mirror of
https://github.com/Team-Resurgent/xbmc4xbox-redux.git
synced 2026-08-15 20:18:49 -07:00
6230 lines
201 KiB
C++
Executable File
6230 lines
201 KiB
C++
Executable File
/*
|
|
* 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 "xbox/Network.h"
|
|
#include "threads/SystemClock.h"
|
|
#include "system.h"
|
|
#include "Application.h"
|
|
#include "interfaces/builtins/Builtins.h"
|
|
#include "utils/Variant.h"
|
|
#include "utils/Splash.h"
|
|
#include "LangInfo.h"
|
|
#include "Util.h"
|
|
#include "URL.h"
|
|
#include "guilib/TextureManager.h"
|
|
#include "cores/IPlayer.h"
|
|
#include "cores/dvdplayer/DVDFileInfo.h"
|
|
#include "PlayListPlayer.h"
|
|
#include "Autorun.h"
|
|
#include "video/Bookmark.h"
|
|
#include "video/VideoLibraryQueue.h"
|
|
#include "music/MusicLibraryQueue.h"
|
|
#include "network/NetworkServices.h"
|
|
#include "utils/LangCodeExpander.h"
|
|
#include "GUIInfoManager.h"
|
|
#include "playlists/PlayListFactory.h"
|
|
#include "guilib/GUIFontManager.h"
|
|
#include "guilib/GUIColorManager.h"
|
|
#include "guilib/GUIImage.h"
|
|
#include "addons/LanguageResource.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 "messaging/ThreadMessage.h"
|
|
#include "messaging/ApplicationMessenger.h"
|
|
#include "messaging/helpers/DialogHelper.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/PluginDirectory.h"
|
|
#ifdef HAS_FILESYSTEM_SAP
|
|
#include "filesystem/SAPDirectory.h"
|
|
#endif
|
|
#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 "settings/Settings.h"
|
|
#include "settings/AdvancedSettings.h"
|
|
#include "settings/DisplaySettings.h"
|
|
#include "settings/MediaSettings.h"
|
|
#include "settings/SkinSettings.h"
|
|
#include "guilib/LocalizeStrings.h"
|
|
#include "utils/SeekHandler.h"
|
|
|
|
#include "input/KeyboardLayoutManager.h"
|
|
|
|
#if HAVE_SDL_VERSION == 1
|
|
#include <SDL/SDL.h>
|
|
#elif HAVE_SDL_VERSION == 2
|
|
#include <SDL2/SDL.h>
|
|
#endif
|
|
|
|
#ifdef HAS_UPNP
|
|
#include "network/upnp/UPnP.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_SFTP
|
|
#include "filesystem/SFTPFile.h"
|
|
#endif
|
|
#include "PartyModeManager.h"
|
|
#ifdef HAS_VIDEO_PLAYBACK
|
|
#include "cores/VideoRenderers/RenderManager.h"
|
|
#endif
|
|
#ifdef HAS_KARAOKE
|
|
#include "CdgParser.h"
|
|
#endif
|
|
#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"
|
|
#endif
|
|
#include "interfaces/AnnouncementManager.h"
|
|
#include "music/infoscanner/MusicInfoScanner.h"
|
|
#include "music/MusicUtils.h"
|
|
#include "music/MusicThumbLoader.h"
|
|
|
|
// Windows includes
|
|
#include "guilib/GUIWindowManager.h"
|
|
#include "video/dialogs/GUIDialogVideoInfo.h"
|
|
#include "windows/GUIWindowScreensaver.h"
|
|
#include "video/windows/GUIWindowFullScreen.h"
|
|
#include "video/dialogs/GUIDialogVideoOSD.h"
|
|
#include "video/PlayerController.h"
|
|
|
|
// Dialog includes
|
|
#include "video/dialogs/GUIDialogVideoBookmarks.h"
|
|
#include "dialogs/GUIDialogOK.h"
|
|
#include "dialogs/GUIDialogKaiToast.h"
|
|
#include "dialogs/GUIDialogSubMenu.h"
|
|
#include "dialogs/GUIDialogButtonMenu.h"
|
|
#include "dialogs/GUIDialogYesNo.h"
|
|
#include "addons/GUIDialogAddonSettings.h"
|
|
|
|
#include "video/dialogs/GUIDialogFullScreenInfo.h"
|
|
#include "guilib/GUIControlFactory.h"
|
|
#include "dialogs/GUIDialogCache.h"
|
|
#include "utils/URIUtils.h"
|
|
#include "utils/XMLUtils.h"
|
|
#include "addons/AddonInstaller.h"
|
|
#include "addons/AddonManager.h"
|
|
#include "addons/RepositoryUpdater.h"
|
|
#include "music/tags/MusicInfoTag.h"
|
|
#include "music/tags/MusicInfoTagLoaderFactory.h"
|
|
|
|
#ifdef HAS_PERFORMANCE_SAMPLE
|
|
#include "utils/PerformanceSample.h"
|
|
#else
|
|
#define MEASURE_FUNCTION
|
|
#endif
|
|
|
|
#ifdef TARGET_WINDOWS
|
|
#include "win32util.h"
|
|
#endif
|
|
|
|
#ifdef TARGET_DARWIN_OSX
|
|
#include "osx/CocoaInterface.h"
|
|
#include "osx/XBMCHelper.h"
|
|
#endif
|
|
#ifdef TARGET_DARWIN
|
|
#include "osx/DarwinUtils.h"
|
|
#endif
|
|
|
|
#include "storage/MediaManager.h"
|
|
#include "utils/JobManager.h"
|
|
#include "utils/SaveFileStateJob.h"
|
|
#include "utils/AlarmClock.h"
|
|
#include "utils/StringUtils.h"
|
|
#include "utils/Weather.h"
|
|
#include "DatabaseManager.h"
|
|
|
|
#ifdef TARGET_POSIX
|
|
#include "XHandle.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 "utils/CharsetConverter.h"
|
|
#include "pictures/GUIWindowSlideShow.h"
|
|
#include "windows/GUIWindowLoginScreen.h"
|
|
|
|
#ifdef HAS_WEB_SERVER
|
|
#include "libGoAhead/XBMChttp.h"
|
|
#endif
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
#ifdef HAS_XFONT
|
|
#include <xfont.h> // for textout functions
|
|
#endif
|
|
#include "XBVideoConfig.h"
|
|
#include "XBAudioConfig.h"
|
|
#include "utils/LCD.h"
|
|
#include "utils/LCDFactory.h"
|
|
#include "utils/LED.h"
|
|
#include "utils/MemoryUnitManager.h"
|
|
#include "utils/FanController.h"
|
|
#include "utils/Trainer.h"
|
|
#include "xbox/IoSupport.h"
|
|
#include "xbox/XKEEPROM.h"
|
|
#include "xbox/XKHDD.h"
|
|
#include "xbox/xbeheader.h"
|
|
#endif
|
|
|
|
using namespace std;
|
|
using namespace ADDON;
|
|
using namespace XFILE;
|
|
using namespace MEDIA_DETECT;
|
|
using namespace PLAYLIST;
|
|
using namespace VIDEO;
|
|
using namespace MUSIC_INFO;
|
|
#ifdef HAS_EVENT_SERVER
|
|
using namespace EVENTSERVER;
|
|
#endif
|
|
using namespace ANNOUNCEMENT;
|
|
using namespace KODI::MESSAGING;
|
|
|
|
using namespace KODI::MESSAGING::HELPERS;
|
|
|
|
// 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
|
|
|
|
#ifdef HAS_LCD
|
|
#pragma comment (lib,"lib/libXenium/XeniumSPIg.lib")
|
|
#endif
|
|
|
|
#if defined(_DEBUG) && !defined(USE_RELEASE_LIBS)
|
|
#ifdef HAS_FILESYSTEM
|
|
#pragma comment (lib,"lib/libsmb/libsmbd.lib") // SECTIONNAME=LIBSMB
|
|
#endif
|
|
#ifdef _XBOX
|
|
#pragma comment (lib,"lib/libGoAhead/goaheadd.lib") // SECTIONNAME=LIBHTTP
|
|
#pragma comment (lib,"lib/sqLite/libSQLite3d.lib")
|
|
#pragma comment (lib,"lib/libshout/libshoutd.lib" )
|
|
#pragma comment (lib,"lib/libcdio/libcdiod.lib" )
|
|
#pragma comment (lib,"lib/libiconv/libiconvd.lib")
|
|
#pragma comment (lib,"lib/libfribidi/libfribidid.lib")
|
|
#pragma comment (lib,"lib/libpcre/libpcred.lib")
|
|
#else
|
|
#pragma comment (lib,"../../lib/libGoAhead/goahead_win32d.lib") // SECTIONNAME=LIBHTTP
|
|
#pragma comment (lib,"../../lib/sqLite/libSQLite3_win32d.lib")
|
|
#pragma comment (lib,"../../lib/libcdio/libcdio_win32d.lib" )
|
|
#pragma comment (lib,"../../lib/libiconv/libiconvd.lib")
|
|
#pragma comment (lib,"../../lib/libfribidi/libfribidid.lib")
|
|
#pragma comment (lib,"../../lib/libpcre/libpcred.lib")
|
|
#endif
|
|
#ifdef HAS_MIKMOD
|
|
#pragma comment (lib,"lib/mikxbox/mikxboxd.lib") // SECTIONNAME=MOD_RW,MOD_RX
|
|
#endif
|
|
#else
|
|
#ifdef HAS_FILESYSTEM
|
|
#pragma comment (lib,"lib/libsmb/libsmb.lib")
|
|
#endif
|
|
#ifdef _XBOX
|
|
#pragma comment (lib,"lib/libGoAhead/goahead.lib")
|
|
#pragma comment (lib,"lib/sqLite/libSQLite3.lib")
|
|
#pragma comment (lib,"lib/libcdio/libcdio.lib")
|
|
#pragma comment (lib,"lib/libiconv/libiconv.lib")
|
|
#pragma comment (lib,"lib/libfribidi/libfribidi.lib")
|
|
#pragma comment (lib,"lib/libpcre/libpcre.lib")
|
|
#else
|
|
#pragma comment (lib,"../../lib/libGoAhead/goahead_win32.lib")
|
|
#pragma comment (lib,"../../lib/sqLite/libSQLite3_win32.lib")
|
|
#pragma comment (lib,"../../lib/libshout/libshout_win32.lib" )
|
|
#pragma comment (lib,"../../lib/libcdio/libcdio_win32.lib" )
|
|
#pragma comment (lib,"../../lib/libiconv/libiconv.lib")
|
|
#pragma comment (lib,"../../lib/libfribidi/libfribidi.lib")
|
|
#pragma comment (lib,"../../lib/libpcre/libpcre.lib")
|
|
#endif
|
|
#ifdef HAS_MIKMOD
|
|
#pragma comment (lib,"lib/mikxbox/mikxbox.lib")
|
|
#endif
|
|
#endif
|
|
|
|
#define MAX_FFWD_SPEED 5
|
|
|
|
#ifdef HAS_XBOX_D3D
|
|
static void WaitCallback(DWORD flags)
|
|
{
|
|
#ifndef PROFILE
|
|
/* if cpu is far ahead of gpu, sleep instead of yield */
|
|
if( flags & D3DWAIT_PRESENT )
|
|
while(D3DDevice::GetPushDistance(D3DDISTANCE_FENCES_TOWAIT) > 0)
|
|
Sleep(1);
|
|
else if( flags & (D3DWAIT_OBJECTLOCK | D3DWAIT_BLOCKONFENCE | D3DWAIT_BLOCKUNTILIDLE) )
|
|
while(D3DDevice::GetPushDistance(D3DDISTANCE_FENCES_TOWAIT) > 1)
|
|
Sleep(1);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//extern IDirectSoundRenderer* m_pAudioDecoder;
|
|
CApplication::CApplication(void)
|
|
: m_pPlayer(new CApplicationPlayer)
|
|
, m_ctrDpad(220, 220)
|
|
, m_itemCurrentFile(new CFileItem)
|
|
, m_stackFileItemToUpdate(new CFileItem)
|
|
, m_progressTrackingItem(new CFileItem)
|
|
, m_ignoreSkinSettingChanges(false)
|
|
{
|
|
m_network = NULL;
|
|
m_bSpinDown = false;
|
|
m_bNetworkSpinDown = false;
|
|
m_dwSpinDownTime = timeGetTime();
|
|
m_pXbmcHttp = NULL;
|
|
m_prevMedia="";
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
XSetProcessQuantumLength(5); //default=20msec
|
|
XSetFileCacheSize (256*1024); //default=64kb
|
|
#endif
|
|
m_bScreenSave = false; // CB: SCREENSAVER PATCH
|
|
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;
|
|
|
|
//true while we in IsPaused mode! Workaround for OnPaused, which must be add. after v2.0
|
|
m_bIsPaused = false;
|
|
|
|
/* for now always keep this around */
|
|
#ifdef HAS_KARAOKE
|
|
m_pCdgParser = new CCdgParser();
|
|
#endif
|
|
m_currentStack = new CFileItemList;
|
|
m_debugLayout = NULL;
|
|
|
|
m_volumeLevel = 0;
|
|
m_dynamicRangeCompressionLevel = 0;
|
|
m_muted = false;
|
|
}
|
|
|
|
CApplication::~CApplication(void)
|
|
{
|
|
delete m_currentStack;
|
|
|
|
delete m_pPlayer;
|
|
m_actionListeners.clear();
|
|
}
|
|
|
|
// text out routine for below
|
|
#ifdef HAS_XFONT
|
|
static void __cdecl FEH_TextOut(XFONT* pFont, int iLine, const wchar_t* fmt, ...)
|
|
{
|
|
wchar_t buf[100];
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
_vsnwprintf(buf, 100, fmt, args);
|
|
va_end(args);
|
|
|
|
if (!(iLine & 0x8000))
|
|
CLog::Log(LOGFATAL, "%S", buf);
|
|
|
|
bool Center = (iLine & 0x10000) > 0;
|
|
pFont->SetTextAlignment(Center ? XFONT_TOP | XFONT_CENTER : XFONT_TOP | XFONT_LEFT);
|
|
|
|
iLine &= 0x7fff;
|
|
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
D3DRECT rc = { 0, 50 + 25 * iLine, 720, 50 + 25 * (iLine + 1) };
|
|
D3DDevice::Clear(1, &rc, D3DCLEAR_TARGET, 0, 0, 0);
|
|
pFont->TextOut(g_application.m_pBackBuffer, buf, -1, Center ? 360 : 80, 50 + 25*iLine);
|
|
D3DDevice::Present(0, 0, 0, 0);
|
|
}
|
|
}
|
|
#else
|
|
static void __cdecl FEH_TextOut(void* pFont, int iLine, const wchar_t* fmt, ...) {}
|
|
#endif
|
|
|
|
HWND g_hWnd = NULL;
|
|
|
|
void CApplication::InitBasicD3D()
|
|
{
|
|
bool bPal = g_videoConfig.HasPAL();
|
|
CLog::Log(LOGINFO, "Init display in default mode: %s", bPal ? "PAL" : "NTSC");
|
|
// init D3D with defaults (NTSC or PAL standard res)
|
|
m_d3dpp.BackBufferWidth = 720;
|
|
m_d3dpp.BackBufferHeight = bPal ? 576 : 480;
|
|
m_d3dpp.BackBufferFormat = D3DFMT_LIN_X8R8G8B8;
|
|
m_d3dpp.BackBufferCount = 1;
|
|
m_d3dpp.EnableAutoDepthStencil = FALSE;
|
|
m_d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
|
|
#ifdef HAS_XBOX_D3D
|
|
m_d3dpp.FullScreen_PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;
|
|
#else
|
|
m_d3dpp.FullScreen_PresentationInterval = 0;
|
|
m_d3dpp.Windowed = TRUE;
|
|
m_d3dpp.hDeviceWindow = g_hWnd;
|
|
#endif
|
|
|
|
if (!(m_pD3D = Direct3DCreate8(D3D_SDK_VERSION)))
|
|
{
|
|
CLog::Log(LOGFATAL, "FATAL ERROR: Unable to create Direct3D!");
|
|
Sleep(INFINITE); // die
|
|
}
|
|
|
|
// Check if we have the required modes available
|
|
g_videoConfig.GetModes(m_pD3D);
|
|
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(g_videoConfig.GetSafeMode(), true);
|
|
CLog::Log(LOGERROR, "Resetting to mode %s", CDisplaySettings::Get().GetCurrentResolutionInfo().strMode.c_str());
|
|
CLog::Log(LOGERROR, "Done reset");
|
|
}
|
|
|
|
// Transfer the resolution information to our graphics context
|
|
g_graphicsContext.SetD3DParameters(&m_d3dpp);
|
|
g_graphicsContext.SetVideoResolution(CDisplaySettings::Get().GetCurrentResolution(), TRUE);
|
|
|
|
// Create the device
|
|
#ifdef HAS_XBOX_D3D
|
|
// Xbox MUST use HAL / Hardware Vertex Processing!
|
|
if (m_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &m_d3dpp, &m_pd3dDevice) != S_OK)
|
|
{
|
|
CLog::Log(LOGFATAL, "FATAL ERROR: Unable to create D3D Device!");
|
|
Sleep(INFINITE); // die
|
|
}
|
|
m_pd3dDevice->GetBackBuffer(0, 0, &m_pBackBuffer);
|
|
#else
|
|
if (m_pD3D->CreateDevice(0, D3DDEVTYPE_REF, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &m_d3dpp, &m_pd3dDevice) != S_OK)
|
|
{
|
|
CLog::Log(LOGFATAL, "FATAL ERROR: Unable to create D3D Device!");
|
|
Sleep(INFINITE); // die
|
|
}
|
|
#endif
|
|
|
|
m_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0, 0, 0);
|
|
m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
|
|
}
|
|
|
|
// This function does not return!
|
|
void CApplication::FatalErrorHandler(bool InitD3D, bool MapDrives, bool InitNetwork)
|
|
{
|
|
// XBMC couldn't start for some reason...
|
|
CLog::Log(LOGWARNING, "Emergency recovery console starting...");
|
|
|
|
bool HaveGamepad = true; // should always have the gamepad when we get here
|
|
if (InitD3D)
|
|
InitBasicD3D();
|
|
|
|
m_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, 0, 0, 0);
|
|
m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
|
|
|
|
// D3D is up, load default font
|
|
#ifdef HAS_XFONT
|
|
XFONT* pFont;
|
|
if (XFONT_OpenDefaultFont(&pFont) != S_OK)
|
|
{
|
|
CLog::Log(LOGFATAL, "FATAL ERROR: Unable to open default font!");
|
|
Sleep(INFINITE); // die
|
|
}
|
|
|
|
// defaults for text
|
|
pFont->SetBkMode(XFONT_OPAQUE);
|
|
pFont->SetBkColor(D3DCOLOR_XRGB(0, 0, 0));
|
|
pFont->SetTextColor(D3DCOLOR_XRGB(0xff, 0x20, 0x20));
|
|
#else
|
|
void *pFont = NULL;
|
|
#endif
|
|
int iLine = 0;
|
|
FEH_TextOut(pFont, iLine++, L"XBMC Fatal Error:");
|
|
char buf[500];
|
|
strncpy(buf, "Check xbmc log file for more information.", 500);
|
|
buf[499] = 0;
|
|
char* pNewline = strtok(buf, "\n");
|
|
while (pNewline)
|
|
{
|
|
FEH_TextOut(pFont, iLine++, L"%S", pNewline);
|
|
pNewline = strtok(NULL, "\n");
|
|
}
|
|
++iLine;
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
if (MapDrives)
|
|
{
|
|
// map in default drives
|
|
CIoSupport::RemapDriveLetter('C',"Harddisk0\\Partition2");
|
|
CIoSupport::RemapDriveLetter('D',"Cdrom0");
|
|
CIoSupport::RemapDriveLetter('E',"Harddisk0\\Partition1");
|
|
|
|
//Add. also Drive F/G
|
|
if (CIoSupport::PartitionExists(6))
|
|
CIoSupport::RemapDriveLetter('F',"Harddisk0\\Partition6");
|
|
if (CIoSupport::PartitionExists(7))
|
|
CIoSupport::RemapDriveLetter('G',"Harddisk0\\Partition7");
|
|
}
|
|
#endif
|
|
bool Pal = g_graphicsContext.GetVideoResolution() == RES_PAL_4x3;
|
|
|
|
if (HaveGamepad)
|
|
FEH_TextOut(pFont, (Pal ? 16 : 12) | 0x18000, L"Press any button to reboot");
|
|
|
|
bool NetworkUp = false;
|
|
|
|
// Boot up the network for FTP
|
|
if (InitNetwork)
|
|
{
|
|
std::vector<int> netorder;
|
|
netorder.push_back(NETWORK_DASH);
|
|
netorder.push_back(NETWORK_DHCP);
|
|
netorder.push_back(NETWORK_STATIC);
|
|
|
|
while(1)
|
|
{
|
|
std::vector<int>::iterator it;
|
|
for( it = netorder.begin();it != netorder.end(); it++)
|
|
{
|
|
m_network->Deinitialize();
|
|
|
|
#ifdef HAS_XBOX_NETWORK
|
|
if (!m_network->IsEthernetConnected())
|
|
{
|
|
FEH_TextOut(pFont, iLine, L"Network cable unplugged");
|
|
break;
|
|
}
|
|
#endif
|
|
switch( (*it) )
|
|
{
|
|
case NETWORK_DASH:
|
|
FEH_TextOut(pFont, iLine, L"Init network using dash settings...");
|
|
m_network->Initialize(NETWORK_DASH, "","","","","");
|
|
break;
|
|
case NETWORK_DHCP:
|
|
FEH_TextOut(pFont, iLine, L"Init network using DHCP...");
|
|
m_network->Initialize(NETWORK_DHCP, "","","","","");
|
|
break;
|
|
default:
|
|
FEH_TextOut(pFont, iLine, L"Init network using static ip...");
|
|
m_network->Initialize(NETWORK_STATIC,
|
|
"192.168.0.42",
|
|
"255.255.255.0",
|
|
"192.168.0.1",
|
|
"192.168.0.1",
|
|
"0.0.0.0" );
|
|
break;
|
|
}
|
|
|
|
#ifdef HAS_XBOX_NETWORK
|
|
DWORD dwState = XNET_GET_XNADDR_PENDING;
|
|
|
|
while (dwState == XNET_GET_XNADDR_PENDING)
|
|
{
|
|
dwState = m_network->UpdateState();
|
|
|
|
if (HaveGamepad && AnyButtonDown())
|
|
CApplicationMessenger::Get().PostMsg(TMSG_RESTART);
|
|
|
|
Sleep(50);
|
|
}
|
|
|
|
if ((dwState & XNET_GET_XNADDR_DHCP || dwState & XNET_GET_XNADDR_STATIC) && !(dwState & XNET_GET_XNADDR_NONE || dwState & XNET_GET_XNADDR_TROUBLESHOOT))
|
|
{
|
|
/* yay, we got network */
|
|
NetworkUp = true;
|
|
break;
|
|
}
|
|
#endif
|
|
/* increment line before next attempt */
|
|
++iLine;
|
|
}
|
|
|
|
/* break out of the continous loop if we have network*/
|
|
if( NetworkUp )
|
|
break;
|
|
else
|
|
{
|
|
int n = 10;
|
|
while (n)
|
|
{
|
|
FEH_TextOut(pFont, (iLine + 1) | 0x8000, L"Unable to init network, retrying in %d seconds", n--);
|
|
for (int i = 0; i < 20; ++i)
|
|
{
|
|
Sleep(50);
|
|
|
|
if (HaveGamepad && AnyButtonDown())
|
|
CApplicationMessenger::Get().PostMsg(TMSG_RESTART);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( NetworkUp )
|
|
{
|
|
FEH_TextOut(pFont, iLine++, L"IP Address: %S", m_network->m_networkinfo.ip);
|
|
++iLine;
|
|
}
|
|
|
|
if (NetworkUp)
|
|
{
|
|
#ifdef HAS_FTP_SERVER
|
|
// Start FTP with default settings
|
|
FEH_TextOut(pFont, iLine++, L"Starting FTP server...");
|
|
CNetworkServices::Get().StartFtpEmergencyRecoveryMode();
|
|
FEH_TextOut(pFont, iLine++, L"FTP server running on port %d, login: xbox/xbox", CNetworkServices::Get().GetFtpServerPort());
|
|
#endif
|
|
++iLine;
|
|
}
|
|
|
|
if (HaveGamepad)
|
|
{
|
|
for (;;)
|
|
{
|
|
Sleep(50);
|
|
if (AnyButtonDown())
|
|
{
|
|
g_application.Stop();
|
|
Sleep(200);
|
|
#ifdef _XBOX
|
|
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
|
|
XKUtils::XBOXPowerCycle();
|
|
#endif
|
|
#else
|
|
SendMessage(g_hWnd, WM_CLOSE, 0, 0);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#ifdef _XBOX
|
|
Sleep(INFINITE);
|
|
#else
|
|
SendMessage(g_hWnd, WM_CLOSE, 0, 0);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
LONG WINAPI CApplication::UnhandledExceptionFilter(struct _EXCEPTION_POINTERS *ExceptionInfo)
|
|
{
|
|
PCSTR pExceptionString = "Unknown exception code";
|
|
|
|
#define STRINGIFY_EXCEPTION(code) case code: pExceptionString = #code; break
|
|
|
|
switch (ExceptionInfo->ExceptionRecord->ExceptionCode)
|
|
{
|
|
STRINGIFY_EXCEPTION(EXCEPTION_ACCESS_VIOLATION);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_ARRAY_BOUNDS_EXCEEDED);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_BREAKPOINT);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_FLT_DENORMAL_OPERAND);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_FLT_DIVIDE_BY_ZERO);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_FLT_INEXACT_RESULT);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_FLT_INVALID_OPERATION);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_FLT_OVERFLOW);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_FLT_STACK_CHECK);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_ILLEGAL_INSTRUCTION);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_INT_DIVIDE_BY_ZERO);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_INT_OVERFLOW);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_INVALID_DISPOSITION);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_NONCONTINUABLE_EXCEPTION);
|
|
STRINGIFY_EXCEPTION(EXCEPTION_SINGLE_STEP);
|
|
}
|
|
#undef STRINGIFY_EXCEPTION
|
|
|
|
CLog::Log(LOGFATAL, "%s (0x%08x)\n at 0x%08x",
|
|
pExceptionString, ExceptionInfo->ExceptionRecord->ExceptionCode,
|
|
ExceptionInfo->ExceptionRecord->ExceptionAddress);
|
|
|
|
return ExceptionInfo->ExceptionRecord->ExceptionCode;
|
|
}
|
|
|
|
#ifdef _XBOX
|
|
#include "xbox/Undocumented.h"
|
|
extern "C" HANDLE __stdcall KeGetCurrentThread(VOID);
|
|
#endif
|
|
extern "C" void __stdcall init_emu_environ();
|
|
extern "C" void __stdcall update_emu_environ();
|
|
|
|
HRESULT CApplication::Create(HWND hWnd)
|
|
{
|
|
m_ServiceManager.reset(new CServiceManager());
|
|
if (!m_ServiceManager->Init1())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if defined(HAS_LINUX_NETWORK)
|
|
m_network = new CNetworkLinux();
|
|
#elif defined(HAS_WIN32_NETWORK)
|
|
m_network = new CNetworkWin32();
|
|
#else
|
|
m_network = new CNetwork();
|
|
#endif
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
// better 128mb ram support
|
|
// set MTRRDefType memory type to write-back as done in other XBox apps - seems a bit of a hack as really the def type
|
|
// should be uncachable and the mtrr/mask for ram instead set up for 128MB with writeback as is done in cromwell.
|
|
m_128MBHack = false;
|
|
MEMORYSTATUS status;
|
|
GlobalMemoryStatus( &status );
|
|
// if we have more than 64MB free
|
|
if( status.dwTotalPhys > 67108864 )
|
|
{
|
|
__asm
|
|
{
|
|
mov ecx, 0x2ff
|
|
rdmsr
|
|
mov al, 0x06
|
|
wrmsr
|
|
}
|
|
m_128MBHack = true;
|
|
}
|
|
g_advancedSettings.m_guiKeepInMemory = m_128MBHack;
|
|
#endif
|
|
|
|
// here we register all global classes for the CApplicationMessenger,
|
|
// after that we can send messages to the corresponding modules
|
|
CApplicationMessenger::Get().RegisterReceiver(this);
|
|
CApplicationMessenger::Get().RegisterReceiver(&g_playlistPlayer);
|
|
CApplicationMessenger::Get().RegisterReceiver(&g_infoManager);
|
|
|
|
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);
|
|
}
|
|
|
|
g_hWnd = hWnd;
|
|
|
|
HRESULT hr;
|
|
//grab a handle to our thread to be used later in identifying the render thread
|
|
m_threadID = GetCurrentThreadId();
|
|
|
|
//floating point precision to 24 bits (faster performance)
|
|
_controlfp(_PC_24, _MCW_PC);
|
|
|
|
CStdString strExecutablePath;
|
|
char szDevicePath[MAX_PATH];
|
|
|
|
// map Q to home drive of xbe to load the config file
|
|
CUtil::GetHomePath(strExecutablePath);
|
|
CIoSupport::GetPartition(strExecutablePath.c_str()[0], szDevicePath);
|
|
strcat(szDevicePath, &strExecutablePath.c_str()[2]);
|
|
CIoSupport::RemapDriveLetter('Q', szDevicePath);
|
|
|
|
// Do all the special:// & driveletter mapping
|
|
InitDirectoriesXbox();
|
|
|
|
if (!CLog::Init(CSpecialProtocol::TranslatePath(g_advancedSettings.m_logFolder).c_str()))
|
|
{
|
|
fprintf(stderr,"Could not init logging classes. Permission errors on ~/.xbmc (%s)\n",
|
|
CSpecialProtocol::TranslatePath(g_advancedSettings.m_logFolder).c_str());
|
|
return false;
|
|
}
|
|
|
|
init_emu_environ();
|
|
|
|
CProfilesManager::Get().Load();
|
|
|
|
/* install win32 exception translator, win32 exceptions
|
|
* can now be caught using c++ try catch */
|
|
win32_exception::install_handler();
|
|
|
|
CLog::Log(LOGNOTICE, "-----------------------------------------------------------------------");
|
|
CLog::Log(LOGNOTICE, "Starting XBMC4Xbox %s (SVN:%s, compiler %i). Built on %s ", VERSION_STRING, SVN_REV, _MSC_VER, __DATE__);
|
|
CSpecialProtocol::LogPaths();
|
|
|
|
char szXBEFileName[1024];
|
|
CIoSupport::GetXbePath(szXBEFileName);
|
|
CLog::Log(LOGNOTICE, "The executable running is: %s", szXBEFileName);
|
|
CLog::Log(LOGNOTICE, "Log File is located: %sxbmc.log", g_advancedSettings.m_logFolder.c_str());
|
|
CLog::Log(LOGNOTICE, "-----------------------------------------------------------------------");
|
|
|
|
// if we are running from DVD our UserData location will be TDATA
|
|
if (URIUtils::IsDVD(strExecutablePath))
|
|
{
|
|
// TODO: Should we copy over any UserData folder from the DVD?
|
|
if (!CFile::Exists("special://masterprofile/guisettings.xml")) // first run - cache userdata folder
|
|
{
|
|
CFileItemList items;
|
|
CUtil::GetRecursiveListing("special://xbmc/userdata",items,"", DIR_FLAG_NO_FILE_DIRS);
|
|
for (int i=0;i<items.Size();++i)
|
|
CFile::Copy(items[i]->GetPath(),"special://masterprofile/"+URIUtils::GetFileName(items[i]->GetPath()));
|
|
}
|
|
g_advancedSettings.m_logFolder = "special://masterprofile/";
|
|
}
|
|
else
|
|
{
|
|
CStdString strMnt = CSpecialProtocol::TranslatePath(CProfilesManager::Get().GetUserDataFolder());
|
|
if (strMnt.Left(2).Equals("Q:"))
|
|
{
|
|
CUtil::GetHomePath(strMnt);
|
|
strMnt += CSpecialProtocol::TranslatePath(CProfilesManager::Get().GetUserDataFolder()).substr(2);
|
|
}
|
|
|
|
CIoSupport::GetPartition(strMnt.c_str()[0], szDevicePath);
|
|
strcat(szDevicePath, &strMnt.c_str()[2]);
|
|
CIoSupport::RemapDriveLetter('T', szDevicePath);
|
|
}
|
|
|
|
if (m_128MBHack)
|
|
CLog::Log(LOGNOTICE, "128MB hack enabled");
|
|
|
|
CLog::Log(LOGNOTICE, "Setup DirectX");
|
|
// Create the Direct3D object
|
|
if ( NULL == ( m_pD3D = Direct3DCreate8(D3D_SDK_VERSION) ) )
|
|
{
|
|
CLog::Log(LOGFATAL, "XBAppEx: Unable to create Direct3D!" );
|
|
return E_FAIL;
|
|
}
|
|
|
|
//list available videomodes
|
|
g_videoConfig.GetModes(m_pD3D);
|
|
//init the present parameters with values that are supported
|
|
RESOLUTION initialResolution = g_videoConfig.GetInitialMode(m_pD3D, &m_d3dpp);
|
|
// Transfer the resolution information to our graphics context
|
|
g_graphicsContext.SetD3DParameters(&m_d3dpp);
|
|
g_graphicsContext.SetVideoResolution(initialResolution, TRUE);
|
|
|
|
// 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.
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
XInitDevices( m_dwNumInputDeviceTypes, m_InputDeviceTypes );
|
|
|
|
// Create the gamepad devices
|
|
if ( FAILED(hr = XBInput_CreateGamepads(&m_Gamepad)) )
|
|
{
|
|
CLog::Log(LOGERROR, "XBAppEx: Call to CreateGamepads() failed!" );
|
|
return hr;
|
|
}
|
|
|
|
if ( FAILED(hr = XBInput_CreateIR_Remotes()) )
|
|
{
|
|
CLog::Log(LOGERROR, "XBAppEx: Call to CreateIRRemotes() failed!" );
|
|
return hr;
|
|
}
|
|
#endif
|
|
|
|
// Create the Mouse and Keyboard devices
|
|
g_Mouse.Initialize(&hWnd);
|
|
g_Keyboard.Initialize(hWnd);
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
// Wait for controller polling to finish. in an elegant way, instead of a Sleep(1000)
|
|
while (XGetDeviceEnumerationStatus() == XDEVICE_ENUMERATION_BUSY)
|
|
{
|
|
ReadInput();
|
|
}
|
|
Sleep(10); // needed or the readinput doesnt fetch anything
|
|
ReadInput();
|
|
#endif
|
|
#ifdef HAS_GAMEPAD
|
|
//Check for LTHUMBCLICK+RTHUMBCLICK and BLACK+WHITE, no LTRIGGER+RTRIGGER
|
|
if (((m_DefaultGamepad.wButtons & (XINPUT_GAMEPAD_LEFT_THUMB + XINPUT_GAMEPAD_RIGHT_THUMB)) && !(m_DefaultGamepad.wButtons & (KEY_BUTTON_LEFT_TRIGGER+KEY_BUTTON_RIGHT_TRIGGER))) ||
|
|
((m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_BLACK] && m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE]) && !(m_DefaultGamepad.wButtons & KEY_BUTTON_LEFT_TRIGGER+KEY_BUTTON_RIGHT_TRIGGER)))
|
|
{
|
|
CLog::Log(LOGINFO, "Key combination detected for userdata deletion (LTHUMB+RTHUMB or BLACK+WHITE)");
|
|
InitBasicD3D();
|
|
// D3D is up, load default font
|
|
XFONT* pFont;
|
|
if (XFONT_OpenDefaultFont(&pFont) != S_OK)
|
|
{
|
|
CLog::Log(LOGFATAL, "FATAL ERROR: Unable to open default font!");
|
|
Sleep(INFINITE); // die
|
|
}
|
|
// defaults for text
|
|
pFont->SetBkMode(XFONT_OPAQUE);
|
|
pFont->SetBkColor(D3DCOLOR_XRGB(0, 0, 0));
|
|
pFont->SetTextColor(D3DCOLOR_XRGB(0xff, 0x20, 0x20));
|
|
int iLine = 0;
|
|
FEH_TextOut(pFont, iLine++, L"Key combination for userdata deletion detected!");
|
|
FEH_TextOut(pFont, iLine++, L"Are you sure you want to proceed?");
|
|
iLine++;
|
|
FEH_TextOut(pFont, iLine++, L"A for yes, any other key for no");
|
|
bool bAnyAnalogKey = false;
|
|
while (m_DefaultGamepad.wPressedButtons != XBGAMEPAD_NONE) // wait for user to let go of lclick + rclick
|
|
{
|
|
ReadInput();
|
|
}
|
|
while (m_DefaultGamepad.wPressedButtons == XBGAMEPAD_NONE && !bAnyAnalogKey)
|
|
{
|
|
ReadInput();
|
|
bAnyAnalogKey = m_DefaultGamepad.bPressedAnalogButtons[0] || m_DefaultGamepad.bPressedAnalogButtons[1] || m_DefaultGamepad.bPressedAnalogButtons[2] || m_DefaultGamepad.bPressedAnalogButtons[3] || m_DefaultGamepad.bPressedAnalogButtons[4] || m_DefaultGamepad.bPressedAnalogButtons[5] || m_DefaultGamepad.bPressedAnalogButtons[6] || m_DefaultGamepad.bPressedAnalogButtons[7];
|
|
}
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_A])
|
|
{
|
|
CUtil::DeleteGUISettings();
|
|
CUtil::WipeDir(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"database\\"));
|
|
CUtil::WipeDir(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"thumbnails\\"));
|
|
CUtil::WipeDir(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"playlists\\"));
|
|
CUtil::WipeDir(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"cache\\"));
|
|
CUtil::WipeDir(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"profiles\\"));
|
|
CUtil::WipeDir(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"visualisations\\"));
|
|
CFile::Delete(URIUtils::AddFileToFolder(CProfilesManager::Get().GetUserDataFolder(),"avpacksettings.xml"));
|
|
// delete all profiles
|
|
for (size_t i = 0; i < CProfilesManager::Get().GetNumberOfProfiles(); ++i)
|
|
CProfilesManager::Get().DeleteProfile(i);
|
|
|
|
CProfilesManager::Get().Save();
|
|
|
|
char szXBEFileName[1024];
|
|
|
|
CIoSupport::GetXbePath(szXBEFileName);
|
|
CUtil::RunXBE(szXBEFileName);
|
|
}
|
|
m_pd3dDevice->Release();
|
|
}
|
|
#endif
|
|
|
|
CIoSupport::RemapDriveLetter('C', "Harddisk0\\Partition2");
|
|
CIoSupport::RemapDriveLetter('E', "Harddisk0\\Partition1");
|
|
|
|
CIoSupport::Dismount("Cdrom0");
|
|
CIoSupport::RemapDriveLetter('D', "Cdrom0");
|
|
|
|
// Attempt to read the LBA48 v3 patch partition table, if kernel supports the command and it exists.
|
|
CIoSupport::ReadPartitionTable();
|
|
if (CIoSupport::HasPartitionTable())
|
|
{
|
|
// Mount up to Partition15 if they are available.
|
|
for (int i=EXTEND_PARTITION_BEGIN; i <= (EXTEND_PARTITION_BEGIN+EXTEND_PARTITIONS_LIMIT-1); i++)
|
|
{
|
|
char szDevice[32];
|
|
if (CIoSupport::PartitionExists(i))
|
|
{
|
|
char cDriveLetter = 'A' + i - 1;
|
|
|
|
char extendDriveLetter = CIoSupport::GetExtendedPartitionDriveLetter(cDriveLetter-EXTEND_DRIVE_BEGIN);
|
|
CLog::Log(LOGNOTICE, " map extended drive %c:", extendDriveLetter);
|
|
|
|
sprintf(szDevice, "Harddisk0\\Partition%u", i);
|
|
|
|
CIoSupport::RemapDriveLetter(extendDriveLetter, szDevice);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CIoSupport::DriveExists('F'))
|
|
CIoSupport::RemapDriveLetter('F', "Harddisk0\\Partition6");
|
|
if (CIoSupport::DriveExists('G'))
|
|
CIoSupport::RemapDriveLetter('G', "Harddisk0\\Partition7");
|
|
}
|
|
|
|
CIoSupport::RemapDriveLetter('X',"Harddisk0\\Partition3");
|
|
CIoSupport::RemapDriveLetter('Y',"Harddisk0\\Partition4");
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
CIoSupport::RemapDriveLetter('Z',"Harddisk0\\Partition5");
|
|
#endif
|
|
|
|
CLog::Log(LOGINFO, "Drives are mapped");
|
|
|
|
// Initialize default Settings - don't move
|
|
CLog::Log(LOGNOTICE, "load settings...");
|
|
if (!CSettings::GetInstance().Initialize())
|
|
return false;
|
|
|
|
// load the actual values
|
|
if (!CSettings::GetInstance().Load())
|
|
{
|
|
CLog::Log(LOGERROR, "Unable to load settings");
|
|
FatalErrorHandler(true, true, true);
|
|
}
|
|
CSettings::GetInstance().SetLoaded();
|
|
|
|
CLog::Log(LOGINFO, "creating subdirectories");
|
|
CLog::Log(LOGINFO, "userdata folder: %s", CURL::GetRedacted(CProfilesManager::Get().GetProfileUserDataFolder()).c_str());
|
|
CLog::Log(LOGINFO, "recording folder: %s", CURL::GetRedacted(CSettings::GetInstance().GetString("audiocds.recordingpath")).c_str());
|
|
CLog::Log(LOGINFO, "screenshots folder: %s", CURL::GetRedacted(CSettings::GetInstance().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
|
|
|
|
// initialize the addon database (must be before the addon manager is init'd)
|
|
CDatabaseManager::GetInstance().Initialize(true);
|
|
|
|
if (!m_ServiceManager->Init2())
|
|
{
|
|
FatalErrorHandler(true, true, true);
|
|
}
|
|
|
|
// set logging from debug add-on
|
|
AddonPtr addon;
|
|
CServiceBroker::GetAddonMgr().GetAddon("xbmc.debug", addon);
|
|
if (addon)
|
|
g_advancedSettings.SetExtraLogsFromAddon(addon.get());
|
|
|
|
// load the keyboard layouts
|
|
if (!CKeyboardLayoutManager::GetInstance().Load())
|
|
{
|
|
CLog::Log(LOGFATAL, "CApplication::Create: Unable to load keyboard layouts");
|
|
FatalErrorHandler(true, true, true);
|
|
}
|
|
|
|
// Check for WHITE + Y for forced Error Handler (to recover if something screwy happens)
|
|
#ifdef HAS_GAMEPAD
|
|
if (m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_Y] && m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_WHITE])
|
|
{
|
|
CLog::Log(LOGERROR, "Key code detected for Error Recovery mode");
|
|
FatalErrorHandler(true, true, true);
|
|
}
|
|
#endif
|
|
|
|
//Check for X+Y - if pressed, set debug log mode and mplayer debuging on
|
|
CheckForDebugButtonCombo();
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
bool bNeedReboot = false;
|
|
char temp[1024];
|
|
CIoSupport::GetXbePath(temp);
|
|
char temp2[1024];
|
|
char temp3;
|
|
temp3 = temp[0];
|
|
CIoSupport::GetPartition(temp3,temp2);
|
|
CStdString strTemp(temp+2);
|
|
int iLastSlash = strTemp.rfind('\\');
|
|
strcat(temp2,strTemp.substr(0,iLastSlash).c_str());
|
|
F_VIDEO ForceVideo = VIDEO_NULL;
|
|
F_COUNTRY ForceCountry = COUNTRY_NULL;
|
|
|
|
if (CTrainer::RemoveTrainer())
|
|
bNeedReboot = true;
|
|
|
|
// now check if we are switching video modes. if, are we in the wrong mode according to eeprom?
|
|
if (CSettings::GetInstance().GetBool("myprograms.gameautoregion"))
|
|
{
|
|
bool fDoPatchTest = false;
|
|
|
|
// should use xkeeprom.h :/
|
|
EEPROMDATA EEPROM;
|
|
ZeroMemory(&EEPROM, sizeof(EEPROMDATA));
|
|
|
|
if( XKUtils::ReadEEPROMFromXBOX((LPBYTE)&EEPROM))
|
|
{
|
|
DWORD DWVideo = *(LPDWORD)(&EEPROM.VideoStandard[0]);
|
|
char temp[1024];
|
|
CIoSupport::GetXbePath(temp);
|
|
char temp2[1024];
|
|
char temp3;
|
|
temp3 = temp[0];
|
|
CIoSupport::GetPartition(temp3,temp2);
|
|
CStdString strTemp(temp+2);
|
|
int iLastSlash = strTemp.rfind('\\');
|
|
strcat(temp2,strTemp.substr(0,iLastSlash).c_str());
|
|
|
|
if ((DWVideo == XKEEPROM::VIDEO_STANDARD::NTSC_M) && ((XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I) || (XGetVideoStandard() == XC_VIDEO_STANDARD_NTSC_J) || initialResolution > 5))
|
|
{
|
|
CLog::Log(LOGINFO, "Rebooting to change resolution from %s back to NTSC_M", (XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I) ? "PAL" : "NTSC_J");
|
|
ForceVideo = VIDEO_NTSCM;
|
|
ForceCountry = COUNTRY_USA;
|
|
bNeedReboot = true;
|
|
fDoPatchTest = true;
|
|
}
|
|
else if ((DWVideo == XKEEPROM::VIDEO_STANDARD::PAL_I) && ((XGetVideoStandard() == XC_VIDEO_STANDARD_NTSC_M) || (XGetVideoStandard() == XC_VIDEO_STANDARD_NTSC_J) || initialResolution < 6))
|
|
{
|
|
CLog::Log(LOGINFO, "Rebooting to change resolution from %s back to PAL_I", (XGetVideoStandard() == XC_VIDEO_STANDARD_NTSC_M) ? "NTSC_M" : "NTSC_J");
|
|
ForceVideo = VIDEO_PAL50;
|
|
ForceCountry = COUNTRY_EUR;
|
|
bNeedReboot = true;
|
|
fDoPatchTest = true;
|
|
}
|
|
else if ((DWVideo == XKEEPROM::VIDEO_STANDARD::NTSC_J) && ((XGetVideoStandard() == XC_VIDEO_STANDARD_NTSC_M) || (XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I) || initialResolution > 5))
|
|
{
|
|
CLog::Log(LOGINFO, "Rebooting to change resolution from %s back to NTSC_J", (XGetVideoStandard() == XC_VIDEO_STANDARD_PAL_I) ? "PAL" : "NTSC_M");
|
|
ForceVideo = VIDEO_NTSCJ;
|
|
ForceCountry = COUNTRY_JAP;
|
|
bNeedReboot = true;
|
|
fDoPatchTest = true;
|
|
}
|
|
else
|
|
CUtil::RemoveKernelPatch(); // This removes the Resolution patch from the kernel if it is not needed (if actual resolution matches eeprom setting)
|
|
|
|
if (fDoPatchTest) // Is set if we have to test whether our patch is in the kernel & therefore responsible for the mismatch of resolution & eeprom setting
|
|
{
|
|
if (!CUtil::LookForKernelPatch()) // If our patch is not present we are not responsible for the mismatch of current resolution & eeprom setting
|
|
{
|
|
// We do a hard reset to come back to default resolution and avoid infinite reboots
|
|
CLog::Log(LOGINFO, "No infinite reboot loop...");
|
|
CApplicationMessenger::Get().PostMsg(TMSG_RESET);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bNeedReboot)
|
|
{
|
|
Destroy();
|
|
CUtil::LaunchXbe(temp2,("D:\\"+strTemp.substr(iLastSlash+1)).c_str(),NULL,ForceVideo,ForceCountry);
|
|
}
|
|
#endif
|
|
|
|
CLog::Log(LOGINFO, "load keymapping");
|
|
if (!CButtonTranslator::GetInstance().Load())
|
|
FatalErrorHandler(true, false, true);
|
|
|
|
// Retrieve the matching resolution based on GUI settings
|
|
CDisplaySettings::Get().SetCurrentResolution(CDisplaySettings::Get().GetDisplayResolution());
|
|
CLog::Log(LOGNOTICE, "Checking resolution %i", CDisplaySettings::Get().GetCurrentResolution());
|
|
if (!g_graphicsContext.IsValidResolution(CDisplaySettings::Get().GetCurrentResolution()))
|
|
{
|
|
#ifdef _XBOX
|
|
RESOLUTION newRes = g_videoConfig.GetBestMode();
|
|
#else
|
|
RESOLUTION newRes = g_videoConfig.GetSafeMode();
|
|
#endif
|
|
CLog::Log(LOGNOTICE, "Setting safe mode %i", newRes);
|
|
CDisplaySettings::Get().SetCurrentResolution(newRes, true);
|
|
}
|
|
|
|
// Transfer the new resolution information to our graphics context
|
|
#ifndef HAS_XBOX_D3D
|
|
m_d3dpp.Windowed = TRUE;
|
|
m_d3dpp.hDeviceWindow = g_hWnd;
|
|
#else
|
|
#define D3DCREATE_MULTITHREADED 0
|
|
#endif
|
|
|
|
g_graphicsContext.SetD3DParameters(&m_d3dpp);
|
|
g_graphicsContext.SetVideoResolution(CDisplaySettings::Get().GetCurrentResolution(), TRUE);
|
|
|
|
if ( FAILED( hr = m_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
|
|
D3DCREATE_MULTITHREADED | D3DCREATE_HARDWARE_VERTEXPROCESSING,
|
|
&m_d3dpp, &m_pd3dDevice ) ) )
|
|
{
|
|
// try software vertex processing
|
|
if ( FAILED( hr = m_pD3D->CreateDevice(0, D3DDEVTYPE_HAL, NULL,
|
|
D3DCREATE_MULTITHREADED | D3DCREATE_SOFTWARE_VERTEXPROCESSING,
|
|
&m_d3dpp, &m_pd3dDevice ) ) )
|
|
{
|
|
// and slow as arse reference processing
|
|
if ( FAILED( hr = m_pD3D->CreateDevice(0, D3DDEVTYPE_REF, NULL,
|
|
D3DCREATE_MULTITHREADED | D3DCREATE_SOFTWARE_VERTEXPROCESSING,
|
|
&m_d3dpp, &m_pd3dDevice ) ) )
|
|
{
|
|
|
|
CLog::Log(LOGFATAL, "XBAppEx: Could not create D3D device!" );
|
|
CLog::Log(LOGFATAL, " width/height:(%ix%i)" , m_d3dpp.BackBufferWidth, m_d3dpp.BackBufferHeight);
|
|
CLog::Log(LOGFATAL, " refreshrate:%i" , m_d3dpp.FullScreen_RefreshRateInHz);
|
|
if (m_d3dpp.Flags & D3DPRESENTFLAG_WIDESCREEN)
|
|
CLog::Log(LOGFATAL, " 16:9 widescreen");
|
|
else
|
|
CLog::Log(LOGFATAL, " 4:3");
|
|
|
|
if (m_d3dpp.Flags & D3DPRESENTFLAG_INTERLACED)
|
|
CLog::Log(LOGFATAL, " interlaced");
|
|
if (m_d3dpp.Flags & D3DPRESENTFLAG_PROGRESSIVE)
|
|
CLog::Log(LOGFATAL, " progressive");
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_graphicsContext.SetD3DDevice(m_pd3dDevice);
|
|
g_graphicsContext.CaptureStateBlock();
|
|
// set filters
|
|
g_graphicsContext.Get3DDevice()->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR /*g_settings.m_minFilter*/ );
|
|
g_graphicsContext.Get3DDevice()->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR /*g_settings.m_maxFilter*/ );
|
|
CUtil::InitGamma();
|
|
|
|
// set GUI res and force the clear of the screen
|
|
g_graphicsContext.SetVideoResolution(CDisplaySettings::Get().GetCurrentResolution(), TRUE, true);
|
|
|
|
CSplash::GetInstance().Show();
|
|
|
|
int iResolution = g_graphicsContext.GetVideoResolution();
|
|
CLog::Log(LOGINFO, "GUI format %ix%i %s",
|
|
CDisplaySettings::Get().GetResolutionInfo(iResolution).iWidth,
|
|
CDisplaySettings::Get().GetResolutionInfo(iResolution).iHeight,
|
|
CDisplaySettings::Get().GetResolutionInfo(iResolution).strMode.c_str());
|
|
|
|
// show recovery console on fatal error instead of freezing
|
|
CLog::Log(LOGINFO, "install unhandled exception filter");
|
|
SetUnhandledExceptionFilter(UnhandledExceptionFilter);
|
|
|
|
#ifdef HAS_XBOX_D3D
|
|
D3DDevice::SetWaitCallback(WaitCallback);
|
|
#endif
|
|
|
|
g_Mouse.SetEnabled(CSettings::GetInstance().GetBool("input.enablemouse"));
|
|
|
|
CUtil::InitRandomSeed();
|
|
|
|
g_windowManager.Initialize();
|
|
|
|
return CXBApplicationEx::Create(hWnd);
|
|
}
|
|
|
|
|
|
HRESULT CApplication::Initialize()
|
|
{
|
|
// load the language and its translated strings
|
|
if (!LoadLanguage(false))
|
|
return false;
|
|
|
|
/* setup network based on our settings */
|
|
/* network will start it's init procedure */
|
|
if(m_network->SetupNetwork())
|
|
m_network->WaitForSetup();
|
|
|
|
// initialize (and update as needed) our databases
|
|
CDatabaseManager::GetInstance().Initialize();
|
|
|
|
StartServices();
|
|
|
|
bool uiInitializationFinished = true;
|
|
|
|
g_windowManager.CreateWindows();
|
|
/* window id's 3000 - 3100 are reserved for python */
|
|
|
|
std::string defaultSkin = ((const CSettingString*)CSettings::GetInstance().GetSetting("lookandfeel.skin"))->GetDefault();
|
|
if (!LoadSkin(CSettings::GetInstance().GetString("lookandfeel.skin")))
|
|
{
|
|
CLog::Log(LOGERROR, "Failed to load skin '%s'", CSettings::GetInstance().GetString("lookandfeel.skin").c_str());
|
|
if (!LoadSkin(defaultSkin))
|
|
{
|
|
CLog::Log(LOGFATAL, "Default skin '%s' could not be loaded! Terminating..", defaultSkin.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// initialize splash window after splash screen disappears
|
|
// because we need a real window in the background which gets
|
|
// rendered while we load the main window or enter the master lock key
|
|
if (g_advancedSettings.m_splashImage)
|
|
g_windowManager.ActivateWindow(WINDOW_SPLASH);
|
|
|
|
m_ctrDpad.SetDelays(100, 500); //g_settings.m_iMoveDelayController, g_settings.m_iRepeatDelayController);
|
|
|
|
if (CSettings::GetInstance().GetBool("masterlock.startuplock") &&
|
|
CProfilesManager::Get().GetMasterProfile().getLockMode() != LOCK_MODE_EVERYONE &&
|
|
!CProfilesManager::Get().GetMasterProfile().getLockCode().IsEmpty())
|
|
{
|
|
g_passwordManager.CheckStartUpLock();
|
|
}
|
|
|
|
// check if we should use the login screen
|
|
if (CProfilesManager::Get().UsingLoginScreen())
|
|
{
|
|
// the login screen still needs to perform additional initialization
|
|
uiInitializationFinished = false;
|
|
|
|
g_windowManager.ActivateWindow(WINDOW_LOGIN_SCREEN);
|
|
}
|
|
else
|
|
{
|
|
CServiceBroker::GetAddonMgr().StartServices(false);
|
|
|
|
// activate the configured start window
|
|
int firstWindow = g_SkinInfo->GetFirstWindow();
|
|
g_windowManager.ActivateWindow(firstWindow);
|
|
|
|
if (g_windowManager.GetActiveWindowID() == WINDOW_STARTUP_ANIM)
|
|
{
|
|
CLog::Log(LOGWARNING, "CApplication::Initialize - startup.xml taints init process");
|
|
}
|
|
|
|
// the startup window is considered part of the initialization as it most likely switches to the final window
|
|
uiInitializationFinished = firstWindow != WINDOW_STARTUP_ANIM;
|
|
|
|
if (!m_ServiceManager->Init3())
|
|
{
|
|
CLog::Log(LOGERROR, "Application - Init3 failed");
|
|
}
|
|
}
|
|
|
|
//g_sysinfo.Refresh();
|
|
|
|
CLog::Log(LOGINFO, "removing tempfiles");
|
|
CUtil::RemoveTempFiles();
|
|
|
|
// Show mute symbol
|
|
if (m_muted)
|
|
Mute();
|
|
SetVolume(m_volumeLevel, false);
|
|
|
|
if (!CProfilesManager::Get().UsingLoginScreen())
|
|
{
|
|
UpdateLibraries();
|
|
SetLoggingIn(true);
|
|
}
|
|
|
|
m_slowTimer.StartZero();
|
|
|
|
#ifdef __APPLE__
|
|
g_xbmcHelper.CaptureAllInput();
|
|
#endif
|
|
CServiceBroker::GetAddonMgr().StartServices(true);
|
|
|
|
// configure seek handler
|
|
CSeekHandler::Get().Configure();
|
|
|
|
// register action listeners
|
|
RegisterActionListener(&CSeekHandler::Get());
|
|
RegisterActionListener(&CPlayerController::GetInstance());
|
|
|
|
CRepositoryUpdater::GetInstance().Start();
|
|
|
|
CLog::Log(LOGNOTICE, "initialize done");
|
|
|
|
m_bInitializing = false;
|
|
|
|
// final check for debugging combo
|
|
CheckForDebugButtonCombo();
|
|
|
|
// reset our screensaver (starts timers etc.)
|
|
ResetScreenSaver();
|
|
|
|
// if the user interfaces has been fully initialized let everyone know
|
|
if (uiInitializationFinished)
|
|
{
|
|
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UI_READY);
|
|
g_windowManager.SendThreadMessage(msg);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void CApplication::PrintXBEToLCD(const char* xbePath)
|
|
{
|
|
#ifdef HAS_LCD
|
|
int pLine = 0;
|
|
CStdString strXBEName;
|
|
if (!CUtil::GetXBEDescription(xbePath, strXBEName))
|
|
{
|
|
CUtil::GetDirectoryName(xbePath, strXBEName);
|
|
CUtil::ShortenFileName(strXBEName);
|
|
CUtil::RemoveIllegalChars(strXBEName);
|
|
}
|
|
// crop to LCD screen size
|
|
if ((int)strXBEName.size() > g_advancedSettings.m_lcdColumns)
|
|
strXBEName = strXBEName.Left(g_advancedSettings.m_lcdColumns);
|
|
if (g_lcd)
|
|
{
|
|
g_infoManager.SetLaunchingXBEName(strXBEName);
|
|
g_lcd->Render(ILCD::LCD_MODE_XBE_LAUNCH);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CApplication::StartIdleThread()
|
|
{
|
|
m_idleThread.Create(false, 0x100);
|
|
}
|
|
|
|
void CApplication::StopIdleThread()
|
|
{
|
|
m_idleThread.StopThread();
|
|
}
|
|
|
|
void CApplication::RefreshEventServer()
|
|
{
|
|
#ifdef HAS_EVENT_SERVER
|
|
if (CSettings::GetInstance().GetBool("services.esenabled"))
|
|
{
|
|
CEventServer::GetInstance()->RefreshSettings();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CApplication::StartLEDControl(bool switchoff)
|
|
{
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
if (switchoff && CSettings::GetInstance().GetInt("system.ledcolour") != LED_COLOUR_NO_CHANGE)
|
|
{
|
|
if ( m_pPlayer->IsPlayingVideo() && (CSettings::GetInstance().GetInt("system.leddisableonplayback") == LED_PLAYBACK_VIDEO))
|
|
ILED::CLEDControl(LED_COLOUR_OFF);
|
|
if ( m_pPlayer->IsPlayingAudio() && (CSettings::GetInstance().GetInt("system.leddisableonplayback") == LED_PLAYBACK_MUSIC))
|
|
ILED::CLEDControl(LED_COLOUR_OFF);
|
|
if ( ((m_pPlayer->IsPlayingVideo() || m_pPlayer->IsPlayingAudio())) && (CSettings::GetInstance().GetInt("system.leddisableonplayback") == LED_PLAYBACK_VIDEO_MUSIC))
|
|
ILED::CLEDControl(LED_COLOUR_OFF);
|
|
}
|
|
else if (!switchoff)
|
|
ILED::CLEDControl(CSettings::GetInstance().GetInt("system.ledcolour"));
|
|
#endif
|
|
}
|
|
|
|
void CApplication::DimLCDOnPlayback(bool dim)
|
|
{
|
|
#ifdef HAS_LCD
|
|
if(g_lcd && dim && (CSettings::GetInstance().GetInt("lcd.disableonplayback") != LED_PLAYBACK_OFF) && (CSettings::GetInstance().GetInt("lcd.type") != LCD_TYPE_NONE))
|
|
{
|
|
if ( (m_pPlayer->IsPlayingVideo()) && CSettings::GetInstance().GetInt("lcd.disableonplayback") == LED_PLAYBACK_VIDEO)
|
|
g_lcd->SetBackLight(0);
|
|
if ( (m_pPlayer->IsPlayingAudio()) && CSettings::GetInstance().GetInt("lcd.disableonplayback") == LED_PLAYBACK_MUSIC)
|
|
g_lcd->SetBackLight(0);
|
|
if ( ((m_pPlayer->IsPlayingVideo() || m_pPlayer->IsPlayingAudio())) && CSettings::GetInstance().GetInt("lcd.disableonplayback") == LED_PLAYBACK_VIDEO_MUSIC)
|
|
g_lcd->SetBackLight(0);
|
|
}
|
|
else if(!dim)
|
|
g_lcd->SetBackLight(CSettings::GetInstance().GetInt("lcd.backlight"));
|
|
#endif
|
|
}
|
|
|
|
void CApplication::StartServices()
|
|
{
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
if (g_advancedSettings.m_bPowerSave)
|
|
{
|
|
CLog::Log(LOGNOTICE, "Using idle thread with HLT (power saving)");
|
|
StartIdleThread();
|
|
}
|
|
else
|
|
CLog::Log(LOGNOTICE, "Not using idle thread with HLT (no power saving)");
|
|
#endif
|
|
|
|
CheckDate();
|
|
StartLEDControl(false);
|
|
|
|
// Start Thread for DVD Mediatype detection
|
|
CLog::Log(LOGNOTICE, "start dvd mediatype detection");
|
|
m_DetectDVDType.Create(false, THREAD_MINSTACKSIZE);
|
|
|
|
#ifdef HAS_LCD
|
|
CLCDFactory factory;
|
|
g_lcd = factory.Create();
|
|
if (g_lcd)
|
|
{
|
|
g_lcd->Initialize();
|
|
}
|
|
#endif
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
if (CSettings::GetInstance().GetBool("system.autotemperature"))
|
|
{
|
|
CLog::Log(LOGNOTICE, "start fancontroller");
|
|
CFanController::Instance()->Start(CSettings::GetInstance().GetInt("system.targettemperature"), CSettings::GetInstance().GetInt("system.minfanspeed"));
|
|
}
|
|
else if (CSettings::GetInstance().GetBool("system.fanspeedcontrol"))
|
|
{
|
|
CLog::Log(LOGNOTICE, "setting fanspeed");
|
|
CFanController::Instance()->SetFanSpeed(CSettings::GetInstance().GetInt("system.fanspeed"));
|
|
}
|
|
int setting_level = CSettings::GetInstance().GetInt("harddisk.aamlevel");
|
|
if (setting_level == AAM_QUIET)
|
|
XKHDD::SetAAMLevel(0x80);
|
|
else if (setting_level == AAM_FAST)
|
|
XKHDD::SetAAMLevel(0xFE);
|
|
setting_level = CSettings::GetInstance().GetInt("harddisk.apmlevel");
|
|
switch(setting_level)
|
|
{
|
|
case APM_LOPOWER:
|
|
XKHDD::SetAPMLevel(0x80);
|
|
break;
|
|
case APM_HIPOWER:
|
|
XKHDD::SetAPMLevel(0xFE);
|
|
break;
|
|
case APM_LOPOWER_STANDBY:
|
|
XKHDD::SetAPMLevel(0x01);
|
|
break;
|
|
case APM_HIPOWER_STANDBY:
|
|
XKHDD::SetAPMLevel(0x7F);
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CApplication::CheckDate()
|
|
{
|
|
CLog::Log(LOGNOTICE, "Checking the Date!");
|
|
// Check the Date: Year, if it is above 2099 set to 2004!
|
|
SYSTEMTIME CurTime;
|
|
SYSTEMTIME NewTime;
|
|
GetLocalTime(&CurTime);
|
|
GetLocalTime(&NewTime);
|
|
CLog::Log(LOGINFO, "Current Date is: %i-%i-%i", CurTime.wDay, CurTime.wMonth, CurTime.wYear);
|
|
if ((CurTime.wYear > 2099) || (CurTime.wYear < 2001) ) // XBOX MS Dashboard also uses min/max DateYear 2001/2099 !!
|
|
{
|
|
CLog::Log(LOGNOTICE, "- The Date is Wrong: Setting New Date!");
|
|
NewTime.wYear = 2004; // 2004
|
|
NewTime.wMonth = 1; // January
|
|
NewTime.wDayOfWeek = 1; // Monday
|
|
NewTime.wDay = 5; // Monday 05.01.2004!!
|
|
NewTime.wHour = 12;
|
|
NewTime.wMinute = 0;
|
|
|
|
FILETIME stNewTime, stCurTime;
|
|
SystemTimeToFileTime(&NewTime, &stNewTime);
|
|
SystemTimeToFileTime(&CurTime, &stCurTime);
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
NtSetSystemTime(&stNewTime, &stCurTime); // Set a Default Year 2004!
|
|
#endif
|
|
CLog::Log(LOGNOTICE, "- New Date is now: %i-%i-%i",NewTime.wDay, NewTime.wMonth, NewTime.wYear);
|
|
}
|
|
return ;
|
|
}
|
|
|
|
void CApplication::StopServices()
|
|
{
|
|
m_network->NetworkMessage(CNetwork::SERVICES_DOWN, 0);
|
|
|
|
CLog::Log(LOGNOTICE, "stop dvd detect media");
|
|
m_DetectDVDType.StopThread();
|
|
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
CLog::Log(LOGNOTICE, "stop fancontroller");
|
|
CFanController::Instance()->Stop();
|
|
CFanController::RemoveInstance();
|
|
if (g_advancedSettings.m_bPowerSave)
|
|
StopIdleThread();
|
|
#endif
|
|
}
|
|
|
|
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::GetInstance().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)
|
|
{
|
|
if (HELPERS::ShowYesNoDialogText(13123, 13111, "", "", 10000) !=
|
|
YES)
|
|
{
|
|
m_skinReverting = true;
|
|
if (oldSkin.empty())
|
|
CSettings::GetInstance().GetSetting("lookandfeel.skin")->Reset();
|
|
else
|
|
CSettings::GetInstance().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::GetInstance().GetSetting("lookandfeel.skin"))->GetDefault();
|
|
if (newSkin != defaultSkin)
|
|
{
|
|
m_skinReverting = true;
|
|
CSettings::GetInstance().GetSetting("lookandfeel.skin")->Reset();
|
|
CGUIDialogKaiToast::QueueNotification(CGUIDialogKaiToast::Error, g_localizeStrings.Get(24102), g_localizeStrings.Get(24103));
|
|
}
|
|
}
|
|
m_skinReverting = 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;
|
|
}
|
|
|
|
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::GetInt(audioElement, "volumelevel", m_volumeLevel, VOLUME_MINIMUM, VOLUME_MAXIMUM))
|
|
m_volumeLevel = VOLUME_MAXIMUM;
|
|
if (!XMLUtils::GetInt(audioElement, "dynamicrangecompression", m_dynamicRangeCompressionLevel, 0/*VOLUME_DRC_MINIMUM*/, 3000/*VOLUME_DRC_MAXIMUM*/))
|
|
m_dynamicRangeCompressionLevel = 0;
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
CStdString setting;
|
|
setting.Format("karaoke%i", i);
|
|
#ifndef HAS_XBOX_AUDIO
|
|
#define XVOICE_MASK_PARAM_DISABLED (-1.0f)
|
|
#endif
|
|
if(!XMLUtils::GetFloat(audioElement, setting + "energy", m_karaokeVoiceMask[i].energy, XVOICE_MASK_PARAM_DISABLED, 1.0f))
|
|
m_karaokeVoiceMask[i].energy = XVOICE_MASK_PARAM_DISABLED;
|
|
if(!XMLUtils::GetFloat(audioElement, setting + "pitch", m_karaokeVoiceMask[i].pitch, XVOICE_MASK_PARAM_DISABLED, 1.0f))
|
|
m_karaokeVoiceMask[i].pitch = XVOICE_MASK_PARAM_DISABLED;
|
|
if(!XMLUtils::GetFloat(audioElement, setting + "whisper", m_karaokeVoiceMask[i].whisper, XVOICE_MASK_PARAM_DISABLED, 1.0f))
|
|
m_karaokeVoiceMask[i].whisper = XVOICE_MASK_PARAM_DISABLED;
|
|
if(!XMLUtils::GetFloat(audioElement, setting + "robotic", m_karaokeVoiceMask[i].robotic, XVOICE_MASK_PARAM_DISABLED, 1.0f))
|
|
m_karaokeVoiceMask[i].robotic = XVOICE_MASK_PARAM_DISABLED;
|
|
}
|
|
}
|
|
|
|
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::SetInt(audioNode, "volumelevel", m_volumeLevel);
|
|
XMLUtils::SetInt(audioNode, "dynamicrangecompression", m_dynamicRangeCompressionLevel);
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
CStdString setting;
|
|
setting.Format("karaoke%i", i);
|
|
XMLUtils::SetFloat(audioNode, setting + "energy", m_karaokeVoiceMask[i].energy);
|
|
XMLUtils::SetFloat(audioNode, setting + "pitch", m_karaokeVoiceMask[i].pitch);
|
|
XMLUtils::SetFloat(audioNode, setting + "whisper", m_karaokeVoiceMask[i].whisper);
|
|
XMLUtils::SetFloat(audioNode, setting + "robotic", m_karaokeVoiceMask[i].robotic);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CApplication::LoadSkin(const std::string& skinID)
|
|
{
|
|
SkinPtr skin;
|
|
{
|
|
AddonPtr addon;
|
|
if (!CServiceBroker::GetAddonMgr().GetAddon(skinID, addon, ADDON_SKIN))
|
|
return false;
|
|
skin = boost::static_pointer_cast<ADDON::CSkinInfo>(addon);
|
|
}
|
|
|
|
bool bPreviousPlayingState=false;
|
|
bool bPreviousRenderingState=false;
|
|
if (m_pPlayer->IsPlayingVideo())
|
|
{
|
|
bPreviousPlayingState = !m_pPlayer->IsPausedPlayback();
|
|
if (bPreviousPlayingState)
|
|
m_pPlayer->Pause();
|
|
#ifdef HAS_VIDEO_PLAYBACK
|
|
if (!g_renderManager.Paused())
|
|
{
|
|
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();
|
|
std::vector<int> currentModelessWindows;
|
|
g_windowManager.GetActiveModelessWindows(currentModelessWindows);
|
|
|
|
UnloadSkin();
|
|
|
|
skin->Start();
|
|
|
|
// migrate any skin-specific settings that are still stored in guisettings.xml
|
|
CSkinSettings::Get().MigrateSettings(skin);
|
|
|
|
// check if the skin has been properly loaded and if it has a Home.xml
|
|
if (!skin->HasSkinFile("Home.xml"))
|
|
{
|
|
CLog::Log(LOGERROR, "failed to load requested skin '%s'", skin->ID().c_str());
|
|
return false;
|
|
}
|
|
|
|
CLog::Log(LOGINFO, " load skin from: %s (version: %s)", skin->Path().c_str(), skin->Version().asString().c_str());
|
|
g_SkinInfo = skin;
|
|
|
|
CLog::Log(LOGINFO, " load fonts for skin...");
|
|
g_graphicsContext.SetMediaDir(skin->Path());
|
|
g_directoryCache.ClearSubPaths(skin->Path());
|
|
|
|
g_colorManager.Load(CSettings::GetInstance().GetString("lookandfeel.skincolors"));
|
|
|
|
g_fontManager.LoadFonts(CSettings::GetInstance().GetString("lookandfeel.font"));
|
|
|
|
// load in the skin strings
|
|
std::string langPath = URIUtils::AddFileToFolder(skin->Path(), "language");
|
|
URIUtils::AddSlashAtEnd(langPath);
|
|
|
|
g_localizeStrings.LoadSkinStrings(langPath, CSettings::GetInstance().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.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);
|
|
|
|
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->Open();
|
|
}
|
|
if (iCtrlID != -1)
|
|
{
|
|
pWindow = g_windowManager.GetWindow(currentWindow);
|
|
if (pWindow && pWindow->HasSaveLastControl())
|
|
{
|
|
CGUIMessage msg(GUI_MSG_SETFOCUS, currentWindow, iCtrlID, 0);
|
|
pWindow->OnMessage(msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (m_pPlayer->IsPlayingVideo())
|
|
{
|
|
if (bPreviousPlayingState)
|
|
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 " : "");
|
|
|
|
if (g_SkinInfo != NULL)
|
|
g_SkinInfo->SaveSettings();
|
|
|
|
g_audioManager.Enable(false);
|
|
|
|
g_windowManager.DeInitialize();
|
|
CTextureCache::Get().Deinitialize();
|
|
|
|
//These windows are not handled by the windowmanager (why not?) so we should unload them manually
|
|
CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0);
|
|
|
|
delete m_debugLayout;
|
|
m_debugLayout = NULL;
|
|
|
|
// remove the skin-dependent window
|
|
g_windowManager.Delete(WINDOW_DIALOG_FULLSCREEN_INFO);
|
|
|
|
g_TextureManager.Cleanup();
|
|
|
|
g_fontManager.Clear();
|
|
|
|
g_colorManager.Clear();
|
|
|
|
g_infoManager.Clear();
|
|
|
|
g_SkinInfo.reset();
|
|
}
|
|
|
|
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 (skinFile.Left(6).CompareNoCase("custom") == 0)
|
|
{
|
|
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, visibleCondition.empty() ? MODAL : MODELESS);
|
|
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;
|
|
}
|
|
|
|
#ifdef HAS_XBOX_D3D // needed for screenshot
|
|
void CApplication::Render()
|
|
{
|
|
#else
|
|
void CApplication::RenderNoPresent()
|
|
{
|
|
#endif
|
|
// don't do anything that would require graphiccontext to be locked before here in fullscreen.
|
|
// that stuff should go into renderfullscreen instead as that is called from the renderin thread
|
|
#ifdef HAS_XBOX_HARDWARE // Win32 renders from the main thread, not from the player thread
|
|
// dont show GUI when playing full screen video
|
|
if (g_graphicsContext.IsFullScreenVideo() && m_pPlayer->IsPlaying() && !m_pPlayer->IsPaused())
|
|
{
|
|
Sleep(50);
|
|
ResetScreenSaver();
|
|
g_infoManager.ResetCache();
|
|
return;
|
|
}
|
|
#endif
|
|
if(!m_pd3dDevice)
|
|
return;
|
|
|
|
g_graphicsContext.Lock();
|
|
|
|
m_pd3dDevice->BeginScene();
|
|
|
|
//SWATHWIDTH of 4 improves fillrates (performance investigator)
|
|
#ifdef HAS_XBOX_D3D
|
|
m_pd3dDevice->SetRenderState(D3DRS_SWATHWIDTH, 4);
|
|
#endif
|
|
g_windowManager.Render();
|
|
|
|
// if we're recording an audio stream then show blinking REC
|
|
if (!g_graphicsContext.IsFullScreenVideo())
|
|
{
|
|
if (m_pPlayer && m_pPlayer->IsRecording() )
|
|
{
|
|
static int iBlinkRecord = 0;
|
|
iBlinkRecord++;
|
|
if (iBlinkRecord > 25)
|
|
{
|
|
CGUIFont* pFont = g_fontManager.GetFont("font13");
|
|
CGUITextLayout::DrawText(pFont, 60, 50, 0xffff0000, 0, "REC", 0);
|
|
}
|
|
|
|
if (iBlinkRecord > 50)
|
|
iBlinkRecord = 0;
|
|
}
|
|
}
|
|
|
|
{
|
|
// free memory if we got les then 10megs free ram
|
|
MEMORYSTATUS stat;
|
|
GlobalMemoryStatus(&stat);
|
|
DWORD dwMegFree = (DWORD)(stat.dwAvailPhys / (1024 * 1024));
|
|
if (dwMegFree <= 10)
|
|
{
|
|
g_TextureManager.Flush();
|
|
}
|
|
|
|
// reset image scaling and effect states
|
|
g_graphicsContext.SetRenderingResolution(g_graphicsContext.GetResInfo(), false);
|
|
|
|
// If we have the remote codes enabled, then show them
|
|
if (g_advancedSettings.m_displayRemoteCodes)
|
|
{
|
|
#ifdef HAS_IR_REMOTE
|
|
XBIR_REMOTE* pRemote = &m_DefaultIR_Remote;
|
|
static iRemoteCode = 0;
|
|
static iShowRemoteCode = 0;
|
|
if (pRemote->wButtons)
|
|
{
|
|
iRemoteCode = 255 - pRemote->wButtons; // remote OBC code is 255-wButtons
|
|
iShowRemoteCode = 50;
|
|
}
|
|
if (iShowRemoteCode > 0)
|
|
{
|
|
std::string wszText = StringUtils::Format("Remote Code: %i", iRemoteCode);
|
|
float x = 0.08f * g_graphicsContext.GetWidth();
|
|
float y = 0.12f * g_graphicsContext.GetHeight();
|
|
#ifndef _DEBUG
|
|
if (LOG_LEVEL_DEBUG_FREEMEM > g_advancedSettings.m_logLevel)
|
|
y = 0.08f * g_graphicsContext.GetHeight();
|
|
#endif
|
|
CGUITextLayout::DrawText(g_fontManager.GetFont("font13"), x, y, 0xffffffff, 0xff000000, wszText, 0);
|
|
iShowRemoteCode--;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
RenderMemoryStatus();
|
|
}
|
|
|
|
m_pd3dDevice->EndScene();
|
|
#ifdef HAS_XBOX_D3D
|
|
m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
|
|
#endif
|
|
g_graphicsContext.Unlock();
|
|
|
|
// 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();
|
|
}
|
|
|
|
#ifndef HAS_XBOX_D3D
|
|
void CApplication::Render()
|
|
{
|
|
g_graphicsContext.Lock();
|
|
{ // frame rate limiter (really bad, but it does the trick :p)
|
|
const static unsigned int singleFrameTime = 10;
|
|
static unsigned int lastFrameTime = 0;
|
|
unsigned int currentTime = timeGetTime();
|
|
if (lastFrameTime + singleFrameTime > currentTime)
|
|
Sleep(lastFrameTime + singleFrameTime - currentTime);
|
|
lastFrameTime = timeGetTime();
|
|
}
|
|
RenderNoPresent();
|
|
// Present the backbuffer contents to the display
|
|
if (m_pd3dDevice) m_pd3dDevice->Present( NULL, NULL, NULL, NULL );
|
|
CTimeUtils::UpdateFrameTime();
|
|
g_graphicsContext.Unlock();
|
|
}
|
|
#endif
|
|
|
|
void CApplication::RenderMemoryStatus()
|
|
{
|
|
g_infoManager.UpdateFPS();
|
|
|
|
if (!m_debugLayout)
|
|
{
|
|
CGUIFont *font13 = g_fontManager.GetDefaultFont();
|
|
CGUIFont *font13border = g_fontManager.GetDefaultFont(true);
|
|
if (font13)
|
|
m_debugLayout = new CGUITextLayout(font13, true, 0, font13border);
|
|
}
|
|
if (!m_debugLayout)
|
|
return;
|
|
|
|
#if !defined(_DEBUG) && !defined(PROFILE)
|
|
if (LOG_LEVEL_DEBUG_FREEMEM <= g_advancedSettings.m_logLevel)
|
|
#endif
|
|
{
|
|
// reset the window scaling and fade status
|
|
RESOLUTION res = g_graphicsContext.GetVideoResolution();
|
|
g_graphicsContext.SetRenderingResolution(g_graphicsContext.GetResInfo(), false);
|
|
|
|
CStdString info;
|
|
MEMORYSTATUS stat;
|
|
GlobalMemoryStatus(&stat);
|
|
info.Format("FreeMem %d/%d KB, FPS %2.1f, CPU %2.0f%%", stat.dwAvailPhys/1024, stat.dwTotalPhys/1024, g_infoManager.GetFPS(), (1.0f - m_idleThread.GetRelativeUsage())*100);
|
|
|
|
if(g_SkinInfo->IsDebugging())
|
|
{
|
|
if (!info.IsEmpty())
|
|
info += "\n";
|
|
CGUIWindow *window = g_windowManager.GetWindow(g_windowManager.GetFocusedWindow());
|
|
CGUIWindow *pointer = g_windowManager.GetWindow(105);
|
|
CPoint point;
|
|
if (pointer)
|
|
point = CPoint(pointer->GetXPosition(), pointer->GetYPosition());
|
|
if (window)
|
|
{
|
|
CStdString windowName = CButtonTranslator::TranslateWindow(window->GetID());
|
|
if (!windowName.IsEmpty())
|
|
windowName += " (" + window->GetProperty("xmlfile").asString() + ")";
|
|
else
|
|
windowName = window->GetProperty("xmlfile").asString();
|
|
info += "Window: " + windowName + " ";
|
|
// transform the mouse coordinates to this window's coordinates
|
|
g_graphicsContext.SetScalingResolution(window->GetCoordsRes(), true);
|
|
point.x *= g_graphicsContext.GetGUIScaleX();
|
|
point.y *= g_graphicsContext.GetGUIScaleY();
|
|
g_graphicsContext.SetRenderingResolution(g_graphicsContext.GetResInfo(), false);
|
|
}
|
|
info.AppendFormat("Mouse: (%d,%d) ", (int)point.x, (int)point.y);
|
|
if (window)
|
|
{
|
|
CGUIControl *control = window->GetFocusedControl();
|
|
if (control)
|
|
info.AppendFormat("Focused: %i (%s)", control->GetID(), CGUIControlFactory::TranslateControlType(control->GetControlType()).c_str());
|
|
}
|
|
}
|
|
float x = 0.04f * g_graphicsContext.GetWidth() + CDisplaySettings::Get().GetResolutionInfo(res).Overscan.left;
|
|
float y = 0.04f * g_graphicsContext.GetHeight() + CDisplaySettings::Get().GetResolutionInfo(res).Overscan.top;
|
|
|
|
m_debugLayout->Update(info);
|
|
m_debugLayout->RenderOutline(x, y, 0xffffffff, 0xff000000, 0, 0);
|
|
}
|
|
}
|
|
|
|
// 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(CKey& key)
|
|
{
|
|
// Turn the mouse off, as we've just got a keypress from controller or remote
|
|
g_Mouse.SetInactive();
|
|
|
|
// get the current active window
|
|
int iWin = g_windowManager.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 the screensaver timer
|
|
// but not for the analog thumbsticks/triggers
|
|
if (!key.IsAnalogButton())
|
|
{
|
|
// reset harddisk spindown timer
|
|
m_bSpinDown = false;
|
|
m_bNetworkSpinDown = false;
|
|
|
|
// reset Idle Timer
|
|
m_idleTimer.StartZero();
|
|
|
|
ResetScreenSaver();
|
|
|
|
// allow some keys to be processed while the screensaver is active
|
|
if (ResetScreenSaverWindow())
|
|
{
|
|
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 (control->GetControlType() == CGUIControl::GUICONTROL_EDIT ||
|
|
(control->IsContainer() && g_Keyboard.GetShift()))
|
|
useKeyboard = true;
|
|
}
|
|
}
|
|
if (useKeyboard)
|
|
{
|
|
action = CAction(0); // reset our action
|
|
if (key.GetFromHttpApi())
|
|
action = CAction(key.GetButtonCode() != KEY_INVALID ? key.GetButtonCode() : 0, key.GetUnicode());
|
|
else
|
|
{ // see if we've got an ascii key
|
|
if (g_Keyboard.GetUnicode())
|
|
action = CAction(g_Keyboard.GetAscii() | KEY_ASCII, g_Keyboard.GetUnicode());
|
|
else
|
|
action = CAction(g_Keyboard.GetKey() | KEY_VKEY);
|
|
}
|
|
#ifdef HAS_SDL
|
|
g_Keyboard.Reset();
|
|
#endif
|
|
|
|
CLog::Log(LOGDEBUG, "%s: %i pressed, trying keyboard action %i", __FUNCTION__, (int) key.GetButtonCode(), action.GetID());
|
|
|
|
if (OnAction(action))
|
|
return true;
|
|
// failed to handle the keyboard action, drop down through to standard action
|
|
}
|
|
if (key.GetFromHttpApi())
|
|
{
|
|
if (key.GetButtonCode() != KEY_INVALID)
|
|
action = CButtonTranslator::GetInstance().GetAction(iWin, key);
|
|
}
|
|
else
|
|
action = CButtonTranslator::GetInstance().GetAction(iWin, key);
|
|
}
|
|
if (!key.IsAnalogButton())
|
|
CLog::Log(LOGDEBUG, "%s: %i pressed, action is %s", __FUNCTION__, (int) key.GetButtonCode(), action.GetName().c_str());
|
|
|
|
return ExecuteInputAction(action);
|
|
}
|
|
|
|
bool CApplication::OnAction(CAction &action)
|
|
{
|
|
// Let's tell the outside world about this action, ignoring mouse moves
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=2 && action.GetID() != ACTION_MOUSE_MOVE)
|
|
{
|
|
CStdString tmp;
|
|
tmp.Format("%i",action.GetID());
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnAction:"+tmp+";2");
|
|
}
|
|
|
|
// 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.IsMouse())
|
|
g_Mouse.SetActive(true);
|
|
|
|
// 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
|
|
|
|
// notify action listeners
|
|
if (NotifyActionListeners(action))
|
|
return true;
|
|
|
|
// screenshot : take a screenshot :)
|
|
if (action.GetID() == ACTION_TAKE_SCREENSHOT)
|
|
{
|
|
CUtil::TakeScreenshot();
|
|
return true;
|
|
}
|
|
// built in functions : execute the built-in
|
|
if (action.GetID() == ACTION_BUILT_IN_FUNCTION)
|
|
{
|
|
CBuiltins::GetInstance().Execute(action.GetName());
|
|
m_navigationTimer.StartZero();
|
|
return true;
|
|
}
|
|
|
|
// power down : turn off after 3 seconds of button down
|
|
static bool PowerButtonDown = false;
|
|
static DWORD PowerButtonCode;
|
|
static unsigned int MarkTime;
|
|
if (action.GetID() == ACTION_POWERDOWN)
|
|
{
|
|
// Hold button for 3 secs to power down
|
|
if (!PowerButtonDown)
|
|
{
|
|
MarkTime = XbmcThreads::SystemClockMillis();
|
|
PowerButtonDown = true;
|
|
PowerButtonCode = action.GetButtonCode();
|
|
}
|
|
}
|
|
if (PowerButtonDown)
|
|
{
|
|
if (g_application.IsButtonDown(PowerButtonCode))
|
|
{
|
|
if (XbmcThreads::SystemClockMillis() >= MarkTime + 3000)
|
|
{
|
|
CApplicationMessenger::Get().PostMsg(TMSG_SHUTDOWN);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
PowerButtonDown = false;
|
|
}
|
|
// 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_SET_RATING) && m_pPlayer->IsPlayingAudio())
|
|
{
|
|
int userrating = MUSIC_UTILS::ShowSelectRatingDialog(m_itemCurrentFile->GetMusicInfoTag()->GetUserrating());
|
|
if (userrating < 0) // Nothing selected, so user rating unchanged
|
|
return true;
|
|
userrating = std::min(userrating, 10);
|
|
if (userrating != m_itemCurrentFile->GetMusicInfoTag()->GetUserrating())
|
|
{
|
|
m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating);
|
|
// Mirror changes to GUI item
|
|
g_infoManager.SetCurrentItem(*m_itemCurrentFile);
|
|
|
|
// Asynchronously update song userrating in music library
|
|
MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, userrating);
|
|
|
|
// Tell all windows (e.g. playlistplayer, media windows) to update the fileitem
|
|
CGUIMessage msg(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_ITEM, 0, m_itemCurrentFile);
|
|
g_windowManager.SendMessage(msg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_pPlayer->IsPlayingAudio())
|
|
{
|
|
int userrating = m_itemCurrentFile->GetMusicInfoTag()->GetUserrating();
|
|
bool needsUpdate(false);
|
|
if (userrating > 0 && action.GetID() == ACTION_DECREASE_RATING)
|
|
{
|
|
m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating - 1);
|
|
needsUpdate = true;
|
|
}
|
|
else if (userrating < 10 && action.GetID() == ACTION_INCREASE_RATING)
|
|
{
|
|
m_itemCurrentFile->GetMusicInfoTag()->SetUserrating(userrating + 1);
|
|
needsUpdate = true;
|
|
}
|
|
if (needsUpdate)
|
|
{
|
|
// Mirror changes to current GUI item
|
|
g_infoManager.SetCurrentItem(*m_itemCurrentFile);
|
|
|
|
// Asynchronously update song userrating in music library
|
|
MUSIC_UTILS::UpdateSongRatingJob(m_itemCurrentFile, m_itemCurrentFile->GetMusicInfoTag()->GetUserrating());
|
|
|
|
// 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;
|
|
}
|
|
else if ((action.GetID() == ACTION_INCREASE_RATING || action.GetID() == ACTION_DECREASE_RATING) && m_pPlayer->IsPlayingVideo())
|
|
{
|
|
int rating = m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating;
|
|
bool needsUpdate(false);
|
|
if (rating > 1 && action.GetID() == ACTION_DECREASE_RATING)
|
|
{
|
|
m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating - 1;
|
|
needsUpdate = true;
|
|
}
|
|
else if (rating < 10 && action.GetID() == ACTION_INCREASE_RATING)
|
|
{
|
|
m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating = rating + 1;
|
|
needsUpdate = true;
|
|
}
|
|
if (needsUpdate)
|
|
{
|
|
// Mirror changes to GUI item
|
|
g_infoManager.SetCurrentItem(*m_itemCurrentFile);
|
|
|
|
CVideoDatabase db;
|
|
if (db.Open())
|
|
{
|
|
db.SetVideoUserRating(m_itemCurrentFile->GetVideoInfoTag()->m_iDbId,
|
|
m_itemCurrentFile->GetVideoInfoTag()->m_iUserRating,
|
|
m_itemCurrentFile->GetVideoInfoTag()->m_type);
|
|
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;
|
|
}
|
|
|
|
// stop : stops playing current audio song
|
|
if (action.GetID() == ACTION_STOP)
|
|
{
|
|
StopPlaying();
|
|
return true;
|
|
}
|
|
|
|
// previous : play previous song from playlist
|
|
if (action.GetID() == ACTION_PREV_ITEM)
|
|
{
|
|
// first check whether we're within 3 seconds of the start of the track
|
|
// if not, we just revert to the start of the track
|
|
if (m_pPlayer->CanSeek() && GetTime() > 3)
|
|
{
|
|
SeekTime(0);
|
|
m_pPlayer->SetPlaySpeed(1, g_application.m_muted);
|
|
}
|
|
else
|
|
{
|
|
g_playlistPlayer.PlayPrevious();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// next : play next song from playlist
|
|
if (action.GetID() == ACTION_NEXT_ITEM)
|
|
{
|
|
if (m_pPlayer->IsPlaying() && m_pPlayer->SkipNext())
|
|
return true;
|
|
|
|
g_playlistPlayer.PlayNext();
|
|
|
|
return true;
|
|
}
|
|
|
|
if ( m_pPlayer->IsPlaying())
|
|
{
|
|
// pause : pauses current audio song
|
|
if (action.GetID() == ACTION_PAUSE)
|
|
{
|
|
m_pPlayer->Pause();
|
|
if (!m_pPlayer->IsPaused())
|
|
{ // unpaused - set the playspeed back to normal
|
|
m_pPlayer->SetPlaySpeed(1, g_application.m_muted);
|
|
}
|
|
g_audioManager.Enable(m_pPlayer->IsPaused());
|
|
return true;
|
|
}
|
|
if (!m_pPlayer->IsPaused())
|
|
{
|
|
// if we do a FF/RW in my music then map PLAY action togo back to normal speed
|
|
// if we are playing at normal speed, then allow play to pause
|
|
if (action.GetID() == ACTION_PLAYER_PLAY)
|
|
{
|
|
if (m_pPlayer->GetPlaySpeed() != 1)
|
|
{
|
|
m_pPlayer->SetPlaySpeed(1, g_application.m_muted);
|
|
}
|
|
else
|
|
{
|
|
m_pPlayer->Pause();
|
|
}
|
|
return true;
|
|
}
|
|
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;
|
|
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());
|
|
|
|
m_pPlayer->SetPlaySpeed(1, g_application.m_muted);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (action.GetID() == ACTION_MUTE)
|
|
{
|
|
ToggleMute();
|
|
return true;
|
|
}
|
|
|
|
if (action.GetID() == ACTION_TOGGLE_DIGITAL_ANALOG)
|
|
{
|
|
if(CSettings::GetInstance().GetInt("audiooutput.mode")==AUDIO_DIGITAL)
|
|
CSettings::GetInstance().SetInt("audiooutput.mode", AUDIO_ANALOG);
|
|
else
|
|
CSettings::GetInstance().SetInt("audiooutput.mode", AUDIO_DIGITAL);
|
|
g_application.Restart();
|
|
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 || action.GetID() == ACTION_VOLUME_SET))
|
|
{
|
|
if (m_muted)
|
|
UnMute();
|
|
int volume = m_volumeLevel + m_dynamicRangeCompressionLevel;
|
|
|
|
// calculate speed so that a full press will equal 1 second from min to max
|
|
float speed = float(VOLUME_MAXIMUM - VOLUME_MINIMUM);
|
|
if( action.GetRepeat() )
|
|
speed *= action.GetRepeat();
|
|
else
|
|
speed /= 50; //50 fps
|
|
|
|
if (action.GetID() == ACTION_VOLUME_UP)
|
|
volume += (int)((float)fabs(action.GetAmount()) * action.GetAmount() * speed);
|
|
else if (action.GetID() == ACTION_VOLUME_DOWN)
|
|
volume -= (int)((float)fabs(action.GetAmount()) * action.GetAmount() * speed);
|
|
else
|
|
volume = action.GetAmount() * speed;
|
|
if (volume != m_volumeLevel)
|
|
{
|
|
SetVolume(volume, false);
|
|
// show visual feedback of volume change...
|
|
ShowVolumeBar(&action);
|
|
}
|
|
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;
|
|
}
|
|
|
|
int CApplication::GetMessageMask()
|
|
{
|
|
return TMSG_MASK_APPLICATION;
|
|
}
|
|
|
|
void CApplication::OnApplicationMessage(ThreadMessage* pMsg)
|
|
{
|
|
switch (pMsg->dwMessage)
|
|
{
|
|
#ifndef _XBOX
|
|
case TMSG_POWERDOWN:
|
|
Stop(EXITCODE_POWERDOWN);
|
|
g_powerManager.Powerdown();
|
|
break;
|
|
#endif
|
|
|
|
case TMSG_QUIT:
|
|
#ifdef _XBOX
|
|
CBuiltins::GetInstance().Execute("XBMC.Dashboard()");
|
|
#else
|
|
Stop(EXITCODE_QUIT);
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_SHUTDOWN:
|
|
#ifdef _XBOX
|
|
case TMSG_POWERDOWN:
|
|
{
|
|
g_application.Stop();
|
|
Sleep(200);
|
|
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
|
|
XKHDD::SpindownHarddisk(); // Spindown the Harddisk
|
|
XKUtils::XBOXPowerOff();
|
|
while(1){Sleep(0);}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
switch (CSettings::GetInstance().GetInt("powermanagement.shutdownstate"))
|
|
{
|
|
case POWERSTATE_SHUTDOWN:
|
|
CApplicationMessenger::Get().PostMsg(TMSG_SHUTDOWN);
|
|
break;
|
|
|
|
case POWERSTATE_SUSPEND:
|
|
CApplicationMessenger::Get().PostMsg(TMSG_SUSPEND);
|
|
break;
|
|
|
|
case POWERSTATE_HIBERNATE:
|
|
CApplicationMessenger::Get().PostMsg(TMSG_HIBERNATE);
|
|
break;
|
|
|
|
case POWERSTATE_QUIT:
|
|
CApplicationMessenger::Get().PostMsg(TMSG_QUIT);
|
|
break;
|
|
|
|
case POWERSTATE_MINIMIZE:
|
|
CApplicationMessenger::Get().PostMsg(TMSG_MINIMIZE);
|
|
break;
|
|
|
|
case TMSG_RENDERER_FLUSH:
|
|
g_renderManager.Flush();
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_HIBERNATE:
|
|
#ifndef _XBOX
|
|
g_PVRManager.SetWakeupCommand();
|
|
g_powerManager.Hibernate();
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_SUSPEND:
|
|
#ifndef _XBOX
|
|
g_PVRManager.SetWakeupCommand();
|
|
g_powerManager.Suspend();
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_RESTART:
|
|
#ifdef _XBOX
|
|
{
|
|
g_application.Stop();
|
|
Sleep(200);
|
|
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
|
|
XKUtils::XBOXPowerCycle();
|
|
while(1){Sleep(0);}
|
|
#endif
|
|
}
|
|
break;
|
|
#endif
|
|
case TMSG_RESET:
|
|
#ifdef _XBOX
|
|
{
|
|
g_application.Stop();
|
|
Sleep(200);
|
|
#ifndef _DEBUG // don't actually shut off if debug build, it hangs VS for a long time
|
|
XKUtils::XBOXPowerCycle();
|
|
while(1){Sleep(0);}
|
|
#endif
|
|
}
|
|
#else
|
|
Stop(EXITCODE_REBOOT);
|
|
g_powerManager.Reboot();
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_RESTARTAPP:
|
|
#if defined(TARGET_WINDOWS) || defined(TARGET_LINUX)
|
|
Stop(EXITCODE_RESTARTAPP);
|
|
#elif defined (_XBOX)
|
|
{
|
|
char szXBEFileName[1024];
|
|
|
|
CIoSupport::GetXbePath(szXBEFileName);
|
|
CUtil::RunXBE(szXBEFileName);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_INHIBITIDLESHUTDOWN:
|
|
#ifndef _XBOX
|
|
InhibitIdleShutdown(pMsg->param1 != 0);
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_ACTIVATESCREENSAVER:
|
|
ActivateScreenSaver();
|
|
break;
|
|
|
|
case TMSG_VOLUME_SHOW:
|
|
{
|
|
CAction action(pMsg->param1);
|
|
ShowVolumeBar(&action);
|
|
}
|
|
break;
|
|
|
|
case TMSG_DISPLAY_SETUP:
|
|
#ifndef _XBOX
|
|
*static_cast<bool*>(pMsg->lpVoid) = InitWindow();
|
|
SetRenderGUI(true);
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_DISPLAY_DESTROY:
|
|
#ifndef _XBOX
|
|
*static_cast<bool*>(pMsg->lpVoid) = DestroyWindow();
|
|
SetRenderGUI(false);
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_START_ANDROID_ACTIVITY:
|
|
{
|
|
#if defined(TARGET_ANDROID)
|
|
if (pMsg->params.size())
|
|
{
|
|
CXBMCApp::StartActivity(pMsg->params[0],
|
|
pMsg->params.size() > 1 ? pMsg->params[1] : "",
|
|
pMsg->params.size() > 2 ? pMsg->params[2] : "",
|
|
pMsg->params.size() > 3 ? pMsg->params[3] : "");
|
|
}
|
|
#endif
|
|
}
|
|
break;
|
|
|
|
case TMSG_NETWORKMESSAGE:
|
|
getNetwork().NetworkMessage((CNetwork::EMESSAGE)pMsg->param1, pMsg->param2);
|
|
break;
|
|
|
|
case TMSG_SETLANGUAGE:
|
|
SetLanguage(pMsg->strParam);
|
|
break;
|
|
|
|
|
|
case TMSG_SWITCHTOFULLSCREEN:
|
|
if (g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO)
|
|
SwitchToFullScreen(true);
|
|
break;
|
|
|
|
case TMSG_VIDEORESIZE:
|
|
#ifndef _XBOX
|
|
{
|
|
XBMC_Event newEvent;
|
|
memset(&newEvent, 0, sizeof(newEvent));
|
|
newEvent.type = XBMC_VIDEORESIZE;
|
|
newEvent.resize.w = pMsg->param1;
|
|
newEvent.resize.h = pMsg->param2;
|
|
OnEvent(newEvent);
|
|
g_windowManager.MarkDirty();
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_SETVIDEORESOLUTION:
|
|
g_graphicsContext.SetVideoResolution(static_cast<RESOLUTION>(pMsg->param1), pMsg->strParam == "true" ? TRUE : FALSE, pMsg->param2 == 1);
|
|
break;
|
|
|
|
case TMSG_TOGGLEFULLSCREEN:
|
|
#ifndef _XBOX
|
|
g_graphicsContext.Lock();
|
|
g_graphicsContext.ToggleFullScreenRoot();
|
|
g_graphicsContext.Unlock();
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_MINIMIZE:
|
|
#ifndef _XBOX
|
|
Minimize();
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_EXECUTE_OS:
|
|
#ifndef _XBOX
|
|
/* Suspend AE temporarily so exclusive or hog-mode sinks */
|
|
/* don't block external player's access to audio device */
|
|
if (!CAEFactory::Suspend())
|
|
{
|
|
CLog::Log(LOGNOTICE, "%s: Failed to suspend AudioEngine before launching external program", __FUNCTION__);
|
|
}
|
|
#if defined( TARGET_POSIX) && !defined(TARGET_DARWIN)
|
|
CUtil::RunCommandLine(pMsg->strParam.c_str(), (pMsg->param1 == 1));
|
|
#elif defined(TARGET_WINDOWS)
|
|
CWIN32Util::XBMCShellExecute(pMsg->strParam.c_str(), (pMsg->param1 == 1));
|
|
#endif
|
|
/* Resume AE processing of XBMC native audio */
|
|
if (!CAEFactory::Resume())
|
|
{
|
|
CLog::Log(LOGFATAL, "%s: Failed to restart AudioEngine after return from external player", __FUNCTION__);
|
|
}
|
|
#endif
|
|
break;
|
|
|
|
case TMSG_EXECUTE_SCRIPT:
|
|
CScriptInvocationManager::GetInstance().ExecuteAsync(pMsg->strParam);
|
|
break;
|
|
|
|
case TMSG_EXECUTE_BUILT_IN:
|
|
CBuiltins::GetInstance().Execute(pMsg->strParam.c_str());
|
|
break;
|
|
|
|
case TMSG_PICTURE_SHOW:
|
|
{
|
|
CGUIWindowSlideShow *pSlideShow = static_cast<CGUIWindowSlideShow *>(g_windowManager.GetWindow(WINDOW_SLIDESHOW));
|
|
if (!pSlideShow) return;
|
|
|
|
// stop playing file
|
|
if (m_pPlayer->IsPlayingVideo()) g_application.StopPlaying();
|
|
|
|
if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
|
|
g_windowManager.PreviousWindow();
|
|
|
|
g_application.ResetScreenSaver();
|
|
g_application.ResetScreenSaverWindow();
|
|
|
|
g_graphicsContext.Lock();
|
|
|
|
if (g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW)
|
|
g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
|
|
if (URIUtils::IsZIP(pMsg->strParam) || URIUtils::IsRAR(pMsg->strParam)) // actually a cbz/cbr
|
|
{
|
|
CFileItemList items;
|
|
CURL pathToUrl;
|
|
if (URIUtils::IsZIP(pMsg->strParam))
|
|
pathToUrl = URIUtils::CreateArchivePath("zip", CURL(pMsg->strParam), "");
|
|
else
|
|
pathToUrl = URIUtils::CreateArchivePath("rar", CURL(pMsg->strParam), "");
|
|
|
|
CUtil::GetRecursiveListing(pathToUrl.Get(), items, g_advancedSettings.m_pictureExtensions, XFILE::DIR_FLAG_NO_FILE_DIRS);
|
|
if (items.Size() > 0)
|
|
{
|
|
pSlideShow->Reset();
|
|
for (int i = 0; i<items.Size(); ++i)
|
|
{
|
|
pSlideShow->Add(items[i].get());
|
|
}
|
|
pSlideShow->Select(items[0]->GetPath());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CFileItem item(pMsg->strParam, false);
|
|
pSlideShow->Reset();
|
|
pSlideShow->Add(&item);
|
|
pSlideShow->Select(pMsg->strParam);
|
|
}
|
|
g_graphicsContext.Unlock();
|
|
}
|
|
break;
|
|
|
|
case TMSG_SLIDESHOW_SCREENSAVER:
|
|
case TMSG_PICTURE_SLIDESHOW:
|
|
{
|
|
CGUIWindowSlideShow *pSlideShow = static_cast<CGUIWindowSlideShow *>(g_windowManager.GetWindow(WINDOW_SLIDESHOW));
|
|
if (!pSlideShow) return;
|
|
|
|
if (m_pPlayer->IsPlayingVideo())
|
|
g_application.StopPlaying();
|
|
|
|
g_graphicsContext.Lock();
|
|
pSlideShow->Reset();
|
|
|
|
CFileItemList items;
|
|
std::string strPath = pMsg->strParam;
|
|
std::string extensions = g_advancedSettings.m_pictureExtensions;
|
|
if (pMsg->param1)
|
|
extensions += "|.tbn";
|
|
CUtil::GetRecursiveListing(strPath, items, extensions);
|
|
|
|
if (items.Size() > 0)
|
|
{
|
|
for (int i = 0; i<items.Size(); ++i)
|
|
pSlideShow->Add(items[i].get());
|
|
pSlideShow->StartSlideShow(pMsg->dwMessage == TMSG_SLIDESHOW_SCREENSAVER); //Start the slideshow!
|
|
}
|
|
if (pMsg->dwMessage == TMSG_SLIDESHOW_SCREENSAVER)
|
|
pSlideShow->Shuffle();
|
|
|
|
if (g_windowManager.GetActiveWindow() != WINDOW_SLIDESHOW)
|
|
{
|
|
if (items.Size() == 0)
|
|
{
|
|
CSettings::GetInstance().SetString("screensaver.mode", "screensaver.xbmc.builtin.dim");
|
|
g_application.ActivateScreenSaver();
|
|
}
|
|
else
|
|
g_windowManager.ActivateWindow(WINDOW_SLIDESHOW);
|
|
}
|
|
|
|
g_graphicsContext.Unlock();
|
|
}
|
|
break;
|
|
|
|
case TMSG_LOADPROFILE:
|
|
{
|
|
CGUIWindowLoginScreen::LoadProfile(pMsg->param1);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void CApplication::UpdateLCD()
|
|
{
|
|
#ifdef HAS_LCD
|
|
static unsigned int lTickCount = 0;
|
|
|
|
if (!g_lcd || CSettings::GetInstance().GetInt("lcd.type") == LCD_TYPE_NONE)
|
|
return ;
|
|
unsigned int lTimeOut = 1000;
|
|
if (m_pPlayer->GetPlaySpeed() != 1)
|
|
lTimeOut = 0;
|
|
if ( (XbmcThreads::SystemClockMillis() - lTickCount) >= lTimeOut)
|
|
{
|
|
if (g_application.NavigationIdleTime() < 5)
|
|
g_lcd->Render(ILCD::LCD_MODE_NAVIGATION);
|
|
else if (m_pPlayer->IsPlayingVideo())
|
|
g_lcd->Render(ILCD::LCD_MODE_VIDEO);
|
|
else if (m_pPlayer->IsPlayingAudio())
|
|
g_lcd->Render(ILCD::LCD_MODE_MUSIC);
|
|
else if (IsInScreenSaver())
|
|
g_lcd->Render(ILCD::LCD_MODE_SCREENSAVER);
|
|
else
|
|
g_lcd->Render(ILCD::LCD_MODE_GENERAL);
|
|
|
|
// reset tick count
|
|
lTickCount = XbmcThreads::SystemClockMillis();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CApplication::FrameMove(bool processEvents, bool processGUI)
|
|
{
|
|
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)
|
|
{
|
|
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->Open();
|
|
}
|
|
}
|
|
g_graphicsContext.Unlock();
|
|
}
|
|
|
|
UpdateLCD();
|
|
|
|
// read raw input from controller, remote control, mouse and keyboard
|
|
ReadInput();
|
|
// process input actions
|
|
ProcessMouse();
|
|
ProcessHTTPApiButtons();
|
|
ProcessKeyboard();
|
|
ProcessRemote(frameTime);
|
|
ProcessGamepad(frameTime);
|
|
ProcessEventServer(frameTime);
|
|
if (processGUI)
|
|
{
|
|
CSeekHandler::Get().FrameMove();
|
|
}
|
|
}
|
|
if (processGUI)
|
|
{
|
|
// Process events and animate controls
|
|
if (!m_bStop)
|
|
g_windowManager.Process(CTimeUtils::GetFrameTime());
|
|
g_windowManager.FrameMove();
|
|
}
|
|
}
|
|
|
|
bool CApplication::ProcessGamepad(float frameTime)
|
|
{
|
|
#ifdef HAS_GAMEPAD
|
|
// Handle the gamepad button presses. We check for button down,
|
|
// then call OnKey() which handles the translation to actions, and sends the
|
|
// action to our window manager's OnAction() function, which filters the messages
|
|
// to where they're supposed to end up, returning true if the message is successfully
|
|
// processed. If OnKey() returns false, then the key press wasn't processed at all,
|
|
// and we can safely process the next key (or next check on the same key in the
|
|
// case of the analog sticks which can produce more than 1 key event.)
|
|
|
|
WORD wButtons = m_DefaultGamepad.wButtons;
|
|
WORD wDpad = wButtons & (XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_DPAD_RIGHT);
|
|
|
|
BYTE bLeftTrigger = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_LEFT_TRIGGER];
|
|
BYTE bRightTrigger = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_RIGHT_TRIGGER];
|
|
BYTE bButtonA = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_A];
|
|
BYTE bButtonB = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_B];
|
|
BYTE bButtonX = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_X];
|
|
BYTE bButtonY = m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_Y];
|
|
|
|
// pass them through the delay
|
|
WORD wDir = m_ctrDpad.DpadInput(wDpad, 0 != bLeftTrigger, 0 != bRightTrigger);
|
|
|
|
// map all controller & remote actions to their keys
|
|
if (m_DefaultGamepad.fX1 || m_DefaultGamepad.fY1)
|
|
{
|
|
CKey key(KEY_BUTTON_LEFT_THUMB_STICK, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.fX2 || m_DefaultGamepad.fY2)
|
|
{
|
|
CKey key(KEY_BUTTON_RIGHT_THUMB_STICK, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
// direction specific keys (for defining different actions for each direction)
|
|
// We need to be able to know when it last had a direction, so that we can
|
|
// post the reset direction code the next time around (to reset scrolling,
|
|
// fastforwarding and other analog actions)
|
|
|
|
// For the sticks, once it is pushed in one direction (eg up) it will only
|
|
// detect movement in that direction of movement (eg up or down) - the other
|
|
// direction (eg left and right) will not be registered until the stick has
|
|
// been recentered for at least 2 frames.
|
|
|
|
// first the right stick
|
|
static lastRightStickKey = 0;
|
|
int newRightStickKey = 0;
|
|
if (lastRightStickKey == KEY_BUTTON_RIGHT_THUMB_STICK_UP || lastRightStickKey == KEY_BUTTON_RIGHT_THUMB_STICK_DOWN)
|
|
{
|
|
if (m_DefaultGamepad.fY2 > 0)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
|
|
else if (m_DefaultGamepad.fY2 < 0)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
|
|
else if (m_DefaultGamepad.fX2 != 0)
|
|
{
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
|
|
//m_DefaultGamepad.fY2 = 0.00001f; // small amount of movement
|
|
}
|
|
}
|
|
else if (lastRightStickKey == KEY_BUTTON_RIGHT_THUMB_STICK_LEFT || lastRightStickKey == KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT)
|
|
{
|
|
if (m_DefaultGamepad.fX2 > 0)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
|
|
else if (m_DefaultGamepad.fX2 < 0)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
|
|
else if (m_DefaultGamepad.fY2 != 0)
|
|
{
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
|
|
//m_DefaultGamepad.fX2 = 0.00001f; // small amount of movement
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_DefaultGamepad.fY2 > 0 && m_DefaultGamepad.fX2*2 < m_DefaultGamepad.fY2 && -m_DefaultGamepad.fX2*2 < m_DefaultGamepad.fY2)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_UP;
|
|
else if (m_DefaultGamepad.fY2 < 0 && m_DefaultGamepad.fX2*2 < -m_DefaultGamepad.fY2 && -m_DefaultGamepad.fX2*2 < -m_DefaultGamepad.fY2)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_DOWN;
|
|
else if (m_DefaultGamepad.fX2 > 0 && m_DefaultGamepad.fY2*2 < m_DefaultGamepad.fX2 && -m_DefaultGamepad.fY2*2 < m_DefaultGamepad.fX2)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_RIGHT;
|
|
else if (m_DefaultGamepad.fX2 < 0 && m_DefaultGamepad.fY2*2 < -m_DefaultGamepad.fX2 && -m_DefaultGamepad.fY2*2 < -m_DefaultGamepad.fX2)
|
|
newRightStickKey = KEY_BUTTON_RIGHT_THUMB_STICK_LEFT;
|
|
}
|
|
if (lastRightStickKey && newRightStickKey != lastRightStickKey)
|
|
{ // was held down last time - and we have a new key now
|
|
// post old key reset message...
|
|
CKey key(lastRightStickKey, 0, 0, 0, 0, 0, 0);
|
|
lastRightStickKey = newRightStickKey;
|
|
if (OnKey(key)) return true;
|
|
}
|
|
lastRightStickKey = newRightStickKey;
|
|
// post the new key's message
|
|
if (newRightStickKey)
|
|
{
|
|
CKey key(newRightStickKey, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
|
|
// now the left stick
|
|
static lastLeftStickKey = 0;
|
|
int newLeftStickKey = 0;
|
|
if (lastLeftStickKey == KEY_BUTTON_LEFT_THUMB_STICK_UP || lastLeftStickKey == KEY_BUTTON_LEFT_THUMB_STICK_DOWN)
|
|
{
|
|
if (m_DefaultGamepad.fY1 > 0)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_UP;
|
|
else if (m_DefaultGamepad.fY1 < 0)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
|
|
}
|
|
else if (lastLeftStickKey == KEY_BUTTON_LEFT_THUMB_STICK_LEFT || lastLeftStickKey == KEY_BUTTON_LEFT_THUMB_STICK_RIGHT)
|
|
{
|
|
if (m_DefaultGamepad.fX1 > 0)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
|
|
else if (m_DefaultGamepad.fX1 < 0)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
|
|
}
|
|
else
|
|
{ // check for a new control movement
|
|
if (m_DefaultGamepad.fY1 > 0 && m_DefaultGamepad.fX1 < m_DefaultGamepad.fY1 && -m_DefaultGamepad.fX1 < m_DefaultGamepad.fY1)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_UP;
|
|
else if (m_DefaultGamepad.fY1 < 0 && m_DefaultGamepad.fX1 < -m_DefaultGamepad.fY1 && -m_DefaultGamepad.fX1 < -m_DefaultGamepad.fY1)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_DOWN;
|
|
else if (m_DefaultGamepad.fX1 > 0 && m_DefaultGamepad.fY1 < m_DefaultGamepad.fX1 && -m_DefaultGamepad.fY1 < m_DefaultGamepad.fX1)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_RIGHT;
|
|
else if (m_DefaultGamepad.fX1 < 0 && m_DefaultGamepad.fY1 < -m_DefaultGamepad.fX1 && -m_DefaultGamepad.fY1 < -m_DefaultGamepad.fX1)
|
|
newLeftStickKey = KEY_BUTTON_LEFT_THUMB_STICK_LEFT;
|
|
}
|
|
|
|
if (lastLeftStickKey && newLeftStickKey != lastLeftStickKey)
|
|
{ // was held down last time - and we have a new key now
|
|
// post old key reset message...
|
|
CKey key(lastLeftStickKey, 0, 0, 0, 0, 0, 0);
|
|
lastLeftStickKey = newLeftStickKey;
|
|
if (OnKey(key)) return true;
|
|
}
|
|
lastLeftStickKey = newLeftStickKey;
|
|
// post the new key's message
|
|
if (newLeftStickKey)
|
|
{
|
|
CKey key(newLeftStickKey, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
|
|
// Trigger detection
|
|
static lastTriggerKey = 0;
|
|
int newTriggerKey = 0;
|
|
if (bLeftTrigger)
|
|
newTriggerKey = KEY_BUTTON_LEFT_ANALOG_TRIGGER;
|
|
else if (bRightTrigger)
|
|
newTriggerKey = KEY_BUTTON_RIGHT_ANALOG_TRIGGER;
|
|
if (lastTriggerKey && newTriggerKey != lastTriggerKey)
|
|
{ // was held down last time - and we have a new key now
|
|
// post old key reset message...
|
|
CKey key(lastTriggerKey, 0, 0, 0, 0, 0, 0);
|
|
lastTriggerKey = newTriggerKey;
|
|
if (OnKey(key)) return true;
|
|
}
|
|
lastTriggerKey = newTriggerKey;
|
|
// post the new key's message
|
|
if (newTriggerKey)
|
|
{
|
|
CKey key(newTriggerKey, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
|
|
// Now the digital buttons...
|
|
if ( wDir & DC_LEFTTRIGGER)
|
|
{
|
|
CKey key(KEY_BUTTON_LEFT_TRIGGER, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if ( wDir & DC_RIGHTTRIGGER)
|
|
{
|
|
CKey key(KEY_BUTTON_RIGHT_TRIGGER, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if ( wDir & DC_LEFT )
|
|
{
|
|
CKey key(KEY_BUTTON_DPAD_LEFT, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if ( wDir & DC_RIGHT)
|
|
{
|
|
CKey key(KEY_BUTTON_DPAD_RIGHT, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if ( wDir & DC_UP )
|
|
{
|
|
CKey key(KEY_BUTTON_DPAD_UP, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if ( wDir & DC_DOWN )
|
|
{
|
|
CKey key(KEY_BUTTON_DPAD_DOWN, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
|
|
if (m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_BACK )
|
|
{
|
|
CKey key(KEY_BUTTON_BACK, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_START)
|
|
{
|
|
CKey key(KEY_BUTTON_START, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_LEFT_THUMB)
|
|
{
|
|
CKey key(KEY_BUTTON_LEFT_THUMB_BUTTON, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.wPressedButtons & XINPUT_GAMEPAD_RIGHT_THUMB)
|
|
{
|
|
CKey key(KEY_BUTTON_RIGHT_THUMB_BUTTON, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_A])
|
|
{
|
|
CKey key(KEY_BUTTON_A, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_B])
|
|
{
|
|
CKey key(KEY_BUTTON_B, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_X])
|
|
{
|
|
CKey key(KEY_BUTTON_X, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_Y])
|
|
{
|
|
CKey key(KEY_BUTTON_Y, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_BLACK])
|
|
{
|
|
CKey key(KEY_BUTTON_BLACK, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[XINPUT_GAMEPAD_WHITE])
|
|
{
|
|
CKey key(KEY_BUTTON_WHITE, bLeftTrigger, bRightTrigger, m_DefaultGamepad.fX1, m_DefaultGamepad.fY1, m_DefaultGamepad.fX2, m_DefaultGamepad.fY2, frameTime);
|
|
if (OnKey(key)) return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::ProcessRemote(float frameTime)
|
|
{
|
|
#ifdef HAS_IR_REMOTE
|
|
if (m_DefaultIR_Remote.wButtons)
|
|
{
|
|
// time depends on whether the movement is repeated (held down) or not.
|
|
// If it is, we use the FPS timer to get a repeatable speed.
|
|
// If it isn't, we use 20 to get repeatable jumps.
|
|
float time = (m_DefaultIR_Remote.bHeldDown) ? frameTime : 0.020f;
|
|
CKey key(m_DefaultIR_Remote.wButtons, 0, 0, 0, 0, 0, 0, time);
|
|
return OnKey(key);
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::ProcessMouse()
|
|
{
|
|
if (!g_Mouse.IsActive())
|
|
return false;
|
|
|
|
// Reset the screensaver and idle timers
|
|
m_idleTimer.StartZero();
|
|
ResetScreenSaver();
|
|
if (ResetScreenSaverWindow())
|
|
return true;
|
|
|
|
// Get the mouse command ID
|
|
uint32_t mousecommand = g_Mouse.GetAction();
|
|
|
|
// Retrieve the corresponding action
|
|
int iWin = g_windowManager.GetActiveWindowID();
|
|
CKey key(mousecommand | KEY_MOUSE, (unsigned int) 0);
|
|
CAction mouseaction = CButtonTranslator::GetInstance().GetAction(iWin, key);
|
|
|
|
// If we couldn't find an action return false to indicate we have not
|
|
// handled this mouse action
|
|
if (!mouseaction.GetID())
|
|
{
|
|
CLog::Log(LOGDEBUG, "%s: unknown mouse command %d", __FUNCTION__, mousecommand);
|
|
return false;
|
|
}
|
|
|
|
// Process the appcommand
|
|
CAction newmouseaction = 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());
|
|
|
|
// Log mouse actions except for move and noop
|
|
if (newmouseaction.GetID() != ACTION_MOUSE_MOVE && newmouseaction.GetID() != ACTION_NOOP)
|
|
CLog::Log(LOGDEBUG, "%s: trying mouse action %s", __FUNCTION__, mouseaction.GetName().c_str());
|
|
|
|
return OnAction(newmouseaction);
|
|
}
|
|
|
|
void CApplication::CheckForTitleChange()
|
|
{
|
|
if (CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
{
|
|
if (m_pPlayer->IsPlayingVideo())
|
|
{
|
|
const CVideoInfoTag* tagVal = g_infoManager.GetCurrentMovieTag();
|
|
if (m_pXbmcHttp && tagVal && !(tagVal->m_strTitle.empty()))
|
|
{
|
|
CStdString msg=m_pXbmcHttp->GetOpenTag()+"MovieTitle:"+tagVal->m_strTitle.c_str()+m_pXbmcHttp->GetCloseTag();
|
|
if (m_prevMedia!=msg && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
{
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; MediaChanged:"+msg+";1");
|
|
m_prevMedia=msg;
|
|
}
|
|
}
|
|
}
|
|
else if (m_pPlayer->IsPlayingAudio())
|
|
{
|
|
const CMusicInfoTag* tagVal=g_infoManager.GetCurrentSongTag();
|
|
if (m_pXbmcHttp && tagVal)
|
|
{
|
|
CStdString msg="";
|
|
if (!tagVal->GetTitle().empty())
|
|
msg=m_pXbmcHttp->GetOpenTag()+"AudioTitle:"+tagVal->GetTitle().c_str()+m_pXbmcHttp->GetCloseTag();
|
|
if (!tagVal->GetArtist().empty())
|
|
msg+=m_pXbmcHttp->GetOpenTag()+"AudioArtist:"+StringUtils::Join(tagVal->GetArtist(), g_advancedSettings.m_musicItemSeparator).c_str()+m_pXbmcHttp->GetCloseTag();
|
|
if (m_prevMedia!=msg)
|
|
{
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; MediaChanged:"+msg+";1");
|
|
m_prevMedia=msg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CApplication::ProcessHTTPApiButtons()
|
|
{
|
|
if (m_pXbmcHttp)
|
|
{
|
|
// copy key from webserver, and reset it in case we're called again before
|
|
// whatever happens in OnKey()
|
|
CKey keyHttp(m_pXbmcHttp->GetKey());
|
|
m_pXbmcHttp->ResetKey();
|
|
if (keyHttp.GetButtonCode() != KEY_INVALID)
|
|
{
|
|
if (keyHttp.GetButtonCode() == KEY_VMOUSE) //virtual mouse
|
|
{
|
|
int actionID = ACTION_MOUSE_MOVE;
|
|
if (keyHttp.GetLeftTrigger() == 1)
|
|
actionID = ACTION_MOUSE_LEFT_CLICK;
|
|
else if (keyHttp.GetLeftTrigger() == 2)
|
|
actionID = ACTION_MOUSE_RIGHT_CLICK;
|
|
else if (keyHttp.GetLeftTrigger() == 3)
|
|
actionID = ACTION_MOUSE_MIDDLE_CLICK;
|
|
else if (keyHttp.GetRightTrigger() == 1)
|
|
actionID = ACTION_MOUSE_DOUBLE_CLICK;
|
|
CAction action(actionID, keyHttp.GetLeftThumbX(), keyHttp.GetLeftThumbY());
|
|
OnAction(action);
|
|
}
|
|
else
|
|
OnKey(keyHttp);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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();
|
|
ResetScreenSaverWindow();
|
|
}
|
|
|
|
// now handle any buttons or axis
|
|
std::string joystickName;
|
|
bool isAxis = false;
|
|
float fAmount = 0.0;
|
|
|
|
WORD 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, fAmount);
|
|
}
|
|
else
|
|
{
|
|
CKey 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);
|
|
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, true, (*iterAxis).second);
|
|
}
|
|
}
|
|
|
|
{
|
|
CPoint pos;
|
|
if (es->GetMousePos(pos.x, pos.y) && g_Mouse.IsEnabled())
|
|
return OnAction(CAction(ACTION_MOUSE_MOVE, pos.x, pos.y));
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::ProcessJoystickEvent(const std::string& joystickName, int wKeyID, bool isAxis, float fAmount, unsigned int holdTime /*=0*/)
|
|
{
|
|
#ifdef HAS_EVENT_SERVER
|
|
m_idleTimer.StartZero();
|
|
|
|
// Make sure to reset screen saver, mouse.
|
|
ResetScreenSaver();
|
|
if (ResetScreenSaverWindow())
|
|
return true;
|
|
|
|
#ifdef HAS_SDL_JOYSTICK
|
|
g_Joystick.Reset();
|
|
#endif
|
|
g_Mouse.SetInactive();
|
|
|
|
int iWin = g_windowManager.GetActiveWindowID();
|
|
int actionID;
|
|
CStdString actionName;
|
|
bool fullRange = false;
|
|
|
|
// Translate using regular joystick translator.
|
|
if (CButtonTranslator::GetInstance().TranslateJoystickString(iWin, joystickName.c_str(), wKeyID, isAxis, actionID, actionName, fullRange))
|
|
return ExecuteInputAction( CAction(actionID, fAmount, 0.0f, actionName, holdTime) );
|
|
else
|
|
CLog::Log(LOGDEBUG, "ERROR mapping joystick action");
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::ExecuteInputAction(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;
|
|
}
|
|
|
|
bool CApplication::ProcessKeyboard()
|
|
{
|
|
// process the keyboard buttons etc.
|
|
BYTE vkey = g_Keyboard.GetKey();
|
|
WCHAR unicode = g_Keyboard.GetUnicode();
|
|
if (vkey || unicode)
|
|
{
|
|
// got a valid keypress - convert to a key code
|
|
int keyID;
|
|
if (vkey) // FIXME, every ascii has a vkey so vkey would always and ascii would never be processed, but fortunately OnKey uses wkeyID only to detect keyboard use and the real key is recalculated correctly.
|
|
keyID = vkey | KEY_VKEY;
|
|
else
|
|
keyID = KEY_UNICODE;
|
|
// CLog::Log(LOGDEBUG,"Keyboard: time=%i key=%i", CTimeUtils::GetFrameTime(), vkey);
|
|
CKey key(keyID);
|
|
key.SetHeld(g_Keyboard.KeyHeld());
|
|
return OnKey(key);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::IsButtonDown(DWORD code)
|
|
{
|
|
#ifdef HAS_GAMEPAD
|
|
if (code >= KEY_BUTTON_A && code <= KEY_BUTTON_RIGHT_TRIGGER)
|
|
{
|
|
// analogue
|
|
return (m_DefaultGamepad.bAnalogButtons[code - KEY_BUTTON_A + XINPUT_GAMEPAD_A] > XINPUT_GAMEPAD_MAX_CROSSTALK);
|
|
}
|
|
else if (code >= KEY_BUTTON_DPAD_UP && code <= KEY_BUTTON_RIGHT_THUMB_BUTTON)
|
|
{
|
|
// digital
|
|
return (m_DefaultGamepad.wButtons & (1 << (code - KEY_BUTTON_DPAD_UP))) != 0;
|
|
}
|
|
else
|
|
{
|
|
// remote
|
|
return m_DefaultIR_Remote.wButtons == code;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::AnyButtonDown()
|
|
{
|
|
ReadInput();
|
|
#ifdef HAS_GAMEPAD
|
|
if (m_DefaultGamepad.wPressedButtons || m_DefaultIR_Remote.wButtons)
|
|
return true;
|
|
|
|
for (int i = 0; i < 6; ++i)
|
|
{
|
|
if (m_DefaultGamepad.bPressedAnalogButtons[i])
|
|
return true;
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
HRESULT CApplication::Cleanup()
|
|
{
|
|
try
|
|
{
|
|
g_windowManager.DestroyWindows();
|
|
|
|
CLog::Log(LOGNOTICE, "unload sections");
|
|
CSectionLoader::UnloadAll();
|
|
// reset our d3d params before we destroy
|
|
g_graphicsContext.SetD3DDevice(NULL);
|
|
g_graphicsContext.SetD3DParameters(NULL);
|
|
|
|
#ifdef _DEBUG
|
|
// 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
|
|
g_infoManager.Clear();
|
|
DllLoaderContainer::Clear();
|
|
g_playlistPlayer.Clear();
|
|
CSettings::GetInstance().Uninitialize();
|
|
g_advancedSettings.Clear();
|
|
|
|
#ifdef HAS_DVD_DRIVE
|
|
CLibcdio::ReleaseInstance();
|
|
#endif
|
|
#endif
|
|
#ifdef _CRTDBG_MAP_ALLOC
|
|
_CrtDumpMemoryLeaks();
|
|
while(1); // execution ends
|
|
#endif
|
|
|
|
delete m_network;
|
|
m_network = NULL;
|
|
|
|
// Cleanup was called more than once on exit during my tests
|
|
if (m_ServiceManager)
|
|
{
|
|
m_ServiceManager->Deinit();
|
|
m_ServiceManager.reset();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
catch (...)
|
|
{
|
|
CLog::Log(LOGERROR, "Exception in CApplication::Cleanup()");
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
void CApplication::Stop(bool bLCDStop)
|
|
{
|
|
try
|
|
{
|
|
SaveFileState(true);
|
|
|
|
if (m_pXbmcHttp)
|
|
{
|
|
if(CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; ShutDown;1");
|
|
|
|
m_pXbmcHttp->shuttingDown=true;
|
|
//Sleep(100);
|
|
}
|
|
|
|
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::GetInstance().Save();
|
|
}
|
|
else
|
|
CLog::Log(LOGNOTICE, "Not saving settings (settings.xml is not present)");
|
|
|
|
m_bStop = true;
|
|
CLog::Log(LOGNOTICE, "stop all");
|
|
|
|
// stop scanning before we kill the network and so on
|
|
if (CMusicLibraryQueue::GetInstance().IsRunning())
|
|
CMusicLibraryQueue::GetInstance().CancelAllJobs();
|
|
|
|
if (CVideoLibraryQueue::GetInstance().IsRunning())
|
|
CVideoLibraryQueue::GetInstance().CancelAllJobs();
|
|
|
|
StopServices();
|
|
//Sleep(5000);
|
|
|
|
#ifdef __APPLE__
|
|
g_xbmcHelper.ReleaseAllInput();
|
|
#endif
|
|
|
|
CLog::Log(LOGNOTICE, "stop player");
|
|
m_pPlayer->ClosePlayer();
|
|
|
|
//g_lcd->StopThread();
|
|
CApplicationMessenger::Get().Cleanup();
|
|
|
|
CLog::Log(LOGNOTICE, "clean cached files!");
|
|
g_RarManager.ClearCache(true);
|
|
|
|
CLog::Log(LOGNOTICE, "unload skin");
|
|
UnloadSkin();
|
|
|
|
// Stop services before unloading Python
|
|
CServiceBroker::GetAddonMgr().StopServices(false);
|
|
|
|
// unregister action listeners
|
|
UnregisterActionListener(&CSeekHandler::Get());
|
|
UnregisterActionListener(&CPlayerController::GetInstance());
|
|
|
|
// 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::GetInstance().Uninitialize();
|
|
|
|
#ifdef HAS_LCD
|
|
if (g_lcd && bLCDStop)
|
|
{
|
|
g_lcd->Stop();
|
|
delete g_lcd;
|
|
g_lcd=NULL;
|
|
}
|
|
#endif
|
|
CLog::Log(LOGNOTICE, "stopped");
|
|
}
|
|
catch (...)
|
|
{
|
|
CLog::Log(LOGERROR, "Exception in CApplication::Stop()");
|
|
}
|
|
}
|
|
|
|
bool CApplication::PlayMedia(const CFileItem& item, const std::string &player, int iPlaylist)
|
|
{
|
|
//If item is a plugin, expand out now and run ourselves again
|
|
if (item.IsPlugin())
|
|
{
|
|
bool resume = item.m_lStartOffset == STARTOFFSET_RESUME;
|
|
CFileItem item_new(item);
|
|
if (XFILE::CPluginDirectory::GetPluginResult(item.GetPath(), item_new, resume))
|
|
return PlayMedia(item_new, player, 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())
|
|
{
|
|
//is or could be a playlist
|
|
auto_ptr<CPlayList> pPlayList (CPlayListFactory::Create(item));
|
|
if (pPlayList.get() && pPlayList->Load(item.GetPath()))
|
|
{
|
|
if (iPlaylist != PLAYLIST_NONE)
|
|
{
|
|
int track=0;
|
|
if (item.HasProperty("playlist_starting_track"))
|
|
track = 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//nothing special just play
|
|
return PlayFile(item, player, 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
|
|
// 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;
|
|
if (dbs.GetResumeBookMark(item.GetPath(), bookmark))
|
|
{
|
|
startoffset = (int)(bookmark.timeInSeconds*75);
|
|
selectedFile = bookmark.partNumber;
|
|
}
|
|
dbs.Close();
|
|
}
|
|
else
|
|
CLog::Log(LOGERROR, "%s - Cannot open VideoDatabase", __FUNCTION__);
|
|
}
|
|
|
|
// make sure that the selected part is within the boundaries
|
|
if (selectedFile <= 0)
|
|
{
|
|
CLog::Log(LOGWARNING, "%s - Selected part %d out of range, playing part 1", __FUNCTION__, selectedFile);
|
|
selectedFile = 1;
|
|
}
|
|
else if (selectedFile > movieList.Size())
|
|
{
|
|
CLog::Log(LOGWARNING, "%s - Selected part %d out of range, playing part %d", __FUNCTION__, 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
|
|
{
|
|
// 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())
|
|
{
|
|
dbs.GetVideoSettings(item.GetPath(), CMediaSettings::Get().GetCurrentVideoSettings());
|
|
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;
|
|
if( dbs.GetResumeBookMark(item.GetPath(), 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(CFileItem item, const std::string& player, bool bRestart)
|
|
{
|
|
// Ensure the MIME type has been retrieved for http:// and shout:// streams
|
|
if (item.GetMimeType().empty())
|
|
item.FillInMimeType();
|
|
|
|
if (!bRestart)
|
|
{
|
|
SaveCurrentFileSettings();
|
|
|
|
OutputDebugString("new file set audiostream:0\n");
|
|
// 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.IsPlayList())
|
|
return PLAYBACK_FAIL;
|
|
|
|
if (item.IsPlugin())
|
|
{ // we modify the item so that it becomes a real URL
|
|
bool resume = item.m_lStartOffset == STARTOFFSET_RESUME;
|
|
CFileItem item_new(item);
|
|
if (XFILE::CPluginDirectory::GetPluginResult(item.GetPath(), item_new, resume))
|
|
return PlayFile(boost::move(item_new), player, false);
|
|
return PLAYBACK_FAIL;
|
|
}
|
|
|
|
if (URIUtils::IsUPnP(item.GetPath()))
|
|
{
|
|
CFileItem item_new(item);
|
|
if (XFILE::CUPnPDirectory::GetResource(item.GetURL(), item_new))
|
|
return PlayFile(boost::move(item_new), player, false);
|
|
return PLAYBACK_FAIL;
|
|
}
|
|
|
|
// 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);
|
|
|
|
CPlayerOptions options;
|
|
|
|
if( item.HasProperty("StartPercent") )
|
|
{
|
|
options.startpercent = item.GetProperty("StartPercent").asDouble();
|
|
}
|
|
|
|
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;
|
|
|
|
if (item.IsVideo())
|
|
{
|
|
// open the d/b and retrieve the bookmarks for the current movie
|
|
CVideoDatabase dbs;
|
|
dbs.Open();
|
|
dbs.GetVideoSettings(item.GetPath(), CMediaSettings::Get().GetCurrentVideoSettings());
|
|
|
|
if( item.m_lStartOffset == STARTOFFSET_RESUME )
|
|
{
|
|
options.starttime = 0.0f;
|
|
CBookmark bookmark;
|
|
CStdString path = item.GetPath();
|
|
if (item.IsDVD())
|
|
path = item.GetVideoInfoTag()->m_strFileNameAndPath;
|
|
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.HasVideoInfoTag() && item.GetVideoInfoTag()->m_resumePoint.IsSet())
|
|
options.starttime = item.GetVideoInfoTag()->m_resumePoint.timeInSeconds;
|
|
}
|
|
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 (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);
|
|
// reset any forced player
|
|
m_eForcedNextPlayer = EPC_NONE;
|
|
|
|
#ifdef HAS_KARAOKE
|
|
//We have to stop parsing a cdg before mplayer is deallocated
|
|
// WHY do we have to do this????
|
|
if(m_pCdgParser)
|
|
m_pCdgParser->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::Log(LOGDEBUG,"%s : Ignored %d playback thread messages", __FUNCTION__, 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( 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 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(true);
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
if (g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION ||
|
|
g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
|
|
g_windowManager.PreviousWindow();
|
|
}
|
|
|
|
g_audioManager.Enable(false);
|
|
}
|
|
|
|
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::Log(LOGDEBUG,"%s : OpenFile succeed, play state %d", __FUNCTION__, 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::Log(LOGDEBUG,"%s : play state was %d, starting %d", __FUNCTION__, 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)
|
|
g_pythonParser.OnPlayBackEnded();
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnPlayBackEnded;1");
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback has finished", __FUNCTION__);
|
|
|
|
CGUIMessage msg(GUI_MSG_PLAYBACK_ENDED, 0, 0);
|
|
g_windowManager.SendThreadMessage(msg);
|
|
}
|
|
|
|
void CApplication::OnPlayBackStarted()
|
|
{
|
|
CSingleLock lock(m_playStateMutex);
|
|
CLog::Log(LOGDEBUG,"%s : play state was %d, starting %d", __FUNCTION__, m_ePlayState, m_bPlaybackStarting);
|
|
m_ePlayState = PLAY_STATE_PLAYING;
|
|
if(m_bPlaybackStarting)
|
|
return;
|
|
|
|
// informs python script currently running playback has started
|
|
// (does nothing if python is not loaded)
|
|
g_pythonParser.OnPlayBackStarted();
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnPlayBackStarted;1");
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback has started", __FUNCTION__);
|
|
|
|
CGUIMessage msg(GUI_MSG_PLAYBACK_STARTED, 0, 0);
|
|
g_windowManager.SendThreadMessage(msg);
|
|
}
|
|
|
|
void CApplication::OnQueueNextItem()
|
|
{
|
|
CSingleLock lock(m_playStateMutex);
|
|
CLog::Log(LOGDEBUG,"%s : play state was %d, starting %d", __FUNCTION__, 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)
|
|
g_pythonParser.OnQueueNextItem(); // currently unimplemented
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnQueueNextItem;1");
|
|
|
|
CLog::Log(LOGDEBUG, "Player has asked for the next item");
|
|
|
|
CGUIMessage msg(GUI_MSG_QUEUE_NEXT_ITEM, 0, 0);
|
|
g_windowManager.SendThreadMessage(msg);
|
|
}
|
|
|
|
void CApplication::OnPlayBackStopped()
|
|
{
|
|
CSingleLock lock(m_playStateMutex);
|
|
CLog::Log(LOGDEBUG,"%s : play state was %d, starting %d", __FUNCTION__, 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)
|
|
g_pythonParser.OnPlayBackStopped();
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnPlayBackStopped;1");
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback was stopped", __FUNCTION__);
|
|
|
|
CGUIMessage msg( GUI_MSG_PLAYBACK_STOPPED, 0, 0 );
|
|
g_windowManager.SendThreadMessage(msg);
|
|
}
|
|
|
|
void CApplication::OnPlayBackPaused()
|
|
{
|
|
g_pythonParser.OnPlayBackPaused();
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnPlayBackPaused;1");
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback was paused", __FUNCTION__);
|
|
}
|
|
|
|
void CApplication::OnPlayBackResumed()
|
|
{
|
|
g_pythonParser.OnPlayBackResumed();
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
CApplicationMessenger::Get().HttpApi("broadcastlevel; OnPlayBackResumed;1");
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback was resumed", __FUNCTION__);
|
|
}
|
|
|
|
void CApplication::OnPlayBackSpeedChanged(int iSpeed)
|
|
{
|
|
g_pythonParser.OnPlayBackSpeedChanged(iSpeed);
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
{
|
|
CStdString tmp;
|
|
tmp.Format("broadcastlevel; OnPlayBackSpeedChanged:%i;1",iSpeed);
|
|
CApplicationMessenger::Get().HttpApi(tmp);
|
|
}
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback speed changed", __FUNCTION__);
|
|
}
|
|
|
|
void CApplication::OnPlayBackSeek(int iTime, int seekOffset)
|
|
{
|
|
g_pythonParser.OnPlayBackSeek(iTime, seekOffset);
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
{
|
|
CStdString tmp;
|
|
tmp.Format("broadcastlevel; OnPlayBackSeek:%i;1",iTime);
|
|
CApplicationMessenger::Get().HttpApi(tmp);
|
|
}
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback skip", __FUNCTION__);
|
|
// g_infoManager.SetDisplayAfterSeek(2500, seekOffset/1000);
|
|
}
|
|
|
|
void CApplication::OnPlayBackSeekChapter(int iChapter)
|
|
{
|
|
g_pythonParser.OnPlayBackSeekChapter(iChapter);
|
|
|
|
// Let's tell the outside world as well
|
|
if (m_pXbmcHttp && CSettings::GetInstance().GetInt("services.httpapibroadcastlevel")>=1)
|
|
{
|
|
CStdString tmp;
|
|
tmp.Format("broadcastlevel; OnPlayBackSkeekChapter:%i;1",iChapter);
|
|
CApplicationMessenger::Get().HttpApi(tmp);
|
|
}
|
|
|
|
CLog::Log(LOGDEBUG, "%s - Playback skip", __FUNCTION__);
|
|
}
|
|
|
|
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;
|
|
|
|
if (bForeground)
|
|
{
|
|
CSaveFileStateJob job(*m_progressTrackingItem,
|
|
*m_stackFileItemToUpdate,
|
|
m_progressTrackingVideoResumeBookmark,
|
|
m_progressTrackingPlayCountUpdate);
|
|
|
|
// Run job in the foreground to make sure it finishes
|
|
job.DoWork();
|
|
}
|
|
else
|
|
{
|
|
CJob* job = new CSaveFileStateJob(*m_progressTrackingItem,
|
|
*m_stackFileItemToUpdate,
|
|
m_progressTrackingVideoResumeBookmark,
|
|
m_progressTrackingPlayCountUpdate);
|
|
CJobManager::GetInstance().AddJob(job, NULL, CJob::PRIORITY_NORMAL);
|
|
}
|
|
}
|
|
|
|
void CApplication::UpdateFileState()
|
|
{
|
|
// Did the file change?
|
|
if (m_progressTrackingItem->GetPath() != "" && m_progressTrackingItem->GetPath() != CurrentFile())
|
|
{
|
|
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())
|
|
{
|
|
// Special case for DVDs: Only extract 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, 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.empty();
|
|
|
|
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::StopPlaying()
|
|
{
|
|
int iWin = g_windowManager.GetActiveWindow();
|
|
if ( m_pPlayer->IsPlaying() )
|
|
{
|
|
#ifdef HAS_KARAOKE
|
|
if( m_pCdgParser )
|
|
m_pCdgParser->Stop();
|
|
#endif
|
|
|
|
m_pPlayer->CloseFile();
|
|
|
|
// turn off visualisation window when stopping
|
|
if (iWin == WINDOW_VISUALISATION
|
|
|| iWin == WINDOW_FULLSCREEN_VIDEO)
|
|
g_windowManager.PreviousWindow();
|
|
|
|
g_partyModeManager.Disable();
|
|
}
|
|
}
|
|
|
|
bool CApplication::NeedRenderFullScreen()
|
|
{
|
|
if (g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
|
|
{
|
|
if (g_windowManager.HasDialogOnScreen()) return true;
|
|
if (g_Mouse.IsActive()) return true;
|
|
|
|
CGUIWindowFullScreen *pFSWin = (CGUIWindowFullScreen *)g_windowManager.GetWindow(WINDOW_FULLSCREEN_VIDEO);
|
|
if (!pFSWin)
|
|
return false;
|
|
return pFSWin->NeedRenderFullScreen();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CApplication::RenderFullScreen()
|
|
{
|
|
if (g_graphicsContext.IsFullScreenVideo())
|
|
{
|
|
CGUIWindowFullScreen *pFSWin = (CGUIWindowFullScreen *)g_windowManager.GetWindow(WINDOW_FULLSCREEN_VIDEO);
|
|
if (!pFSWin)
|
|
return ;
|
|
pFSWin->RenderFullScreen();
|
|
|
|
if (g_windowManager.HasDialogOnScreen())
|
|
g_windowManager.RenderDialogs();
|
|
}
|
|
}
|
|
|
|
void CApplication::ResetScreenSaver()
|
|
{
|
|
// reset our timers
|
|
m_shutdownTimer.StartZero();
|
|
|
|
// screen saver timer is reset only if we're not already in screensaver mode
|
|
if (!m_bScreenSave && m_iScreenSaveLock == 0)
|
|
m_screenSaverTimer.StartZero();
|
|
}
|
|
|
|
bool CApplication::ResetScreenSaverWindow()
|
|
{
|
|
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::GetInstance().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() != "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;
|
|
m_screenSaverTimer.StartZero();
|
|
|
|
float fFadeLevel = 1.0f;
|
|
if (m_screenSaver->ID() == "visualization" || m_screenSaver->ID() == "screensaver.xbmc.builtin.slideshow")
|
|
{
|
|
// we can just continue as usual from vis mode
|
|
return false;
|
|
}
|
|
else if (m_screenSaver->ID() == "screensaver.xbmc.builtin.dim")
|
|
{
|
|
if (!m_screenSaver->GetSetting("level").empty())
|
|
fFadeLevel = 1.0f - 0.01f * (float)atof(m_screenSaver->GetSetting("level").c_str());
|
|
}
|
|
else if (m_screenSaver->ID() == "screensaver.xbmc.builtin.black")
|
|
{
|
|
fFadeLevel = 0;
|
|
}
|
|
else if (!m_screenSaver->ID().empty())
|
|
{ // we're in screensaver window
|
|
if (g_windowManager.GetActiveWindow() == WINDOW_SCREENSAVER)
|
|
g_windowManager.PreviousWindow(); // show the previous window
|
|
return true;
|
|
}
|
|
|
|
// Fade to dim or black screensaver is active --> fade in
|
|
D3DGAMMARAMP Ramp;
|
|
for (float fade = fFadeLevel; fade <= 1; fade += 0.01f)
|
|
{
|
|
for (int i = 0;i < 256;i++)
|
|
{
|
|
Ramp.red[i] = (int)((float)m_OldRamp.red[i] * fade);
|
|
Ramp.green[i] = (int)((float)m_OldRamp.green[i] * fade);
|
|
Ramp.blue[i] = (int)((float)m_OldRamp.blue[i] * fade);
|
|
}
|
|
Sleep(5);
|
|
m_pd3dDevice->SetGammaRamp(GAMMA_RAMP_FLAG, &Ramp); // use immediate to get a smooth fade
|
|
}
|
|
m_pd3dDevice->SetGammaRamp(0, &m_OldRamp); // put the old gamma ramp back in place
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void CApplication::CheckScreenSaver()
|
|
{
|
|
// if the screen saver window is active, then clearly we are already active
|
|
if (g_windowManager.IsWindowActive(WINDOW_SCREENSAVER))
|
|
{
|
|
m_bScreenSave = true;
|
|
return;
|
|
}
|
|
|
|
bool resetTimer = false;
|
|
if (m_pPlayer->IsPlayingVideo() && !m_pPlayer->IsPaused()) // are we playing video and it is not paused?
|
|
resetTimer = true;
|
|
|
|
// are we playing some music in fullscreen vis?
|
|
if (m_pPlayer->IsPlayingAudio() && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION
|
|
&& !CSettings::GetInstance().GetString("musicplayer.visualisation").empty())
|
|
resetTimer = true;
|
|
|
|
if (resetTimer)
|
|
{
|
|
m_screenSaverTimer.StartZero();
|
|
return;
|
|
}
|
|
|
|
if (m_bScreenSave) // already running the screensaver
|
|
return;
|
|
|
|
if ( m_screenSaverTimer.GetElapsedSeconds() > CSettings::GetInstance().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 */)
|
|
{
|
|
D3DGAMMARAMP Ramp;
|
|
FLOAT fFadeLevel = 0;
|
|
|
|
m_bScreenSave = true;
|
|
|
|
// Get Screensaver Mode
|
|
m_screenSaver.reset();
|
|
if (!CServiceBroker::GetAddonMgr().GetAddon(CSettings::GetInstance().GetString("screensaver.mode"), m_screenSaver))
|
|
m_screenSaver.reset(new CScreenSaver(""));
|
|
|
|
// 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::GetInstance().GetBool("screensaver.usedimonpause")))
|
|
{
|
|
if (!CServiceBroker::GetAddonMgr().GetAddon("screensaver.xbmc.builtin.dim", m_screenSaver))
|
|
m_screenSaver.reset(new CScreenSaver(""));
|
|
}
|
|
// Check if we are Playing Audio and Vis instead Screensaver!
|
|
else if (m_pPlayer->IsPlayingAudio() && CSettings::GetInstance().GetBool("screensaver.usemusicvisinstead") && !CSettings::GetInstance().GetString("musicplayer.visualisation").empty())
|
|
{ // activate the visualisation
|
|
m_screenSaver.reset(new CScreenSaver("visualization"));
|
|
g_windowManager.ActivateWindow(WINDOW_VISUALISATION);
|
|
return;
|
|
}
|
|
}
|
|
// Picture slideshow
|
|
if (m_screenSaver->ID() == "screensaver.xbmc.builtin.slideshow")
|
|
{
|
|
// reset our codec info - don't want that on screen
|
|
g_infoManager.SetShowCodec(false);
|
|
CStdString type = m_screenSaver->GetSetting("type");
|
|
CStdString path = m_screenSaver->GetSetting("path");
|
|
if (type == "2" && path.IsEmpty())
|
|
type = "0";
|
|
if (type == "0")
|
|
path = "special://profile/thumbnails/Video/Fanart";
|
|
if (type == "1")
|
|
path = "special://profile/thumbnails/Music/Fanart";
|
|
CApplicationMessenger::Get().PostMsg(TMSG_SLIDESHOW_SCREENSAVER, type != "2" ? 1 : 0, -1, NULL, path);
|
|
return;
|
|
}
|
|
else if (m_screenSaver->ID() == "screensaver.xbmc.builtin.dim")
|
|
{
|
|
if (!m_screenSaver->GetSetting("level").empty())
|
|
fFadeLevel = 1.0f - 0.01f * (float)atof(m_screenSaver->GetSetting("level").c_str());
|
|
}
|
|
else if (m_screenSaver->ID() == "screensaver.xbmc.builtin.black")
|
|
{
|
|
fFadeLevel = 0;
|
|
}
|
|
else if (!m_screenSaver->ID().empty())
|
|
{
|
|
g_windowManager.ActivateWindow(WINDOW_SCREENSAVER);
|
|
return ;
|
|
}
|
|
|
|
// Fade to fFadeLevel
|
|
m_pd3dDevice->GetGammaRamp(&m_OldRamp); // Store the old gamma ramp
|
|
for (float fade = 1.f; fade >= fFadeLevel; fade -= 0.01f)
|
|
{
|
|
for (int i = 0;i < 256;i++)
|
|
{
|
|
Ramp.red[i] = (int)((float)m_OldRamp.red[i] * fade);
|
|
Ramp.green[i] = (int)((float)m_OldRamp.green[i] * fade);
|
|
Ramp.blue[i] = (int)((float)m_OldRamp.blue[i] * fade);
|
|
}
|
|
Sleep(5);
|
|
m_pd3dDevice->SetGammaRamp(GAMMA_RAMP_FLAG, &Ramp); // use immediate to get a smooth fade
|
|
}
|
|
}
|
|
|
|
void CApplication::CheckShutdown()
|
|
{
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
// first check if we should reset the timer
|
|
bool resetTimer = false;
|
|
if (m_pPlayer->IsPlaying()) // is something playing?
|
|
resetTimer = true;
|
|
|
|
#ifdef HAS_FTP_SERVER
|
|
if (CNetworkServices::Get().IsFtpServerRunning() && CNetworkServices::Get().FtpHasActiveConnections()) // is FTP active ?
|
|
resetTimer = true;
|
|
#endif
|
|
|
|
if (CMusicLibraryQueue::GetInstance().IsRunning())
|
|
resetTimer = true;
|
|
|
|
if (CVideoLibraryQueue::GetInstance().IsRunning())
|
|
resetTimer = true;
|
|
|
|
if (g_windowManager.IsWindowActive(WINDOW_DIALOG_PROGRESS)) // progress dialog is onscreen
|
|
resetTimer = true;
|
|
|
|
if (resetTimer)
|
|
{
|
|
m_shutdownTimer.StartZero();
|
|
return;
|
|
}
|
|
|
|
if ( m_shutdownTimer.GetElapsedSeconds() > CSettings::GetInstance().GetInt("powermanagement.shutdowntime") * 60 )
|
|
{
|
|
CApplicationMessenger::Get().PostMsg(TMSG_SHUTDOWN); // Turn off the box
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//Check if hd spindown must be blocked
|
|
bool CApplication::MustBlockHDSpinDown(bool bCheckThisForNormalSpinDown)
|
|
{
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
if (m_pPlayer->IsPlayingVideo())
|
|
{
|
|
//block immediate spindown when playing a video non-fullscreen (videocontrol is playing)
|
|
if ((!bCheckThisForNormalSpinDown) && (!g_graphicsContext.IsFullScreenVideo()))
|
|
{
|
|
return true;
|
|
}
|
|
//allow normal hd spindown always if the movie is paused
|
|
if ((bCheckThisForNormalSpinDown) && (m_pPlayer->IsPaused()))
|
|
{
|
|
return false;
|
|
}
|
|
//don't allow hd spindown when playing files with vobsub subtitles.
|
|
std::string strSubTitelExtension;
|
|
if (m_pPlayer->GetSubtitleExtension(strSubTitelExtension))
|
|
{
|
|
return (strSubTitelExtension == ".idx");
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
void CApplication::CheckNetworkHDSpinDown(bool playbackStarted)
|
|
{
|
|
int iSpinDown = CSettings::GetInstance().GetInt("harddisk.remoteplayspindown");
|
|
if (iSpinDown == SPIN_DOWN_NONE)
|
|
return ;
|
|
if (g_windowManager.HasModalDialog())
|
|
return ;
|
|
if (MustBlockHDSpinDown(false))
|
|
return ;
|
|
|
|
if ((!m_bNetworkSpinDown) || playbackStarted)
|
|
{
|
|
int iDuration = 0;
|
|
if (m_pPlayer->IsPlayingAudio())
|
|
{
|
|
//try to get duration from current tag because mplayer doesn't calculate vbr mp3 correctly
|
|
if (m_itemCurrentFile->HasMusicInfoTag())
|
|
iDuration = m_itemCurrentFile->GetMusicInfoTag()->GetDuration();
|
|
}
|
|
if (m_pPlayer->IsPlaying() && iDuration <= 0)
|
|
{
|
|
iDuration = (int)GetTotalTime();
|
|
}
|
|
//spin down harddisk when the current file being played is not on local harddrive and
|
|
//duration is more then spindown timeoutsetting or duration is unknown (streams)
|
|
if (
|
|
!m_itemCurrentFile->IsHD() &&
|
|
(
|
|
(iSpinDown == SPIN_DOWN_VIDEO && m_pPlayer->IsPlayingVideo()) ||
|
|
(iSpinDown == SPIN_DOWN_MUSIC && m_pPlayer->IsPlayingAudio()) ||
|
|
(iSpinDown == SPIN_DOWN_BOTH && (m_pPlayer->IsPlayingVideo() || m_pPlayer->IsPlayingAudio()))
|
|
) &&
|
|
(
|
|
(iDuration <= 0) ||
|
|
(iDuration > CSettings::GetInstance().GetInt("harddisk.remoteplayspindownminduration")*60)
|
|
)
|
|
)
|
|
{
|
|
m_bNetworkSpinDown = true;
|
|
if (!playbackStarted)
|
|
{ //if we got here not because of a playback start check what screen we are in
|
|
// get the current active window
|
|
int iWin = g_windowManager.GetActiveWindow();
|
|
if (iWin == WINDOW_FULLSCREEN_VIDEO)
|
|
{
|
|
// check if OSD is visible, if so don't do immediate spindown
|
|
CGUIDialogVideoOSD *pOSD = (CGUIDialogVideoOSD *)g_windowManager.GetWindow(WINDOW_DIALOG_VIDEO_OSD);
|
|
if (pOSD)
|
|
m_bNetworkSpinDown = !pOSD->IsDialogRunning();
|
|
}
|
|
}
|
|
if (m_bNetworkSpinDown)
|
|
{
|
|
//do the spindown right now + delayseconds
|
|
m_dwSpinDownTime = timeGetTime();
|
|
}
|
|
}
|
|
}
|
|
if (m_bNetworkSpinDown)
|
|
{
|
|
// check the elapsed time
|
|
DWORD dwTimeSpan = timeGetTime() - m_dwSpinDownTime;
|
|
if ( (m_dwSpinDownTime != 0) && (dwTimeSpan >= ((DWORD)CSettings::GetInstance().GetInt("harddisk.remoteplayspindowndelay")*1000UL)) )
|
|
{
|
|
// time has elapsed, spin it down
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
XKHDD::SpindownHarddisk();
|
|
#endif
|
|
//stop checking until a key is pressed.
|
|
m_dwSpinDownTime = 0;
|
|
m_bNetworkSpinDown = true;
|
|
}
|
|
else if (m_dwSpinDownTime == 0 && m_pPlayer->IsPlaying())
|
|
{
|
|
// we are currently spun down - let's spin back up again if we are playing media
|
|
// and we're within 10 seconds (or 0.5*spindown time) of the end. This should
|
|
// make returning to the GUI a bit snappier + speed up stacked item changes.
|
|
int iMinSpinUp = 10;
|
|
if (iMinSpinUp > CSettings::GetInstance().GetInt("harddisk.remoteplayspindowndelay")*0.5f)
|
|
iMinSpinUp = (int)(CSettings::GetInstance().GetInt("harddisk.remoteplayspindowndelay")*0.5f);
|
|
if (g_infoManager.GetPlayTimeRemaining() == iMinSpinUp)
|
|
{ // spin back up
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
XKHDD::SpindownHarddisk(false);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CApplication::CheckHDSpindown()
|
|
{
|
|
if (!CSettings::GetInstance().GetInt("harddisk.spindowntime"))
|
|
return ;
|
|
if (g_windowManager.HasModalDialog())
|
|
return ;
|
|
if (MustBlockHDSpinDown())
|
|
return ;
|
|
|
|
if (!m_bSpinDown &&
|
|
(
|
|
!m_pPlayer->IsPlaying() ||
|
|
(m_pPlayer->IsPlaying() && !m_itemCurrentFile->IsHD())
|
|
)
|
|
)
|
|
{
|
|
m_bSpinDown = true;
|
|
m_bNetworkSpinDown = false; // let networkspindown override normal spindown
|
|
m_dwSpinDownTime = timeGetTime();
|
|
}
|
|
|
|
//Can we do a spindown right now?
|
|
if (m_bSpinDown)
|
|
{
|
|
// yes, then check the elapsed time
|
|
DWORD dwTimeSpan = timeGetTime() - m_dwSpinDownTime;
|
|
if ( (m_dwSpinDownTime != 0) && (dwTimeSpan >= ((DWORD)CSettings::GetInstance().GetInt("harddisk.spindowntime")*60UL*1000UL)) )
|
|
{
|
|
// time has elapsed, spin it down
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
XKHDD::SpindownHarddisk();
|
|
#endif
|
|
//stop checking until a key is pressed.
|
|
m_dwSpinDownTime = 0;
|
|
m_bSpinDown = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
else if (message.GetParam1() == GUI_MSG_UI_READY)
|
|
{
|
|
// remove splash window
|
|
g_windowManager.Delete(WINDOW_SPLASH);
|
|
|
|
if (m_fallbackLanguageLoaded)
|
|
CGUIDialogOK::ShowAndGetInput(24133, 24134);
|
|
}
|
|
else if (message.GetParam1() == GUI_MSG_UPDATE_ITEM && message.GetItem())
|
|
{
|
|
CFileItemPtr item = boost::static_pointer_cast<CFileItem>(message.GetItem());
|
|
if (m_itemCurrentFile->IsSamePath(item.get()))
|
|
{
|
|
m_itemCurrentFile->UpdateInfo(*item);
|
|
g_infoManager.SetCurrentItem(*m_itemCurrentFile);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case GUI_MSG_PLAYBACK_STARTED:
|
|
{
|
|
// Update our infoManager with the new details etc.
|
|
if (m_nextPlaylistItem >= 0)
|
|
{ // we've started a previously queued item
|
|
CFileItemPtr item = g_playlistPlayer.GetPlaylist(g_playlistPlayer.GetCurrentPlaylist())[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);
|
|
|
|
CheckNetworkHDSpinDown(true);
|
|
StartLEDControl(true);
|
|
DimLCDOnPlayback(true);
|
|
|
|
if (m_pPlayer->IsPlayingAudio())
|
|
{
|
|
// Start our cdg parser as appropriate
|
|
#ifdef HAS_KARAOKE
|
|
if (m_pCdgParser && CSettings::GetInstance().GetBool("karaoke.enabled") && !m_itemCurrentFile->IsInternetStream())
|
|
{
|
|
if (m_pCdgParser->IsRunning())
|
|
m_pCdgParser->Stop();
|
|
if (m_itemCurrentFile->IsMusicDb())
|
|
{
|
|
if (!m_itemCurrentFile->HasMusicInfoTag() || !m_itemCurrentFile->GetMusicInfoTag()->Loaded())
|
|
{
|
|
IMusicInfoTagLoader* tagloader = CMusicInfoTagLoaderFactory::CreateLoader(*m_itemCurrentFile);
|
|
tagloader->Load(m_itemCurrentFile->GetPath(),*m_itemCurrentFile->GetMusicInfoTag());
|
|
delete tagloader;
|
|
}
|
|
m_pCdgParser->Start(m_itemCurrentFile->GetMusicInfoTag()->GetURL());
|
|
}
|
|
else
|
|
m_pCdgParser->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, false);
|
|
|
|
#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 wants it
|
|
if (m_pPlayer->QueueNextFile(file))
|
|
{ // player wants the next file
|
|
m_nextPlaylistItem = iNext;
|
|
}
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case GUI_MSG_PLAYBACK_STOPPED:
|
|
case GUI_MSG_PLAYBACK_ENDED:
|
|
case GUI_MSG_PLAYLISTPLAYER_STOPPED:
|
|
{
|
|
// 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 our spindown
|
|
m_bNetworkSpinDown = false;
|
|
m_bSpinDown = false;
|
|
|
|
// reset the current playing file
|
|
m_itemCurrentFile->Reset();
|
|
g_infoManager.ResetCurrentItem();
|
|
m_currentStack->Clear();
|
|
|
|
if (message.GetMessage() == GUI_MSG_PLAYBACK_ENDED)
|
|
{
|
|
// sending true to PlayNext() effectively passes bRestart to PlayFile()
|
|
// which is not generally what we want (except for stacks, which are
|
|
// handled above)
|
|
g_playlistPlayer.PlayNext();
|
|
}
|
|
else
|
|
{
|
|
m_pPlayer->ClosePlayer();
|
|
}
|
|
|
|
if (!m_pPlayer->IsPlaying())
|
|
{
|
|
g_audioManager.Enable(true);
|
|
StartLEDControl(false);
|
|
DimLCDOnPlayback(false);
|
|
|
|
#ifdef HAS_KARAOKE
|
|
if(m_pCdgParser)
|
|
m_pCdgParser->Free();
|
|
#endif
|
|
}
|
|
|
|
if (!m_pPlayer->IsPlayingVideo() && g_windowManager.GetActiveWindow() == WINDOW_FULLSCREEN_VIDEO)
|
|
{
|
|
g_windowManager.PreviousWindow();
|
|
}
|
|
|
|
if (!m_pPlayer->IsPlayingAudio() && g_playlistPlayer.GetCurrentPlaylist() == PLAYLIST_NONE && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION)
|
|
{
|
|
CSettings::GetInstance().Save(); // save vis settings
|
|
ResetScreenSaverWindow();
|
|
g_windowManager.PreviousWindow();
|
|
}
|
|
|
|
// DVD ejected while playing in vis ?
|
|
if (!m_pPlayer->IsPlayingAudio() && (m_itemCurrentFile->IsCDDA() || m_itemCurrentFile->IsOnDVD()) && !CDetectDVDMedia::IsDiscInDrive() && g_windowManager.GetActiveWindow() == WINDOW_VISUALISATION)
|
|
{
|
|
// yes, disable vis
|
|
CSettings::GetInstance().Save(); // save vis settings
|
|
ResetScreenSaverWindow();
|
|
g_windowManager.PreviousWindow();
|
|
}
|
|
|
|
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.GetStringParam().length() > 0)
|
|
return ExecuteXBMCAction(message.GetStringParam(), message.GetItem());
|
|
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CApplication::ExecuteXBMCAction(std::string actionStr, const CGUIListItemPtr &item /* = NULL */)
|
|
{
|
|
// 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);
|
|
if (item)
|
|
actionStr = CGUIInfoLabel::GetItemLabel(actionStr, item.get());
|
|
else
|
|
actionStr = CGUIInfoLabel::GetLabel(actionStr);
|
|
|
|
// user has asked for something to be executed
|
|
if (CBuiltins::GetInstance().HasCommand(actionStr))
|
|
CBuiltins::GetInstance().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);
|
|
if (item.IsPythonScript())
|
|
{ // a python script
|
|
CScriptInvocationManager::GetInstance().ExecuteAsync(item.GetPath());
|
|
}
|
|
else if (item.IsXBE())
|
|
{ // an XBE
|
|
int iRegion;
|
|
if (CSettings::GetInstance().GetBool("myprograms.gameautoregion"))
|
|
{
|
|
CXBE xbe;
|
|
iRegion = xbe.ExtractGameRegion(item.GetPath());
|
|
if (iRegion < 1 || iRegion > 7)
|
|
iRegion = 0;
|
|
iRegion = xbe.FilterRegion(iRegion);
|
|
}
|
|
else
|
|
iRegion = 0;
|
|
CUtil::RunXBE(item.GetPath().c_str(),NULL,F_VIDEO(iRegion));
|
|
}
|
|
else if (item.IsAudio() || item.IsVideo())
|
|
{ // an audio or video file
|
|
PlayFile(item , "");
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CApplication::Process()
|
|
{
|
|
// 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::GetInstance().ExecuteAsync(strAutoExecPy);
|
|
else
|
|
CLog::Log(LOGDEBUG, "no profile autoexec.py (%s) found, skipping", strAutoExecPy.c_str());
|
|
}
|
|
|
|
// handle any active scripts
|
|
CScriptInvocationManager::GetInstance().Process();
|
|
|
|
// process messages, even if a movie is playing
|
|
CApplicationMessenger::Get().ProcessMessages();
|
|
|
|
// check for memory unit changes
|
|
#ifdef HAS_XBOX_HARDWARE
|
|
if (g_memoryUnitManager.Update())
|
|
{ // changes have occured - update our shares
|
|
CGUIMessage msg(GUI_MSG_NOTIFY_ALL,0,0,GUI_MSG_REMOVED_MEDIA);
|
|
g_windowManager.SendThreadMessage(msg);
|
|
}
|
|
#endif
|
|
|
|
// check if we can free unused memory
|
|
g_audioManager.FreeUnused();
|
|
|
|
// 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();
|
|
|
|
// process karaoke
|
|
#ifdef HAS_KARAOKE
|
|
if (m_pCdgParser)
|
|
m_pCdgParser->ProcessVoice();
|
|
#endif
|
|
|
|
// do any processing that isn't needed on each run
|
|
if( m_slowTimer.GetElapsedMilliseconds() > 500 )
|
|
{
|
|
m_slowTimer.Reset();
|
|
ProcessSlow();
|
|
}
|
|
|
|
}
|
|
|
|
// We get called every 500ms
|
|
void CApplication::ProcessSlow()
|
|
{
|
|
// check our network state every 15 seconds or when net status changes
|
|
m_network->CheckNetwork(30);
|
|
|
|
// check if we need 2 spin down the harddisk
|
|
CheckNetworkHDSpinDown();
|
|
if (!m_bNetworkSpinDown)
|
|
CheckHDSpindown();
|
|
|
|
// 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 (if enabled).
|
|
if (CSettings::GetInstance().GetString("screensaver.mode") != "None")
|
|
CheckScreenSaver();
|
|
|
|
// check if we need to shutdown (if enabled)
|
|
if (CSettings::GetInstance().GetInt("powermanagement.shutdowntime"))
|
|
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();
|
|
|
|
// Xbox Autodetection - Send in X sec PingTime Interval
|
|
if (g_windowManager.GetActiveWindow() != WINDOW_LOGIN_SCREEN) // sorry jm ;D
|
|
CUtil::AutoDetection();
|
|
|
|
// check for any idle curl connections
|
|
g_curlInterface.CheckIdle();
|
|
|
|
#ifdef HAS_TIME_SERVER
|
|
// check for any needed sntp update
|
|
if(CNetworkServices::Get().IsTimeServerRunning() && CNetworkServices::Get().IsTimeServerUpdateNeeded())
|
|
CNetworkServices::Get().UpdateTimeServer();
|
|
#endif
|
|
|
|
// LED - LCD SwitchOn On Paused! m_bIsPaused=TRUE -> LED/LCD is ON!
|
|
if(m_pPlayer->IsPaused() != m_bIsPaused)
|
|
{
|
|
if(CSettings::GetInstance().GetBool("system.ledenableonpaused"))
|
|
StartLEDControl(m_bIsPaused);
|
|
if(CSettings::GetInstance().GetBool("lcd.enableonpaused"))
|
|
DimLCDOnPlayback(m_bIsPaused);
|
|
m_bIsPaused = m_pPlayer->IsPaused();
|
|
}
|
|
|
|
if (!m_pPlayer->IsPlayingVideo())
|
|
g_largeTextureManager.CleanupUnusedImages();
|
|
|
|
if (!m_pPlayer->IsPlayingVideo())
|
|
g_TextureManager.FreeUnusedTextures(5000);
|
|
|
|
// 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();
|
|
|
|
// update upnp server/renderer states
|
|
if(UPNP::CUPnP::IsInstantiated())
|
|
UPNP::CUPnP::GetInstance()->UpdateState();
|
|
|
|
//Check to see if current playing Title has changed and whether we should broadcast the fact
|
|
CheckForTitleChange();
|
|
}
|
|
|
|
// 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.Stop();
|
|
m_idleTimer.StartZero();
|
|
}
|
|
return (int)m_idleTimer.GetElapsedSeconds();
|
|
}
|
|
|
|
float CApplication::NavigationIdleTime()
|
|
{
|
|
if (!m_navigationTimer.IsRunning())
|
|
{
|
|
m_navigationTimer.Stop();
|
|
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();
|
|
}
|
|
|
|
boost::shared_ptr<CFileItem> CApplication::CurrentFileItemPtr()
|
|
{
|
|
return m_itemCurrentFile;
|
|
}
|
|
|
|
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->Open();
|
|
if (action)
|
|
volumeBar->OnAction(*action);
|
|
}
|
|
}
|
|
|
|
bool CApplication::IsMuted() const
|
|
{
|
|
return m_muted;
|
|
}
|
|
|
|
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()
|
|
{
|
|
m_muted = true;
|
|
}
|
|
|
|
void CApplication::UnMute()
|
|
{
|
|
m_muted = false;
|
|
}
|
|
|
|
void CApplication::SetVolume(long iValue, bool isPercentage /* = true */)
|
|
{
|
|
if (isPercentage)
|
|
iValue = (long)((float)iValue * 0.01f * (VOLUME_MAXIMUM - VOLUME_MINIMUM) + VOLUME_MINIMUM);
|
|
|
|
SetHardwareVolume(iValue);
|
|
#ifndef HAS_SDL_AUDIO
|
|
g_audioManager.SetVolume(m_volumeLevel);
|
|
#else
|
|
g_audioManager.SetVolume((int)(128.f * (m_volumeLevel - VOLUME_MINIMUM) / (float)(VOLUME_MAXIMUM - VOLUME_MINIMUM)));
|
|
#endif
|
|
|
|
CVariant data(CVariant::VariantTypeObject);
|
|
data["volume"] = (int)(((float)(m_volumeLevel - VOLUME_MINIMUM)) / (VOLUME_MAXIMUM - VOLUME_MINIMUM) * 100.0f + 0.5f);
|
|
/* TODO: add once DRC is available
|
|
data["drc"] = (int)(((float)(m_dynamicRangeCompressionLevel - VOLUME_DRC_MINIMUM)) / (VOLUME_DRC_MAXIMUM - VOLUME_DRC_MINIMUM) * 100.0f + 0.5f);*/
|
|
data["muted"] = m_muted;
|
|
CAnnouncementManager::GetInstance().Announce(Application, "xbmc", "OnVolumeChanged", data);
|
|
}
|
|
|
|
void CApplication::SetHardwareVolume(long hardwareVolume)
|
|
{
|
|
// TODO DRC
|
|
if (hardwareVolume >= VOLUME_MAXIMUM) // + VOLUME_DRC_MAXIMUM
|
|
hardwareVolume = VOLUME_MAXIMUM;// + VOLUME_DRC_MAXIMUM;
|
|
if (hardwareVolume <= VOLUME_MINIMUM)
|
|
hardwareVolume = VOLUME_MINIMUM;
|
|
|
|
// update our settings
|
|
if (hardwareVolume > VOLUME_MAXIMUM)
|
|
{
|
|
m_dynamicRangeCompressionLevel = hardwareVolume - VOLUME_MAXIMUM;
|
|
m_volumeLevel = VOLUME_MAXIMUM;
|
|
}
|
|
else
|
|
{
|
|
m_dynamicRangeCompressionLevel = 0;
|
|
m_volumeLevel = hardwareVolume;
|
|
}
|
|
|
|
// and tell our player to update the volume
|
|
if (m_pPlayer)
|
|
{
|
|
m_pPlayer->SetVolume(m_volumeLevel);
|
|
// TODO DRC
|
|
// m_pPlayer->SetDynamicRangeCompression(m_dynamicRangeCompressionLevel);
|
|
}
|
|
}
|
|
|
|
int CApplication::GetVolume(bool percentage /* = true */) const
|
|
{
|
|
if (percentage)
|
|
{ // converts the hardware volume (in mB) to a percentage
|
|
return int(((float)(m_volumeLevel + m_dynamicRangeCompressionLevel - VOLUME_MINIMUM)) / (VOLUME_MAXIMUM - VOLUME_MINIMUM)*100.0f + 0.5f);
|
|
}
|
|
|
|
return m_volumeLevel;
|
|
}
|
|
|
|
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 subtitle 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;
|
|
}
|
|
|
|
// 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)((dTime - startOfNewFile) * 1000.0));
|
|
else
|
|
{ // seeking to a new file
|
|
m_currentStackPosition = i;
|
|
CFileItem *item = new CFileItem(*(*m_currentStack)[i]);
|
|
item->m_lStartOffset = static_cast<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().PostMsg(TMSG_MEDIA_PLAY, 1, 0, static_cast<void*>(item));
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// convert to milliseconds and perform seek
|
|
m_pPlayer->SeekTime( static_cast<__int64>( dTime * 1000.0 ) );
|
|
}
|
|
}
|
|
|
|
float CApplication::GetPercentage() const
|
|
{
|
|
if (m_pPlayer->IsPlaying())
|
|
{
|
|
if (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(bool force /* = false */)
|
|
{
|
|
// 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 the slideshow is active
|
|
if (g_windowManager.GetActiveWindow() == WINDOW_SLIDESHOW)
|
|
return false;
|
|
|
|
int windowID = WINDOW_INVALID;
|
|
// See if we're playing a video, and are in GUI mode
|
|
if (m_pPlayer->IsPlayingVideo() && g_windowManager.GetActiveWindow() != WINDOW_FULLSCREEN_VIDEO)
|
|
windowID = WINDOW_FULLSCREEN_VIDEO;
|
|
|
|
// 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)
|
|
windowID = WINDOW_VISUALISATION;
|
|
|
|
|
|
if (windowID != WINDOW_INVALID)
|
|
{
|
|
if (force)
|
|
g_windowManager.ForceActivateWindow(windowID);
|
|
else
|
|
g_windowManager.ActivateWindow(windowID);
|
|
|
|
g_TextureManager.Flush();
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
PLAYERCOREID CApplication::GetCurrentPlayer()
|
|
{
|
|
return m_pPlayer->GetCurrentPlayer();
|
|
}
|
|
|
|
void CApplication::UpdateLibraries()
|
|
{
|
|
if (CSettings::GetInstance().GetBool("videolibrary.updateonstartup"))
|
|
{
|
|
CLog::Log(LOGNOTICE, "Starting video library startup scan");
|
|
StartVideoScan("", !CSettings::GetInstance().GetBool("videolibrary.backgroundupdate"));
|
|
}
|
|
|
|
if (CSettings::GetInstance().GetBool("musiclibrary.backgroundupdate"))
|
|
{
|
|
CLog::Log(LOGNOTICE, "Starting music library startup scan");
|
|
StartMusicScan("", !CSettings::GetInstance().GetBool("musiclibrary.backgroundupdate"));
|
|
}
|
|
}
|
|
|
|
void CApplication::UpdateCurrentPlayArt()
|
|
{
|
|
if (!m_pPlayer->IsPlayingAudio())
|
|
return;
|
|
//Clear and reload the art for the currenty playing item to show updated art on OSD
|
|
m_itemCurrentFile->ClearArt();
|
|
CMusicThumbLoader loader;
|
|
loader.LoadItem(m_itemCurrentFile.get());
|
|
// Mirror changes to GUI item
|
|
g_infoManager.SetCurrentItem(*m_itemCurrentFile);
|
|
}
|
|
|
|
bool CApplication::IsVideoScanning() const
|
|
{
|
|
return CVideoLibraryQueue::GetInstance().IsScanningLibrary();
|
|
}
|
|
|
|
bool CApplication::IsMusicScanning() const
|
|
{
|
|
return CMusicLibraryQueue::GetInstance().IsScanningLibrary();
|
|
}
|
|
|
|
void CApplication::StopVideoScan()
|
|
{
|
|
CVideoLibraryQueue::GetInstance().StopLibraryScanning();
|
|
}
|
|
|
|
void CApplication::StopMusicScan()
|
|
{
|
|
CMusicLibraryQueue::GetInstance().StopLibraryScanning();
|
|
}
|
|
|
|
void CApplication::StartVideoCleanup(bool userInitiated /* = true */)
|
|
{
|
|
if (userInitiated && CVideoLibraryQueue::GetInstance().IsRunning())
|
|
return;
|
|
|
|
std::set<int> paths;
|
|
if (userInitiated)
|
|
CVideoLibraryQueue::GetInstance().CleanLibraryModal(paths);
|
|
else
|
|
CVideoLibraryQueue::GetInstance().CleanLibrary(paths, true);
|
|
}
|
|
|
|
void CApplication::StartVideoScan(const std::string &strDirectory, bool userInitiated /* = true */, bool scanAll /* = false */)
|
|
{
|
|
CVideoLibraryQueue::GetInstance().ScanLibrary(strDirectory, scanAll, userInitiated);
|
|
}
|
|
|
|
void CApplication::StartMusicCleanup(bool userInitiated /* = true */)
|
|
{
|
|
if (userInitiated && CMusicLibraryQueue::GetInstance().IsRunning())
|
|
return;
|
|
|
|
if (userInitiated)
|
|
/*
|
|
CMusicLibraryQueue::GetInstance().CleanLibraryModal();
|
|
As cleaning is non-granular and does not offer many opportunities to update progress
|
|
dialog rendering, do asynchronously with model dialog
|
|
*/
|
|
CMusicLibraryQueue::GetInstance().CleanLibrary(true);
|
|
else
|
|
CMusicLibraryQueue::GetInstance().CleanLibrary(false);
|
|
}
|
|
|
|
void CApplication::StartMusicScan(const std::string &strDirectory, bool userInitiated /* = true */, int flags /* = 0 */)
|
|
{
|
|
if (IsMusicScanning())
|
|
return;
|
|
|
|
// Setup default flags
|
|
if (!flags)
|
|
{ // Online scraping of additional info during scanning
|
|
if (CSettings::GetInstance().GetBool("musiclibrary.downloadinfo"))
|
|
flags |= CMusicInfoScanner::SCAN_ONLINE;
|
|
}
|
|
if (!userInitiated || CSettings::GetInstance().GetBool("musiclibrary.backgroundupdate"))
|
|
flags |= CMusicInfoScanner::SCAN_BACKGROUND;
|
|
|
|
CMusicLibraryQueue::GetInstance().ScanLibrary(strDirectory, flags, !(flags & CMusicInfoScanner::SCAN_BACKGROUND));
|
|
}
|
|
|
|
void CApplication::StartMusicAlbumScan(const std::string& strDirectory, bool refresh)
|
|
{
|
|
if (IsMusicScanning())
|
|
return;
|
|
|
|
CMusicLibraryQueue::GetInstance().StartAlbumScan(strDirectory, refresh);
|
|
}
|
|
|
|
void CApplication::StartMusicArtistScan(const std::string& strDirectory,
|
|
bool refresh)
|
|
{
|
|
if (IsMusicScanning())
|
|
return;
|
|
|
|
CMusicLibraryQueue::GetInstance().StartArtistScan(strDirectory, refresh);
|
|
}
|
|
|
|
void CApplication::CheckPlayingProgress()
|
|
{
|
|
// check if we haven't rewound past the start of the file
|
|
if (m_pPlayer->IsPlaying())
|
|
{
|
|
int iSpeed = m_pPlayer->GetPlaySpeed();
|
|
if (iSpeed < 1)
|
|
{
|
|
iSpeed *= -1;
|
|
int iPower = 0;
|
|
while (iSpeed != 1)
|
|
{
|
|
iSpeed >>= 1;
|
|
iPower++;
|
|
}
|
|
if (g_infoManager.GetPlayTime() / 1000 < iPower)
|
|
{
|
|
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;
|
|
}
|
|
|
|
void CApplication::CheckForDebugButtonCombo()
|
|
{
|
|
#ifdef HAS_GAMEPAD
|
|
ReadInput();
|
|
if (m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_X] && m_DefaultGamepad.bAnalogButtons[XINPUT_GAMEPAD_Y])
|
|
{
|
|
g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG_FREEMEM;
|
|
CLog::Log(LOGINFO, "Key combination detected for full debug logging (X+Y)");
|
|
}
|
|
#ifdef _DEBUG
|
|
g_advancedSettings.m_logLevel = LOG_LEVEL_DEBUG_FREEMEM;
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void CApplication::SaveCurrentFileSettings()
|
|
{
|
|
if (m_itemCurrentFile->IsVideo())
|
|
{
|
|
// save video settings
|
|
if (CMediaSettings::Get().GetCurrentVideoSettings() != CMediaSettings::Get().GetDefaultVideoSettings())
|
|
{
|
|
CVideoDatabase dbs;
|
|
dbs.Open();
|
|
dbs.SetVideoSettings(m_itemCurrentFile->GetPath(), CMediaSettings::Get().GetCurrentVideoSettings());
|
|
dbs.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
CNetwork& CApplication::getNetwork()
|
|
{
|
|
return *m_network;
|
|
}
|
|
|
|
bool CApplication::IsCurrentThread() const
|
|
{
|
|
return CThread::IsCurrentThread(m_threadID);
|
|
}
|
|
|
|
void CApplication::InitDirectoriesXbox()
|
|
{
|
|
// map our special drives to the correct drive letter
|
|
CSpecialProtocol::SetXBMCBinPath("Q:\\");
|
|
CSpecialProtocol::SetXBMCPath("Q:\\"); // Use Q as ie. F doesn't exist yet!!!
|
|
CSpecialProtocol::SetHomePath("Q:\\home");
|
|
// First profile is always the Master Profile
|
|
CSpecialProtocol::SetMasterProfilePath("Q:\\home\\userdata");
|
|
CSpecialProtocol::SetTempPath("Z:\\");
|
|
CSpecialProtocol::SetXBMCBinAddonPath("Q:\\addons");
|
|
|
|
g_advancedSettings.m_logFolder = "special://home/";
|
|
|
|
CreateUserDirs();
|
|
}
|
|
|
|
void CApplication::CreateUserDirs() const
|
|
{
|
|
CDirectory::Create("special://home/");
|
|
CDirectory::Create("special://home/addons");
|
|
CDirectory::Create("special://home/addons/packages");
|
|
CDirectory::Create("special://home/addons/temp");
|
|
CDirectory::Create("special://masterprofile/");
|
|
#ifdef _XBOX
|
|
CUtil::WipeDir("special://temp/");
|
|
#else
|
|
CDirectory::Create("special://temp/");
|
|
#endif
|
|
CDirectory::Create("special://temp/temp"); // temp directory for python and dllGetTempPathA
|
|
}
|
|
|
|
bool CApplication::SetLanguage(const std::string &strLanguage)
|
|
{
|
|
// nothing to be done if the language hasn't changed
|
|
if (strLanguage == CSettings::GetInstance().GetString("locale.language"))
|
|
return true;
|
|
|
|
return CSettings::GetInstance().SetString("locale.language", strLanguage);
|
|
}
|
|
|
|
bool CApplication::LoadLanguage(bool reload)
|
|
{
|
|
// load the configured langauge
|
|
if (!g_langInfo.SetLanguage(m_fallbackLanguageLoaded, "", reload))
|
|
return false;
|
|
|
|
// set the proper audio and subtitle languages
|
|
g_langInfo.SetAudioLanguage(CSettings::GetInstance().GetString("locale.audiolanguage"));
|
|
g_langInfo.SetSubtitleLanguage(CSettings::GetInstance().GetString("locale.subtitlelanguage"));
|
|
|
|
return true;
|
|
}
|
|
|
|
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.skintheme" ||
|
|
settingId == "lookandfeel.skincolors")
|
|
{
|
|
// check if we should ignore this change event due to changing skins in which case we have to
|
|
// change several settings and each one of them could lead to a complete skin reload which would
|
|
// result in multiple skin reloads. Therefore we manually specify to ignore specific settings
|
|
// which are going to be changed.
|
|
if (m_ignoreSkinSettingChanges)
|
|
return;
|
|
|
|
// if the skin changes and the current color/theme/font is not the default one, reset
|
|
// the it to the default value
|
|
if (settingId == "lookandfeel.skin")
|
|
{
|
|
CSetting* skinRelatedSetting = CSettings::GetInstance().GetSetting("lookandfeel.skincolors");
|
|
if (!skinRelatedSetting->IsDefault())
|
|
{
|
|
m_ignoreSkinSettingChanges = true;
|
|
skinRelatedSetting->Reset();
|
|
}
|
|
|
|
skinRelatedSetting = CSettings::GetInstance().GetSetting("lookandfeel.skintheme");
|
|
if (!skinRelatedSetting->IsDefault())
|
|
{
|
|
m_ignoreSkinSettingChanges = true;
|
|
skinRelatedSetting->Reset();
|
|
}
|
|
|
|
skinRelatedSetting = CSettings::GetInstance().GetSetting("lookandfeel.font");
|
|
if (!skinRelatedSetting->IsDefault())
|
|
{
|
|
m_ignoreSkinSettingChanges = true;
|
|
skinRelatedSetting->Reset();
|
|
}
|
|
}
|
|
else if (settingId == "lookandfeel.skintheme")
|
|
{
|
|
CSettingString* skinColorsSetting = static_cast<CSettingString*>(CSettings::GetInstance().GetSetting("lookandfeel.skincolors"));
|
|
m_ignoreSkinSettingChanges = true;
|
|
|
|
// we also need to adjust the skin color setting
|
|
std::string colorTheme = ((CSettingString*)setting)->GetValue();
|
|
URIUtils::RemoveExtension(colorTheme);
|
|
if (setting->IsDefault() || StringUtils::EqualsNoCase(colorTheme, "Textures"))
|
|
skinColorsSetting->Reset();
|
|
else
|
|
skinColorsSetting->SetValue(colorTheme);
|
|
}
|
|
|
|
m_ignoreSkinSettingChanges = false;
|
|
|
|
// now we can finally reload skins
|
|
std::string builtin("ReloadSkin");
|
|
if (settingId == "lookandfeel.skin" && !m_skinReverting)
|
|
builtin += "(confirm)";
|
|
CApplicationMessenger::Get().PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, NULL, builtin);
|
|
}
|
|
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."))
|
|
{
|
|
if (settingId == "audiooutput.ac3passthrough")
|
|
g_audioConfig.SetAC3Enabled(((CSettingBool*)setting)->GetValue());
|
|
else if (settingId == "audiooutput.dtspassthrough")
|
|
g_audioConfig.SetDTSEnabled(((CSettingBool*)setting)->GetValue());
|
|
else if (settingId == "audiooutput.aacpassthrough")
|
|
g_audioConfig.SetAACEnabled(((CSettingBool*)setting)->GetValue());
|
|
else if (settingId == "audiooutput.mp1passthrough")
|
|
g_audioConfig.SetMP1Enabled(((CSettingBool*)setting)->GetValue());
|
|
else if (settingId == "audiooutput.mp2passthrough")
|
|
g_audioConfig.SetMP2Enabled(((CSettingBool*)setting)->GetValue());
|
|
else if (settingId == "audiooutput.mp3passthrough")
|
|
g_audioConfig.SetMP3Enabled(((CSettingBool*)setting)->GetValue());
|
|
|
|
if (g_audioConfig.NeedsSave())
|
|
g_audioConfig.Save();
|
|
// if (settingId == "audiooutput.guisoundmode")
|
|
// CAEFactory::SetSoundMode(((CSettingInt*)setting)->GetValue());
|
|
// CAEFactory::OnSettingsChange(settingId);
|
|
}
|
|
else if (settingId == "harddisk.aamlevel")
|
|
{
|
|
if (((CSettingInt*)setting)->GetValue() == AAM_QUIET)
|
|
XKHDD::SetAAMLevel(0x80);
|
|
else if (((CSettingInt*)setting)->GetValue() == AAM_FAST)
|
|
XKHDD::SetAAMLevel(0xFE);
|
|
}
|
|
else if (settingId == "harddisk.apmlevel")
|
|
{
|
|
switch(((CSettingInt*)setting)->GetValue())
|
|
{
|
|
case APM_LOPOWER:
|
|
XKHDD::SetAPMLevel(0x80);
|
|
break;
|
|
case APM_HIPOWER:
|
|
XKHDD::SetAPMLevel(0xFE);
|
|
break;
|
|
case APM_LOPOWER_STANDBY:
|
|
XKHDD::SetAPMLevel(0x01);
|
|
break;
|
|
case APM_HIPOWER_STANDBY:
|
|
XKHDD::SetAPMLevel(0x7F);
|
|
break;
|
|
}
|
|
}
|
|
else if (settingId == "karaoke.port0voicemask")
|
|
CCdgParser::FillInVoiceMaskValues(0, ((CSettingString*)setting)->GetValue());
|
|
else if (settingId == "karaoke.port1voicemask")
|
|
CCdgParser::FillInVoiceMaskValues(1, ((CSettingString*)setting)->GetValue());
|
|
else if (settingId == "karaoke.port2voicemask")
|
|
CCdgParser::FillInVoiceMaskValues(2, ((CSettingString*)setting)->GetValue());
|
|
else if (settingId == "karaoke.port3voicemask")
|
|
CCdgParser::FillInVoiceMaskValues(3, ((CSettingString*)setting)->GetValue());
|
|
else if (settingId == "lcd.type")
|
|
g_lcd->Initialize();
|
|
else if (settingId == "lcd.backlight")
|
|
g_lcd->SetBackLight(((CSettingInt*)setting)->GetValue());
|
|
else if (settingId == "lcd.modchip")
|
|
{
|
|
g_lcd->Stop();
|
|
CLCDFactory factory;
|
|
delete g_lcd;
|
|
g_lcd = factory.Create();
|
|
g_lcd->Initialize();
|
|
}
|
|
else if (settingId == "lcd.contrast")
|
|
g_lcd->SetContrast(((CSettingInt*)setting)->GetValue());
|
|
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();
|
|
else if (settingId == "network.assignment" || settingId == "network.ipaddress" ||
|
|
settingId == "network.subnet" || settingId == "network.gateway" ||
|
|
settingId == "network.dns" || settingId == "network.dns2")
|
|
{
|
|
m_network->NetworkMessage(CNetwork::SERVICES_DOWN,1);
|
|
m_network->SetupNetwork();
|
|
}
|
|
else if (settingId == "system.ledcolour")
|
|
{
|
|
// Alter LED Colour immediately
|
|
int iData = ((CSettingInt*)setting)->GetValue();
|
|
if (iData == LED_COLOUR_NO_CHANGE)
|
|
// LED_COLOUR_NO_CHANGE: to prevent "led off" on colour immediately change, set to default green!
|
|
// (we have no previos reference LED COLOUR, to set the LED colour back)
|
|
// on next boot the colour will not changed and the default BIOS led colour will used
|
|
ILED::CLEDControl(LED_COLOUR_GREEN);
|
|
else
|
|
ILED::CLEDControl(iData);
|
|
}
|
|
}
|
|
|
|
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 (CServiceBroker::GetAddonMgr().GetAddon(CSettings::GetInstance().GetString("screensaver.mode"), addon, ADDON_SCREENSAVER))
|
|
CGUIDialogAddonSettings::ShowAndGetInput(addon);
|
|
}
|
|
else if (settingId == "videoscreen.guicalibration")
|
|
g_windowManager.ActivateWindow(WINDOW_SCREEN_CALIBRATION);
|
|
else if (settingId == "source.videos")
|
|
{
|
|
std::vector<std::string> params;
|
|
params.push_back("library://video/files.xml");
|
|
params.push_back("return");
|
|
g_windowManager.ActivateWindow(WINDOW_VIDEO_NAV, params);
|
|
}
|
|
else if (settingId == "source.music")
|
|
{
|
|
std::vector<std::string> params;
|
|
params.push_back("library://music/files.xml");
|
|
params.push_back("return");
|
|
g_windowManager.ActivateWindow(WINDOW_MUSIC_NAV, params);
|
|
}
|
|
else if (settingId == "source.pictures")
|
|
g_windowManager.ActivateWindow(WINDOW_PICTURES);
|
|
else if (settingId == "myprograms.trainerscan")
|
|
CTrainer::ScanTrainers();
|
|
}
|
|
|
|
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::GetInstance().GetString("audiooutput.audiodevice");
|
|
// CAEFactory::VerifyOutputDevice(audiodevice, false);
|
|
// ret |= CSettings::GetInstance().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;
|
|
// }
|
|
// }
|
|
|
|
return false;
|
|
}
|
|
|
|
void CApplication::RegisterActionListener(IActionListener *listener)
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
std::vector<IActionListener *>::iterator it = std::find(m_actionListeners.begin(), m_actionListeners.end(), listener);
|
|
if (it == m_actionListeners.end())
|
|
m_actionListeners.push_back(listener);
|
|
}
|
|
|
|
void CApplication::UnregisterActionListener(IActionListener *listener)
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
std::vector<IActionListener *>::iterator it = std::find(m_actionListeners.begin(), m_actionListeners.end(), listener);
|
|
if (it != m_actionListeners.end())
|
|
m_actionListeners.erase(it);
|
|
}
|
|
|
|
bool CApplication::NotifyActionListeners(const CAction &action) const
|
|
{
|
|
CSingleLock lock(m_critSection);
|
|
for (std::vector<IActionListener *>::const_iterator it = m_actionListeners.begin(); it != m_actionListeners.end(); ++it)
|
|
{
|
|
if ((*it)->OnAction(action))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|