1295 lines
43 KiB
C++
1295 lines
43 KiB
C++
/********************************************************************************
|
|
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
|
|
|
|
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 <intrin.h>
|
|
|
|
|
|
//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<MonitorInfo> &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.Num(); i++)
|
|
{
|
|
if( posX >= 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; i<numScenes; i++)
|
|
{
|
|
XElement *scene = scenes->GetElementByID(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; i<numScenes; i++)
|
|
{
|
|
XElement *scene = scenes->GetElementByID(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("<unknown>");
|
|
|
|
//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; i<plugins.Num(); i++)
|
|
{
|
|
PluginInfo &pluginInfo = plugins[i];
|
|
|
|
UNLOADPLUGINPROC unloadPlugin = (UNLOADPLUGINPROC)GetProcAddress(pluginInfo.hModule, "UnloadPlugin");
|
|
if(unloadPlugin)
|
|
unloadPlugin();
|
|
|
|
FreeLibrary(pluginInfo.hModule);
|
|
pluginInfo.strFile.Clear();
|
|
}
|
|
|
|
//DestroyWindow(hwndMain);
|
|
|
|
RECT rcWindow;
|
|
GetWindowRect(hwndMain, &rcWindow);
|
|
|
|
GlobalConfig->SetInt(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; i<Icons.Num(); i++)
|
|
DeleteObject(Icons[i].hIcon);
|
|
Icons.Clear();
|
|
|
|
for(UINT i=0; i<Fonts.Num(); i++)
|
|
{
|
|
DeleteObject(Fonts[i].hFont);
|
|
Fonts[i].strFontFace.Clear();
|
|
}
|
|
Fonts.Clear();
|
|
|
|
for(UINT i=0; i<sceneClasses.Num(); i++)
|
|
sceneClasses[i].FreeData();
|
|
for(UINT i=0; i<imageSourceClasses.Num(); i++)
|
|
imageSourceClasses[i].FreeData();
|
|
|
|
if(hSceneMutex)
|
|
OSCloseMutex(hSceneMutex);
|
|
|
|
if(hAuxAudioMutex)
|
|
OSCloseMutex(hAuxAudioMutex);
|
|
|
|
delete API;
|
|
API = NULL;
|
|
|
|
if(hInfoMutex)
|
|
OSCloseMutex(hInfoMutex);
|
|
if(hHotkeyMutex)
|
|
OSCloseMutex(hHotkeyMutex);
|
|
}
|
|
|
|
void OBS::ResizeRenderFrame(bool bRedrawRenderFrame)
|
|
{
|
|
int x = controlPadding, y = controlPadding;
|
|
|
|
UINT newRenderFrameWidth = clientWidth - (controlPadding*2);
|
|
UINT newRenderFrameHeight = clientHeight - (controlPadding*2) - totalControlAreaHeight;
|
|
|
|
Vect2 renderSize = Vect2(float(newRenderFrameWidth), float(newRenderFrameHeight));
|
|
|
|
float renderAspect = renderSize.x/renderSize.y;
|
|
float mainAspect;
|
|
|
|
if(bRunning)
|
|
mainAspect = float(baseCX)/float(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;
|
|
|
|
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; i<Icons.Num(); i++)
|
|
{
|
|
if(Icons[i].resource == resource && Icons[i].hInst == hInst)
|
|
return Icons[i].hIcon;
|
|
}
|
|
|
|
//---------------------
|
|
|
|
IconInfo ii;
|
|
ii.hInst = hInst;
|
|
ii.resource = resource;
|
|
ii.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(resource));
|
|
|
|
Icons << ii;
|
|
|
|
return ii.hIcon;
|
|
}
|
|
|
|
HFONT OBS::GetFont(CTSTR lpFontFace, int fontSize, int fontWeight)
|
|
{
|
|
for(UINT i=0; i<Fonts.Num(); i++)
|
|
{
|
|
if(Fonts[i].strFontFace.CompareI(lpFontFace) && Fonts[i].fontSize == fontSize && Fonts[i].fontWeight == fontWeight)
|
|
return Fonts[i].hFont;
|
|
}
|
|
|
|
//---------------------
|
|
|
|
HFONT hFont = NULL;
|
|
|
|
LOGFONT lf;
|
|
zero(&lf, sizeof(lf));
|
|
scpy_n(lf.lfFaceName, lpFontFace, 31);
|
|
lf.lfHeight = fontSize;
|
|
lf.lfWeight = fontWeight;
|
|
lf.lfQuality = ANTIALIASED_QUALITY;
|
|
|
|
if(hFont = CreateFontIndirect(&lf))
|
|
{
|
|
FontInfo &fi = *Fonts.CreateNew();
|
|
|
|
fi.hFont = hFont;
|
|
fi.fontSize = fontSize;
|
|
fi.fontWeight = fontWeight;
|
|
fi.strFontFace = lpFontFace;
|
|
}
|
|
|
|
return hFont;
|
|
}
|
|
|
|
StatusBarDrawData statusBarData;
|
|
|
|
void OBS::ClearStatusBar()
|
|
{
|
|
HWND hwndStatusBar = GetDlgItem(hwndMain, ID_STATUS);
|
|
PostMessage(hwndStatusBar, SB_SETTEXT, 0, NULL);
|
|
PostMessage(hwndStatusBar, SB_SETTEXT, 1, NULL);
|
|
PostMessage(hwndStatusBar, SB_SETTEXT, 2, NULL);
|
|
PostMessage(hwndStatusBar, SB_SETTEXT, 3, NULL);
|
|
PostMessage(hwndStatusBar, SB_SETTEXT, 4, NULL);
|
|
}
|
|
|
|
void OBS::SetStatusBarData()
|
|
{
|
|
if (OSTryEnterMutex(hStartupShutdownMutex))
|
|
{
|
|
if (!App->network)
|
|
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<UINT> 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;
|
|
}
|
|
}
|
|
}
|