obs/GraphicsCapture/GraphicsCapture.cpp
HomeWorld aa3d74f790 Added DWM as an option to game capture. (Monitor capture while Aero is on)
This option is shown in the Application list if composition is enabled and os version < 8 (Windows Vista/7)
2014-04-12 11:31:37 +03:00

484 lines
18 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 "GraphicsCapture.h"
#include <dwmapi.h>
extern "C" __declspec(dllexport) bool LoadPlugin();
extern "C" __declspec(dllexport) void UnloadPlugin();
extern "C" __declspec(dllexport) CTSTR GetPluginName();
extern "C" __declspec(dllexport) CTSTR GetPluginDescription();
HINSTANCE hinstMain = NULL;
HANDLE textureMutexes[2] = {NULL, NULL};
#define GRAPHICSCAPTURE_CLASSNAME TEXT("GraphicsCapture")
struct WindowInfo
{
String strClass;
BOOL bRequiresAdmin;
};
struct ConfigDialogData
{
CTSTR lpName;
XElement *data;
List<WindowInfo> windowData;
StringList adminWindows;
UINT cx, cy;
inline void ClearData()
{
for(UINT i=0; i<windowData.Num(); i++)
windowData[i].strClass.Clear();
windowData.Clear();
adminWindows.Clear();
}
inline ~ConfigDialogData()
{
for(UINT i=0; i<windowData.Num(); i++)
windowData[i].strClass.Clear();
}
};
typedef HANDLE (WINAPI *OPPROC) (DWORD, BOOL, DWORD);
void RefreshWindowList(HWND hwndCombobox, ConfigDialogData &configData)
{
SendMessage(hwndCombobox, CB_RESETCONTENT, 0, 0);
configData.ClearData();
HWND hwndCurrent = GetWindow(GetDesktopWindow(), GW_CHILD);
do
{
if(IsWindowVisible(hwndCurrent))
{
RECT clientRect;
GetClientRect(hwndCurrent, &clientRect);
String strWindowName;
strWindowName.SetLength(GetWindowTextLength(hwndCurrent));
GetWindowText(hwndCurrent, strWindowName, strWindowName.Length()+1);
HWND hwndParent = GetParent(hwndCurrent);
DWORD exStyles = (DWORD)GetWindowLongPtr(hwndCurrent, GWL_EXSTYLE);
DWORD styles = (DWORD)GetWindowLongPtr(hwndCurrent, GWL_STYLE);
if((exStyles & WS_EX_TOOLWINDOW) == 0 && (styles & WS_CHILD) == 0 /*&& hwndParent == NULL*/)
{
//-------
DWORD processID;
GetWindowThreadProcessId(hwndCurrent, &processID);
if(processID == GetCurrentProcessId())
continue;
TCHAR fileName[MAX_PATH+1];
scpy(fileName, TEXT("unknown"));
char pOPStr[12];
mcpy(pOPStr, "NpflUvhel{x", 12);
for (int i=0; i<11; i++) pOPStr[i] ^= i^1;
OPPROC pOpenProcess = (OPPROC)GetProcAddress(GetModuleHandle(TEXT("KERNEL32")), pOPStr);
HANDLE hProcess = (*pOpenProcess)(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE, FALSE, processID);
if(hProcess)
{
DWORD dwSize = MAX_PATH;
QueryFullProcessImageName(hProcess, 0, fileName, &dwSize);
StringList moduleList;
OSGetLoadedModuleList(hProcess, moduleList);
CloseHandle(hProcess);
//note: this doesn't actually work cross-bit
/*BOOL bFoundModule = FALSE;
for(UINT i=0; i<moduleList.Num(); i++)
{
CTSTR moduleName = moduleList[i];
if (!scmp(moduleName, TEXT("d3d9.dll")) ||
!scmp(moduleName, TEXT("d3d10.dll")) ||
!scmp(moduleName, TEXT("d3d10_1.dll")) ||
!scmp(moduleName, TEXT("d3d11.dll")) ||
!scmp(moduleName, TEXT("opengl32.dll")))
{
bFoundModule = true;
break;
}
}
if (!bFoundModule)
continue;*/
}
else
{
hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID);
if(hProcess)
{
configData.adminWindows << strWindowName;
CloseHandle(hProcess);
}
continue;
}
//-------
String strFileName = fileName;
strFileName.FindReplace(TEXT("\\"), TEXT("/"));
String strText;
strText << TEXT("[") << GetPathFileName(strFileName);
strText << TEXT("]: ") << strWindowName;
int id = (int)SendMessage(hwndCombobox, CB_ADDSTRING, 0, (LPARAM)strText.Array());
SendMessage(hwndCombobox, CB_SETITEMDATA, id, (LPARAM)hwndCurrent);
String strClassName;
strClassName.SetLength(256);
GetClassName(hwndCurrent, strClassName.Array(), 255);
strClassName.SetLength(slen(strClassName));
WindowInfo &info = *configData.windowData.CreateNew();
info.strClass = strClassName;
info.bRequiresAdmin = false; //todo: add later
}
}
} while (hwndCurrent = GetNextWindow(hwndCurrent, GW_HWNDNEXT));
if(OSGetVersion() < 8)
{
BOOL isCompositionEnabled = FALSE;
DwmIsCompositionEnabled(&isCompositionEnabled);
if(isCompositionEnabled)
{
String strText;
strText << TEXT("[DWM]: ") << Str("Sources.SoftwareCaptureSource.MonitorCapture");
int id = (int)SendMessage(hwndCombobox, CB_ADDSTRING, 0, (LPARAM)strText.Array());
SendMessage(hwndCombobox, CB_SETITEMDATA, id, (LPARAM)NULL);
WindowInfo &info = *configData.windowData.CreateNew();
info.strClass = TEXT("Dwm");
info.bRequiresAdmin = false; //todo: add later
}
}
}
int SetSliderText(HWND hwndParent, int controlSlider, int controlText)
{
HWND hwndSlider = GetDlgItem(hwndParent, controlSlider);
HWND hwndText = GetDlgItem(hwndParent, controlText);
int sliderVal = (int)SendMessage(hwndSlider, TBM_GETPOS, 0, 0);
float floatVal = float(sliderVal)*0.01f;
SetWindowText(hwndText, FormattedString(TEXT("%.02f"), floatVal));
return sliderVal;
}
INT_PTR CALLBACK ConfigureDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
{
ConfigDialogData *info = (ConfigDialogData*)lParam;
XElement *data = info->data;
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
LocalizeWindow(hwnd);
//--------------------------------------------
SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_REFRESH, BN_CLICKED), (LPARAM)GetDlgItem(hwnd, IDC_APPLIST));
//--------------------------------------------
BOOL bCaptureMouse = data->GetInt(TEXT("captureMouse"), 1);
BOOL bStretchImage = data->GetInt(TEXT("stretchImage"));
SendMessage(GetDlgItem(hwnd, IDC_STRETCHTOSCREEN), BM_SETCHECK, bStretchImage ? BST_CHECKED : BST_UNCHECKED, 0);
SendMessage(GetDlgItem(hwnd, IDC_IGNOREASPECT), BM_SETCHECK, data->GetInt(TEXT("ignoreAspect")) ? BST_CHECKED : BST_UNCHECKED, 0);
SendMessage(GetDlgItem(hwnd, IDC_CAPTUREMOUSE), BM_SETCHECK, bCaptureMouse ? BST_CHECKED : BST_UNCHECKED, 0);
SendMessage(GetDlgItem(hwnd, IDC_INVERTMOUSEONCLICK), BM_SETCHECK, data->GetInt(TEXT("invertMouse")) ? BST_CHECKED : BST_UNCHECKED, 0);
SendMessage(GetDlgItem(hwnd, IDC_USESAFEHOOK), BM_SETCHECK, data->GetInt(TEXT("safeHook")) ? BST_CHECKED : BST_UNCHECKED, 0);
EnableWindow(GetDlgItem(hwnd, IDC_INVERTMOUSEONCLICK), bCaptureMouse);
EnableWindow(GetDlgItem(hwnd, IDC_IGNOREASPECT), bStretchImage);
//------------------------------------------
bool bUseHotkey = data->GetInt(TEXT("useHotkey"), 0) != 0;
EnableWindow(GetDlgItem(hwnd, IDC_APPLIST), !bUseHotkey);
EnableWindow(GetDlgItem(hwnd, IDC_REFRESH), !bUseHotkey);
EnableWindow(GetDlgItem(hwnd, IDC_HOTKEY), bUseHotkey);
DWORD hotkey = data->GetInt(TEXT("hotkey"), VK_F12);
SendMessage(GetDlgItem(hwnd, IDC_HOTKEY), HKM_SETHOTKEY, hotkey, 0);
SendMessage(GetDlgItem(hwnd, IDC_SELECTAPP), BM_SETCHECK, bUseHotkey ? BST_UNCHECKED : BST_CHECKED, 0);
SendMessage(GetDlgItem(hwnd, IDC_USEHOTKEY), BM_SETCHECK, bUseHotkey ? BST_CHECKED : BST_UNCHECKED, 0);
//------------------------------------------
int gammaVal = data->GetInt(TEXT("gamma"), 100);
HWND hwndTemp = GetDlgItem(hwnd, IDC_GAMMA);
SendMessage(hwndTemp, TBM_CLEARTICS, FALSE, 0);
SendMessage(hwndTemp, TBM_SETRANGE, FALSE, MAKELPARAM(50, 175));
SendMessage(hwndTemp, TBM_SETTIC, 0, 100);
SendMessage(hwndTemp, TBM_SETPOS, TRUE, gammaVal);
SetSliderText(hwnd, IDC_GAMMA, IDC_GAMMAVAL);
return TRUE;
}
case WM_HSCROLL:
{
if(GetDlgCtrlID((HWND)lParam) == IDC_GAMMA)
{
int gamma = SetSliderText(hwnd, IDC_GAMMA, IDC_GAMMAVAL);
ConfigDialogData *info = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
ImageSource *source = API->GetSceneImageSource(info->lpName);
if(source)
source->SetInt(TEXT("gamma"), gamma);
}
}
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case IDC_CAPTUREMOUSE:
{
BOOL bCaptureMouse = SendMessage(GetDlgItem(hwnd, IDC_CAPTUREMOUSE), BM_GETCHECK, 0, 0) == BST_CHECKED;
EnableWindow(GetDlgItem(hwnd, IDC_INVERTMOUSEONCLICK), bCaptureMouse);
}
break;
case IDC_SELECTAPP:
case IDC_USEHOTKEY:
if (HIWORD(wParam) == BN_CLICKED)
{
bool bUseHotkey = LOWORD(wParam) == IDC_USEHOTKEY;
EnableWindow(GetDlgItem(hwnd, IDC_APPLIST), !bUseHotkey);
EnableWindow(GetDlgItem(hwnd, IDC_REFRESH), !bUseHotkey);
EnableWindow(GetDlgItem(hwnd, IDC_HOTKEY), bUseHotkey);
}
break;
case IDC_STRETCHTOSCREEN:
{
BOOL bStretchToScreen = SendMessage(GetDlgItem(hwnd, IDC_STRETCHTOSCREEN), BM_GETCHECK, 0, 0) == BST_CHECKED;
EnableWindow(GetDlgItem(hwnd, IDC_IGNOREASPECT), bStretchToScreen);
}
break;
case IDC_REFRESH:
{
ConfigDialogData *info = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
XElement *data = info->data;
CTSTR lpWindowName = data->GetString(TEXT("window"));
HWND hwndWindowList = GetDlgItem(hwnd, IDC_APPLIST);
RefreshWindowList(hwndWindowList, *info);
UINT windowID = 0;
if(lpWindowName)
windowID = (UINT)SendMessage(hwndWindowList, CB_FINDSTRINGEXACT, -1, (LPARAM)lpWindowName);
if(windowID != CB_ERR)
SendMessage(hwndWindowList, CB_SETCURSEL, windowID, 0);
else
SendMessage(hwndWindowList, CB_SETCURSEL, 0, 0);
String strInfoText;
if(info->adminWindows.Num())
{
strInfoText << Str("Sources.GameCaptureSource.RequiresAdmin") << TEXT("\r\n");
for(UINT i=0; i<info->adminWindows.Num(); i++)
strInfoText << info->adminWindows[i] << TEXT("\r\n");
}
SetWindowText(GetDlgItem(hwnd, IDC_INFO), strInfoText);
}
break;
case IDOK:
{
UINT windowID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_APPLIST), CB_GETCURSEL, 0, 0);
if(windowID == CB_ERR) windowID = 0;
ConfigDialogData *info = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
XElement *data = info->data;
if(!info->windowData.Num())
return 0;
String strWindow = GetCBText(GetDlgItem(hwnd, IDC_APPLIST), windowID);
data->SetString(TEXT("window"), strWindow);
data->SetString(TEXT("windowClass"), info->windowData[windowID].strClass);
data->SetInt(TEXT("stretchImage"), SendMessage(GetDlgItem(hwnd, IDC_STRETCHTOSCREEN), BM_GETCHECK, 0, 0) == BST_CHECKED);
data->SetInt(TEXT("ignoreAspect"), SendMessage(GetDlgItem(hwnd, IDC_IGNOREASPECT), BM_GETCHECK, 0, 0) == BST_CHECKED);
data->SetInt(TEXT("captureMouse"), SendMessage(GetDlgItem(hwnd, IDC_CAPTUREMOUSE), BM_GETCHECK, 0, 0) == BST_CHECKED);
data->SetInt(TEXT("invertMouse"), SendMessage(GetDlgItem(hwnd, IDC_INVERTMOUSEONCLICK), BM_GETCHECK, 0, 0) == BST_CHECKED);
data->SetInt(TEXT("safeHook"), SendMessage(GetDlgItem(hwnd, IDC_USESAFEHOOK), BM_GETCHECK, 0, 0) == BST_CHECKED);
data->SetInt(TEXT("useHotkey"), SendMessage(GetDlgItem(hwnd, IDC_USEHOTKEY), BM_GETCHECK, 0, 0) == BST_CHECKED);
data->SetInt(TEXT("hotkey"), (DWORD)SendMessage(GetDlgItem(hwnd, IDC_HOTKEY), HKM_GETHOTKEY, 0, 0));
data->SetInt(TEXT("gamma"), (int)SendMessage(GetDlgItem(hwnd, IDC_GAMMA), TBM_GETPOS, 0, 0));
EndDialog(hwnd, LOWORD(wParam));
}
break;
case IDCANCEL:
{
ConfigDialogData *info = (ConfigDialogData*)GetWindowLongPtr(hwnd, DWLP_USER);
ImageSource *source = API->GetSceneImageSource(info->lpName);
XElement *data = info->data;
if(source)
{
source->SetInt(TEXT("gamma"), data->GetInt(TEXT("gamma"), 100));
}
EndDialog(hwnd, LOWORD(wParam));
}
}
break;
case WM_CLOSE:
EndDialog(hwnd, IDCANCEL);
}
return 0;
}
bool STDCALL ConfigureGraphicsCaptureSource(XElement *element, bool bCreating)
{
if(!element)
{
AppWarning(TEXT("ConfigureGraphicsCaptureSource: NULL element"));
return false;
}
XElement *data = element->GetElement(TEXT("data"));
if(!data)
data = element->CreateElement(TEXT("data"));
ConfigDialogData *configData = new ConfigDialogData;
configData->data = data;
configData->lpName = element->GetName();
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(IDD_CONFIG), API->GetMainWindow(), ConfigureDialogProc, (LPARAM)configData) == IDOK)
{
UINT width, height;
API->GetBaseSize(width, height);
element->SetInt(TEXT("cx"), width);
element->SetInt(TEXT("cy"), height);
delete configData;
return true;
}
delete configData;
return false;
}
ImageSource* STDCALL CreateGraphicsCaptureSource(XElement *data)
{
GraphicsCaptureSource *source = new GraphicsCaptureSource;
if(!source->Init(data))
{
delete source;
return NULL;
}
return source;
}
bool LoadPlugin()
{
InitHotkeyExControl(hinstMain);
textureMutexes[0] = CreateMutex(NULL, NULL, TEXTURE_MUTEX1);
if(!textureMutexes[0])
{
AppWarning(TEXT("Could not create texture mutex 1, GetLastError = %u"), GetLastError());
return false;
}
textureMutexes[1] = CreateMutex(NULL, NULL, TEXTURE_MUTEX2);
if(!textureMutexes[1])
{
AppWarning(TEXT("Could not create texture mutex 2, GetLastError = %u"), GetLastError());
return false;
}
API->RegisterImageSourceClass(GRAPHICSCAPTURE_CLASSNAME, Str("Sources.GameCaptureSource"), (OBSCREATEPROC)CreateGraphicsCaptureSource, (OBSCONFIGPROC)ConfigureGraphicsCaptureSource);
return true;
}
void UnloadPlugin()
{
CloseHandle(textureMutexes[0]);
CloseHandle(textureMutexes[1]);
}
CTSTR GetPluginName()
{
return Str("Sources.GameCaptureSource.PluginName");
}
CTSTR GetPluginDescription()
{
return Str("Sources.GameCaptureSource.PluginDescription");
}
BOOL CALLBACK DllMain(HINSTANCE hInst, DWORD dwReason, LPVOID lpBla)
{
if(dwReason == DLL_PROCESS_ATTACH)
hinstMain = hInst;
return TRUE;
}