/******************************************************************************** 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" #include void SetupSceneCollection(CTSTR scenecollection); //primarily main window stuff an initialization/destruction code typedef bool (*LOADPLUGINPROC)(); typedef bool (*LOADPLUGINEXPROC)(UINT); typedef void (*UNLOADPLUGINPROC)(); typedef CTSTR (*GETPLUGINNAMEPROC)(); ImageSource* STDCALL CreateDesktopSource(XElement *data); bool STDCALL ConfigureDesktopSource(XElement *data, bool bCreating); bool STDCALL ConfigureWindowCaptureSource(XElement *data, bool bCreating); bool STDCALL ConfigureMonitorCaptureSource(XElement *data, bool bCreating); ImageSource* STDCALL CreateBitmapSource(XElement *data); bool STDCALL ConfigureBitmapSource(XElement *element, bool bCreating); ImageSource* STDCALL CreateBitmapTransitionSource(XElement *data); bool STDCALL ConfigureBitmapTransitionSource(XElement *element, bool bCreating); ImageSource* STDCALL CreateTextSource(XElement *data); bool STDCALL ConfigureTextSource(XElement *element, bool bCreating); ImageSource* STDCALL CreateGlobalSource(XElement *data); void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown); APIInterface* CreateOBSApiInterface(); #define QuickClearHotkey(hotkeyID) \ if(hotkeyID) \ { \ API->DeleteHotkey(hotkeyID); \ hotkeyID = NULL; \ } //---------------------------- WNDPROC listboxProc = NULL; WNDPROC listviewProc = NULL; //---------------------------- const float defaultBlendFactor[4] = {1.0f, 1.0f, 1.0f, 1.0f}; //---------------------------- BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, List &monitors) { monitors << MonitorInfo(hMonitor, lprcMonitor); return TRUE; } const int controlPadding = 3; const int totalControlAreaWidth = minClientWidth - 10; const int miscAreaWidth = 290; const int totalControlAreaHeight = 171;//170;// const int listAreaWidth = totalControlAreaWidth-miscAreaWidth; const int controlWidth = miscAreaWidth/2; const int controlHeight = 22; const int volControlHeight = 32; const int volMeterHeight = 10; const int textControlHeight = 16; const int listControlWidth = listAreaWidth/2; Scene* STDCALL CreateNormalScene(XElement *data) { return new Scene; } BOOL IsWebrootLoaded() { BOOL ret = FALSE; StringList moduleList; OSGetLoadedModuleList (GetCurrentProcess(), moduleList); HMODULE msIMG = GetModuleHandle(TEXT("MSIMG32")); if (msIMG) { FARPROC alphaBlend = GetProcAddress(msIMG, "AlphaBlend"); if (alphaBlend) { if (!IsBadReadPtr(alphaBlend, 5)) { BYTE opCode = *(BYTE *)alphaBlend; if (opCode == 0xE9) { if (moduleList.HasValue(TEXT("wrusr.dll"))) ret = TRUE; } } } } return ret; } // Checks the AppData path is writable and the disk has enough space VOID CheckPermissionsAndDiskSpace() { ULARGE_INTEGER freeSpace; if (GetDiskFreeSpaceEx (lpAppDataPath, &freeSpace, NULL, NULL)) { // 1MB ought to be enough for anybody... if (freeSpace.QuadPart < 1048576) { OBSMessageBox(OBSGetMainWindow(), Str("DiskFull"), NULL, MB_ICONERROR); } } HANDLE tempFile; String testPath; testPath = lpAppDataPath; testPath += TEXT("\\.test"); tempFile = CreateFile(testPath.Array(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (tempFile == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); if (err == ERROR_ACCESS_DENIED || err == ERROR_FILE_READ_ONLY) OBSMessageBox(OBSGetMainWindow(), Str("BadAppDataPermissions"), NULL, MB_ICONERROR); // TODO: extra handling for unknown errors (maybe some av returns weird codes?) } else { CloseHandle(tempFile); } } //--------------------------------------------------------------------------- VOID OBS::LoadAllPlugins() { //----------------------------------------------------- // load plugins OSFindData ofd; HANDLE hFind = OSFindFirstFile(TEXT("plugins/*.dll"), ofd); if (hFind) { do { if (!ofd.bDirectory) //why would someone give a directory a .dll extension in the first place? pranksters. { String strLocation; strLocation << TEXT("plugins/") << ofd.fileName; HMODULE hPlugin = LoadLibrary(strLocation); if (hPlugin) { bool bLoaded = false; //slightly redundant I suppose seeing as both these things are being added at the same time LOADPLUGINEXPROC loadPluginEx = (LOADPLUGINEXPROC)GetProcAddress(hPlugin, "LoadPluginEx"); if (loadPluginEx) { bLoaded = loadPluginEx(OBSGetAPIVersion()); } else { LOADPLUGINPROC loadPlugin = (LOADPLUGINPROC)GetProcAddress(hPlugin, "LoadPlugin"); bLoaded = loadPlugin && loadPlugin(); } if (bLoaded) { PluginInfo *pluginInfo = plugins.CreateNew(); pluginInfo->hModule = hPlugin; pluginInfo->strFile = ofd.fileName; /* get event callbacks for the plugin */ pluginInfo->startStreamCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStartStream"); pluginInfo->stopStreamCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStopStream"); pluginInfo->startStreamingCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStartStreaming"); pluginInfo->stopStreamingCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStopStreaming"); pluginInfo->startRecordingCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStartRecording"); pluginInfo->stopRecordingCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStopRecording"); pluginInfo->statusCallback = (OBS_STATUS_CALLBACK)GetProcAddress(hPlugin, "OnOBSStatus"); pluginInfo->streamStatusCallback = (OBS_STREAM_STATUS_CALLBACK)GetProcAddress(hPlugin, "OnStreamStatus"); pluginInfo->sceneSwitchCallback = (OBS_SCENE_SWITCH_CALLBACK)GetProcAddress(hPlugin, "OnSceneSwitch"); pluginInfo->sceneCollectionSwitchCallback = (OBS_SCENE_SWITCH_CALLBACK)GetProcAddress(hPlugin, "OnSceneCollectionSwitch"); pluginInfo->scenesChangedCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnScenesChanged"); pluginInfo->sceneCollectionsChangedCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnSceneCollectionsChanged"); pluginInfo->sourceOrderChangedCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnSourceOrderChanged"); pluginInfo->sourceChangedCallback = (OBS_SOURCE_CHANGED_CALLBACK)GetProcAddress(hPlugin, "OnSourceChanged"); pluginInfo->sourcesAddedOrRemovedCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnSourcesAddedOrRemoved"); pluginInfo->micVolumeChangeCallback = (OBS_VOLUME_CHANGED_CALLBACK)GetProcAddress(hPlugin, "OnMicVolumeChanged"); pluginInfo->desktopVolumeChangeCallback = (OBS_VOLUME_CHANGED_CALLBACK)GetProcAddress(hPlugin, "OnDesktopVolumeChanged"); pluginInfo->logUpdateCallback = (OBS_LOG_UPDATE_CALLBACK)GetProcAddress(hPlugin, "OnLogUpdate"); pluginInfo->startRecordingReplayBufferCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStartRecordingReplayBuffer"); pluginInfo->stopRecordingReplayBufferCallback = (OBS_CALLBACK)GetProcAddress(hPlugin, "OnStopRecordingReplayBuffer"); pluginInfo->replayBufferSavedCallback = (OBS_REPLAY_BUFFER_SAVED_CALLBACK)GetProcAddress(hPlugin, "OnReplayBufferSaved"); //GETPLUGINNAMEPROC getName = (GETPLUGINNAMEPROC)GetProcAddress(hPlugin, "GetPluginName"); //CTSTR lpName = (getName) ? getName() : TEXT(""); //FIXME: TODO: log this somewhere else, it comes before the OBS version info and looks weird. //Log(TEXT("Loaded plugin '%s', %s"), lpName, strLocation); } else { Log(TEXT("Failed to initialize plugin %s"), strLocation.Array()); FreeLibrary(hPlugin); } } else { DWORD err = GetLastError(); Log(TEXT("Failed to load plugin %s, %d"), strLocation.Array(), err); #ifndef _DEBUG if (err == 193) { #ifdef _M_X64 String message = FormattedString(Str("Plugins.InvalidVersion"), ofd.fileName, 32); #else String message = FormattedString(Str("Plugins.InvalidVersion"), ofd.fileName, 64); #endif OBSMessageBox(hwndMain, message.Array(), NULL, MB_ICONEXCLAMATION); } #endif } } } while (OSFindNextFile(hFind, ofd)); OSFindClose(hFind); } } OBS::OBS() { App = this; performTransition = true; //Default to true and don't set the conf. //We don't want to let plugins disable standard behavior permanently. hSceneMutex = OSCreateMutex(); hAuxAudioMutex = OSCreateMutex(); hVideoEvent = CreateEvent(NULL, FALSE, FALSE, NULL); monitors.Clear(); EnumDisplayMonitors(NULL, NULL, (MONITORENUMPROC)MonitorInfoEnumProc, (LPARAM)&monitors); INITCOMMONCONTROLSEX ecce; ecce.dwSize = sizeof(ecce); ecce.dwICC = ICC_STANDARD_CLASSES; if(!InitCommonControlsEx(&ecce)) CrashError(TEXT("Could not initalize common shell controls")); InitHotkeyExControl(hinstMain); InitColorControl(hinstMain); InitVolumeControl(hinstMain); InitVolumeMeter(hinstMain); // still need this here for API strLanguage = GlobalConfig->GetString(TEXT("General"), TEXT("Language"), TEXT("en")); //----------------------------------------------------- // load classes RegisterSceneClass(TEXT("Scene"), Str("Scene"), (OBSCREATEPROC)CreateNormalScene, NULL, false); RegisterImageSourceClass(TEXT("DesktopImageSource"), Str("Sources.SoftwareCaptureSource"), (OBSCREATEPROC)CreateDesktopSource, (OBSCONFIGPROC)ConfigureDesktopSource, true); RegisterImageSourceClass(TEXT("WindowCaptureSource"), Str("Sources.SoftwareCaptureSource.WindowCapture"), (OBSCREATEPROC)CreateDesktopSource, (OBSCONFIGPROC)ConfigureWindowCaptureSource, false); RegisterImageSourceClass(TEXT("MonitorCaptureSource"), Str("Sources.SoftwareCaptureSource.MonitorCapture"), (OBSCREATEPROC)CreateDesktopSource, (OBSCONFIGPROC)ConfigureMonitorCaptureSource, false); RegisterImageSourceClass(TEXT("BitmapImageSource"), Str("Sources.BitmapSource"), (OBSCREATEPROC)CreateBitmapSource, (OBSCONFIGPROC)ConfigureBitmapSource, false); RegisterImageSourceClass(TEXT("BitmapTransitionSource"), Str("Sources.TransitionSource"), (OBSCREATEPROC)CreateBitmapTransitionSource, (OBSCONFIGPROC)ConfigureBitmapTransitionSource, false); RegisterImageSourceClass(TEXT("GlobalSource"), Str("Sources.GlobalSource"), (OBSCREATEPROC)CreateGlobalSource, (OBSCONFIGPROC)OBS::ConfigGlobalSource, false); RegisterImageSourceClass(TEXT("TextSource"), Str("Sources.TextSource"), (OBSCREATEPROC)CreateTextSource, (OBSCONFIGPROC)ConfigureTextSource, false); //----------------------------------------------------- // render frame class WNDCLASS wc; zero(&wc, sizeof(wc)); wc.hInstance = hinstMain; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.lpszClassName = OBS_RENDERFRAME_CLASS; wc.lpfnWndProc = (WNDPROC)OBS::RenderFrameProc; wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); if(!RegisterClass(&wc)) CrashError(TEXT("Could not register render frame class")); //----------------------------------------------------- // projector frame class wc.lpszClassName = OBS_PROJECTORFRAME_CLASS; wc.lpfnWndProc = (WNDPROC)OBS::ProjectorFrameProc; wc.hbrBackground = GetSysColorBrush(COLOR_BTNFACE); if(!RegisterClass(&wc)) CrashError(TEXT("Could not register projector frame class")); //----------------------------------------------------- // log window class wc.lpszClassName = OBS_LOGWINDOW_CLASS; wc.lpfnWndProc = (WNDPROC)OBS::LogWindowProc; wc.hIcon = LoadIcon(hinstMain, MAKEINTRESOURCE(IDI_ICON1)); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; if(!RegisterClass(&wc)) CrashError(TEXT("Could not register main window class")); //----------------------------------------------------- // main window class wc.lpszClassName = OBS_WINDOW_CLASS; wc.lpfnWndProc = (WNDPROC)OBSProc; wc.hIcon = LoadIcon(hinstMain, MAKEINTRESOURCE(IDI_ICON1)); wc.hbrBackground = (HBRUSH)COLOR_WINDOW; wc.lpszMenuName = MAKEINTRESOURCE(IDR_MAINMENU); if(!RegisterClass(&wc)) CrashError(TEXT("Could not register main window class")); //----------------------------------------------------- // create main window int fullscreenX = GetSystemMetrics(SM_CXFULLSCREEN); int fullscreenY = GetSystemMetrics(SM_CYFULLSCREEN); borderXSize = borderYSize = 0; borderXSize += GetSystemMetrics(SM_CXSIZEFRAME)*2; borderYSize += GetSystemMetrics(SM_CYSIZEFRAME)*2; borderYSize += GetSystemMetrics(SM_CYMENU); borderYSize += GetSystemMetrics(SM_CYCAPTION); clientWidth = GlobalConfig->GetInt(TEXT("General"), TEXT("Width"), defaultClientWidth); clientHeight = GlobalConfig->GetInt(TEXT("General"), TEXT("Height"), defaultClientHeight); if(clientWidth < minClientWidth) clientWidth = minClientWidth; if(clientHeight < minClientHeight) clientHeight = minClientHeight; int maxCX = fullscreenX-borderXSize; int maxCY = fullscreenY-borderYSize; if(clientWidth > maxCX) clientWidth = maxCX; if(clientHeight > maxCY) clientHeight = maxCY; int cx = clientWidth + borderXSize; int cy = clientHeight + borderYSize; int x = (fullscreenX/2)-(cx/2); int y = (fullscreenY/2)-(cy/2); int posX = GlobalConfig->GetInt(TEXT("General"), TEXT("PosX"), -9999); int posY = GlobalConfig->GetInt(TEXT("General"), TEXT("PosY"), -9999); bool bInsideMonitors = false; for(UINT i=0; i= monitors[i].rect.left && posX < monitors[i].rect.right && posY >= monitors[i].rect.top && posY < monitors[i].rect.bottom ) { bInsideMonitors = true; break; } } if(bInsideMonitors) { x = posX; y = posY; } bPanelVisibleWindowed = GlobalConfig->GetInt(TEXT("General"), TEXT("PanelVisibleWindowed"), 1) != 0; bPanelVisibleFullscreen = GlobalConfig->GetInt(TEXT("General"), TEXT("PanelVisibleFullscreen"), 0) != 0; bPanelVisible = bPanelVisibleWindowed; // Assuming OBS always starts windowed bPanelVisibleProcessed = false; // Force immediate process bFullscreenMode = false; hwndMain = CreateWindowEx(WS_EX_CONTROLPARENT|WS_EX_WINDOWEDGE|(LocaleIsRTL() ? WS_EX_LAYOUTRTL : 0), OBS_WINDOW_CLASS, GetApplicationName(), WS_OVERLAPPED | WS_THICKFRAME | WS_MAXIMIZEBOX | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU | WS_CLIPCHILDREN, x, y, cx, cy, NULL, NULL, hinstMain, NULL); if(!hwndMain) CrashError(TEXT("Could not create main window")); hmenuMain = GetMenu(hwndMain); LocalizeMenu(hmenuMain); //----------------------------------------------------- // render frame hwndRenderFrame = CreateWindow(OBS_RENDERFRAME_CLASS, NULL, WS_CHILDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN, 0, 0, 0, 0, hwndMain, NULL, hinstMain, NULL); if(!hwndRenderFrame) CrashError(TEXT("Could not create render frame")); //----------------------------------------------------- // projector window hwndProjector = CreateWindow(OBS_PROJECTORFRAME_CLASS, L"OBS Projector Window", WS_POPUP, 0, 0, 0, 0, NULL, NULL, hinstMain, NULL); //----------------------------------------------------- // log window x = (fullscreenX/2)-(600/2); y = (fullscreenY/2)-(500/2); int logPosX = GlobalConfig->GetInt(TEXT("General"), TEXT("LogPosX"), -9999); int logPosY = GlobalConfig->GetInt(TEXT("General"), TEXT("LogPosY"), -9999); int logSizeX = GlobalConfig->GetInt(TEXT("General"), TEXT("LogSizeX"), 600); int logSizeY = GlobalConfig->GetInt(TEXT("General"), TEXT("LogSizeY"), 500); bInsideMonitors = false; for(UINT i=0; i= monitors[i].rect.left && logPosX < monitors[i].rect.right && logPosY >= monitors[i].rect.top && logPosY < monitors[i].rect.bottom ) { bInsideMonitors = true; break; } } if(bInsideMonitors) { x = logPosX; y = logPosY; } else { logSizeX = 600; logSizeY = 500; } hwndLogWindow = CreateWindowEx(LocaleIsRTL() ? WS_EX_LAYOUTRTL | WS_EX_NOINHERITLAYOUT : 0, OBS_LOGWINDOW_CLASS, L"LogWindow", WS_OVERLAPPEDWINDOW, x, y, logSizeX, logSizeY, NULL, NULL, hinstMain, NULL); LocalizeWindow(hwndLogWindow); RECT client; GetClientRect(hwndLogWindow, &client); hwndLog = CreateWindowEx(WS_EX_CLIENTEDGE, L"EDIT", L"", ES_MULTILINE | WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_VSCROLL | WS_HSCROLL | ES_READONLY | ES_NOHIDESEL, client.left, client.top, client.right, client.bottom, hwndLogWindow, (HMENU)ID_LOG_WINDOW, 0, 0); SendMessage(hwndLog, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); SendMessage(hwndLog, EM_SETLIMITTEXT, 0, 0); ShowWindow(hwndLog, SW_SHOW); ResetLogUpdateCallback([] { PostMessage(hwndLogWindow, WM_COMMAND, MAKEWPARAM(ID_LOG_WINDOW, 0), 0); }); //----------------------------------------------------- // render frame text hwndRenderMessage = CreateWindow(TEXT("STATIC"), Str("MainWindow.BeginMessage"), WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS|SS_CENTER, 0, 0, 0, 0, hwndRenderFrame, NULL, hinstMain, NULL); SendMessage(hwndRenderMessage, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // scenes listbox HWND hwndTemp; hwndTemp = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("LISTBOX"), NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|LBS_HASSTRINGS|WS_VSCROLL|LBS_NOTIFY|LBS_NOINTEGRALHEIGHT|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_SCENES, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); listboxProc = (WNDPROC)GetWindowLongPtr(hwndTemp, GWLP_WNDPROC); SetWindowLongPtr(hwndTemp, GWLP_WNDPROC, (LONG_PTR)OBS::ListboxHook); //----------------------------------------------------- // elements listview hwndTemp = CreateWindowEx(WS_EX_CLIENTEDGE, WC_LISTVIEW, NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|WS_VSCROLL|WS_CLIPSIBLINGS|LVS_REPORT|LVS_NOCOLUMNHEADER| LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOLABELWRAP, 0, 0, 0, 0, hwndMain, (HMENU)ID_SOURCES, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); ListView_SetExtendedListViewStyle(hwndTemp, LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER); //add single column needed for report style LVCOLUMN column; column.mask = LVCF_TEXT; column.fmt = LVCFMT_FIXED_WIDTH; column.cx = 0; column.pszText = TEXT(""); ListView_InsertColumn(hwndTemp, 0, &column); ListView_InsertColumn(hwndTemp, 1, &column); listviewProc = (WNDPROC)GetWindowLongPtr(hwndTemp, GWLP_WNDPROC); SetWindowLongPtr(hwndTemp, GWLP_WNDPROC, (LONG_PTR)OBS::ListboxHook); HWND hwndSources = hwndTemp; //----------------------------------------------------- // status control hwndTemp = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD|WS_VISIBLE|SBARS_SIZEGRIP|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_STATUS, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // mic volume control hwndTemp = CreateWindow(VOLUME_CONTROL_CLASS, NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_MICVOLUME, 0, 0); SetVolumeControlIcons(hwndTemp, GetIcon(hinstMain, IDI_SOUND_MIC), GetIcon(hinstMain, IDI_SOUND_MIC_MUTED)); //----------------------------------------------------- // mic volume meter hwndTemp = CreateWindow(VOLUME_METER_CLASS, NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_MICVOLUMEMETER, 0, 0); //----------------------------------------------------- // desktop volume meter hwndTemp = CreateWindow(VOLUME_METER_CLASS, NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_DESKTOPVOLUMEMETER, 0, 0); //----------------------------------------------------- // desktop volume control hwndTemp = CreateWindow(VOLUME_CONTROL_CLASS, NULL, WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_DESKTOPVOLUME, 0, 0); SetVolumeControlIcons(hwndTemp, GetIcon(hinstMain, IDI_SOUND_DESKTOP), GetIcon(hinstMain, IDI_SOUND_DESKTOP_MUTED)); //----------------------------------------------------- // start/stop recording button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.StartRecording"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_SPLITBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_TOGGLERECORDING, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // settings button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("Settings"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_SETTINGS, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // start/stop stream button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.StartStream"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_STARTSTOP, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // edit scene button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.SceneEditor"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_AUTOCHECKBOX|BS_PUSHLIKE|WS_DISABLED|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_SCENEEDITOR, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // global sources button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("GlobalSources"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_GLOBALSOURCES, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // test stream button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.TestStream"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_TESTSTREAM, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // plugins button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.Plugins"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_PLUGINS, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // dashboard button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.Dashboard"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_DASHBOARD, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // exit button hwndTemp = CreateWindow(TEXT("BUTTON"), Str("MainWindow.Exit"), WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|BS_TEXT|BS_PUSHBUTTON|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_EXIT, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // scenes text hwndTemp = CreateWindow(TEXT("STATIC"), Str("MainWindow.Scenes"), WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_SCENES_TEXT, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // scenes text hwndTemp = CreateWindow(TEXT("STATIC"), Str("MainWindow.Sources"), WS_CHILDWINDOW|WS_VISIBLE|WS_CLIPSIBLINGS, 0, 0, 0, 0, hwndMain, (HMENU)ID_SOURCES_TEXT, 0, 0); SendMessage(hwndTemp, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); //----------------------------------------------------- // notification area bNotificationAreaIcon = false; wmExplorerRestarted = RegisterWindowMessage(TEXT("TaskbarCreated")); if (AppConfig->GetInt(TEXT("General"), TEXT("ShowNotificationAreaIcon"), 0) != 0) { ShowNotificationAreaIcon(); } //----------------------------------------------------- // populate scenes hwndTemp = GetDlgItem(hwndMain, ID_SCENES); String collection = GetCurrentSceneCollection(); if (!OSFileExists(String() << lpAppDataPath << L"\\sceneCollection\\" << collection << L".xconfig")) collection.Clear(); if (collection.IsEmpty()) { OSFindData ofd; HANDLE hFind = OSFindFirstFile(String() << lpAppDataPath << L"\\sceneCollection\\*.xconfig", ofd); if (hFind) { do { if (!ofd.bDirectory) { collection = GetPathWithoutExtension(ofd.fileName); break; } } while (OSFindNextFile(hFind, ofd)); OSFindClose(hFind); } if (collection.IsEmpty()) { CopyFile(String() << lpAppDataPath << L"\\scenes.xconfig", String() << lpAppDataPath << L"\\sceneCollection\\scenes.xconfig", true); collection = L"scenes"; GlobalConfig->SetString(L"General", L"SceneCollection", collection); } } String strScenesConfig; strScenesConfig = FormattedString(L"%s\\sceneCollection\\%s.xconfig", lpAppDataPath, collection.Array()); if(!scenesConfig.Open(strScenesConfig)) CrashError(TEXT("Could not open '%s'"), strScenesConfig.Array()); XElement *scenes = scenesConfig.GetElement(TEXT("scenes")); if(!scenes) scenes = scenesConfig.CreateElement(TEXT("scenes")); UINT numScenes = scenes->NumElements(); if(!numScenes) { XElement *scene = scenes->CreateElement(Str("Scene")); scene->SetString(TEXT("class"), TEXT("Scene")); numScenes++; } for(UINT i=0; iGetElementByID(i); //scene->SetString(TEXT("class"), TEXT("Scene")); SendMessage(hwndTemp, LB_ADDSTRING, 0, (LPARAM)scene->GetName()); } //----------------------------------------------------- // populate sources if(numScenes) { String strScene = AppConfig->GetString(TEXT("General"), TEXT("CurrentScene")); int id = (int)SendMessage(hwndTemp, LB_FINDSTRINGEXACT, -1, (LPARAM)strScene.Array()); if(id == LB_ERR) id = 0; SendMessage(hwndTemp, LB_SETCURSEL, (WPARAM)id, 0); SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SCENES, LBN_SELCHANGE), (LPARAM)GetDlgItem(hwndMain, ID_SCENES)); } //----------------------------------------------------- hHotkeyMutex = OSCreateMutex(); hInfoMutex = OSCreateMutex(); hStartupShutdownMutex = OSCreateMutex(); //----------------------------------------------------- API = CreateOBSApiInterface(); bDragResize = false; if(GlobalConfig->GetInt(TEXT("General"), TEXT("Maximized"))) { // Window was maximized last session SendMessage(hwndMain, WM_SYSCOMMAND, SC_MAXIMIZE, 0); } //----------------------------------------------------- for(UINT i=0; iGetElementByID(i); DWORD hotkey = scene->GetInt(TEXT("hotkey")); if(hotkey) { SceneHotkeyInfo hotkeyInfo; hotkeyInfo.hotkey = hotkey; hotkeyInfo.scene = scene; hotkeyInfo.hotkeyID = API->CreateHotkey(hotkey, SceneHotkey, 0); if(hotkeyInfo.hotkeyID) sceneHotkeys << hotkeyInfo; } } //----------------------------------------------------- // Add built-in settings panes currentSettingsPane = NULL; AddBuiltInSettingsPanes(); AddEncoderSettingsPanes(); //----------------------------------------------------- ReloadIniSettings(); ResetProfileMenu(); ResetSceneCollectionMenu(); ResetLogUploadMenu(); //----------------------------------------------------- bAutoReconnect = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnect"), 1) != 0; reconnectTimeout = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnectTimeout"), 10); hHotkeyThread = OSCreateThread((XTHREAD)HotkeyThread, NULL); #ifndef OBS_DISABLE_AUTOUPDATE ULARGE_INTEGER lastUpdateTime; ULARGE_INTEGER currentTime; FILETIME systemTime; lastUpdateTime.QuadPart = GlobalConfig->GetInt(TEXT("General"), OBS_CONFIG_UPDATE_KEY, 0); GetSystemTimeAsFileTime(&systemTime); currentTime.LowPart = systemTime.dwLowDateTime; currentTime.HighPart = systemTime.dwHighDateTime; //OBS doesn't support 64 bit ints in the config file, so we have to normalize it to a 32 bit int currentTime.QuadPart /= 10000000; currentTime.QuadPart -= 13000000000; if (currentTime.QuadPart - lastUpdateTime.QuadPart >= 3600) { GlobalConfig->SetInt(TEXT("General"), OBS_CONFIG_UPDATE_KEY, (int)currentTime.QuadPart); OSCloseThread(OSCreateThread((XTHREAD)CheckUpdateThread, (LPVOID)0)); } #endif // TODO: Should these be stored in the config file? bRenderViewEnabled = GlobalConfig->GetInt(TEXT("General"), TEXT("PreviewEnabled"), 1) != 0; bForceRenderViewErase = false; renderFrameIn1To1Mode = false; if(GlobalConfig->GetInt(TEXT("General"), TEXT("ShowWebrootWarning"), TRUE) && IsWebrootLoaded()) OBSMessageBox(hwndMain, TEXT("Webroot Secureanywhere appears to be active. This product will cause problems with OBS as the security features block OBS from accessing Windows GDI functions. It is highly recommended that you disable Secureanywhere and restart OBS.\r\n\r\nOf course you can always just ignore this message if you want, but it may prevent you from being able to stream certain things. Please do not report any bugs you may encounter if you leave Secureanywhere enabled."), TEXT("Just a slight issue you might want to be aware of"), MB_OK); CheckPermissionsAndDiskSpace(); ConfigureStreamButtons(); ResizeWindow(false); ShowWindow(hwndMain, SW_SHOW); renderFrameIn1To1Mode = !!GlobalConfig->GetInt(L"General", L"1to1Preview", false); App->bAlwaysOnTop = !!GlobalConfig->GetInt(L"General", L"AlwaysOnTop"); CheckMenuItem(GetMenu(hwndMain), ID_ALWAYSONTOP, (App->bAlwaysOnTop) ? MF_CHECKED : MF_UNCHECKED); SetWindowPos(hwndMain, (App->bAlwaysOnTop) ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); // make sure sources listview column widths are as expected after obs window is shown ListView_SetColumnWidth(hwndSources,0,LVSCW_AUTOSIZE_USEHEADER); ListView_SetColumnWidth(hwndSources,1,LVSCW_AUTOSIZE_USEHEADER); if (GlobalConfig->GetInt(L"General", L"ShowLogWindowOnLaunch") != 0) PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SHOWLOG, 0), 0); if (bStreamOnStart) PostMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_STARTSTOP, 0), NULL); } OBS::~OBS() { Stop(true, true); bShuttingDown = true; OSTerminateThread(hHotkeyThread, 2500); ClosePendingStreams(); for(UINT i=0; iGetInt(TEXT("General"), TEXT("ShowNotificationAreaIcon"), 0) != 0) { App->HideNotificationAreaIcon(); } if (logDirectoryMonitor) OSMonitorDirectoryCallbackStop(logDirectoryMonitor); //DestroyWindow(hwndMain); // Remember window state for next launch WINDOWPLACEMENT placement; placement.length = sizeof(placement); GetWindowPlacement(hwndMain, &placement); RECT rect = { 0 }; GetWindowRect(hwndMain, &rect); GlobalConfig->SetInt(TEXT("General"), TEXT("PosX"), rect.left); GlobalConfig->SetInt(TEXT("General"), TEXT("PosY"), rect.top); GlobalConfig->SetInt(TEXT("General"), TEXT("Width"), rect.right - rect.left - GetSystemMetrics(SM_CXSIZEFRAME) * 2); GlobalConfig->SetInt(TEXT("General"), TEXT("Height"), rect.bottom - rect.top - GetSystemMetrics(SM_CYSIZEFRAME) * 2 - GetSystemMetrics(SM_CYCAPTION) - GetSystemMetrics(SM_CYMENU)); GlobalConfig->SetInt(TEXT("General"), TEXT("Maximized"), placement.showCmd == SW_SHOWMAXIMIZED ? 1 : 0); GetWindowRect(hwndLogWindow, &rect); GlobalConfig->SetInt(TEXT("General"), TEXT("LogPosX"), rect.left); GlobalConfig->SetInt(TEXT("General"), TEXT("LogPosY"), rect.top); GlobalConfig->SetInt(TEXT("General"), TEXT("LogSizeX"), rect.right - rect.left); GlobalConfig->SetInt(TEXT("General"), TEXT("LogSizeY"), rect.bottom - rect.top); GlobalConfig->SetInt(L"General", L"1to1Preview", renderFrameIn1To1Mode); GlobalConfig->SetInt(L"General", L"AlwaysOnTop", App->bAlwaysOnTop); // Save control panel visibility GlobalConfig->SetInt(TEXT("General"), TEXT("PanelVisibleWindowed"), bPanelVisibleWindowed ? 1 : 0); GlobalConfig->SetInt(TEXT("General"), TEXT("PanelVisibleFullscreen"), bPanelVisibleFullscreen ? 1 : 0); // Save preview enabled/disabled state GlobalConfig->SetInt(TEXT("General"), TEXT("PreviewEnabled"), bRenderViewEnabled ? 1 : 0); scenesConfig.SaveTo(String() << lpAppDataPath << "\\scenes.xconfig"); scenesConfig.Close(true); for(UINT i=0; iGetInt(TEXT("Video"), TEXT("Monitor")); if(monitorID >= (int)monitors.Num()) monitorID = 0; RECT &screenRect = monitors[monitorID].rect; int defCX = screenRect.right - screenRect.left; int defCY = screenRect.bottom - screenRect.top; // Calculate output size using the same algorithm that's in OBS::Start() float scale = AppConfig->GetFloat(TEXT("Video"), TEXT("Downscale"), 1.0f); curCX = AppConfig->GetInt(TEXT("Video"), TEXT("BaseWidth"), defCX); curCY = AppConfig->GetInt(TEXT("Video"), TEXT("BaseHeight"), defCY); curCX = MIN(MAX(curCX, 128), 4096); curCY = MIN(MAX(curCY, 128), 4096); curCX = UINT(double(curCX) / double(scale)); curCY = UINT(double(curCY) / double(scale)); curCX = curCX & 0xFFFFFFFC; // Align width to 128bit for fast SSE YUV4:2:0 conversion curCY = curCY & 0xFFFFFFFE; mainAspect = float(curCX)/float(curCY); } // Get area to render in int x, y; UINT controlWidth = clientWidth; UINT controlHeight = clientHeight; if(bPanelVisible) controlHeight -= totalControlAreaHeight + controlPadding; UINT newRenderFrameWidth, newRenderFrameHeight; if(renderFrameIn1To1Mode) { newRenderFrameWidth = (UINT)curCX; newRenderFrameHeight = (UINT)curCY; x = (int)controlWidth / 2 - curCX / 2; y = (int)controlHeight / 2 - curCY / 2; } else { // Scale to fit Vect2 renderSize = Vect2(float(controlWidth), float(controlHeight)); float renderAspect = renderSize.x/renderSize.y; if(renderAspect > mainAspect) { renderSize.x = renderSize.y*mainAspect; x = int((float(controlWidth)-renderSize.x)*0.5f); y = 0; } else { renderSize.y = renderSize.x/mainAspect; x = 0; y = int((float(controlHeight)-renderSize.y)*0.5f); } // Round and ensure even size newRenderFrameWidth = int(renderSize.x+0.5f)&0xFFFFFFFE; newRenderFrameHeight = int(renderSize.y+0.5f)&0xFFFFFFFE; } // Fill the majority of the window with the 3D scene. We'll render everything in DirectX SetWindowPos(hwndRenderFrame, NULL, 0, 0, controlWidth, controlHeight, SWP_NOOWNERZORDER); //---------------------------------------------- renderFrameX = x; renderFrameY = y; renderFrameWidth = newRenderFrameWidth; renderFrameHeight = newRenderFrameHeight; renderFrameCtrlWidth = controlWidth; renderFrameCtrlHeight = controlHeight; if(!bRunning) { oldRenderFrameCtrlWidth = renderFrameCtrlWidth; oldRenderFrameCtrlHeight = renderFrameCtrlHeight; InvalidateRect(hwndRenderMessage, NULL, true); // Repaint text } else if(bRunning && bRedrawRenderFrame) { oldRenderFrameCtrlWidth = renderFrameCtrlWidth; oldRenderFrameCtrlHeight = renderFrameCtrlHeight; bResizeRenderView = true; } } void OBS::SetFullscreenMode(bool fullscreen) { if(App->bFullscreenMode == fullscreen) return; // Nothing to do App->bFullscreenMode = fullscreen; if(fullscreen) { // Remember current window placement fullscreenPrevPlacement.length = sizeof(fullscreenPrevPlacement); GetWindowPlacement(hwndMain, &fullscreenPrevPlacement); // Update panel visibility if required if(bPanelVisible != bPanelVisibleFullscreen) { bPanelVisible = bPanelVisibleFullscreen; bPanelVisibleProcessed = false; } // Hide borders LONG style = GetWindowLong(hwndMain, GWL_STYLE); SetWindowLong(hwndMain, GWL_STYLE, style & ~(WS_CAPTION | WS_THICKFRAME)); // Hide menu and status bar SetMenu(hwndMain, NULL); // Fill entire screen HMONITOR monitorForWidow = MonitorFromWindow(hwndMain, MONITOR_DEFAULTTONEAREST); MONITORINFO monitorInfo; monitorInfo.cbSize = sizeof(monitorInfo); GetMonitorInfo(monitorForWidow, &monitorInfo); int x = monitorInfo.rcMonitor.left; int y = monitorInfo.rcMonitor.top; int cx = monitorInfo.rcMonitor.right - monitorInfo.rcMonitor.left; int cy = monitorInfo.rcMonitor.bottom - monitorInfo.rcMonitor.top; SetWindowPos(hwndMain, HWND_TOPMOST, x, y, cx, cy, SWP_FRAMECHANGED); // Update menu checkboxes CheckMenuItem(hmenuMain, ID_FULLSCREENMODE, MF_CHECKED); } else { // Show borders LONG style = GetWindowLong(hwndMain, GWL_STYLE); SetWindowLong(hwndMain, GWL_STYLE, style | WS_CAPTION | WS_THICKFRAME); // Show menu and status bar SetMenu(hwndMain, hmenuMain); // Restore control panel visible state if required if(bPanelVisible != bPanelVisibleWindowed) { bPanelVisible = bPanelVisibleWindowed; bPanelVisibleProcessed = false; } // Restore original window size SetWindowPlacement(hwndMain, &fullscreenPrevPlacement); // Update menu checkboxes CheckMenuItem(hmenuMain, ID_FULLSCREENMODE, MF_UNCHECKED); // Disable always-on-top if needed SetWindowPos(hwndMain, (App->bAlwaysOnTop)?HWND_TOPMOST:HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); } // Workaround: If the window is maximized, resize isn't called, so do it manually // Also, when going into fullscreen, this can prevent pixelation ResizeWindow(true); } /** * Show or hide the control panel. */ void OBS::ProcessPanelVisible(bool fromResizeWindow) { if(bPanelVisibleProcessed) return; // Already done const int visible = bPanelVisible ? SW_SHOW : SW_HIDE; ShowWindow(GetDlgItem(hwndMain, ID_MICVOLUME), visible); ShowWindow(GetDlgItem(hwndMain, ID_DESKTOPVOLUME), visible); ShowWindow(GetDlgItem(hwndMain, ID_MICVOLUMEMETER), visible); ShowWindow(GetDlgItem(hwndMain, ID_DESKTOPVOLUMEMETER), visible); ShowWindow(GetDlgItem(hwndMain, ID_SETTINGS), visible); ShowWindow(GetDlgItem(hwndMain, ID_TOGGLERECORDING), visible); ShowWindow(GetDlgItem(hwndMain, ID_STARTSTOP), visible); ShowWindow(GetDlgItem(hwndMain, ID_SCENEEDITOR), visible); ShowWindow(GetDlgItem(hwndMain, ID_TESTSTREAM), visible); ShowWindow(GetDlgItem(hwndMain, ID_GLOBALSOURCES), visible); ShowWindow(GetDlgItem(hwndMain, ID_PLUGINS), visible); if(!bPanelVisible) ShowWindow(GetDlgItem(hwndMain, ID_DASHBOARD), SW_HIDE); ShowWindow(GetDlgItem(hwndMain, ID_EXIT), visible); ShowWindow(GetDlgItem(hwndMain, ID_SCENES_TEXT), visible); ShowWindow(GetDlgItem(hwndMain, ID_SOURCES_TEXT), visible); ShowWindow(GetDlgItem(hwndMain, ID_SCENES), visible); ShowWindow(GetDlgItem(hwndMain, ID_SOURCES), visible); bPanelVisibleProcessed = true; // HACK: Force resize to fix dashboard button. The setting should not be calculated every resize if(bPanelVisible && !fromResizeWindow) ResizeWindow(false); } void OBS::GetBaseSize(UINT &width, UINT &height) const { if(bRunning) { width = baseCX; height = baseCY; } else { int monitorID = AppConfig->GetInt(TEXT("Video"), TEXT("Monitor")); if(monitorID >= (int)monitors.Num()) monitorID = 0; RECT &screenRect = monitors[monitorID].rect; int defCX = screenRect.right - screenRect.left; int defCY = screenRect.bottom - screenRect.top; width = AppConfig->GetInt(TEXT("Video"), TEXT("BaseWidth"), defCX); height = AppConfig->GetInt(TEXT("Video"), TEXT("BaseHeight"), defCY); } } void OBS::ResizeWindow(bool bRedrawRenderFrame) { //const int listControlHeight = totalControlAreaHeight - textControlHeight - controlHeight - controlPadding; ResizeRenderFrame(bRedrawRenderFrame); //----------------------------------------------------- DWORD flags = SWP_NOOWNERZORDER|SWP_SHOWWINDOW; int xStart = clientWidth/2 - totalControlAreaWidth/2 + (controlPadding/2 + 1); int yStart = clientHeight - totalControlAreaHeight; int xPos = xStart; int yPos = yStart; //----------------------------------------------------- HWND hwndTemp = GetDlgItem(hwndMain, ID_STATUS); //SetWindowPos(GetDlgItem(hwndMain, ID_STATUS), NULL, xPos, yPos+listControlHeight, totalWidth-controlPadding, statusHeight, 0); SendMessage(hwndTemp, WM_SIZE, SIZE_RESTORED, 0); int parts[5]; parts[4] = -1; parts[3] = clientWidth-100; parts[2] = parts[3]-60; parts[1] = parts[2]-170; parts[0] = parts[1]-170; SendMessage(hwndTemp, SB_SETPARTS, 5, (LPARAM)parts); int resetXPos = xStart+listControlWidth*2; //----------------------------------------------------- UpdateRenderViewMessage(); SetWindowPos(hwndRenderMessage, NULL, 0, renderFrameCtrlHeight / 2 - 10, renderFrameCtrlWidth, 50, flags & ~SWP_SHOWWINDOW); //----------------------------------------------------- // Don't waste time resizing invisible controls if(!bPanelVisibleProcessed) ProcessPanelVisible(true); if(!bPanelVisible) return; xPos = resetXPos; yPos = yStart; SetWindowPos(GetDlgItem(hwndMain, ID_MICVOLUME), NULL, xPos, yPos, controlWidth-controlPadding, volControlHeight, flags); xPos += controlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_DESKTOPVOLUME), NULL, xPos, yPos, controlWidth-controlPadding, volControlHeight, flags); xPos += controlWidth; yPos += volControlHeight+controlPadding; //----------------------------------------------------- xPos = resetXPos; SetWindowPos(GetDlgItem(hwndMain, ID_MICVOLUMEMETER), NULL, xPos, yPos, controlWidth-controlPadding, volMeterHeight, flags); xPos += controlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_DESKTOPVOLUMEMETER), NULL, xPos, yPos, controlWidth-controlPadding, volMeterHeight, flags); xPos += controlWidth; yPos += volMeterHeight+controlPadding; //----------------------------------------------------- xPos = resetXPos; SetWindowPos(GetDlgItem(hwndMain, ID_SETTINGS), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_STARTSTOP), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; yPos += controlHeight+controlPadding; //----------------------------------------------------- xPos = resetXPos; SetWindowPos(GetDlgItem(hwndMain, ID_SCENEEDITOR), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_TOGGLERECORDING), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; yPos += controlHeight+controlPadding; //----------------------------------------------------- xPos = resetXPos; SetWindowPos(GetDlgItem(hwndMain, ID_GLOBALSOURCES), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_TESTSTREAM), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; yPos += controlHeight+controlPadding; //----------------------------------------------------- xPos = resetXPos; SetWindowPos(GetDlgItem(hwndMain, ID_PLUGINS), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; UpdateDashboardButton(); SetWindowPos(GetDlgItem(hwndMain, ID_EXIT), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; yPos += controlHeight; //----------------------------------------------------- int listControlHeight = yPos-yStart-textControlHeight; xPos = xStart; yPos = yStart; SetWindowPos(GetDlgItem(hwndMain, ID_SCENES_TEXT), NULL, xPos+2, yPos, listControlWidth-controlPadding-2, textControlHeight, flags); xPos += listControlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_SOURCES_TEXT), NULL, xPos+2, yPos, listControlWidth-controlPadding-2, textControlHeight, flags); xPos += listControlWidth; yPos += textControlHeight; xPos = xStart; //----------------------------------------------------- SetWindowPos(GetDlgItem(hwndMain, ID_SCENES), NULL, xPos, yPos, listControlWidth-controlPadding, listControlHeight, flags); xPos += listControlWidth; SetWindowPos(GetDlgItem(hwndMain, ID_SOURCES), NULL, xPos, yPos, listControlWidth-controlPadding, listControlHeight, flags); xPos += listControlWidth; } void OBS::GetProfiles(StringList &profileList) { String strProfilesWildcard; OSFindData ofd; HANDLE hFind; profileList.Clear(); String profileDir(FormattedString(L"%s/profiles/", OBSGetAppDataPath())); strProfilesWildcard << profileDir << "*.ini"; if(hFind = OSFindFirstFile(strProfilesWildcard, ofd)) { do { String profile(GetPathWithoutExtension(ofd.fileName)); String profilePath(FormattedString(L"%s%s.ini", profileDir.Array(), profile.Array())); if(ofd.bDirectory || !OSFileExists(profilePath) || profileList.HasValue(profile)) continue; profileList << profile; } while(OSFindNextFile(hFind, ofd)); OSFindClose(hFind); } } void OBS::GetSceneCollection(StringList &sceneCollectionList) { String strSceneCollectionWildcard; OSFindData ofd; HANDLE hFind; sceneCollectionList.Clear(); String sceneCollectionDir(FormattedString(L"%s/sceneCollection/", OBSGetAppDataPath())); strSceneCollectionWildcard << sceneCollectionDir << "*.xconfig"; if (hFind = OSFindFirstFile(strSceneCollectionWildcard, ofd)) { do { String sceneCollection(GetPathWithoutExtension(ofd.fileName)); String sceneCollectionPath(FormattedString(L"%s%s.xconfig", sceneCollectionDir.Array(), sceneCollection.Array())); if (ofd.bDirectory || !OSFileExists(sceneCollectionPath) || sceneCollectionList.HasValue(sceneCollection)) continue; sceneCollectionList << sceneCollection; } while (OSFindNextFile(hFind, ofd)); OSFindClose(hFind); } } void OBS::RefreshStreamButtons(bool disable) { if (bShuttingDown) return; int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2); bRecordingOnly = (networkMode == 1); bool canStream = networkMode == 0 && !bTestStream && bStreamFlushed; canRecord = !bTestStream; bool canTest = !bRecordingReplayBuffer && !bRecording && (!bStreaming || bTestStream); EnableWindow(GetDlgItem(hwndMain, ID_STARTSTOP), !disable && canStream); EnableWindow(GetDlgItem(hwndMain, ID_TOGGLERECORDING), !disable && canRecord); EnableWindow(GetDlgItem(hwndMain, ID_TESTSTREAM), !disable && canTest); HMENU fileMenu = GetSubMenu(GetMenu(hwndMain), 0); EnableMenuItem(fileMenu, 2, (canRecord ? MF_ENABLED : MF_DISABLED) | MF_BYPOSITION); } void OBS::ConfigureStreamButtons() { if (bShuttingDown) return; if (GetWindowThreadProcessId(hwndMain, nullptr) != GetCurrentThreadId()) return PostConfigureStreamButtons(); RefreshStreamButtons(); SetWindowText(GetDlgItem(hwndMain, ID_STARTSTOP), (bStreaming && !bTestStream) ? Str("MainWindow.StopStream") : Str("MainWindow.StartStream")); SetWindowText(GetDlgItem(hwndMain, ID_TOGGLERECORDING), bRecording ? Str("MainWindow.StopRecording") : Str("MainWindow.StartRecording")); SetWindowText(GetDlgItem(hwndMain, ID_TESTSTREAM), bTestStream ? Str("MainWindow.StopTest") : Str("MainWindow.TestStream")); } void OBS::PostConfigureStreamButtons() { if (hwndMain) PostMessage(hwndMain, OBS_CONFIGURE_STREAM_BUTTONS, 0, 0); } void OBS::ReloadSceneCollection() { HWND hwndTemp; hwndTemp = GetDlgItem(hwndMain, ID_SCENES); CTSTR collection = GetCurrentSceneCollection(); String strScenesConfig = FormattedString(L"%s\\sceneCollection\\%s.xconfig", lpAppDataPath, collection); if (!scenesConfig.Open(strScenesConfig)) CrashError(TEXT("Could not open '%s'"), strScenesConfig.Array()); XElement *scenes = scenesConfig.GetElement(TEXT("scenes")); if (!scenes) scenes = scenesConfig.CreateElement(TEXT("scenes")); SendMessage(hwndTemp, LB_RESETCONTENT, 0, 0); App->sceneElement = NULL; UINT numScenes = scenes->NumElements(); if (!numScenes) { XElement *scene = scenes->CreateElement(Str("Scene")); scene->SetString(TEXT("class"), TEXT("Scene")); numScenes++; } for (UINT i = 0; iGetElementByID(i); SendMessage(hwndTemp, LB_ADDSTRING, 0, (LPARAM)scene->GetName()); } //----------------------------------------------------- // populate sources if (numScenes) { String strScene = AppConfig->GetString(TEXT("General"), TEXT("CurrentScene")); int id = (int)SendMessage(hwndTemp, LB_FINDSTRINGEXACT, -1, (LPARAM)strScene.Array()); if (id == LB_ERR) id = 0; SendMessage(hwndTemp, LB_SETCURSEL, (WPARAM)id, 0); SendMessage(hwndMain, WM_COMMAND, MAKEWPARAM(ID_SCENES, LBN_SELCHANGE), (LPARAM)GetDlgItem(hwndMain, ID_SCENES)); } //----------------------------------------------------- for (UINT i = 0; iGetElementByID(i); DWORD hotkey = scene->GetInt(TEXT("hotkey")); if (hotkey) { SceneHotkeyInfo hotkeyInfo; hotkeyInfo.hotkey = hotkey; hotkeyInfo.scene = scene; hotkeyInfo.hotkeyID = API->CreateHotkey(hotkey, SceneHotkey, 0); if (hotkeyInfo.hotkeyID) sceneHotkeys << hotkeyInfo; } } } void OBS::ReloadIniSettings() { HWND hwndTemp; //------------------------------------------- // mic volume data hwndTemp = GetDlgItem(hwndMain, ID_MICVOLUME); if(!AppConfig->HasKey(TEXT("Audio"), TEXT("MicVolume"))) AppConfig->SetFloat(TEXT("Audio"), TEXT("MicVolume"), 1.0f); SetVolumeControlValue(hwndTemp, AppConfig->GetFloat(TEXT("Audio"), TEXT("MicVolume"), 1.0f)); SetVolumeControlMutedVal(hwndTemp, AppConfig->GetFloat(TEXT("Audio"), TEXT("MicMutedVolume"), 1.0f)); AudioDeviceList audioDevices; GetAudioDevices(audioDevices, ADT_RECORDING, false, true); String strDevice = AppConfig->GetString(TEXT("Audio"), TEXT("Device"), NULL); if(strDevice.IsEmpty() || !audioDevices.HasID(strDevice)) { AppConfig->SetString(TEXT("Audio"), TEXT("Device"), TEXT("Disable")); strDevice = TEXT("Disable"); } audioDevices.FreeData(); EnableWindow(hwndTemp, !strDevice.CompareI(TEXT("Disable"))); //------------------------------------------- // desktop volume hwndTemp = GetDlgItem(hwndMain, ID_DESKTOPVOLUME); if(!AppConfig->HasKey(TEXT("Audio"), TEXT("DesktopVolume"))) AppConfig->SetFloat(TEXT("Audio"), TEXT("DesktopVolume"), 1.0f); SetVolumeControlValue(hwndTemp, AppConfig->GetFloat(TEXT("Audio"), TEXT("DesktopVolume"), 0.0f)); SetVolumeControlMutedVal(hwndTemp, AppConfig->GetFloat(TEXT("Audio"), TEXT("DesktopMutedVolume"), 1.0f)); //------------------------------------------- // desktop boost DWORD desktopBoostMultiple = GlobalConfig->GetInt(TEXT("Audio"), TEXT("DesktopBoostMultiple"), 1); if(desktopBoostMultiple < 1) desktopBoostMultiple = 1; else if(desktopBoostMultiple > 20) desktopBoostMultiple = 20; desktopBoost = float(desktopBoostMultiple); //------------------------------------------- // mic boost DWORD micBoostMultiple = AppConfig->GetInt(TEXT("Audio"), TEXT("MicBoostMultiple"), 1); if(micBoostMultiple < 1) micBoostMultiple = 1; else if(micBoostMultiple > 20) micBoostMultiple = 20; micBoost = float(micBoostMultiple); //------------------------------------------- // dashboard /* strDashboard = AppConfig->GetString(TEXT("Publish"), TEXT("Dashboard")); strDashboard.KillSpaces(); UpdateDashboardButton(); */ //------------------------------------------- // hotkeys QuickClearHotkey(pushToTalkHotkeyID); QuickClearHotkey(pushToTalkHotkey2ID); QuickClearHotkey(muteMicHotkeyID); QuickClearHotkey(muteDesktopHotkeyID); QuickClearHotkey(stopStreamHotkeyID); QuickClearHotkey(startStreamHotkeyID); QuickClearHotkey(stopRecordingHotkeyID); QuickClearHotkey(startRecordingHotkeyID); QuickClearHotkey(stopReplayBufferHotkeyID); QuickClearHotkey(startReplayBufferHotkeyID); QuickClearHotkey(saveReplayBufferHotkeyID); QuickClearHotkey(recordFromReplayBufferHotkeyID); bUsingPushToTalk = !!AppConfig->GetInt(L"Audio", L"UsePushToTalk") != 0; DWORD hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("PushToTalkHotkey")); DWORD hotkey2 = AppConfig->GetInt(TEXT("Audio"), TEXT("PushToTalkHotkey2")); pushToTalkDelay = AppConfig->GetInt(TEXT("Audio"), TEXT("PushToTalkDelay"), 200); if(bUsingPushToTalk && hotkey) pushToTalkHotkeyID = API->CreateHotkey(hotkey, OBS::PushToTalkHotkey, NULL); if(bUsingPushToTalk && hotkey2) pushToTalkHotkey2ID = API->CreateHotkey(hotkey2, OBS::PushToTalkHotkey, NULL); hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("MuteMicHotkey")); if(hotkey) muteMicHotkeyID = API->CreateHotkey(hotkey, OBS::MuteMicHotkey, NULL); hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("MuteDesktopHotkey")); if(hotkey) muteDesktopHotkeyID = API->CreateHotkey(hotkey, OBS::MuteDesktopHotkey, NULL); hotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StopStreamHotkey")); if (hotkey) stopStreamHotkeyID = API->CreateHotkey(hotkey, OBS::StopStreamHotkey, NULL); hotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StartStreamHotkey")); if (hotkey) startStreamHotkeyID = API->CreateHotkey(hotkey, OBS::StartStreamHotkey, NULL); hotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StopRecordingHotkey")); if (hotkey) stopRecordingHotkeyID = API->CreateHotkey(hotkey, OBS::StopRecordingHotkey, NULL); hotkey = AppConfig->GetInt(TEXT("Publish"), TEXT("StartRecordingHotkey")); if (hotkey) startRecordingHotkeyID = API->CreateHotkey(hotkey, OBS::StartRecordingHotkey, NULL); hotkey = AppConfig->GetInt(L"Publish", L"StopReplayBufferHotkey"); if (hotkey) stopReplayBufferHotkeyID = API->CreateHotkey(hotkey, OBS::StopReplayBufferHotkey, NULL); hotkey = AppConfig->GetInt(L"Publish", L"StartReplayBufferHotkey"); if (hotkey) startReplayBufferHotkeyID = API->CreateHotkey(hotkey, OBS::StartReplayBufferHotkey, NULL); hotkey = AppConfig->GetInt(L"Publish", L"SaveReplayBufferHotkey"); if (hotkey) saveReplayBufferHotkeyID = API->CreateHotkey(hotkey, OBS::SaveReplayBufferHotkey, NULL); hotkey = AppConfig->GetInt(L"Publish", L"RecordFromReplayBufferHotkey"); if (hotkey) recordFromReplayBufferHotkeyID = API->CreateHotkey(hotkey, OBS::RecordFromReplayBufferHotkey, NULL); //------------------------------------------- // Notification Area icon bool showIcon = AppConfig->GetInt(TEXT("General"), TEXT("ShowNotificationAreaIcon"), 0) != 0; bool minimizeToIcon = AppConfig->GetInt(TEXT("General"), TEXT("MinimizeToNotificationArea"), 0) != 0; if (showIcon) { ShowNotificationAreaIcon(); if (minimizeToIcon && IsIconic(hwndMain)) ShowWindow(hwndMain, SW_HIDE); } else HideNotificationAreaIcon(); bKeepRecording = AppConfig->GetInt(TEXT("Publish"), TEXT("KeepRecording")) != 0; if (!minimizeToIcon && !IsWindowVisible(hwndMain)) ShowWindow(hwndMain, SW_SHOW); ConfigureStreamButtons(); //-------------------------------------------- // Update old config, transition old encoder selection int qsv = AppConfig->GetInt(L"Video Encoding", L"UseQSV", -1); int nvenc = AppConfig->GetInt(L"Video Encoding", L"UseNVENC", -1); if (qsv != -1 || nvenc != -1) { AppConfig->SetString(L"Video Encoding", L"Encoder", (qsv > 0) ? L"QSV" : (nvenc > 0) ? L"NVENC" : L"x264"); AppConfig->Remove(L"Video Encoding", L"UseQSV"); AppConfig->Remove(L"Video Encoding", L"UseNVENC"); int custom = AppConfig->GetInt(L"Video Encoding", L"UseCustomSettings", -1); int customQSV = AppConfig->GetInt(L"Video Encoding", L"QSVUseVideoEncoderSettings", -1); if (custom > 0 && customQSV > 0) AppConfig->SetString(L"Video Encoding", L"CustomQSVSettings", AppConfig->GetString(L"Video Encoding", L"CustomSettings")); } } void OBS::UpdateAudioMeters() { SetVolumeMeterValue(GetDlgItem(hwndMain, ID_DESKTOPVOLUMEMETER), desktopMag, desktopMax, desktopPeak); SetVolumeMeterValue(GetDlgItem(hwndMain, ID_MICVOLUMEMETER), micMag, micMax, micPeak); } HICON OBS::GetIcon(HINSTANCE hInst, int resource) { for(UINT i=0; ibytesPerSec, App->curStrain, (UINT)this->totalStreamTime, (UINT)network->NumTotalVideoFrames(), (UINT)App->curFramesDropped, (UINT) App->captureFPS); } ReportOBSStatus(bRunning, bStreaming, bRecording, bTestStream, bReconnecting); OSLeaveMutex(hStartupShutdownMutex); } } void OBS::DrawStatusBar(DRAWITEMSTRUCT &dis) { if(!App->bRunning && !App->bStreaming && !App->bRecording) return; HDC hdcTemp = CreateCompatibleDC(dis.hDC); HBITMAP hbmpTemp = CreateCompatibleBitmap(dis.hDC, dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top); SelectObject(hdcTemp, hbmpTemp); SelectObject(hdcTemp, GetCurrentObject(dis.hDC, OBJ_FONT)); SetTextColor(hdcTemp, GetTextColor(dis.hDC)); //HBRUSH hColorBrush = CreateSolidBrush((green<<8)|red); RECT rc; mcpy(&rc, &dis.rcItem, sizeof(rc)); rc.left -= dis.rcItem.left; rc.right -= dis.rcItem.left; rc.top -= dis.rcItem.top; rc.bottom -= dis.rcItem.top; FillRect(hdcTemp, &rc, (HBRUSH)(COLOR_BTNFACE+1)); //DeleteObject(hColorBrush); //-------------------------------- if(dis.itemID == 4) { HBRUSH hColorBrush; DWORD green = 0xFF, red; statusBarData.bytesPerSec = App->bytesPerSec; statusBarData.strain = App->curStrain; //statusBarData.strain = rand()%101; //show grey rather than green when not connected if (App->network && App->network->NumTotalVideoFrames() == 0) { hColorBrush = CreateSolidBrush(RGB(100,100,100)); } else { if(statusBarData.strain > 50.0) green = DWORD(((50.0-(statusBarData.strain-50.0))/50.0)*255.0); double redStrain = statusBarData.strain/50.0; if(redStrain > 1.0) redStrain = 1.0; red = DWORD(redStrain*255.0); //-------------------------------- hColorBrush = CreateSolidBrush((green<<8)|red); } RECT rcBox = {0, 0, 20, 20}; /*rc.left += dis.rcItem.left; rc.right += dis.rcItem.left; rc.top += dis.rcItem.top; rc.bottom += dis.rcItem.top;*/ FillRect(hdcTemp, &rcBox, hColorBrush); DeleteObject(hColorBrush); //-------------------------------- SetBkMode(hdcTemp, TRANSPARENT); rc.left += 22; String strKBPS; strKBPS << IntString((statusBarData.bytesPerSec*8) / 1000) << TEXT("kb/s"); //strKBPS << IntString(rand()) << TEXT("kb/s"); DrawText(hdcTemp, strKBPS, strKBPS.Length(), &rc, DT_VCENTER|DT_SINGLELINE|DT_LEFT); } else { String strOutString; DWORD color = 0x000000; switch(dis.itemID) { case 0: { StreamInfoPriority priority; strOutString << App->GetMostImportantInfo(priority); if (priority == StreamInfoPriority_Critical) color = 0x0000FF; break; } case 1: { DWORD streamTimeSecondsTotal = App->totalStreamTime/1000; DWORD streamTimeMinutesTotal = streamTimeSecondsTotal/60; DWORD streamTimeSeconds = streamTimeSecondsTotal%60; DWORD streamTimeHours = streamTimeMinutesTotal/60; DWORD streamTimeMinutes = streamTimeMinutesTotal%60; int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2); strOutString = FormattedString(TEXT("%u:%02u:%02u"), streamTimeHours, streamTimeMinutes, streamTimeSeconds); StringList mods; if (App->bStreaming && !App->bTestStream) mods << L"LIVE"; if (App->bRecording) mods << L"REC"; if (App->bRecordingReplayBuffer) mods << L"BUF"; if (App->bTestStream) mods << L"Preview"; strOutString << FormattedString(L" (%s)", mods.Join(L" + ").Array()); } break; case 2: { double percentageDropped = 0.0; if (OSTryEnterMutex(App->hStartupShutdownMutex)) { if(App->network) { UINT numTotalFrames = App->network->NumTotalVideoFrames(); if(numTotalFrames) percentageDropped = (double(App->network->NumDroppedFrames())/double(numTotalFrames))*100.0; } OSLeaveMutex(App->hStartupShutdownMutex); } strOutString << Str("MainWindow.DroppedFrames") << FormattedString(TEXT(" %u (%0.2f%%)"), App->curFramesDropped, percentageDropped); } break; case 3: strOutString << TEXT("FPS: ") << IntString(App->captureFPS); break; } if(strOutString.IsValid()) { SetTextColor(hdcTemp, color); SetBkMode(hdcTemp, TRANSPARENT); DrawText(hdcTemp, strOutString, strOutString.Length(), &rc, DT_VCENTER|DT_SINGLELINE|DT_LEFT); } } //-------------------------------- BitBlt(dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top, hdcTemp, 0, 0, SRCCOPY); DeleteObject(hdcTemp); DeleteObject(hbmpTemp); } void OBS::SelectSources() { if(scene) scene->DeselectAll(); HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); UINT numSelected = ListView_GetSelectedCount(hwndSources); if(numSelected) { List selectedItems; selectedItems.SetSize(numSelected); //SendMessage(hwndSources, LB_GETSELITEMS, numSelected, (LPARAM)selectedItems.Array()); if(scene) { int iPos = ListView_GetNextItem(hwndSources, -1, LVNI_SELECTED); while (iPos != -1) { SceneItem *sceneItem = scene->GetSceneItem(iPos); sceneItem->bSelected = true; iPos = ListView_GetNextItem(hwndSources, iPos, LVNI_SELECTED); } } } } void OBS::CheckSources() { XElement *curSceneElement = App->sceneElement; XElement *sources = curSceneElement->GetElement(TEXT("sources")); if(!sources) return; HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); UINT numSources = ListView_GetItemCount(hwndSources); for(UINT i = 0; i < numSources; i++) { bool checked = ListView_GetCheckState(hwndSources, i) > 0; XElement *source =sources->GetElementByID(i); bool curRender = source->GetInt(TEXT("render"), 0) > 0; if(curRender != checked) { source->SetInt(TEXT("render"), (checked)?1:0); if(scene && i < scene->NumSceneItems()) { SceneItem *sceneItem = scene->GetSceneItem(i); sceneItem->bRender = checked; sceneItem->SetRender(checked); } ReportSourceChanged(source->GetName(), source); } } } void OBS::SetSourceRender(CTSTR sourceName, bool render) { XElement *curSceneElement = App->sceneElement; XElement *sources = curSceneElement->GetElement(TEXT("sources")); if(!sources) return; HWND hwndSources = GetDlgItem(hwndMain, ID_SOURCES); UINT numSources = ListView_GetItemCount(hwndSources); for(UINT i = 0; i < numSources; i++) { bool checked = ListView_GetCheckState(hwndSources, i) > 0; XElement *source =sources->GetElementByID(i); if(scmp(source->GetName(), sourceName) == 0 && checked != render) { if(scene && i < scene->NumSceneItems()) { SceneItem *sceneItem = scene->GetSceneItem(i); sceneItem->SetRender(render); } else { source->SetInt(TEXT("render"), (render)?1:0); } App->bChangingSources = true; ListView_SetCheckState(hwndSources, i, render); App->bChangingSources = false; ReportSourceChanged(sourceName, source); break; } } } BOOL OBS::SetNotificationAreaIcon(DWORD dwMessage, int idIcon, const String &tooltip) { NOTIFYICONDATA niData; BOOL result; ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); niData.cbSize = sizeof(niData); niData.hWnd = hwndMain; niData.uID = 0; if (NIM_DELETE != dwMessage) { niData.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP; niData.uCallbackMessage = OBS_NOTIFICATIONAREA; niData.hIcon = LoadIcon(hinstMain, MAKEINTRESOURCE(idIcon)); scpy_n (niData.szTip, tooltip, _countof(niData.szTip)-1); } result = Shell_NotifyIcon(dwMessage, &niData); if(niData.hIcon) DestroyIcon(niData.hIcon); return result; } BOOL OBS::ShowNotificationAreaIcon() { BOOL result = FALSE; int idIcon = (bRunning && !bTestStream) ? IDI_ICON2 : IDI_ICON1; if (!bNotificationAreaIcon) { bNotificationAreaIcon = true; result = SetNotificationAreaIcon(NIM_ADD, idIcon, GetApplicationName()); } else { result = SetNotificationAreaIcon(NIM_MODIFY, idIcon, GetApplicationName()); } return result; } BOOL OBS::UpdateNotificationAreaIcon() { if (bNotificationAreaIcon) return ShowNotificationAreaIcon(); return TRUE; } BOOL OBS::HideNotificationAreaIcon() { bNotificationAreaIcon = false; return SetNotificationAreaIcon(NIM_DELETE, 0, TEXT("")); } BOOL OBS::UpdateDashboardButton() { //Are we in live stream mode, and do we have a non-empty dashboard string? /* BOOL bStreamOutput = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode")) == 0; BOOL bDashboardValid = strDashboard.IsValid(); BOOL bShowDashboardButton = bPanelVisible && bStreamOutput && bDashboardValid; return ShowWindow(GetDlgItem(hwndMain, ID_DASHBOARD), bShowDashboardButton ? SW_SHOW : SW_HIDE); */ return false; } void OBS::ActuallyEnableProjector() { DisableProjector(); D3D10System *sys = static_cast(GS); DXGI_SWAP_CHAIN_DESC swapDesc; zero(&swapDesc, sizeof(swapDesc)); swapDesc.BufferCount = 2; swapDesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; swapDesc.BufferDesc.Width = projectorWidth; swapDesc.BufferDesc.Height = projectorHeight; swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swapDesc.OutputWindow = hwndProjector; swapDesc.SampleDesc.Count = 1; swapDesc.Windowed = TRUE; if (!bShutdownEncodeThread) SetWindowPos(hwndProjector, NULL, projectorX, projectorY, projectorWidth, projectorHeight, SWP_SHOWWINDOW); ID3D10Texture2D *backBuffer = NULL; ID3D10RenderTargetView *target = NULL; if (FAILED(sys->factory->CreateSwapChain(sys->d3d, &swapDesc, &projectorSwap))) { AppWarning(L"Could not create projector swap chain"); goto exit; } if (FAILED(projectorSwap->GetBuffer(0, IID_ID3D10Texture2D, (void**)&backBuffer))) { AppWarning(TEXT("Unable to get projector back buffer")); goto exit; } if(FAILED(sys->d3d->CreateRenderTargetView(backBuffer, NULL, &target))) { AppWarning(TEXT("Unable to get render view from projector back buffer")); goto exit; } D3D10Texture *tex = new D3D10Texture(); tex->width = projectorWidth; tex->height = projectorHeight; tex->format = GS_BGRA; tex->texture = backBuffer; tex->renderTarget = target; projectorTexture = tex; bProjector = true; exit: if (!bProjector) { SafeRelease(projectorSwap); SafeRelease(backBuffer); } bPleaseEnableProjector = false; } void OBS::EnableProjector(UINT monitorID) { const MonitorInfo &mi = GetMonitor(monitorID); projectorMonitorID = monitorID; projectorWidth = mi.rect.right-mi.rect.left; projectorHeight = mi.rect.bottom-mi.rect.top; projectorX = mi.rect.left; projectorY = mi.rect.top; bPleaseEnableProjector = true; } void OBS::DisableProjector() { SafeRelease(projectorSwap); delete projectorTexture; projectorTexture = NULL; bProjector = false; bPleaseDisableProjector = false; if (!bShutdownEncodeThread) ShowWindow(hwndProjector, SW_HIDE); } void OBS::GetThreadHandles (HANDLE *videoThread, HANDLE *encodeThread) { if (hVideoThread) *videoThread = hVideoThread; if (hEncodeThread) *encodeThread = hEncodeThread; } NetworkStream* CreateRTMPPublisher(); NetworkStream* CreateDelayedPublisher(DWORD delayTime); NetworkStream* CreateNullNetwork(); void OBS::RestartNetwork() { OSEnterMutex(App->hStartupShutdownMutex); //delete the old one App->network.reset(); //start up a new one App->bSentHeaders = false; App->network.reset(CreateRTMPPublisher()); OSLeaveMutex(App->hStartupShutdownMutex); }