diff options
Diffstat (limited to 'xbmc/guilib/GUIWindowManager.cpp')
-rw-r--r-- | xbmc/guilib/GUIWindowManager.cpp | 1808 |
1 files changed, 1808 insertions, 0 deletions
diff --git a/xbmc/guilib/GUIWindowManager.cpp b/xbmc/guilib/GUIWindowManager.cpp new file mode 100644 index 0000000..f15489e --- /dev/null +++ b/xbmc/guilib/GUIWindowManager.cpp @@ -0,0 +1,1808 @@ +/* + * Copyright (C) 2005-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "GUIWindowManager.h" + +#include "GUIAudioManager.h" +#include "GUIDialog.h" +#include "GUIInfoManager.h" +#include "GUIPassword.h" +#include "GUITexture.h" +#include "ServiceBroker.h" +#include "WindowIDs.h" +#include "addons/Skin.h" +#include "addons/gui/GUIWindowAddonBrowser.h" +#include "addons/interfaces/gui/Window.h" +#include "application/Application.h" +#include "application/ApplicationComponents.h" +#include "application/ApplicationPlayer.h" +#include "events/windows/GUIWindowEventLog.h" +#include "favourites/GUIDialogFavourites.h" +#include "favourites/GUIWindowFavourites.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "messaging/ApplicationMessenger.h" +#include "messaging/helpers/DialogHelper.h" +#include "music/dialogs/GUIDialogInfoProviderSettings.h" +#include "music/dialogs/GUIDialogMusicInfo.h" +#include "music/windows/GUIWindowMusicNav.h" +#include "music/windows/GUIWindowMusicPlaylist.h" +#include "music/windows/GUIWindowMusicPlaylistEditor.h" +#include "music/windows/GUIWindowVisualisation.h" +#include "pictures/GUIWindowPictures.h" +#include "pictures/GUIWindowSlideShow.h" +#include "profiles/windows/GUIWindowSettingsProfile.h" +#include "programs/GUIWindowPrograms.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "settings/windows/GUIWindowSettings.h" +#include "settings/windows/GUIWindowSettingsCategory.h" +#include "settings/windows/GUIWindowSettingsScreenCalibration.h" +#include "threads/SingleLock.h" +#include "utils/StringUtils.h" +#include "utils/URIUtils.h" +#include "utils/Variant.h" +#include "utils/log.h" +#include "video/dialogs/GUIDialogVideoInfo.h" +#include "video/dialogs/GUIDialogVideoOSD.h" +#include "video/windows/GUIWindowFullScreen.h" +#include "video/windows/GUIWindowVideoNav.h" +#include "video/windows/GUIWindowVideoPlaylist.h" +#include "weather/GUIWindowWeather.h" +#include "windows/GUIWindowDebugInfo.h" +#include "windows/GUIWindowFileManager.h" +#include "windows/GUIWindowHome.h" +#include "windows/GUIWindowLoginScreen.h" +#include "windows/GUIWindowPointer.h" +#include "windows/GUIWindowScreensaver.h" +#include "windows/GUIWindowScreensaverDim.h" +#include "windows/GUIWindowSplash.h" +#include "windows/GUIWindowStartup.h" +#include "windows/GUIWindowSystemInfo.h" + +#include <mutex> + +// Dialog includes +#include "music/dialogs/GUIDialogMusicOSD.h" +#include "music/dialogs/GUIDialogVisualisationPresetList.h" +#include "dialogs/GUIDialogTextViewer.h" +#include "network/GUIDialogNetworkSetup.h" +#include "dialogs/GUIDialogMediaSource.h" +#if defined(HAS_GL) || defined(HAS_DX) +#include "video/dialogs/GUIDialogCMSSettings.h" +#endif +#include "addons/gui/GUIDialogAddonInfo.h" +#include "addons/gui/GUIDialogAddonSettings.h" +#include "dialogs/GUIDialogBusy.h" +#include "dialogs/GUIDialogBusyNoCancel.h" +#include "dialogs/GUIDialogButtonMenu.h" +#include "dialogs/GUIDialogColorPicker.h" +#include "dialogs/GUIDialogContextMenu.h" +#include "dialogs/GUIDialogExtendedProgressBar.h" +#include "dialogs/GUIDialogGamepad.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogKeyboardGeneric.h" +#include "dialogs/GUIDialogKeyboardTouch.h" +#include "dialogs/GUIDialogNumeric.h" +#include "dialogs/GUIDialogOK.h" +#include "dialogs/GUIDialogPlayerControls.h" +#include "dialogs/GUIDialogPlayerProcessInfo.h" +#include "dialogs/GUIDialogProgress.h" +#include "dialogs/GUIDialogSeekBar.h" +#include "dialogs/GUIDialogSelect.h" +#include "dialogs/GUIDialogSmartPlaylistEditor.h" +#include "dialogs/GUIDialogSmartPlaylistRule.h" +#include "dialogs/GUIDialogSubMenu.h" +#include "dialogs/GUIDialogVolumeBar.h" +#include "dialogs/GUIDialogYesNo.h" +#include "music/dialogs/GUIDialogSongInfo.h" +#include "pictures/GUIDialogPictureInfo.h" +#include "profiles/dialogs/GUIDialogLockSettings.h" +#include "profiles/dialogs/GUIDialogProfileSettings.h" +#include "settings/dialogs/GUIDialogContentSettings.h" +#include "settings/dialogs/GUIDialogLibExportSettings.h" +#include "video/dialogs/GUIDialogAudioSettings.h" +#include "video/dialogs/GUIDialogSubtitleSettings.h" +#include "video/dialogs/GUIDialogVideoBookmarks.h" +#include "video/dialogs/GUIDialogVideoSettings.h" + +/* PVR related include Files */ +#include "pvr/dialogs/GUIDialogPVRChannelGuide.h" +#include "pvr/dialogs/GUIDialogPVRChannelManager.h" +#include "pvr/dialogs/GUIDialogPVRChannelsOSD.h" +#include "pvr/dialogs/GUIDialogPVRClientPriorities.h" +#include "pvr/dialogs/GUIDialogPVRGroupManager.h" +#include "pvr/dialogs/GUIDialogPVRGuideControls.h" +#include "pvr/dialogs/GUIDialogPVRGuideInfo.h" +#include "pvr/dialogs/GUIDialogPVRGuideSearch.h" +#include "pvr/dialogs/GUIDialogPVRRadioRDSInfo.h" +#include "pvr/dialogs/GUIDialogPVRRecordingInfo.h" +#include "pvr/dialogs/GUIDialogPVRRecordingSettings.h" +#include "pvr/dialogs/GUIDialogPVRTimerSettings.h" +#include "pvr/windows/GUIWindowPVRChannels.h" +#include "pvr/windows/GUIWindowPVRGuide.h" +#include "pvr/windows/GUIWindowPVRRecordings.h" +#include "pvr/windows/GUIWindowPVRSearch.h" +#include "pvr/windows/GUIWindowPVRTimerRules.h" +#include "pvr/windows/GUIWindowPVRTimers.h" + +#include "video/dialogs/GUIDialogTeletext.h" +#include "dialogs/GUIDialogSlider.h" +#ifdef HAS_DVD_DRIVE +#include "dialogs/GUIDialogPlayEject.h" +#endif +#include "dialogs/GUIDialogMediaFilter.h" +#include "video/dialogs/GUIDialogSubtitles.h" + +#include "peripherals/dialogs/GUIDialogPeripherals.h" +#include "peripherals/dialogs/GUIDialogPeripheralSettings.h" + +/* Game related include files */ +#include "cores/RetroPlayer/guiwindows/GameWindowFullScreen.h" +#include "games/controllers/windows/GUIControllerWindow.h" +#include "games/dialogs/osd/DialogGameAdvancedSettings.h" +#include "games/dialogs/osd/DialogGameOSD.h" +#include "games/dialogs/osd/DialogGameSaves.h" +#include "games/dialogs/osd/DialogGameStretchMode.h" +#include "games/dialogs/osd/DialogGameVideoFilter.h" +#include "games/dialogs/osd/DialogGameVideoRotation.h" +#include "games/dialogs/osd/DialogGameVolume.h" +#include "games/dialogs/osd/DialogInGameSaves.h" +#include "games/ports/windows/GUIPortWindow.h" +#include "games/windows/GUIWindowGames.h" + +using namespace KODI; +using namespace PVR; +using namespace PERIPHERALS; + +CGUIWindowManager::CGUIWindowManager() +{ + m_pCallback = nullptr; + m_iNested = 0; + m_initialized = false; +} + +CGUIWindowManager::~CGUIWindowManager() = default; + +void CGUIWindowManager::Initialize() +{ + m_tracker.SelectAlgorithm(); + + m_initialized = true; + + LoadNotOnDemandWindows(); + + CServiceBroker::GetAppMessenger()->RegisterReceiver(this); +} + +void CGUIWindowManager::CreateWindows() +{ + Add(new CGUIWindowHome); + Add(new CGUIWindowPrograms); + Add(new CGUIWindowPictures); + Add(new CGUIWindowFileManager); + Add(new CGUIWindowSettings); + Add(new CGUIWindowSystemInfo); + Add(new CGUIWindowSettingsScreenCalibration); + Add(new CGUIWindowSettingsCategory); + Add(new CGUIWindowVideoNav); + Add(new CGUIWindowVideoPlaylist); + Add(new CGUIWindowLoginScreen); + Add(new CGUIWindowSettingsProfile); + Add(new CGUIWindow(WINDOW_SKIN_SETTINGS, "SkinSettings.xml")); + Add(new CGUIWindowAddonBrowser); + Add(new CGUIWindowScreensaverDim); + Add(new CGUIWindowDebugInfo); + Add(new CGUIWindowPointer); + Add(new CGUIDialogYesNo); + Add(new CGUIDialogProgress); + Add(new CGUIDialogExtendedProgressBar); + Add(new CGUIDialogKeyboardGeneric); + Add(new CGUIDialogKeyboardTouch); + Add(new CGUIDialogVolumeBar); + Add(new CGUIDialogSeekBar); + Add(new CGUIDialogSubMenu); + Add(new CGUIDialogContextMenu); + Add(new CGUIDialogKaiToast); + Add(new CGUIDialogNumeric); + Add(new CGUIDialogGamepad); + Add(new CGUIDialogButtonMenu); + Add(new CGUIDialogPlayerControls); + Add(new CGUIDialogPlayerProcessInfo); + Add(new CGUIDialogSlider); + Add(new CGUIDialogMusicOSD); + Add(new CGUIDialogVisualisationPresetList); +#if defined(HAS_GL) || defined(HAS_DX) + Add(new CGUIDialogCMSSettings); +#endif + Add(new CGUIDialogVideoSettings); + Add(new CGUIDialogAudioSettings); + Add(new CGUIDialogSubtitleSettings); + Add(new CGUIDialogVideoBookmarks); + // Don't add the filebrowser dialog - it's created and added when it's needed + Add(new CGUIDialogNetworkSetup); + Add(new CGUIDialogMediaSource); + Add(new CGUIDialogProfileSettings); + Add(new CGUIDialogFavourites); + Add(new CGUIDialogSongInfo); + Add(new CGUIDialogSmartPlaylistEditor); + Add(new CGUIDialogSmartPlaylistRule); + Add(new CGUIDialogBusy); + Add(new CGUIDialogBusyNoCancel); + Add(new CGUIDialogPictureInfo); + Add(new CGUIDialogAddonInfo); + Add(new CGUIDialogAddonSettings); + + Add(new CGUIDialogLockSettings); + + Add(new CGUIDialogContentSettings); + + Add(new CGUIDialogLibExportSettings); + + Add(new CGUIDialogInfoProviderSettings); + +#ifdef HAS_DVD_DRIVE + Add(new CGUIDialogPlayEject); +#endif + + Add(new CGUIDialogPeripherals); + Add(new CGUIDialogPeripheralSettings); + + Add(new CGUIDialogMediaFilter); + Add(new CGUIDialogSubtitles); + + Add(new CGUIWindowMusicPlayList); + Add(new CGUIWindowMusicNav); + Add(new CGUIWindowMusicPlaylistEditor); + + /* Load PVR related Windows and Dialogs */ + Add(new CGUIDialogTeletext); + Add(new CGUIWindowPVRTVChannels); + Add(new CGUIWindowPVRTVRecordings); + Add(new CGUIWindowPVRTVGuide); + Add(new CGUIWindowPVRTVTimers); + Add(new CGUIWindowPVRTVTimerRules); + Add(new CGUIWindowPVRTVSearch); + Add(new CGUIWindowPVRRadioChannels); + Add(new CGUIWindowPVRRadioRecordings); + Add(new CGUIWindowPVRRadioGuide); + Add(new CGUIWindowPVRRadioTimers); + Add(new CGUIWindowPVRRadioTimerRules); + Add(new CGUIWindowPVRRadioSearch); + Add(new CGUIDialogPVRRadioRDSInfo); + Add(new CGUIDialogPVRGuideInfo); + Add(new CGUIDialogPVRRecordingInfo); + Add(new CGUIDialogPVRTimerSettings); + Add(new CGUIDialogPVRGroupManager); + Add(new CGUIDialogPVRChannelManager); + Add(new CGUIDialogPVRGuideSearch); + Add(new CGUIDialogPVRChannelsOSD); + Add(new CGUIDialogPVRChannelGuide); + Add(new CGUIDialogPVRRecordingSettings); + Add(new CGUIDialogPVRClientPriorities); + Add(new CGUIDialogPVRGuideControls); + + Add(new CGUIDialogSelect); + Add(new CGUIDialogColorPicker); + Add(new CGUIDialogMusicInfo); + Add(new CGUIDialogOK); + Add(new CGUIDialogVideoInfo); + Add(new CGUIDialogTextViewer); + Add(new CGUIWindowFullScreen); + Add(new CGUIWindowVisualisation); + Add(new CGUIWindowSlideShow); + + Add(new CGUIDialogVideoOSD); + Add(new CGUIWindowScreensaver); + Add(new CGUIWindowWeather); + Add(new CGUIWindowStartup); + Add(new CGUIWindowSplash); + + Add(new CGUIWindowEventLog); + + Add(new CGUIWindowFavourites); + + Add(new GAME::CGUIControllerWindow); + Add(new GAME::CGUIPortWindow); + Add(new GAME::CGUIWindowGames); + Add(new GAME::CDialogGameOSD); + Add(new GAME::CDialogGameSaves); + Add(new GAME::CDialogGameVideoFilter); + Add(new GAME::CDialogGameStretchMode); + Add(new GAME::CDialogGameVolume); + Add(new GAME::CDialogGameAdvancedSettings); + Add(new GAME::CDialogGameVideoRotation); + Add(new GAME::CDialogInGameSaves); + Add(new RETRO::CGameWindowFullScreen); +} + +bool CGUIWindowManager::DestroyWindows() +{ + try + { + DestroyWindow(WINDOW_SPLASH); + DestroyWindow(WINDOW_MUSIC_PLAYLIST); + DestroyWindow(WINDOW_MUSIC_PLAYLIST_EDITOR); + DestroyWindow(WINDOW_MUSIC_NAV); + DestroyWindow(WINDOW_DIALOG_MUSIC_INFO); + DestroyWindow(WINDOW_DIALOG_VIDEO_INFO); + DestroyWindow(WINDOW_VIDEO_PLAYLIST); + DestroyWindow(WINDOW_VIDEO_NAV); + DestroyWindow(WINDOW_FILES); + DestroyWindow(WINDOW_DIALOG_YES_NO); + DestroyWindow(WINDOW_DIALOG_PROGRESS); + DestroyWindow(WINDOW_DIALOG_NUMERIC); + DestroyWindow(WINDOW_DIALOG_GAMEPAD); + DestroyWindow(WINDOW_DIALOG_SUB_MENU); + DestroyWindow(WINDOW_DIALOG_BUTTON_MENU); + DestroyWindow(WINDOW_DIALOG_CONTEXT_MENU); + DestroyWindow(WINDOW_DIALOG_PLAYER_CONTROLS); + DestroyWindow(WINDOW_DIALOG_PLAYER_PROCESS_INFO); + DestroyWindow(WINDOW_DIALOG_MUSIC_OSD); + DestroyWindow(WINDOW_DIALOG_VIS_PRESET_LIST); + DestroyWindow(WINDOW_DIALOG_SELECT); + DestroyWindow(WINDOW_DIALOG_OK); + DestroyWindow(WINDOW_DIALOG_KEYBOARD); + DestroyWindow(WINDOW_DIALOG_KEYBOARD_TOUCH); + DestroyWindow(WINDOW_FULLSCREEN_VIDEO); + DestroyWindow(WINDOW_DIALOG_PROFILE_SETTINGS); + DestroyWindow(WINDOW_DIALOG_LOCK_SETTINGS); + DestroyWindow(WINDOW_DIALOG_NETWORK_SETUP); + DestroyWindow(WINDOW_DIALOG_MEDIA_SOURCE); + DestroyWindow(WINDOW_DIALOG_CMS_OSD_SETTINGS); + DestroyWindow(WINDOW_DIALOG_VIDEO_OSD_SETTINGS); + DestroyWindow(WINDOW_DIALOG_AUDIO_OSD_SETTINGS); + DestroyWindow(WINDOW_DIALOG_SUBTITLE_OSD_SETTINGS); + DestroyWindow(WINDOW_DIALOG_VIDEO_BOOKMARKS); + DestroyWindow(WINDOW_DIALOG_CONTENT_SETTINGS); + DestroyWindow(WINDOW_DIALOG_INFOPROVIDER_SETTINGS); + DestroyWindow(WINDOW_DIALOG_LIBEXPORT_SETTINGS); + DestroyWindow(WINDOW_DIALOG_FAVOURITES); + DestroyWindow(WINDOW_DIALOG_SONG_INFO); + DestroyWindow(WINDOW_DIALOG_SMART_PLAYLIST_EDITOR); + DestroyWindow(WINDOW_DIALOG_SMART_PLAYLIST_RULE); + DestroyWindow(WINDOW_DIALOG_BUSY); + DestroyWindow(WINDOW_DIALOG_BUSY_NOCANCEL); + DestroyWindow(WINDOW_DIALOG_PICTURE_INFO); + DestroyWindow(WINDOW_DIALOG_ADDON_INFO); + DestroyWindow(WINDOW_DIALOG_ADDON_SETTINGS); + DestroyWindow(WINDOW_DIALOG_SLIDER); + DestroyWindow(WINDOW_DIALOG_MEDIA_FILTER); + DestroyWindow(WINDOW_DIALOG_SUBTITLES); + DestroyWindow(WINDOW_DIALOG_COLOR_PICKER); + + /* Delete PVR related windows and dialogs */ + DestroyWindow(WINDOW_TV_CHANNELS); + DestroyWindow(WINDOW_TV_RECORDINGS); + DestroyWindow(WINDOW_TV_GUIDE); + DestroyWindow(WINDOW_TV_TIMERS); + DestroyWindow(WINDOW_TV_TIMER_RULES); + DestroyWindow(WINDOW_TV_SEARCH); + DestroyWindow(WINDOW_RADIO_CHANNELS); + DestroyWindow(WINDOW_RADIO_RECORDINGS); + DestroyWindow(WINDOW_RADIO_GUIDE); + DestroyWindow(WINDOW_RADIO_TIMERS); + DestroyWindow(WINDOW_RADIO_TIMER_RULES); + DestroyWindow(WINDOW_RADIO_SEARCH); + DestroyWindow(WINDOW_DIALOG_PVR_GUIDE_INFO); + DestroyWindow(WINDOW_DIALOG_PVR_RECORDING_INFO); + DestroyWindow(WINDOW_DIALOG_PVR_TIMER_SETTING); + DestroyWindow(WINDOW_DIALOG_PVR_GROUP_MANAGER); + DestroyWindow(WINDOW_DIALOG_PVR_CHANNEL_MANAGER); + DestroyWindow(WINDOW_DIALOG_PVR_GUIDE_SEARCH); + DestroyWindow(WINDOW_DIALOG_PVR_CHANNEL_SCAN); + DestroyWindow(WINDOW_DIALOG_PVR_RADIO_RDS_INFO); + DestroyWindow(WINDOW_DIALOG_PVR_UPDATE_PROGRESS); + DestroyWindow(WINDOW_DIALOG_PVR_OSD_CHANNELS); + DestroyWindow(WINDOW_DIALOG_PVR_CHANNEL_GUIDE); + DestroyWindow(WINDOW_DIALOG_OSD_TELETEXT); + DestroyWindow(WINDOW_DIALOG_PVR_RECORDING_SETTING); + DestroyWindow(WINDOW_DIALOG_PVR_CLIENT_PRIORITIES); + DestroyWindow(WINDOW_DIALOG_PVR_GUIDE_CONTROLS); + + DestroyWindow(WINDOW_DIALOG_TEXT_VIEWER); +#ifdef HAS_DVD_DRIVE + DestroyWindow(WINDOW_DIALOG_PLAY_EJECT); +#endif + DestroyWindow(WINDOW_STARTUP_ANIM); + DestroyWindow(WINDOW_LOGIN_SCREEN); + DestroyWindow(WINDOW_VISUALISATION); + DestroyWindow(WINDOW_SETTINGS_MENU); + DestroyWindow(WINDOW_SETTINGS_PROFILES); + DestroyWindow(WINDOW_SCREEN_CALIBRATION); + DestroyWindow(WINDOW_SYSTEM_INFORMATION); + DestroyWindow(WINDOW_SCREENSAVER); + DestroyWindow(WINDOW_DIALOG_VIDEO_OSD); + DestroyWindow(WINDOW_SLIDESHOW); + DestroyWindow(WINDOW_ADDON_BROWSER); + DestroyWindow(WINDOW_SKIN_SETTINGS); + + DestroyWindow(WINDOW_HOME); + DestroyWindow(WINDOW_PROGRAMS); + DestroyWindow(WINDOW_PICTURES); + DestroyWindow(WINDOW_WEATHER); + DestroyWindow(WINDOW_DIALOG_GAME_CONTROLLERS); + DestroyWindow(WINDOW_DIALOG_GAME_PORTS); + DestroyWindow(WINDOW_GAMES); + DestroyWindow(WINDOW_DIALOG_GAME_OSD); + DestroyWindow(WINDOW_DIALOG_GAME_SAVES); + DestroyWindow(WINDOW_DIALOG_GAME_VIDEO_FILTER); + DestroyWindow(WINDOW_DIALOG_GAME_STRETCH_MODE); + DestroyWindow(WINDOW_DIALOG_GAME_VOLUME); + DestroyWindow(WINDOW_DIALOG_GAME_ADVANCED_SETTINGS); + DestroyWindow(WINDOW_DIALOG_GAME_VIDEO_ROTATION); + DestroyWindow(WINDOW_DIALOG_IN_GAME_SAVES); + DestroyWindow(WINDOW_FULLSCREEN_GAME); + + Remove(WINDOW_SETTINGS_SERVICE); + Remove(WINDOW_SETTINGS_MYPVR); + Remove(WINDOW_SETTINGS_PLAYER); + Remove(WINDOW_SETTINGS_MEDIA); + Remove(WINDOW_SETTINGS_INTERFACE); + Remove(WINDOW_SETTINGS_MYGAMES); + DestroyWindow(WINDOW_SETTINGS_SYSTEM); // all the settings categories + + Remove(WINDOW_DIALOG_KAI_TOAST); + Remove(WINDOW_DIALOG_SEEK_BAR); + Remove(WINDOW_DIALOG_VOLUME_BAR); + + DestroyWindow(WINDOW_EVENT_LOG); + + DestroyWindow(WINDOW_FAVOURITES); + + DestroyWindow(WINDOW_DIALOG_PERIPHERALS); + DestroyWindow(WINDOW_DIALOG_PERIPHERAL_SETTINGS); + } + catch (...) + { + CLog::Log(LOGERROR, "Exception in CGUIWindowManager::DestroyWindows()"); + return false; + } + + return true; +} + +void CGUIWindowManager::DestroyWindow(int id) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + CGUIWindow *pWindow = GetWindow(id); + if (pWindow) + { + Remove(id); + pWindow->FreeResources(true); + delete pWindow; + } +} + +bool CGUIWindowManager::SendMessage(int message, int senderID, int destID, int param1, int param2) +{ + CGUIMessage msg(message, senderID, destID, param1, param2); + return SendMessage(msg); +} + +bool CGUIWindowManager::SendMessage(CGUIMessage& message) +{ + bool handled = false; + // CLog::Log(LOGDEBUG,"SendMessage: mess={} send={} control={} param1={}", message.GetMessage(), message.GetSenderId(), message.GetControlId(), message.GetParam1()); + // Send the message to all none window targets + for (int i = 0; i < int(m_vecMsgTargets.size()); i++) + { + IMsgTargetCallback* pMsgTarget = m_vecMsgTargets[i]; + + if (pMsgTarget) + { + if (pMsgTarget->OnMessage( message )) handled = true; + } + } + + // A GUI_MSG_NOTIFY_ALL is send to any active modal dialog + // and all windows whether they are active or not + if (message.GetMessage()==GUI_MSG_NOTIFY_ALL) + { + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + for (auto it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it) + { + (*it)->OnMessage(message); + } + + for (const auto& entry : m_mapWindows) + { + entry.second->OnMessage(message); + } + + return true; + } + + // Normal messages are sent to: + // 1. All active modeless dialogs + // 2. The topmost dialog that accepts the message + // 3. The underlying window (only if it is the sender or receiver if a modal dialog is active) + + bool hasModalDialog(false); + bool modalAcceptedMessage(false); + // don't use an iterator for this loop, as some messages mean that m_activeDialogs is altered, + // which will invalidate any iterator + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + size_t topWindow = m_activeDialogs.size(); + while (topWindow) + { + CGUIWindow* dialog = m_activeDialogs[--topWindow]; + + if (!modalAcceptedMessage && dialog->IsModalDialog()) + { // modal window + hasModalDialog = true; + if (!modalAcceptedMessage && dialog->OnMessage( message )) + { + modalAcceptedMessage = handled = true; + } + } + else if (!dialog->IsModalDialog()) + { // modeless + if (dialog->OnMessage( message )) + handled = true; + } + + if (topWindow > m_activeDialogs.size()) + topWindow = m_activeDialogs.size(); + } + + // now send to the underlying window + CGUIWindow* window = GetWindow(GetActiveWindow()); + if (window) + { + if (hasModalDialog) + { + // only send the message to the underlying window if it's the recipient + // or sender (or we have no sender) + if (message.GetSenderId() == window->GetID() || + message.GetControlId() == window->GetID() || + message.GetSenderId() == 0 ) + { + if (window->OnMessage(message)) handled = true; + } + } + else + { + if (window->OnMessage(message)) handled = true; + } + } + return handled; +} + +bool CGUIWindowManager::SendMessage(CGUIMessage& message, int window) +{ + if (window == 0) + // send to no specified windows. + return SendMessage(message); + CGUIWindow* pWindow = GetWindow(window); + if(pWindow) + return pWindow->OnMessage(message); + else + return false; +} + +void CGUIWindowManager::AddUniqueInstance(CGUIWindow *window) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + // increment our instance (upper word of windowID) + // until we get a window we don't have + int instance = 0; + while (GetWindow(window->GetID())) + window->SetID(window->GetID() + (++instance << 16)); + Add(window); +} + +void CGUIWindowManager::Add(CGUIWindow* pWindow) +{ + if (!pWindow) + { + CLog::Log(LOGERROR, "Attempted to add a NULL window pointer to the window manager."); + return; + } + // push back all the windows if there are more than one covered by this class + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + for (int id : pWindow->GetIDRange()) + { + auto it = m_mapWindows.find(id); + if (it != m_mapWindows.end()) + { + CLog::Log(LOGERROR, + "Error, trying to add a second window with id {} " + "to the window manager", + id); + return; + } + + m_mapWindows.insert(std::make_pair(id, pWindow)); + } +} + +void CGUIWindowManager::AddCustomWindow(CGUIWindow* pWindow) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + Add(pWindow); + m_vecCustomWindows.emplace_back(pWindow); +} + +void CGUIWindowManager::RegisterDialog(CGUIWindow* dialog) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + // only add the window if it does not exists + for (const auto& window : m_activeDialogs) + { + if (window->GetID() == dialog->GetID()) + return; + } + m_activeDialogs.emplace_back(dialog); +} + +void CGUIWindowManager::Remove(int id) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + auto it = m_mapWindows.find(id); + if (it != m_mapWindows.end()) + { + CGUIWindow *window = it->second; + m_windowHistory.erase(std::remove_if(m_windowHistory.begin(), + m_windowHistory.end(), + [id](int winId){ return winId == id; }), + m_windowHistory.end()); + m_activeDialogs.erase(std::remove_if(m_activeDialogs.begin(), + m_activeDialogs.end(), + [window](CGUIWindow* w){ return w == window; }), + m_activeDialogs.end()); + m_mapWindows.erase(it); + } + else + { + CLog::Log(LOGWARNING, + "Attempted to remove window {} " + "from the window manager when it didn't exist", + id); + } +} + +// removes and deletes the window. Should only be called +// from the class that created the window using new. +void CGUIWindowManager::Delete(int id) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + CGUIWindow *pWindow = GetWindow(id); + if (pWindow) + { + Remove(id); + m_deleteWindows.emplace_back(pWindow); + } +} + +void CGUIWindowManager::PreviousWindow() +{ + // deactivate any window + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + CLog::Log(LOGDEBUG,"CGUIWindowManager::PreviousWindow: Deactivate"); + int currentWindow = GetActiveWindow(); + CGUIWindow *pCurrentWindow = GetWindow(currentWindow); + if (!pCurrentWindow) + return; // no windows or window history yet + + // check to see whether our current window has a <previouswindow> tag + if (pCurrentWindow->GetPreviousWindow() != WINDOW_INVALID) + { + //! @todo we may need to test here for the + //! whether our history should be changed + + // don't reactivate the previouswindow if it is ourselves. + if (currentWindow != pCurrentWindow->GetPreviousWindow()) + ActivateWindow(pCurrentWindow->GetPreviousWindow()); + return; + } + // get the previous window in our stack + if (m_windowHistory.size() < 2) + { + // no previous window history yet - check if we should just activate home + if (GetActiveWindow() != WINDOW_INVALID && GetActiveWindow() != WINDOW_HOME) + { + CloseWindowSync(pCurrentWindow); + ClearWindowHistory(); + ActivateWindow(WINDOW_HOME); + } + return; + } + m_windowHistory.pop_back(); + int previousWindow = GetActiveWindow(); + m_windowHistory.emplace_back(currentWindow); + + CGUIWindow *pNewWindow = GetWindow(previousWindow); + if (!pNewWindow) + { + CLog::Log(LOGERROR, "Unable to activate the previous window"); + CloseWindowSync(pCurrentWindow); + ClearWindowHistory(); + ActivateWindow(WINDOW_HOME); + return; + } + + // ok to go to the previous window now + + // tell our info manager which window we are going to + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetNextWindow(previousWindow); + + // deinitialize our window + CloseWindowSync(pCurrentWindow); + + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetNextWindow(WINDOW_INVALID); + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetPreviousWindow(currentWindow); + + // remove the current window off our window stack + m_windowHistory.pop_back(); + + // ok, initialize the new window + CLog::Log(LOGDEBUG,"CGUIWindowManager::PreviousWindow: Activate new"); + CGUIMessage msg2(GUI_MSG_WINDOW_INIT, 0, 0, WINDOW_INVALID, GetActiveWindow()); + pNewWindow->OnMessage(msg2); + + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetPreviousWindow(WINDOW_INVALID); +} + +void CGUIWindowManager::ChangeActiveWindow(int newWindow, const std::string& strPath) +{ + std::vector<std::string> params; + if (!strPath.empty()) + params.emplace_back(strPath); + ActivateWindow(newWindow, params, true); +} + +void CGUIWindowManager::ActivateWindow(int iWindowID, const std::string& strPath) +{ + std::vector<std::string> params; + if (!strPath.empty()) + params.emplace_back(strPath); + ActivateWindow(iWindowID, params, false); +} + +void CGUIWindowManager::ForceActivateWindow(int iWindowID, const std::string& strPath) +{ + std::vector<std::string> params; + if (!strPath.empty()) + params.emplace_back(strPath); + ActivateWindow(iWindowID, params, false, true); +} + +void CGUIWindowManager::ActivateWindow(int iWindowID, const std::vector<std::string>& params, bool swappingWindows /* = false */, bool force /* = false */) +{ + if (!CServiceBroker::GetAppMessenger()->IsProcessThread()) + { + // make sure graphics lock is not held + CSingleExit leaveIt(CServiceBroker::GetWinSystem()->GetGfxContext()); + CServiceBroker::GetAppMessenger()->SendMsg(TMSG_GUI_ACTIVATE_WINDOW, iWindowID, + swappingWindows ? 1 : 0, nullptr, "", params); + } + else + { + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + ActivateWindow_Internal(iWindowID, params, swappingWindows, force); + } +} + +void CGUIWindowManager::ActivateWindow_Internal(int iWindowID, const std::vector<std::string>& params, bool swappingWindows, bool force /* = false */) +{ + // translate virtual windows + if (iWindowID == WINDOW_START) + { // virtual start window + iWindowID = g_SkinInfo->GetStartWindow(); + } + + // debug + CLog::Log(LOGDEBUG, "Activating window ID: {}", iWindowID); + + // make sure we check mediasources from home + if (GetActiveWindow() == WINDOW_HOME) + { + g_passwordManager.SetMediaSourcePath(!params.empty() ? params[0] : ""); + } + else + { + g_passwordManager.SetMediaSourcePath(""); + } + + if (!g_passwordManager.CheckMenuLock(iWindowID)) + { + CLog::Log(LOGERROR, + "MasterCode or MediaSource-code is wrong: Window with id {} will not be loaded! " + "Enter a correct code!", + iWindowID); + if (GetActiveWindow() == WINDOW_INVALID && iWindowID != WINDOW_HOME) + ActivateWindow(WINDOW_HOME); + return; + } + + // first check existence of the window we wish to activate. + CGUIWindow *pNewWindow = GetWindow(iWindowID); + if (!pNewWindow) + { // nothing to see here - move along + CLog::Log(LOGERROR, "Unable to locate window with id {}. Check skin files", + iWindowID - WINDOW_HOME); + if (IsWindowActive(WINDOW_STARTUP_ANIM)) + ActivateWindow(WINDOW_HOME); + return ; + } + else if (!pNewWindow->CanBeActivated()) + { + if (IsWindowActive(WINDOW_STARTUP_ANIM)) + ActivateWindow(WINDOW_HOME); + return; + } + else if (pNewWindow->IsDialog()) + { // if we have a dialog, we do a DoModal() rather than activate the window + if (!pNewWindow->IsDialogRunning()) + { + CSingleExit exitit(CServiceBroker::GetWinSystem()->GetGfxContext()); + static_cast<CGUIDialog *>(pNewWindow)->Open(params.size() > 0 ? params[0] : ""); + // Invalidate underlying windows after closing a modal dialog + MarkDirty(); + } + return; + } + + // don't activate a window if there are active modal dialogs of type MODAL + if (!force && HasModalDialog(true)) + { + CLog::Log(LOGINFO, "Activate of window '{}' refused because there are active modal dialogs", + iWindowID); + CServiceBroker::GetGUI()->GetAudioManager().PlayActionSound(CAction(ACTION_ERROR)); + return; + } + + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetNextWindow(iWindowID); + + // deactivate any window + int currentWindow = GetActiveWindow(); + CGUIWindow *pWindow = GetWindow(currentWindow); + if (pWindow) + CloseWindowSync(pWindow, iWindowID); + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetNextWindow(WINDOW_INVALID); + + // Add window to the history list (we must do this before we activate it, + // as all messages done in WINDOW_INIT will want to be sent to the new + // topmost window). If we are swapping windows, we pop the old window + // off the history stack + if (swappingWindows && !m_windowHistory.empty()) + m_windowHistory.pop_back(); + AddToWindowHistory(iWindowID); + + CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetPreviousWindow(currentWindow); + // Send the init message + CGUIMessage msg(GUI_MSG_WINDOW_INIT, 0, 0, currentWindow, iWindowID); + msg.SetStringParams(params); + pNewWindow->OnMessage(msg); +// CServiceBroker::GetGUI()->GetInfoManager().GetInfoProviders().GetGUIControlsInfoProvider().SetPreviousWindow(WINDOW_INVALID); +} + +void CGUIWindowManager::CloseDialogs(bool forceClose) const +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + //This is to avoid an assert about out of bounds iterator + //when m_activeDialogs happens to be empty + if (m_activeDialogs.empty()) + return; + + auto activeDialogs = m_activeDialogs; + for (const auto& window : activeDialogs) + { + if (window->IsModalDialog()) + window->Close(forceClose); + } +} + +void CGUIWindowManager::CloseInternalModalDialogs(bool forceClose) const +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + if (m_activeDialogs.empty()) + return; + + auto activeDialogs = m_activeDialogs; + for (const auto& window : activeDialogs) + { + if (window->IsModalDialog() && !IsAddonWindow(window->GetID()) && !IsPythonWindow(window->GetID())) + window->Close(forceClose); + } +} + +// SwitchToFullScreen() returns true if a switch is made, else returns false +bool CGUIWindowManager::SwitchToFullScreen(bool force /* = false */) +{ + // don't switch if the slideshow is active + if (IsWindowActive(WINDOW_SLIDESHOW)) + return false; + + // if playing from the video info window, close it first! + if (IsModalDialogTopmost(WINDOW_DIALOG_VIDEO_INFO)) + { + CGUIDialogVideoInfo* pDialog = GetWindow<CGUIDialogVideoInfo>(WINDOW_DIALOG_VIDEO_INFO); + if (pDialog) + pDialog->Close(true); + } + + // if playing from the album info window, close it first! + if (IsModalDialogTopmost(WINDOW_DIALOG_MUSIC_INFO)) + { + CGUIDialogVideoInfo* pDialog = GetWindow<CGUIDialogVideoInfo>(WINDOW_DIALOG_MUSIC_INFO); + if (pDialog) + pDialog->Close(true); + } + + // if playing from the song info window, close it first! + if (IsModalDialogTopmost(WINDOW_DIALOG_SONG_INFO)) + { + CGUIDialogVideoInfo* pDialog = GetWindow<CGUIDialogVideoInfo>(WINDOW_DIALOG_SONG_INFO); + if (pDialog) + pDialog->Close(true); + } + + const int activeWindowID = GetActiveWindow(); + int windowID = WINDOW_INVALID; + + const auto& components = CServiceBroker::GetAppComponents(); + const auto appPlayer = components.GetComponent<CApplicationPlayer>(); + + // See if we're playing a game + if (activeWindowID != WINDOW_FULLSCREEN_GAME && appPlayer->IsPlayingGame()) + windowID = WINDOW_FULLSCREEN_GAME; + + // See if we're playing a video + else if (activeWindowID != WINDOW_FULLSCREEN_VIDEO && appPlayer->IsPlayingVideo()) + windowID = WINDOW_FULLSCREEN_VIDEO; + + // See if we're playing an audio song + if (activeWindowID != WINDOW_VISUALISATION && appPlayer->IsPlayingAudio()) + windowID = WINDOW_VISUALISATION; + + if (windowID != WINDOW_INVALID && (force || windowID != activeWindowID)) + { + if (force) + ForceActivateWindow(windowID); + else + ActivateWindow(windowID); + + return true; + } + + return false; +} + +void CGUIWindowManager::OnApplicationMessage(ThreadMessage* pMsg) +{ + switch (pMsg->dwMessage) + { + case TMSG_GUI_DIALOG_OPEN: + { + if (pMsg->lpVoid) + static_cast<CGUIDialog*>(pMsg->lpVoid)->Open(pMsg->param2, pMsg->strParam); + else + { + CGUIDialog* pDialog = static_cast<CGUIDialog*>(GetWindow(pMsg->param1)); + if (pDialog) + pDialog->Open(pMsg->strParam); + } + } + break; + + case TMSG_GUI_WINDOW_CLOSE: + { + CGUIWindow *window = static_cast<CGUIWindow *>(pMsg->lpVoid); + if (window) + window->Close((pMsg->param1 & 0x1) ? true : false, pMsg->param1, (pMsg->param1 & 0x2) ? true : false); + } + break; + + case TMSG_GUI_ACTIVATE_WINDOW: + { + ActivateWindow(pMsg->param1, pMsg->params, pMsg->param2 > 0); + } + break; + + case TMSG_GUI_PREVIOUS_WINDOW: + { + PreviousWindow(); + } + break; + + case TMSG_GUI_ADDON_DIALOG: + { + if (pMsg->lpVoid) + { + static_cast<ADDON::CGUIAddonWindowDialog*>(pMsg->lpVoid)->Show_Internal(pMsg->param2 > 0); + } + } + break; + +#ifdef HAS_PYTHON + case TMSG_GUI_PYTHON_DIALOG: + { + // This hack is not much better but at least I don't need to make ApplicationMessenger + // know about Addon (Python) specific classes. + CAction caction(pMsg->param1); + static_cast<CGUIWindow*>(pMsg->lpVoid)->OnAction(caction); + } + break; +#endif + + case TMSG_GUI_ACTION: + { + if (pMsg->lpVoid) + { + CAction *action = static_cast<CAction *>(pMsg->lpVoid); + if (pMsg->param1 == WINDOW_INVALID) + g_application.OnAction(*action); + else + { + CGUIWindow *pWindow = GetWindow(pMsg->param1); + if (pWindow) + pWindow->OnAction(*action); + else + CLog::Log(LOGWARNING, "Failed to get window with ID {} to send an action to", + pMsg->param1); + } + delete action; + } + } + break; + + case TMSG_GUI_MESSAGE: + if (pMsg->lpVoid) + { + CGUIMessage *message = static_cast<CGUIMessage *>(pMsg->lpVoid); + SendMessage(*message, pMsg->param1); + delete message; + } + break; + + case TMSG_GUI_DIALOG_YESNO: + { + + if (!pMsg->lpVoid && pMsg->param1 < 0 && pMsg->param2 < 0) + return; + + auto dialog = static_cast<CGUIDialogYesNo*>(GetWindow(WINDOW_DIALOG_YES_NO)); + if (!dialog) + return; + + if (pMsg->lpVoid) + pMsg->SetResult(dialog->ShowAndGetInput(*static_cast<HELPERS::DialogYesNoMessage*>(pMsg->lpVoid))); + else + { + HELPERS::DialogYesNoMessage options; + options.heading = pMsg->param1; + options.text = pMsg->param2; + pMsg->SetResult(dialog->ShowAndGetInput(options)); + } + + } + break; + + case TMSG_GUI_DIALOG_OK: + { + + if (!pMsg->lpVoid && pMsg->param1 < 0 && pMsg->param2 < 0) + return; + + auto dialogOK = static_cast<CGUIDialogOK*>(GetWindow(WINDOW_DIALOG_OK)); + if (!dialogOK) + return; + + if (pMsg->lpVoid) + dialogOK->ShowAndGetInput(*static_cast<HELPERS::DialogOKMessage*>(pMsg->lpVoid)); + else + { + HELPERS::DialogOKMessage options; + options.heading = pMsg->param1; + options.text = pMsg->param2; + dialogOK->ShowAndGetInput(options); + } + pMsg->SetResult(static_cast<int>(dialogOK->IsConfirmed())); + } + break; + } +} + +int CGUIWindowManager::GetMessageMask() +{ + return TMSG_MASK_WINDOWMANAGER; +} + +bool CGUIWindowManager::OnAction(const CAction &action) const +{ + auto actionId = action.GetID(); + if (actionId == ACTION_GESTURE_BEGIN) + { + m_touchGestureActive = true; + } + + bool ret; + if (!m_inhibitTouchGestureEvents || !action.IsGesture()) + { + ret = HandleAction(action); + } + else + { + // We swallow the event, so it is handled + ret = true; + CLog::Log(LOGDEBUG, "Swallowing touch action {} due to inhibition on window switch", actionId); + } + + if (actionId == ACTION_GESTURE_END || actionId == ACTION_GESTURE_ABORT) + { + m_touchGestureActive = false; + m_inhibitTouchGestureEvents = false; + } + + return ret; +} + +bool CGUIWindowManager::HandleAction(CAction const& action) const +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + size_t topmost = m_activeDialogs.size(); + while (topmost) + { + CGUIWindow *dialog = m_activeDialogs[--topmost]; + lock.unlock(); + if (dialog->IsModalDialog()) + { // we have the topmost modal dialog + if (!dialog->IsAnimating(ANIM_TYPE_WINDOW_CLOSE)) + { + bool fallThrough = (dialog->GetID() == WINDOW_DIALOG_FULLSCREEN_INFO); + if (dialog->OnAction(action)) + return true; + // dialog didn't want the action - we'd normally return false + // but for some dialogs we want to drop the actions through + if (fallThrough) + { + lock.lock(); + break; + } + return false; + } + CLog::Log(LOGWARNING, + "CGUIWindowManager - {} - ignoring action {}, because topmost modal dialog closing " + "animation is running", + __FUNCTION__, action.GetID()); + return true; // do nothing with the action until the anim is finished + } + lock.lock(); + if (topmost > m_activeDialogs.size()) + topmost = m_activeDialogs.size(); + } + lock.unlock(); + CGUIWindow* window = GetWindow(GetActiveWindow()); + if (window) + return window->OnAction(action); + return false; +} + +bool RenderOrderSortFunction(CGUIWindow *first, CGUIWindow *second) +{ + return first->GetRenderOrder() < second->GetRenderOrder(); +} + +void CGUIWindowManager::Process(unsigned int currentTime) +{ + assert(CServiceBroker::GetAppMessenger()->IsProcessThread()); + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + m_dirtyregions.clear(); + + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->DoProcess(currentTime, m_dirtyregions); + + // process all dialogs - visibility may change etc. + for (const auto& entry : m_mapWindows) + { + CGUIWindow *pWindow = entry.second; + if (pWindow && pWindow->IsDialog()) + pWindow->DoProcess(currentTime, m_dirtyregions); + } + + for (auto& itr : m_dirtyregions) + m_tracker.MarkDirtyRegion(itr); +} + +void CGUIWindowManager::MarkDirty() +{ + MarkDirty(CRect(0, 0, float(CServiceBroker::GetWinSystem()->GetGfxContext().GetWidth()), float(CServiceBroker::GetWinSystem()->GetGfxContext().GetHeight()))); +} + +void CGUIWindowManager::MarkDirty(const CRect& rect) +{ + m_tracker.MarkDirtyRegion(CDirtyRegion(rect)); + + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->MarkDirtyRegion(); + + // make copy of vector as we may remove items from it as we go + auto activeDialogs = m_activeDialogs; + for (const auto& window : activeDialogs) + if (window->IsDialogRunning()) + window->MarkDirtyRegion(); +} + +void CGUIWindowManager::RenderPass() const +{ + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + { + pWindow->ClearBackground(); + pWindow->DoRender(); + } + + // we render the dialogs based on their render order. + auto renderList = m_activeDialogs; + stable_sort(renderList.begin(), renderList.end(), RenderOrderSortFunction); + + for (const auto& window : renderList) + { + if (window->IsDialogRunning()) + window->DoRender(); + } +} + +void CGUIWindowManager::RenderEx() const +{ + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->RenderEx(); + + // We don't call RenderEx for now on dialogs since it is used + // to trigger non gui video rendering. We can activate it later at any time. + /* + vector<CGUIWindow *> &activeDialogs = m_activeDialogs; + for (iDialog it = activeDialogs.begin(); it != activeDialogs.end(); ++it) + { + if ((*it)->IsDialogRunning()) + (*it)->RenderEx(); + } + */ +} + +bool CGUIWindowManager::Render() +{ + assert(CServiceBroker::GetAppMessenger()->IsProcessThread()); + CSingleExit lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + CDirtyRegionList dirtyRegions = m_tracker.GetDirtyRegions(); + + bool hasRendered = false; + // If we visualize the regions we will always render the entire viewport + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiVisualizeDirtyRegions || CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_FILL_VIEWPORT_ALWAYS) + { + RenderPass(); + hasRendered = true; + } + else if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiAlgorithmDirtyRegions == DIRTYREGION_SOLVER_FILL_VIEWPORT_ON_CHANGE) + { + if (!dirtyRegions.empty()) + { + RenderPass(); + hasRendered = true; + } + } + else + { + for (const auto& i : dirtyRegions) + { + if (i.IsEmpty()) + continue; + + CServiceBroker::GetWinSystem()->GetGfxContext().SetScissors(i); + RenderPass(); + hasRendered = true; + } + CServiceBroker::GetWinSystem()->GetGfxContext().ResetScissors(); + } + + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_guiVisualizeDirtyRegions) + { + CServiceBroker::GetWinSystem()->GetGfxContext().SetRenderingResolution(CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo(), false); + const CDirtyRegionList &markedRegions = m_tracker.GetMarkedRegions(); + for (const auto& i : markedRegions) + CGUITexture::DrawQuad(i, 0x0fff0000); + for (const auto& i : dirtyRegions) + CGUITexture::DrawQuad(i, 0x4c00ff00); + } + + return hasRendered; +} + +void CGUIWindowManager::AfterRender() +{ + m_tracker.CleanMarkedRegions(); + + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->AfterRender(); + + // make copy of vector as we may remove items from it as we go + auto activeDialogs = m_activeDialogs; + for (const auto& window : activeDialogs) + { + if (window->IsDialogRunning()) + { + window->AfterRender(); + // Dialog state can affect visibility states + if (pWindow && window->IsControlDirty()) + pWindow->MarkDirtyRegion(); + } + } +} + +void CGUIWindowManager::FrameMove() +{ + assert(CServiceBroker::GetAppMessenger()->IsProcessThread()); + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + if(m_iNested == 0) + { + // delete any windows queued for deletion + for (const auto& window : m_deleteWindows) + { + // Free any window resources + window->FreeResources(true); + delete window; + } + m_deleteWindows.clear(); + } + + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->FrameMove(); + // update any dialogs - we take a copy of the vector as some dialogs may close themselves + // during this call + auto dialogs = m_activeDialogs; + for (const auto& window : dialogs) + { + window->FrameMove(); + } + + CServiceBroker::GetGUI()->GetInfoManager().UpdateAVInfo(); +} + +CGUIDialog* CGUIWindowManager::GetDialog(int id) const +{ + CGUIWindow *window = GetWindow(id); + if (window && window->IsDialog()) + return dynamic_cast<CGUIDialog*>(window); + return nullptr; +} + +CGUIWindow* CGUIWindowManager::GetWindow(int id) const +{ + if (id == 0 || id == WINDOW_INVALID) + return nullptr; + + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + auto it = m_mapWindows.find(id); + if (it != m_mapWindows.end()) + return it->second; + return nullptr; +} + +bool CGUIWindowManager::ProcessRenderLoop(bool renderOnly) +{ + bool renderGui = true; + + if (CServiceBroker::GetAppMessenger()->IsProcessThread() && m_pCallback) + { + renderGui = m_pCallback->GetRenderGUI(); + m_iNested++; + if (!renderOnly) + m_pCallback->Process(); + m_pCallback->FrameMove(!renderOnly); + m_pCallback->Render(); + m_iNested--; + } + if (g_application.m_bStop || !renderGui) + return false; + else + return true; +} + +void CGUIWindowManager::SetCallback(IWindowManagerCallback& callback) +{ + m_pCallback = &callback; +} + +void CGUIWindowManager::DeInitialize() +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + // Need a copy because addon-dialogs removes itself on Close() + std::unordered_map<int, CGUIWindow*> closeMap(m_mapWindows); + for (const auto& entry : closeMap) + { + CGUIWindow* pWindow = entry.second; + if (IsWindowActive(entry.first, false)) + { + pWindow->DisableAnimations(); + pWindow->Close(true); + } + pWindow->ResetControlStates(); + pWindow->FreeResources(true); + } + UnloadNotOnDemandWindows(); + + m_vecMsgTargets.erase( m_vecMsgTargets.begin(), m_vecMsgTargets.end() ); + + // destroy our custom windows... + for (int i = 0; i < int(m_vecCustomWindows.size()); i++) + { + CGUIWindow *pWindow = m_vecCustomWindows[i]; + RemoveFromWindowHistory(pWindow->GetID()); + Remove(pWindow->GetID()); + delete pWindow; + } + + // clear our vectors of windows + m_vecCustomWindows.clear(); + m_activeDialogs.clear(); + + m_initialized = false; +} + +/// \brief Unroute window +/// \param id ID of the window routed +void CGUIWindowManager::RemoveDialog(int id) +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + m_activeDialogs.erase(std::remove_if(m_activeDialogs.begin(), + m_activeDialogs.end(), + [id](CGUIWindow* dialog) { return dialog->GetID() == id; }), + m_activeDialogs.end()); +} + +bool CGUIWindowManager::HasModalDialog(bool ignoreClosing) const +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + for (const auto& window : m_activeDialogs) + { + if (window->IsDialog() && + window->IsModalDialog() && + (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))) + { + return true; + } + } + return false; +} + +bool CGUIWindowManager::HasVisibleModalDialog() const +{ + return HasModalDialog(false); +} + +int CGUIWindowManager::GetTopmostDialog(bool modal, bool ignoreClosing) const +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + for (auto it = m_activeDialogs.rbegin(); it != m_activeDialogs.rend(); ++it) + { + CGUIWindow *dialog = *it; + if ((!modal || dialog->IsModalDialog()) && (!ignoreClosing || !dialog->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))) + return dialog->GetID(); + } + return WINDOW_INVALID; +} + +int CGUIWindowManager::GetTopmostDialog(bool ignoreClosing /*= false*/) const +{ + return GetTopmostDialog(false, ignoreClosing); +} + +int CGUIWindowManager::GetTopmostModalDialog(bool ignoreClosing /*= false*/) const +{ + return GetTopmostDialog(true, ignoreClosing); +} + +void CGUIWindowManager::SendThreadMessage(CGUIMessage& message, int window /*= 0*/) +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + + CGUIMessage* msg = new CGUIMessage(message); + m_vecThreadMessages.emplace_back(std::pair<CGUIMessage*, int>(msg,window)); +} + +void CGUIWindowManager::DispatchThreadMessages() +{ + // This method only be called in the xbmc main thread. + + // XXX: for more info of this method + // check the pr here: https://github.com/xbmc/xbmc/pull/2253 + + // As a thread message queue service, it should follow these rules: + // 1. [Must] Thread safe, message can be pushed into queue in arbitrary thread context. + // 2. Messages [must] be processed in dispatch message thread context with the same + // order as they be pushed into the queue. + // 3. Dispatch function [must] support call itself during message process procedure, + // and do not break other rules listed here. to make it clear: in the + // SendMessage(), it could start another xbmc main thread loop, calling + // DispatchThreadMessages() in it's internal loop, this must be supported. + // 4. During DispatchThreadMessages() processing, any new pushed message [should] not + // be processed by the current loop in DispatchThreadMessages(), prevent dead loop. + // 5. If possible, queued messages can be removed by certain filter condition + // and not break above. + + std::unique_lock<CCriticalSection> lock(m_critSection); + + while (!m_vecThreadMessages.empty()) + { + // pop up one message per time to make messages be processed by order. + // this will ensure rule No.2 & No.3 + CGUIMessage *pMsg = m_vecThreadMessages.front().first; + int window = m_vecThreadMessages.front().second; + m_vecThreadMessages.pop_front(); + + lock.unlock(); + + // XXX: during SendMessage(), there could be a deeper 'xbmc main loop' inited by e.g. doModal + // which may loop there and callback to DispatchThreadMessages() multiple times. + if (window) + SendMessage( *pMsg, window ); + else + SendMessage( *pMsg ); + delete pMsg; + + lock.lock(); + } +} + +int CGUIWindowManager::RemoveThreadMessageByMessageIds(int *pMessageIDList) +{ + std::unique_lock<CCriticalSection> lock(m_critSection); + int removedMsgCount = 0; + for (std::list < std::pair<CGUIMessage*,int> >::iterator it = m_vecThreadMessages.begin(); + it != m_vecThreadMessages.end();) + { + CGUIMessage *pMsg = it->first; + int *pMsgID; + for(pMsgID = pMessageIDList; *pMsgID != 0; ++pMsgID) + if (pMsg->GetMessage() == *pMsgID) + break; + if (*pMsgID) + { + it = m_vecThreadMessages.erase(it); + delete pMsg; + ++removedMsgCount; + } + else + { + ++it; + } + } + return removedMsgCount; +} + +void CGUIWindowManager::AddMsgTarget(IMsgTargetCallback* pMsgTarget) +{ + m_vecMsgTargets.emplace_back(pMsgTarget); +} + +int CGUIWindowManager::GetActiveWindow() const +{ + if (!m_windowHistory.empty()) + return m_windowHistory.back(); + return WINDOW_INVALID; +} + +int CGUIWindowManager::GetActiveWindowOrDialog() const +{ + // if there is a dialog active get the dialog id instead + int iWin = GetTopmostModalDialog() & WINDOW_ID_MASK; + if (iWin != WINDOW_INVALID) + return iWin; + + // get the currently active window + return GetActiveWindow() & WINDOW_ID_MASK; +} + +bool CGUIWindowManager::IsWindowActive(int id, bool ignoreClosing /* = true */) const +{ + // mask out multiple instances of the same window + id &= WINDOW_ID_MASK; + if ((GetActiveWindow() & WINDOW_ID_MASK) == id) + return true; + // run through the dialogs + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + for (const auto& window : m_activeDialogs) + { + if ((window->GetID() & WINDOW_ID_MASK) == id && (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))) + return true; + } + return false; // window isn't active +} + +bool CGUIWindowManager::IsWindowActive(const std::string &xmlFile, bool ignoreClosing /* = true */) const +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + CGUIWindow *window = GetWindow(GetActiveWindow()); + if (window && StringUtils::EqualsNoCase(URIUtils::GetFileName(window->GetProperty("xmlfile").asString()), xmlFile)) + return true; + // run through the dialogs + for (const auto& window : m_activeDialogs) + { + if (StringUtils::EqualsNoCase(URIUtils::GetFileName(window->GetProperty("xmlfile").asString()), xmlFile) && + (!ignoreClosing || !window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE))) + return true; + } + return false; // window isn't active +} + +bool CGUIWindowManager::IsWindowVisible(int id) const +{ + return IsWindowActive(id, false); +} + +bool CGUIWindowManager::IsWindowVisible(const std::string &xmlFile) const +{ + return IsWindowActive(xmlFile, false); +} + +void CGUIWindowManager::LoadNotOnDemandWindows() +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + for (const auto& entry : m_mapWindows) + { + CGUIWindow *pWindow = entry.second; + if (pWindow->GetLoadType() == CGUIWindow::LOAD_ON_GUI_INIT) + { + pWindow->FreeResources(true); + pWindow->Initialize(); + } + } +} + +void CGUIWindowManager::UnloadNotOnDemandWindows() +{ + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + for (const auto& entry : m_mapWindows) + { + CGUIWindow *pWindow = entry.second; + if (pWindow->GetLoadType() == CGUIWindow::LOAD_ON_GUI_INIT || + pWindow->GetLoadType() == CGUIWindow::KEEP_IN_MEMORY) + { + pWindow->FreeResources(true); + } + } +} + +void CGUIWindowManager::AddToWindowHistory(int newWindowID) +{ + // Check the window stack to see if this window is in our history, + // and if so, pop all the other windows off the stack so that we + // always have a predictable "Back" behaviour for each window + std::deque<int> history = m_windowHistory; + while (!history.empty()) + { + if (history.back() == newWindowID) + break; + history.pop_back(); + } + if (!history.empty()) + { // found window in history + m_windowHistory.swap(history); + } + else + { + // didn't find window in history - add it to the stack + m_windowHistory.emplace_back(newWindowID); + } +} + +void CGUIWindowManager::RemoveFromWindowHistory(int windowID) +{ + std::deque<int> history = m_windowHistory; + + // pop windows from stack until we found the window + while (!history.empty()) + { + if (history.back() == windowID) + break; + history.pop_back(); + } + + // found window in history + if (!history.empty()) + { + history.pop_back(); // remove window from stack + m_windowHistory.swap(history); + } +} + +bool CGUIWindowManager::IsModalDialogTopmost(int id) const +{ + return IsDialogTopmost(id, true); +} + +bool CGUIWindowManager::IsModalDialogTopmost(const std::string &xmlFile) const +{ + return IsDialogTopmost(xmlFile, true); +} + +bool CGUIWindowManager::IsDialogTopmost(int id, bool modal /* = false */) const +{ + CGUIWindow *topmost = GetWindow(GetTopmostDialog(modal, false)); + if (topmost && (topmost->GetID() & WINDOW_ID_MASK) == id) + return true; + return false; +} + +bool CGUIWindowManager::IsDialogTopmost(const std::string &xmlFile, bool modal /* = false */) const +{ + CGUIWindow *topmost = GetWindow(GetTopmostDialog(modal, false)); + if (topmost && StringUtils::EqualsNoCase(URIUtils::GetFileName(topmost->GetProperty("xmlfile").asString()), xmlFile)) + return true; + return false; +} + +bool CGUIWindowManager::HasVisibleControls() +{ + CSingleExit lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + + if (m_activeDialogs.empty()) + { + CGUIWindow *window(GetWindow(GetActiveWindow())); + return !window || window->HasVisibleControls(); + } + else + return true; +} + +void CGUIWindowManager::ClearWindowHistory() +{ + while (!m_windowHistory.empty()) + m_windowHistory.pop_back(); +} + +void CGUIWindowManager::CloseWindowSync(CGUIWindow *window, int nextWindowID /*= 0*/) +{ + // Abort touch action if active + if (m_touchGestureActive && !m_inhibitTouchGestureEvents) + { + CLog::Log(LOGDEBUG, "Closing window {} with active touch gesture, sending gesture abort event", + window->GetID()); + window->OnAction({ACTION_GESTURE_ABORT}); + // Don't send any mid-gesture events to next window until new touch starts + m_inhibitTouchGestureEvents = true; + } + + window->Close(false, nextWindowID); + + bool renderLoopProcessed = true; + while (window->IsAnimating(ANIM_TYPE_WINDOW_CLOSE) && renderLoopProcessed) + renderLoopProcessed = ProcessRenderLoop(true); +} + +#ifdef _DEBUG +void CGUIWindowManager::DumpTextureUse() +{ + CGUIWindow* pWindow = GetWindow(GetActiveWindow()); + if (pWindow) + pWindow->DumpTextureUse(); + + std::unique_lock<CCriticalSection> lock(CServiceBroker::GetWinSystem()->GetGfxContext()); + for (const auto& window : m_activeDialogs) + { + if (window->IsDialogRunning()) + window->DumpTextureUse(); + } +} +#endif |