/******************************************************************************** Copyright (C) 2012 Hugh Bailey 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 of the License, 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ********************************************************************************/ #include "Main.h" void OBS::RegisterSceneClass(CTSTR lpClassName, CTSTR lpDisplayName, OBSCREATEPROC createProc, OBSCONFIGPROC configProc) { if(!lpClassName || !*lpClassName) { AppWarning(TEXT("OBS::RegisterSceneClass: No class name specified")); return; } if(!createProc) { AppWarning(TEXT("OBS::RegisterSceneClass: No create procedure specified")); return; } if(GetSceneClass(lpClassName)) { AppWarning(TEXT("OBS::RegisterSceneClass: Tried to register '%s', but it already exists"), lpClassName); return; } ClassInfo *classInfo = sceneClasses.CreateNew(); classInfo->strClass = lpClassName; classInfo->strName = lpDisplayName; classInfo->createProc = createProc; classInfo->configProc = configProc; } void OBS::RegisterImageSourceClass(CTSTR lpClassName, CTSTR lpDisplayName, OBSCREATEPROC createProc, OBSCONFIGPROC configProc) { if(!lpClassName || !*lpClassName) { AppWarning(TEXT("OBS::RegisterImageSourceClass: No class name specified")); return; } if(!createProc) { AppWarning(TEXT("OBS::RegisterImageSourceClass: No create procedure specified")); return; } if(GetImageSourceClass(lpClassName)) { AppWarning(TEXT("OBS::RegisterImageSourceClass: Tried to register '%s', but it already exists"), lpClassName); return; } ClassInfo *classInfo = imageSourceClasses.CreateNew(); classInfo->strClass = lpClassName; classInfo->strName = lpDisplayName; classInfo->createProc = createProc; classInfo->configProc = configProc; } Scene* OBS::CreateScene(CTSTR lpClassName, XElement *data) { for(UINT i=0; iGetString(TEXT("class")); if(!lpClassName) { AppWarning(TEXT("OBS::ConfigureScene: No class specified for scene '%s'"), element->GetName()); return; } for(UINT i=0; iGetString(TEXT("class")); if(!lpClassName) { AppWarning(TEXT("OBS::ConfigureImageSource: No class specified for image source '%s'"), element->GetName()); return; } for(UINT i=0; iGetElement(lpScene); if(!newSceneElement) return false; if(API != NULL) ReportSwitchScenes(lpScene); if(sceneElement == newSceneElement) return true; sceneElement = newSceneElement; CTSTR lpClass = sceneElement->GetString(TEXT("class")); if(!lpClass) { AppWarning(TEXT("OBS::SetScene: no class found for scene '%s'"), newSceneElement->GetName()); return false; } if(bRunning) { Log(TEXT("++++++++++++++++++++++++++++++++++++++++++++++++++++++")); Log(TEXT(" New Scene")); } XElement *sceneData = newSceneElement->GetElement(TEXT("data")); //------------------------- Scene *newScene = NULL; if(bRunning) newScene = CreateScene(lpClass, sceneData); //------------------------- HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)FALSE, (LPARAM) 0); bChangingSources = true; ListView_DeleteAllItems(hwndSources); XElement *sources = sceneElement->GetElement(TEXT("sources")); if(sources) { UINT numSources = sources->NumElements(); ListView_SetItemCount(hwndSources, numSources); for(UINT i=0; iGetElementByID(i); bool render = sourceElement->GetInt(TEXT("render"), 1) > 0; InsertSourceItem(i, (LPWSTR)sourceElement->GetName(), render); if(bRunning && newScene) newScene->AddImageSource(sourceElement); } } bChangingSources = false; SendMessage(hwndSources, WM_SETREDRAW, (WPARAM)TRUE, (LPARAM) 0); RedrawWindow(hwndSources, NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); if(scene && newScene->HasMissingSources()) MessageBox(hwndMain, Str("Scene.MissingSources"), NULL, 0); if(bRunning) { //todo: cache scenes maybe? undecided. not really as necessary with global sources OSEnterMutex(hSceneMutex); if(scene) scene->EndScene(); Scene *previousScene = scene; scene = newScene; scene->BeginScene(); if(!bTransitioning) { bTransitioning = true; transitionAlpha = 0.0f; } OSLeaveMutex(hSceneMutex); delete previousScene; } return true; } struct HotkeyInfo { DWORD hotkeyID; DWORD hotkey; OBSHOTKEYPROC hotkeyProc; UPARAM param; bool bModifiersDown, bHotkeyDown, bDownSent; }; class OBSAPIInterface : public APIInterface { friend class OBS; List hotkeys; DWORD curHotkeyIDVal; void HandleHotkeys(); virtual bool UseHighQualityResampling() const {return AppConfig->GetInt(TEXT("Audio"), TEXT("UseHighQualityResampling"), FALSE) != 0;} public: virtual void EnterSceneMutex() {App->EnterSceneMutex();} virtual void LeaveSceneMutex() {App->LeaveSceneMutex();} virtual void StartStopStream() { PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_STARTSTOP, 0), 0); } virtual void StartStopPreview() { PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_TESTSTREAM, 0), 0); } virtual bool GetStreaming() { return App->bRunning; } virtual bool GetPreviewOnly() { return App->bTestStream; } virtual void RegisterSceneClass(CTSTR lpClassName, CTSTR lpDisplayName, OBSCREATEPROC createProc, OBSCONFIGPROC configProc) { App->RegisterSceneClass(lpClassName, lpDisplayName, createProc, configProc); } virtual void RegisterImageSourceClass(CTSTR lpClassName, CTSTR lpDisplayName, OBSCREATEPROC createProc, OBSCONFIGPROC configProc) { App->RegisterImageSourceClass(lpClassName, lpDisplayName, createProc, configProc); } virtual ImageSource* CreateImageSource(CTSTR lpClassName, XElement *data) { return App->CreateImageSource(lpClassName, data); } virtual XElement* GetSceneListElement() {return App->scenesConfig.GetElement(TEXT("scenes"));} virtual XElement* GetGlobalSourceListElement() {return App->scenesConfig.GetElement(TEXT("global sources"));} virtual void SetSourceOrder(StringList &sourceNames) { StringList* order = new StringList(); order->CopyList(sourceNames); PostMessage(hwndMain, OBS_SETSOURCEORDER, 0, (LPARAM) order); } virtual void SetSourceRender(CTSTR lpSource, bool render) { if(!lpSource || !*lpSource) return; PostMessage(hwndMain, OBS_SETSOURCERENDER, (WPARAM)sdup(lpSource), (LPARAM) render); } virtual bool SetScene(CTSTR lpScene, bool bPost) { assert(lpScene && *lpScene); if(!lpScene || !*lpScene) return false; if(bPost) { PostMessage(hwndMain, OBS_SETSCENE, 0, (LPARAM)sdup(lpScene)); return true; } return App->SetScene(lpScene); } virtual Scene* GetScene() const {return App->scene;} virtual CTSTR GetSceneName() const {return App->GetSceneElement()->GetName();} virtual XElement* GetSceneElement() {return App->GetSceneElement();} virtual UINT CreateHotkey(DWORD hotkey, OBSHOTKEYPROC hotkeyProc, UPARAM param); virtual void DeleteHotkey(UINT hotkeyID); virtual Vect2 GetBaseSize() const {return Vect2(float(App->baseCX), float(App->baseCY));} virtual Vect2 GetRenderFrameSize() const {return Vect2(float(App->renderFrameWidth), float(App->renderFrameHeight));} virtual Vect2 GetOutputSize() const {return Vect2(float(App->outputCX), float(App->outputCY));} virtual void GetBaseSize(UINT &width, UINT &height) const {App->GetBaseSize(width, height);} virtual void GetRenderFrameSize(UINT &width, UINT &height) const {App->GetRenderFrameSize(width, height);} virtual void GetOutputSize(UINT &width, UINT &height) const {App->GetOutputSize(width, height);} virtual UINT GetMaxFPS() const {return App->bRunning ? App->fps : AppConfig->GetInt(TEXT("Video"), TEXT("FPS"), 30);} virtual CTSTR GetLanguage() const {return App->strLanguage;} virtual CTSTR GetAppDataPath() const {return lpAppDataPath;} virtual String GetPluginDataPath() const {return String() << lpAppDataPath << TEXT("\\pluginData");} virtual HWND GetMainWindow() const {return hwndMain;} virtual UINT AddStreamInfo(CTSTR lpInfo, StreamInfoPriority priority) {return App->AddStreamInfo(lpInfo, priority);} virtual void SetStreamInfo(UINT infoID, CTSTR lpInfo) {App->SetStreamInfo(infoID, lpInfo);} virtual void SetStreamInfoPriority(UINT infoID, StreamInfoPriority priority) {App->SetStreamInfoPriority(infoID, priority);} virtual void RemoveStreamInfo(UINT infoID) {App->RemoveStreamInfo(infoID);} virtual bool UseMultithreadedOptimizations() const {return App->bUseMultithreadedOptimizations;} virtual void AddAudioSource(AudioSource *source) {App->AddAudioSource(source);} virtual void RemoveAudioSource(AudioSource *source) {App->RemoveAudioSource(source);} virtual QWORD GetAudioTime() const {return App->GetAudioTime();} virtual CTSTR GetAppPath() const {return lpAppPath;} virtual void AddOBSEventListener(OBSTriggerHandler *handler) { App->AddOBSEventListener(handler); } virtual void RemoveOBSEventListener(OBSTriggerHandler *handler) { App->RemoveOBSEventListener(handler); } }; APIInterface* CreateOBSApiInterface() { return new OBSAPIInterface; } void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown) { if(!bDown) return; XElement *scenes = API->GetSceneListElement(); if(scenes) { UINT numScenes = scenes->NumElements(); for(UINT i=0; iGetElementByID(i); DWORD sceneHotkey = (DWORD)scene->GetInt(TEXT("hotkey")); if(sceneHotkey == hotkey) { App->SetScene(scene->GetName()); return; } } } } DWORD STDCALL OBS::HotkeyThread(LPVOID lpUseless) { //----------------------------------------------- // check hotkeys. // Why are we handling hotkeys like this? Because RegisterHotkey and WM_HOTKEY // does not work with fullscreen apps. Therefore, we use GetAsyncKeyState once // per frame instead. while(!App->bShuttingDown) { static_cast(API)->HandleHotkeys(); OSSleep(30); } return 0; } void OBS::CallHotkey(DWORD hotkeyID, bool bDown) { OBSAPIInterface *apiInterface = (OBSAPIInterface*)API; OBSHOTKEYPROC hotkeyProc = NULL; DWORD hotkey = 0; UPARAM param = NULL; OSEnterMutex(hHotkeyMutex); for(UINT i=0; ihotkeys.Num(); i++) { HotkeyInfo &hi = apiInterface->hotkeys[i]; if(hi.hotkeyID == hotkeyID) { if(!hi.hotkeyProc) return; hotkeyProc = hi.hotkeyProc; param = hi.param; hotkey = hi.hotkey; break; } } OSLeaveMutex(hHotkeyMutex); hotkeyProc(hotkey, param, bDown); } UINT OBSAPIInterface::CreateHotkey(DWORD hotkey, OBSHOTKEYPROC hotkeyProc, UPARAM param) { if(!hotkey) return 0; DWORD vk = LOBYTE(hotkey); DWORD modifier = HIBYTE(hotkey); DWORD fsModifiers = 0; if(modifier & HOTKEYF_ALT) fsModifiers |= MOD_ALT; if(modifier & HOTKEYF_CONTROL) fsModifiers |= MOD_CONTROL; if(modifier & HOTKEYF_SHIFT) fsModifiers |= MOD_SHIFT; OSEnterMutex(App->hHotkeyMutex); HotkeyInfo &hi = *hotkeys.CreateNew(); hi.hotkeyID = ++curHotkeyIDVal; hi.hotkey = hotkey; hi.hotkeyProc = hotkeyProc; hi.param = param; hi.bModifiersDown = false; hi.bHotkeyDown = false; OSLeaveMutex(App->hHotkeyMutex); return curHotkeyIDVal; } void OBSAPIInterface::DeleteHotkey(UINT hotkeyID) { OSEnterMutex(App->hHotkeyMutex); for(UINT i=0; ihHotkeyMutex); } void OBSAPIInterface::HandleHotkeys() { List hitKeys; DWORD modifiers = 0; if(GetAsyncKeyState(VK_MENU) & 0x8000) modifiers |= HOTKEYF_ALT; if(GetAsyncKeyState(VK_CONTROL) & 0x8000) modifiers |= HOTKEYF_CONTROL; if(GetAsyncKeyState(VK_SHIFT) & 0x8000) modifiers |= HOTKEYF_SHIFT; OSEnterMutex(App->hHotkeyMutex); for(UINT i=0; ihHotkeyMutex); } UINT OBS::AddStreamInfo(CTSTR lpInfo, StreamInfoPriority priority) { OSEnterMutex(hInfoMutex); StreamInfo &streamInfo = *streamInfoList.CreateNew(); UINT id = streamInfo.id = ++streamInfoIDCounter; streamInfo.priority = priority; streamInfo.strInfo = lpInfo; OSLeaveMutex(hInfoMutex); return id; } void OBS::SetStreamInfo(UINT infoID, CTSTR lpInfo) { OSEnterMutex(hInfoMutex); for(UINT i=0; i bestInfoPriority) { lpBestInfo = streamInfoList[i].strInfo; bestInfoPriority = streamInfoList[i].priority; } } String strInfo = lpBestInfo; OSLeaveMutex(hInfoMutex); return strInfo; }