/******************************************************************************** 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 //primarily main window stuff an initialization/destruction code typedef bool (*LOADPLUGINPROC)(); typedef void (*UNLOADPLUGINPROC)(); typedef CTSTR (*GETPLUGINNAMEPROC)(); ImageSource* STDCALL CreateDesktopSource(XElement *data); bool STDCALL ConfigureDesktopSource(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; 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; } //--------------------------------------------------------------------------- OBS::OBS() { App = this; __cpuid(cpuInfo, 1); bSSE2Available = (cpuInfo[3] & (1<<26)) != 0; hSceneMutex = OSCreateMutex(); hAuxAudioMutex = OSCreateMutex(); 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(); InitVolumeMeter(); //----------------------------------------------------- // load locale if(!locale->LoadStringFile(TEXT("locale/en.txt"))) AppWarning(TEXT("Could not open locale string file '%s'"), TEXT("locale/en.txt")); strLanguage = GlobalConfig->GetString(TEXT("General"), TEXT("Language"), TEXT("en")); if(!strLanguage.CompareI(TEXT("en"))) { String langFile; langFile << TEXT("locale/") << strLanguage << TEXT(".txt"); if(!locale->LoadStringFile(langFile)) AppWarning(TEXT("Could not open locale string file '%s'"), langFile.Array()); } //----------------------------------------------------- // load classes RegisterSceneClass(TEXT("Scene"), Str("Scene"), (OBSCREATEPROC)CreateNormalScene, NULL); RegisterImageSourceClass(TEXT("DesktopImageSource"), Str("Sources.SoftwareCaptureSource"), (OBSCREATEPROC)CreateDesktopSource, (OBSCONFIGPROC)ConfigureDesktopSource); RegisterImageSourceClass(TEXT("BitmapImageSource"), Str("Sources.BitmapSource"), (OBSCREATEPROC)CreateBitmapSource, (OBSCONFIGPROC)ConfigureBitmapSource); RegisterImageSourceClass(TEXT("BitmapTransitionSource"), Str("Sources.TransitionSource"), (OBSCREATEPROC)CreateBitmapTransitionSource, (OBSCONFIGPROC)ConfigureBitmapTransitionSource); RegisterImageSourceClass(TEXT("GlobalSource"), Str("Sources.GlobalSource"), (OBSCREATEPROC)CreateGlobalSource, (OBSCONFIGPROC)OBS::ConfigGlobalSource); RegisterImageSourceClass(TEXT("TextSource"), Str("Sources.TextSource"), (OBSCREATEPROC)CreateTextSource, (OBSCONFIGPROC)ConfigureTextSource); //----------------------------------------------------- // 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 = (HBRUSH)GetStockObject(GRAY_BRUSH); if(!RegisterClass(&wc)) CrashError(TEXT("Could not register render frame 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"), 700); clientHeight = GlobalConfig->GetInt(TEXT("General"), TEXT("Height"), 553); 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")); int posY = GlobalConfig->GetInt(TEXT("General"), TEXT("PosY")); bool bInsideMonitors = false; if(posX || posY) { 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; } hwndMain = CreateWindowEx(WS_EX_CONTROLPARENT|WS_EX_WINDOWEDGE, OBS_WINDOW_CLASS, OBS_VERSION_STRING, WS_OVERLAPPED | WS_THICKFRAME | 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")); HMENU hMenu = GetMenu(hwndMain); LocalizeMenu(hMenu); //----------------------------------------------------- // render frame hwndRenderFrame = CreateWindow(OBS_RENDERFRAME_CLASS, NULL, WS_CHILDWINDOW|WS_VISIBLE, 0, 0, 0, 0, hwndMain, NULL, hinstMain, NULL); if(!hwndRenderFrame) CrashError(TEXT("Could not create render frame")); //----------------------------------------------------- // 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)); //----------------------------------------------------- // 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); //----------------------------------------------------- // populate scenes hwndTemp = GetDlgItem(hwndMain, ID_SCENES); String strScenesConfig; strScenesConfig << lpAppDataPath << TEXT("\\scenes.xconfig"); if(!scenesConfig.Open(strScenesConfig)) CrashError(TEXT("Could not open '%s'"), strScenesConfig); 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, 0); 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(); ResizeWindow(false); ShowWindow(hwndMain, SW_SHOW); // 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); //----------------------------------------------------- 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; } } //----------------------------------------------------- // 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) { LOADPLUGINPROC loadPlugin = (LOADPLUGINPROC)GetProcAddress(hPlugin, "LoadPlugin"); if(loadPlugin && loadPlugin()) { PluginInfo *pluginInfo = plugins.CreateNew(); pluginInfo->hModule = hPlugin; pluginInfo->strFile = ofd.fileName; GETPLUGINNAMEPROC getName = (GETPLUGINNAMEPROC)GetProcAddress(hPlugin, "GetPluginName"); CTSTR lpName; if(getName) lpName = getName(); else lpName = TEXT(""); //FIXME: 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); FreeLibrary(hPlugin); } } else { Log(TEXT("Failed to load plugin %s, %d"), strLocation, GetLastError()); } } } while (OSFindNextFile(hFind, ofd)); OSFindClose(hFind); } //----------------------------------------------------- ReloadIniSettings(); ResetProfileMenu(); //----------------------------------------------------- bAutoReconnect = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnect"), 1) != 0; reconnectTimeout = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnectTimeout"), 10); if(reconnectTimeout < 5) reconnectTimeout = 5; hHotkeyThread = OSCreateThread((XTHREAD)HotkeyThread, NULL); #ifndef OBS_DISABLE_AUTOUPDATE OSCloseThread(OSCreateThread((XTHREAD)CheckUpdateThread, NULL)); #endif bRenderViewEnabled = true; if(GlobalConfig->GetInt(TEXT("General"), TEXT("ShowWebrootWarning"), TRUE) && IsWebrootLoaded()) MessageBox(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); } OBS::~OBS() { Stop(); bShuttingDown = true; OSTerminateThread(hHotkeyThread, 250); for(UINT i=0; iSetInt(TEXT("General"), TEXT("PosX"), rcWindow.left); GlobalConfig->SetInt(TEXT("General"), TEXT("PosY"), rcWindow.top); GlobalConfig->SetInt(TEXT("General"), TEXT("Width"), clientWidth); GlobalConfig->SetInt(TEXT("General"), TEXT("Height"), clientHeight); 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; int curCX = AppConfig->GetInt(TEXT("Video"), TEXT("BaseWidth"), defCX); int curCY = AppConfig->GetInt(TEXT("Video"), TEXT("BaseHeight"), defCY); mainAspect = float(curCX)/float(curCY); } if(renderAspect > mainAspect) { renderSize.x = renderSize.y*mainAspect; x += int((float(newRenderFrameWidth)-renderSize.x)*0.5f); } else { renderSize.y = renderSize.x/mainAspect; y += int((float(newRenderFrameHeight)-renderSize.y)*0.5f); } newRenderFrameWidth = int(renderSize.x+0.5f)&0xFFFFFFFE; newRenderFrameHeight = int(renderSize.y+0.5f)&0xFFFFFFFE; SetWindowPos(hwndRenderFrame, NULL, x, y, newRenderFrameWidth, newRenderFrameHeight, SWP_NOOWNERZORDER); //---------------------------------------------- if(bRunning) { if(bRedrawRenderFrame) { renderFrameWidth = newRenderFrameWidth; renderFrameHeight = newRenderFrameHeight; bResizeRenderView = true; } } else { renderFrameWidth = newRenderFrameWidth; renderFrameHeight = newRenderFrameHeight; } } 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]-80; SendMessage(hwndTemp, SB_SETPARTS, 5, (LPARAM)parts); int resetXPos = xStart+listControlWidth*2; //----------------------------------------------------- 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_TESTSTREAM), 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_PLUGINS), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; yPos += controlHeight+controlPadding; //----------------------------------------------------- xPos = resetXPos; BOOL bStreamOutput = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode")) == 0; strDashboard = AppConfig->GetString(TEXT("Publish"), TEXT("Dashboard")); BOOL bShowDashboardButton = strDashboard.IsValid() && bStreamOutput; SetWindowPos(GetDlgItem(hwndMain, ID_DASHBOARD), NULL, xPos, yPos, controlWidth-controlPadding, controlHeight, flags); xPos += controlWidth; ShowWindow(GetDlgItem(hwndMain, ID_DASHBOARD), bShowDashboardButton ? SW_SHOW : SW_HIDE); 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(); strProfilesWildcard << lpAppDataPath << TEXT("\\profiles\\*.ini"); if(hFind = OSFindFirstFile(strProfilesWildcard, ofd)) { do { if(ofd.bDirectory) continue; profileList << GetPathWithoutExtension(ofd.fileName); } while(OSFindNextFile(hFind, ofd)); OSFindClose(hFind); } } 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"), 0.0f); SetVolumeControlValue(hwndTemp, AppConfig->GetFloat(TEXT("Audio"), TEXT("MicVolume"), 0.0f)); AudioDeviceList audioDevices; GetAudioDevices(audioDevices, ADT_RECORDING); 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)); //------------------------------------------- // mic boost DWORD micBoostPercentage = AppConfig->GetInt(TEXT("Audio"), TEXT("MicBoostMultiple"), 1); if(micBoostPercentage < 1) micBoostPercentage = 1; else if(micBoostPercentage > 20) micBoostPercentage = 20; micBoost = float(micBoostPercentage); //------------------------------------------- // dashboard strDashboard = AppConfig->GetString(TEXT("Publish"), TEXT("Dashboard")); strDashboard.KillSpaces(); //------------------------------------------- // hotkeys QuickClearHotkey(pushToTalkHotkeyID); QuickClearHotkey(muteMicHotkeyID); QuickClearHotkey(muteDesktopHotkeyID); QuickClearHotkey(stopStreamHotkeyID); QuickClearHotkey(startStreamHotkeyID); bUsingPushToTalk = AppConfig->GetInt(TEXT("Audio"), TEXT("UsePushToTalk")) != 0; DWORD hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("PushToTalkHotkey")); pushToTalkDelay = AppConfig->GetInt(TEXT("Audio"), TEXT("PushToTalkDelay"), 200); if(bUsingPushToTalk && hotkey) pushToTalkHotkeyID = API->CreateHotkey(hotkey, 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); } 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; inetwork) return; HWND hwndStatusBar = GetDlgItem(hwndMain, ID_STATUS); SendMessage(hwndStatusBar, WM_SETREDRAW, 0, 0); SendMessage(hwndStatusBar, SB_SETTEXT, 0 | SBT_OWNERDRAW, NULL); SendMessage(hwndStatusBar, SB_SETTEXT, 1 | SBT_OWNERDRAW, NULL); SendMessage(hwndStatusBar, SB_SETTEXT, 2 | SBT_OWNERDRAW, NULL); SendMessage(hwndStatusBar, SB_SETTEXT, 3 | SBT_OWNERDRAW, NULL); SendMessage(hwndStatusBar, SB_SETTEXT, 4 | SBT_OWNERDRAW, NULL); SendMessage(hwndStatusBar, WM_SETREDRAW, 1, 0); InvalidateRect(hwndStatusBar, NULL, FALSE); ReportStreamStatus(bRunning, bTestStream, (UINT) statusBarData.bytesPerSec, statusBarData.strain, (UINT)this->totalStreamTime, (UINT)App->network->NumTotalVideoFrames(), (UINT)App->curFramesDropped, (UINT) App->captureFPS); OSLeaveMutex(hStartupShutdownMutex); } } void OBS::DrawStatusBar(DRAWITEMSTRUCT &dis) { if(!App->bRunning) 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)); //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) { DWORD green = 0xFF, red = 0xFF; statusBarData.bytesPerSec = App->bytesPerSec; statusBarData.strain = App->curStrain; //statusBarData.strain = rand()%101; 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); //-------------------------------- HBRUSH 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; switch(dis.itemID) { case 0: strOutString << App->GetMostImportantInfo(); break; case 1: { DWORD streamTimeSecondsTotal = App->totalStreamTime/1000; DWORD streamTimeMinutesTotal = streamTimeSecondsTotal/60; DWORD streamTimeSeconds = streamTimeSecondsTotal%60; DWORD streamTimeHours = streamTimeMinutesTotal/60; DWORD streamTimeMinutes = streamTimeMinutesTotal%60; strOutString = FormattedString(TEXT("%u:%02u:%02u"), streamTimeHours, streamTimeMinutes, streamTimeSeconds); } 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()) { 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; } } }