This button was originally added as a convenience feature for users who liked to stream certain windowed games (especially retro games) at a 1:1 pixel ratio without having to constantly fiddle with the settings or figure out exactly what the window client rectangle size was for the target. However, some less knowledgable users would press this button without knowing specifically what it does, causing some support issues. I've readded the button, but with a big warning box that will pop up warn the user and ask for confirmation. However, this problem will have to be solved better in the rewrite. Having this in the software capture sources is not an adequate way of dealing with the problem. There needs to be some way to save custom resolutions in the rewrite so you can easily switch between them. There also needs to be a way to copy the resolution of a window or window client rectangle, and it really shouldn't be in the window capture source properties I think.
2081 lines
78 KiB
C++
2081 lines
78 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 <dwmapi.h>
|
|
|
|
|
|
#define NUM_CAPTURE_TEXTURES 2
|
|
|
|
class DesktopImageSource : public ImageSource
|
|
{
|
|
Texture *renderTextures[NUM_CAPTURE_TEXTURES];
|
|
Texture *lastRendered;
|
|
|
|
UINT captureType;
|
|
String strWindow, strWindowClass;
|
|
BOOL bClientCapture, bCaptureMouse, bCaptureLayered;
|
|
HWND hwndFoundWindow;
|
|
|
|
Shader *colorKeyShader, *alphaIgnoreShader;
|
|
|
|
int width, height;
|
|
RECT captureRect;
|
|
UINT frameTime;
|
|
int curCaptureTexture;
|
|
XElement *data;
|
|
|
|
UINT warningID;
|
|
|
|
bool bUseColorKey, bUsePointFiltering;
|
|
DWORD keyColor;
|
|
UINT keySimilarity, keyBlend;
|
|
|
|
UINT opacity;
|
|
int gamma;
|
|
|
|
float rotateDegrees;
|
|
|
|
//-------------------------
|
|
// stuff for compatibility mode
|
|
bool bCompatibilityMode;
|
|
HDC hdcCompatible;
|
|
HBITMAP hbmpCompatible, hbmpOld;
|
|
BYTE *captureBits;
|
|
|
|
//-------------------------
|
|
// win 8 capture stuff
|
|
Texture *cursorTexture;
|
|
int xHotspot, yHotspot;
|
|
UINT monitor;
|
|
UINT deviceOutputID;
|
|
float retryAcquire;
|
|
bool bWindows8MonitorCapture;
|
|
MonitorInfo monitorData;
|
|
bool bMouseCaptured;
|
|
POINT cursorPos;
|
|
HCURSOR hCurrentCursor;
|
|
bool bInInit;
|
|
|
|
OutputDuplicator *duplicator;
|
|
|
|
public:
|
|
DesktopImageSource(UINT frameTime, XElement *data)
|
|
{
|
|
this->data = data;
|
|
duplicator = NULL;
|
|
|
|
bInInit = true;
|
|
|
|
UpdateSettings();
|
|
|
|
bInInit = false;
|
|
|
|
curCaptureTexture = 0;
|
|
this->frameTime = frameTime;
|
|
|
|
colorKeyShader = CreatePixelShaderFromFile(TEXT("shaders\\ColorKey_RGB.pShader"));
|
|
alphaIgnoreShader = CreatePixelShaderFromFile(TEXT("shaders\\AlphaIgnore.pShader"));
|
|
|
|
if(captureType == 0)
|
|
Log(TEXT("Using Monitor Capture"));
|
|
else if(captureType == 1)
|
|
Log(TEXT("Using Window Capture"));
|
|
}
|
|
|
|
~DesktopImageSource()
|
|
{
|
|
for(int i=0; i<NUM_CAPTURE_TEXTURES; i++)
|
|
delete renderTextures[i];
|
|
|
|
if(warningID)
|
|
App->RemoveStreamInfo(warningID);
|
|
|
|
delete duplicator;
|
|
delete cursorTexture;
|
|
delete alphaIgnoreShader;
|
|
delete colorKeyShader;
|
|
|
|
if(bCompatibilityMode)
|
|
{
|
|
SelectObject(hdcCompatible, hbmpOld);
|
|
DeleteDC(hdcCompatible);
|
|
DeleteObject(hbmpCompatible);
|
|
}
|
|
}
|
|
|
|
void BeginScene()
|
|
{
|
|
if(bWindows8MonitorCapture && !duplicator)
|
|
duplicator = GS->CreateOutputDuplicator(deviceOutputID);
|
|
}
|
|
|
|
void EndScene()
|
|
{
|
|
if(bWindows8MonitorCapture && duplicator)
|
|
{
|
|
delete duplicator;
|
|
duplicator = NULL;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
// TODO - have win8 monitor capture behave even when using it as a global source
|
|
|
|
void GlobalSourceEnterScene()
|
|
{
|
|
}
|
|
|
|
void GlobalSourceLeaveScene()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------------
|
|
|
|
void Tick(float fSeconds)
|
|
{
|
|
if(bWindows8MonitorCapture && !duplicator)
|
|
{
|
|
retryAcquire += fSeconds;
|
|
if(retryAcquire > 1.0f)
|
|
{
|
|
retryAcquire = 0.0f;
|
|
|
|
lastRendered = NULL;
|
|
duplicator = GS->CreateOutputDuplicator(deviceOutputID);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreprocessWindows8MonitorCapture()
|
|
{
|
|
//----------------------------------------------------------
|
|
// capture monitor
|
|
|
|
if(duplicator)
|
|
{
|
|
switch(duplicator->AcquireNextFrame(0))
|
|
{
|
|
case DuplicatorInfo_Lost:
|
|
{
|
|
delete duplicator;
|
|
lastRendered = NULL;
|
|
duplicator = GS->CreateOutputDuplicator(deviceOutputID);
|
|
return;
|
|
}
|
|
|
|
case DuplicatorInfo_Error:
|
|
delete duplicator;
|
|
duplicator = NULL;
|
|
lastRendered = NULL;
|
|
return;
|
|
|
|
case DuplicatorInfo_Timeout:
|
|
return;
|
|
}
|
|
|
|
lastRendered = duplicator->GetCopyTexture();
|
|
}
|
|
|
|
//----------------------------------------------------------
|
|
// capture mouse
|
|
|
|
bMouseCaptured = false;
|
|
if(bCaptureMouse)
|
|
{
|
|
CURSORINFO ci;
|
|
zero(&ci, sizeof(ci));
|
|
ci.cbSize = sizeof(ci);
|
|
|
|
if(GetCursorInfo(&ci))
|
|
{
|
|
mcpy(&cursorPos, &ci.ptScreenPos, sizeof(cursorPos));
|
|
|
|
if(ci.flags & CURSOR_SHOWING)
|
|
{
|
|
if(ci.hCursor == hCurrentCursor)
|
|
bMouseCaptured = true;
|
|
else
|
|
{
|
|
HICON hIcon = CopyIcon(ci.hCursor);
|
|
hCurrentCursor = ci.hCursor;
|
|
|
|
delete cursorTexture;
|
|
cursorTexture = NULL;
|
|
|
|
if(hIcon)
|
|
{
|
|
ICONINFO ii;
|
|
if(GetIconInfo(hIcon, &ii))
|
|
{
|
|
xHotspot = int(ii.xHotspot);
|
|
yHotspot = int(ii.yHotspot);
|
|
|
|
UINT width, height;
|
|
LPBYTE lpData = GetCursorData(hIcon, ii, width, height);
|
|
if(lpData && width && height)
|
|
{
|
|
cursorTexture = CreateTexture(width, height, GS_BGRA, lpData, FALSE);
|
|
if(cursorTexture)
|
|
bMouseCaptured = true;
|
|
|
|
Free(lpData);
|
|
}
|
|
|
|
DeleteObject(ii.hbmColor);
|
|
DeleteObject(ii.hbmMask);
|
|
}
|
|
|
|
DestroyIcon(hIcon);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
HDC GetCurrentHDC()
|
|
{
|
|
HDC hDC = NULL;
|
|
|
|
Texture *captureTexture = renderTextures[curCaptureTexture];
|
|
|
|
if(bCompatibilityMode)
|
|
{
|
|
hDC = hdcCompatible;
|
|
//zero(captureBits, width*height*4);
|
|
}
|
|
else if(captureTexture)
|
|
captureTexture->GetDC(hDC);
|
|
|
|
return hDC;
|
|
}
|
|
|
|
void ReleaseCurrentHDC(HDC hDC)
|
|
{
|
|
Texture *captureTexture = renderTextures[curCaptureTexture];
|
|
|
|
if(!bCompatibilityMode)
|
|
captureTexture->ReleaseDC();
|
|
}
|
|
|
|
void EndPreprocess()
|
|
{
|
|
if(bCompatibilityMode)
|
|
{
|
|
renderTextures[0]->SetImage(captureBits, GS_IMAGEFORMAT_BGRA, width*4);
|
|
lastRendered = renderTextures[0];
|
|
}
|
|
else
|
|
{
|
|
lastRendered = renderTextures[curCaptureTexture];
|
|
|
|
if(++curCaptureTexture == NUM_CAPTURE_TEXTURES)
|
|
curCaptureTexture = 0;
|
|
}
|
|
}
|
|
|
|
void Preprocess()
|
|
{
|
|
if(bWindows8MonitorCapture)
|
|
{
|
|
PreprocessWindows8MonitorCapture();
|
|
return;
|
|
}
|
|
|
|
HDC hDC;
|
|
if(hDC = GetCurrentHDC())
|
|
{
|
|
//----------------------------------------------------------
|
|
// capture screen
|
|
|
|
CURSORINFO ci;
|
|
zero(&ci, sizeof(ci));
|
|
ci.cbSize = sizeof(ci);
|
|
BOOL bMouseCaptured;
|
|
|
|
bMouseCaptured = bCaptureMouse && GetCursorInfo(&ci);
|
|
|
|
bool bWindowNotFound = false;
|
|
HWND hwndCapture = NULL;
|
|
if(captureType == 1)
|
|
{
|
|
if(hwndFoundWindow && IsWindow(hwndFoundWindow))
|
|
{
|
|
TCHAR lpClassName[256];
|
|
BOOL bSuccess = GetClassName(hwndFoundWindow, lpClassName, 255);
|
|
|
|
if(bSuccess && scmpi(lpClassName, strWindowClass) == 0)
|
|
hwndCapture = hwndFoundWindow;
|
|
else
|
|
{
|
|
hwndCapture = FindWindow(strWindowClass, strWindow);
|
|
if(!hwndCapture)
|
|
hwndCapture = FindWindow(strWindowClass, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hwndCapture = FindWindow(strWindowClass, strWindow);
|
|
if(!hwndCapture)
|
|
hwndCapture = FindWindow(strWindowClass, NULL);
|
|
}
|
|
|
|
//note to self - hwndFoundWindow is used to make sure it doesn't pick up another window unintentionally if the title changes
|
|
hwndFoundWindow = hwndCapture;
|
|
|
|
if(!hwndCapture)
|
|
bWindowNotFound = true;
|
|
if(hwndCapture && (IsIconic(hwndCapture) || !IsWindowVisible(hwndCapture)))
|
|
{
|
|
ReleaseCurrentHDC(hDC);
|
|
//bWindowMinimized = true;
|
|
|
|
if(!warningID)
|
|
{
|
|
String strWarning;
|
|
|
|
strWarning << Str("Sources.SoftwareCaptureSource.WindowMinimized");
|
|
warningID = App->AddStreamInfo(strWarning, bWindowNotFound ? StreamInfoPriority_High : StreamInfoPriority_Medium);
|
|
}
|
|
return;
|
|
}
|
|
else if(!bWindowNotFound)
|
|
{
|
|
if(warningID)
|
|
{
|
|
App->RemoveStreamInfo(warningID);
|
|
warningID = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
HDC hCaptureDC;
|
|
if(hwndCapture && captureType == 1 && !bClientCapture)
|
|
hCaptureDC = GetWindowDC(hwndCapture);
|
|
else
|
|
hCaptureDC = GetDC(hwndCapture);
|
|
|
|
if(bWindowNotFound)
|
|
{
|
|
RECT rc = {0, 0, width, height};
|
|
FillRect(hDC, &rc, (HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
|
|
if(!warningID)
|
|
warningID = App->AddStreamInfo(Str("Sources.SoftwareCaptureSource.WindowNotFound"), bWindowNotFound ? StreamInfoPriority_High : StreamInfoPriority_Medium);
|
|
}
|
|
else
|
|
{
|
|
//CAPTUREBLT causes mouse flicker, so make capturing layered optional
|
|
if(!BitBlt(hDC, 0, 0, width, height, hCaptureDC, captureRect.left, captureRect.top, bCaptureLayered ? SRCCOPY|CAPTUREBLT : SRCCOPY))
|
|
{
|
|
RUNONCE AppWarning(TEXT("Capture BitBlt failed (%d).. just so you know"), GetLastError());
|
|
}
|
|
}
|
|
|
|
ReleaseDC(hwndCapture, hCaptureDC);
|
|
|
|
//----------------------------------------------------------
|
|
// capture mouse
|
|
|
|
if(bMouseCaptured && (captureType == 0 || (captureType == 1 && hwndFoundWindow == GetForegroundWindow())))
|
|
{
|
|
if(ci.flags & CURSOR_SHOWING)
|
|
{
|
|
HICON hIcon = CopyIcon(ci.hCursor);
|
|
|
|
if(hIcon)
|
|
{
|
|
ICONINFO ii;
|
|
if(GetIconInfo(hIcon, &ii))
|
|
{
|
|
POINT capturePos = {captureRect.left, captureRect.top};
|
|
|
|
if(captureType == 1)
|
|
{
|
|
if(bClientCapture)
|
|
ClientToScreen(hwndCapture, &capturePos);
|
|
else
|
|
{
|
|
RECT windowRect;
|
|
GetWindowRect(hwndCapture, &windowRect);
|
|
capturePos.x += windowRect.left;
|
|
capturePos.y += windowRect.top;
|
|
}
|
|
}
|
|
|
|
int x = ci.ptScreenPos.x - int(ii.xHotspot) - capturePos.x;
|
|
int y = ci.ptScreenPos.y - int(ii.yHotspot) - capturePos.y;
|
|
|
|
DrawIcon(hDC, x, y, hIcon);
|
|
|
|
DeleteObject(ii.hbmColor);
|
|
DeleteObject(ii.hbmMask);
|
|
}
|
|
|
|
DestroyIcon(hIcon);
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseCurrentHDC(hDC);
|
|
}
|
|
else
|
|
{
|
|
RUNONCE AppWarning(TEXT("Failed to get DC from capture surface"));
|
|
}
|
|
|
|
EndPreprocess();
|
|
}
|
|
|
|
void Render(const Vect2 &pos, const Vect2 &size)
|
|
{
|
|
SamplerState *sampler = NULL;
|
|
/*if(bWindows8MonitorCapture)
|
|
{
|
|
RenderWindows8MonitorCapture(pos, size);
|
|
return;
|
|
}*/
|
|
|
|
Vect2 ulCoord = Vect2(0.0f, 0.0f),
|
|
lrCoord = Vect2(1.0f, 1.0f);
|
|
|
|
if(captureType == 1 && !hwndFoundWindow) {
|
|
// Don't render a giant black rectangle if the window isn't found.
|
|
return;
|
|
}
|
|
|
|
if(bWindows8MonitorCapture)
|
|
{
|
|
LONG monitorWidth = monitorData.rect.right-monitorData.rect.left;
|
|
LONG monitorHeight = monitorData.rect.bottom-monitorData.rect.top;
|
|
RECT captureArea = {captureRect.left-monitorData.rect.left,
|
|
captureRect.top-monitorData.rect.top,
|
|
captureRect.right-monitorData.rect.left,
|
|
captureRect.bottom-monitorData.rect.top};
|
|
|
|
ulCoord.x = float(double(captureArea.left)/double(monitorWidth));
|
|
ulCoord.y = float(double(captureArea.top)/double(monitorHeight));
|
|
|
|
lrCoord.x = float(double(captureArea.right)/double(monitorWidth));
|
|
lrCoord.y = float(double(captureArea.bottom)/double(monitorHeight));
|
|
}
|
|
|
|
if(lastRendered)
|
|
{
|
|
float fGamma = float(-(gamma-100) + 100) * 0.01f;
|
|
|
|
Shader *lastPixelShader = GetCurrentPixelShader();
|
|
|
|
float fOpacity = float(opacity)*0.01f;
|
|
DWORD opacity255 = DWORD(fOpacity*255.0f);
|
|
|
|
if(bUseColorKey)
|
|
{
|
|
LoadPixelShader(colorKeyShader);
|
|
HANDLE hGamma = colorKeyShader->GetParameterByName(TEXT("gamma"));
|
|
if(hGamma)
|
|
colorKeyShader->SetFloat(hGamma, fGamma);
|
|
|
|
float fSimilarity = float(keySimilarity)*0.01f;
|
|
float fBlend = float(keyBlend)*0.01f;
|
|
|
|
colorKeyShader->SetColor(colorKeyShader->GetParameter(2), keyColor);
|
|
colorKeyShader->SetFloat(colorKeyShader->GetParameter(3), fSimilarity);
|
|
colorKeyShader->SetFloat(colorKeyShader->GetParameter(4), fBlend);
|
|
|
|
}
|
|
else
|
|
{
|
|
LoadPixelShader(alphaIgnoreShader);
|
|
HANDLE hGamma = alphaIgnoreShader->GetParameterByName(TEXT("gamma"));
|
|
if(hGamma)
|
|
alphaIgnoreShader->SetFloat(hGamma, fGamma);
|
|
}
|
|
|
|
if(bUsePointFiltering) {
|
|
SamplerInfo samplerinfo;
|
|
samplerinfo.filter = GS_FILTER_POINT;
|
|
sampler = CreateSamplerState(samplerinfo);
|
|
LoadSamplerState(sampler, 0);
|
|
}
|
|
|
|
if(bCompatibilityMode)
|
|
DrawSpriteEx(lastRendered, (opacity255<<24) | 0xFFFFFF,
|
|
pos.x, pos.y+size.y, pos.x+size.x, pos.y,
|
|
ulCoord.x, ulCoord.y,
|
|
lrCoord.x, lrCoord.y);
|
|
else
|
|
GS->DrawSpriteExRotate(lastRendered, (opacity255<<24) | 0xFFFFFF,
|
|
pos.x, pos.y, pos.x+size.x, pos.y+size.y, 0.0f,
|
|
ulCoord.x, ulCoord.y,
|
|
lrCoord.x, lrCoord.y, rotateDegrees);
|
|
|
|
if(bUsePointFiltering) delete(sampler);
|
|
|
|
LoadPixelShader(lastPixelShader);
|
|
|
|
//draw mouse
|
|
if(bWindows8MonitorCapture && cursorTexture && bMouseCaptured && bCaptureMouse)
|
|
{
|
|
POINT newPos = {cursorPos.x-xHotspot, cursorPos.y-yHotspot};
|
|
|
|
if( newPos.x >= captureRect.left && newPos.y >= captureRect.top &&
|
|
newPos.x < captureRect.right && newPos.y < captureRect.bottom)
|
|
{
|
|
Vect2 newCursorPos = Vect2(float(newPos.x-captureRect.left), float(newPos.y-captureRect.top));
|
|
Vect2 newCursorSize = Vect2(float(cursorTexture->Width()), float(cursorTexture->Height()));
|
|
|
|
Vect2 sizeMultiplier = size/GetSize();
|
|
newCursorPos *= sizeMultiplier;
|
|
newCursorPos += pos;
|
|
newCursorSize *= sizeMultiplier;
|
|
|
|
GS->SetCropping(0.0f, 0.0f, 0.0f, 0.0f);
|
|
DrawSprite(cursorTexture, 0xFFFFFFFF, newCursorPos.x, newCursorPos.y, newCursorPos.x+newCursorSize.x, newCursorPos.y+newCursorSize.y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vect2 GetSize() const
|
|
{
|
|
return Vect2(float(width), float(height));
|
|
}
|
|
|
|
void UpdateSettings()
|
|
{
|
|
App->EnterSceneMutex();
|
|
|
|
UINT newCaptureType = data->GetInt(TEXT("captureType"));
|
|
String strNewWindow = data->GetString(TEXT("window"));
|
|
String strNewWindowClass= data->GetString(TEXT("windowClass"));
|
|
BOOL bNewClientCapture = data->GetInt(TEXT("innerWindow"), 1);
|
|
|
|
bCaptureMouse = data->GetInt(TEXT("captureMouse"), 1);
|
|
bCaptureLayered = data->GetInt(TEXT("captureLayered"), 0);
|
|
|
|
bool bNewUsePointFiltering = data->GetInt(TEXT("usePointFiltering"), 0) != 0;
|
|
|
|
int x = data->GetInt(TEXT("captureX"));
|
|
int y = data->GetInt(TEXT("captureY"));
|
|
int cx = data->GetInt(TEXT("captureCX"), 32);
|
|
int cy = data->GetInt(TEXT("captureCY"), 32);
|
|
|
|
bool bNewCompatibleMode = data->GetInt(TEXT("compatibilityMode")) != 0;
|
|
if(bNewCompatibleMode && (OSGetVersion() >= 8 && newCaptureType == 0))
|
|
bNewCompatibleMode = false;
|
|
|
|
gamma = data->GetInt(TEXT("gamma"), 100);
|
|
if(gamma < 50) gamma = 50;
|
|
else if(gamma > 175) gamma = 175;
|
|
|
|
UINT newMonitor = data->GetInt(TEXT("monitor"));
|
|
if(newMonitor > App->NumMonitors())
|
|
newMonitor = 0;
|
|
|
|
if( captureRect.left != x || captureRect.right != (x+cx) || captureRect.top != cy || captureRect.bottom != (y+cy) ||
|
|
newCaptureType != captureType || !strNewWindowClass.CompareI(strWindowClass) || !strNewWindow.CompareI(strWindow) ||
|
|
bNewClientCapture != bClientCapture || (OSGetVersion() >= 8 && newMonitor != monitor) ||
|
|
bNewCompatibleMode != bCompatibilityMode)
|
|
{
|
|
for(int i=0; i<NUM_CAPTURE_TEXTURES; i++)
|
|
{
|
|
delete renderTextures[i];
|
|
renderTextures[i] = NULL;
|
|
}
|
|
|
|
if(duplicator)
|
|
{
|
|
delete duplicator;
|
|
duplicator = NULL;
|
|
}
|
|
|
|
if(cursorTexture)
|
|
{
|
|
delete cursorTexture;
|
|
cursorTexture = NULL;
|
|
}
|
|
|
|
if(bCompatibilityMode)
|
|
{
|
|
SelectObject(hdcCompatible, hbmpOld);
|
|
DeleteDC(hdcCompatible);
|
|
DeleteObject(hbmpCompatible);
|
|
|
|
hdcCompatible = NULL;
|
|
hbmpCompatible = NULL;
|
|
captureBits = NULL;
|
|
}
|
|
|
|
hCurrentCursor = NULL;
|
|
|
|
captureType = newCaptureType;
|
|
strWindow = strNewWindow;
|
|
strWindowClass = strNewWindowClass;
|
|
bClientCapture = bNewClientCapture;
|
|
|
|
bCompatibilityMode = bNewCompatibleMode;
|
|
bUsePointFiltering = bNewUsePointFiltering;
|
|
|
|
captureRect.left = x;
|
|
captureRect.top = y;
|
|
captureRect.right = x+cx;
|
|
captureRect.bottom = y+cy;
|
|
|
|
monitor = newMonitor;
|
|
const MonitorInfo &monitorInfo = App->GetMonitor(monitor);
|
|
mcpy(&monitorData, &monitorInfo, sizeof(monitorInfo));
|
|
|
|
rotateDegrees = 0.0f;
|
|
|
|
if(captureType == 0 && OSGetVersion() >= 8)
|
|
{
|
|
LONG monitorWidth = monitorInfo.rect.right-monitorInfo.rect.left;
|
|
LONG monitorHeight = monitorInfo.rect.bottom-monitorInfo.rect.top;
|
|
|
|
DeviceOutputs outputs;
|
|
GetDisplayDevices(outputs);
|
|
|
|
bWindows8MonitorCapture = false;
|
|
|
|
if(outputs.devices.Num())
|
|
{
|
|
const MonitorInfo &info = App->GetMonitor(monitor);
|
|
for(UINT j=0; j<outputs.devices[0].monitors.Num(); j++)
|
|
{
|
|
if(outputs.devices[0].monitors[j].hMonitor == info.hMonitor)
|
|
{
|
|
deviceOutputID = j;
|
|
rotateDegrees = outputs.devices[0].monitors[j].rotationDegrees;
|
|
bWindows8MonitorCapture = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
width = cx;
|
|
height = cy;
|
|
|
|
if(bCompatibilityMode)
|
|
{
|
|
hdcCompatible = CreateCompatibleDC(NULL);
|
|
|
|
BITMAPINFO bi;
|
|
zero(&bi, sizeof(bi));
|
|
|
|
BITMAPINFOHEADER &bih = bi.bmiHeader;
|
|
bih.biSize = sizeof(bih);
|
|
bih.biBitCount = 32;
|
|
bih.biWidth = width;
|
|
bih.biHeight = height;
|
|
bih.biPlanes = 1;
|
|
|
|
hbmpCompatible = CreateDIBSection(hdcCompatible, &bi, DIB_RGB_COLORS, (void**)&captureBits, NULL, 0);
|
|
hbmpOld = (HBITMAP)SelectObject(hdcCompatible, hbmpCompatible);
|
|
}
|
|
|
|
if(bWindows8MonitorCapture && !bInInit)
|
|
duplicator = GS->CreateOutputDuplicator(deviceOutputID);
|
|
else if(bCompatibilityMode)
|
|
renderTextures[0] = CreateTexture(width, height, GS_BGRA, NULL, FALSE, FALSE);
|
|
else
|
|
{
|
|
for(UINT i=0; i<NUM_CAPTURE_TEXTURES; i++)
|
|
renderTextures[i] = CreateGDITexture(width, height);
|
|
}
|
|
|
|
lastRendered = NULL;
|
|
}
|
|
|
|
bool bNewUseColorKey = data->GetInt(TEXT("useColorKey"), 0) != 0;
|
|
keyColor = data->GetInt(TEXT("keyColor"), 0xFFFFFFFF);
|
|
keySimilarity = data->GetInt(TEXT("keySimilarity"), 10);
|
|
keyBlend = data->GetInt(TEXT("keyBlend"), 0);
|
|
bUsePointFiltering = data->GetInt(TEXT("usePointFiltering"), 0) != 0;
|
|
|
|
bUseColorKey = bNewUseColorKey;
|
|
|
|
opacity = data->GetInt(TEXT("opacity"), 100);
|
|
|
|
App->LeaveSceneMutex();
|
|
}
|
|
|
|
void SetInt(CTSTR lpName, int iVal)
|
|
{
|
|
if(scmpi(lpName, TEXT("useColorKey")) == 0)
|
|
{
|
|
bool bNewVal = iVal != 0;
|
|
bUseColorKey = bNewVal;
|
|
}
|
|
else if(scmpi(lpName, TEXT("keyColor")) == 0)
|
|
{
|
|
keyColor = (DWORD)iVal;
|
|
}
|
|
else if(scmpi(lpName, TEXT("keySimilarity")) == 0)
|
|
{
|
|
keySimilarity = iVal;
|
|
}
|
|
else if(scmpi(lpName, TEXT("keyBlend")) == 0)
|
|
{
|
|
keyBlend = iVal;
|
|
}
|
|
else if(scmpi(lpName, TEXT("opacity")) == 0)
|
|
{
|
|
opacity = (UINT)iVal;
|
|
}
|
|
else if(scmpi(lpName, TEXT("gamma")) == 0)
|
|
{
|
|
gamma = iVal;
|
|
if(gamma < 50) gamma = 50;
|
|
else if(gamma > 175) gamma = 175;
|
|
}
|
|
}
|
|
};
|
|
|
|
ImageSource* STDCALL CreateDesktopSource(XElement *data)
|
|
{
|
|
if(!data)
|
|
return NULL;
|
|
|
|
return new DesktopImageSource(App->GetFrameTime(), data);
|
|
}
|
|
|
|
void RefreshWindowList(HWND hwndCombobox, StringList &classList)
|
|
{
|
|
SendMessage(hwndCombobox, CB_RESETCONTENT, 0, 0);
|
|
classList.Clear();
|
|
|
|
HWND hwndCurrent = GetWindow(GetDesktopWindow(), GW_CHILD);
|
|
do
|
|
{
|
|
if(IsWindowVisible(hwndCurrent) && !IsIconic(hwndCurrent))
|
|
{
|
|
RECT clientRect;
|
|
GetClientRect(hwndCurrent, &clientRect);
|
|
|
|
DWORD exStyles = (DWORD)GetWindowLongPtr(hwndCurrent, GWL_EXSTYLE);
|
|
DWORD styles = (DWORD)GetWindowLongPtr(hwndCurrent, GWL_STYLE);
|
|
|
|
if( (exStyles & WS_EX_TOOLWINDOW) == 0 && (styles & WS_CHILD) == 0 &&
|
|
clientRect.bottom != 0 && clientRect.right != 0 /*&& hwndParent == NULL*/)
|
|
{
|
|
String strWindowName;
|
|
strWindowName.SetLength(GetWindowTextLength(hwndCurrent));
|
|
GetWindowText(hwndCurrent, strWindowName, strWindowName.Length()+1);
|
|
|
|
//-------
|
|
|
|
DWORD processID;
|
|
GetWindowThreadProcessId(hwndCurrent, &processID);
|
|
if(processID == GetCurrentProcessId())
|
|
continue;
|
|
|
|
TCHAR fileName[MAX_PATH+1];
|
|
scpy(fileName, TEXT("unknown"));
|
|
|
|
HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processID);
|
|
if(hProcess)
|
|
{
|
|
DWORD dwSize = MAX_PATH;
|
|
QueryFullProcessImageName(hProcess, 0, fileName, &dwSize);
|
|
CloseHandle(hProcess);
|
|
}
|
|
|
|
//-------
|
|
|
|
String strFileName = fileName;
|
|
strFileName.FindReplace(TEXT("\\"), TEXT("/"));
|
|
|
|
String strText;
|
|
strText << TEXT("[") << GetPathFileName(strFileName) << TEXT("]: ") << strWindowName;
|
|
|
|
if(strWindowName.IsEmpty()) strWindowName = GetPathFileName(strFileName);
|
|
|
|
int id = (int)SendMessage(hwndCombobox, CB_ADDSTRING, 0, (LPARAM)strWindowName.Array());
|
|
SendMessage(hwndCombobox, CB_SETITEMDATA, id, (LPARAM)hwndCurrent);
|
|
|
|
String strClassName;
|
|
strClassName.SetLength(256);
|
|
GetClassName(hwndCurrent, strClassName.Array(), 255);
|
|
strClassName.SetLength(slen(strClassName));
|
|
|
|
classList << strClassName;
|
|
}
|
|
}
|
|
} while (hwndCurrent = GetNextWindow(hwndCurrent, GW_HWNDNEXT));
|
|
}
|
|
|
|
struct ConfigDesktopSourceInfo
|
|
{
|
|
CTSTR lpName;
|
|
XElement *data;
|
|
int dialogID;
|
|
StringList strClasses;
|
|
};
|
|
|
|
void SetDesktopCaptureType(HWND hwnd, UINT type)
|
|
{
|
|
SendMessage(GetDlgItem(hwnd, IDC_MONITORCAPTURE), BM_SETCHECK, type == 0 ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
SendMessage(GetDlgItem(hwnd, IDC_WINDOWCAPTURE), BM_SETCHECK, type == 1 ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_MONITOR), type == 0);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_WINDOW), type == 1);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_REFRESH), type == 1);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_OUTERWINDOW), type == 1);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_INNERWINDOW), type == 1);
|
|
}
|
|
|
|
void SelectTargetWindow(HWND hwnd, bool bRefresh)
|
|
{
|
|
HWND hwndWindowList = GetDlgItem(hwnd, IDC_WINDOW);
|
|
UINT windowID = (UINT)SendMessage(hwndWindowList, CB_GETCURSEL, 0, 0);
|
|
if(windowID == CB_ERR)
|
|
return;
|
|
|
|
String strWindow = GetCBText(hwndWindowList, windowID);
|
|
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
if(windowID >= info->strClasses.Num())
|
|
return;
|
|
|
|
HWND hwndTarget = FindWindow(info->strClasses[windowID], strWindow);
|
|
if(!hwndTarget)
|
|
{
|
|
HWND hwndLastKnownHWND = (HWND)SendMessage(hwndWindowList, CB_GETITEMDATA, windowID, 0);
|
|
if(IsWindow(hwndLastKnownHWND))
|
|
hwndTarget = hwndLastKnownHWND;
|
|
else
|
|
hwndTarget = FindWindow(info->strClasses[windowID], NULL);
|
|
}
|
|
|
|
if(!hwndTarget)
|
|
return;
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bInnerWindow = SendMessage(GetDlgItem(hwnd, IDC_INNERWINDOW), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
RECT rc;
|
|
if(bInnerWindow)
|
|
GetClientRect(hwndTarget, &rc);
|
|
else
|
|
{
|
|
GetWindowRect(hwndTarget, &rc);
|
|
|
|
rc.bottom -= rc.top;
|
|
rc.right -= rc.left;
|
|
rc.left = rc.top = 0;
|
|
}
|
|
|
|
//------------------------------------------
|
|
|
|
rc.bottom -= rc.top;
|
|
rc.right -= rc.left;
|
|
|
|
if(rc.right < 16)
|
|
rc.right = 16;
|
|
if(rc.bottom < 16)
|
|
rc.bottom = 16;
|
|
|
|
BOOL bRegion = SendMessage(GetDlgItem(hwnd, IDC_REGIONCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
if(!bRegion || bRefresh)
|
|
{
|
|
SetWindowText(GetDlgItem(hwnd, IDC_POSX), IntString(rc.left).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_POSY), IntString(rc.top).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_SIZEX), IntString(rc.right).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_SIZEY), IntString(rc.bottom).Array());
|
|
}
|
|
}
|
|
|
|
struct RegionWindowData
|
|
{
|
|
HWND hwndConfigDialog, hwndCaptureWindow;
|
|
POINT startPos;
|
|
RECT captureRect;
|
|
bool bMovingWindow;
|
|
bool bInnerWindowRegion;
|
|
|
|
inline RegionWindowData()
|
|
{
|
|
bMovingWindow = false;
|
|
bInnerWindowRegion = false;
|
|
}
|
|
};
|
|
|
|
RegionWindowData regionWindowData;
|
|
|
|
#define CAPTUREREGIONCLASS TEXT("CaptureRegionThingy")
|
|
|
|
LRESULT WINAPI RegionWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if(message == WM_MOUSEMOVE)
|
|
{
|
|
RECT client;
|
|
GetClientRect(hwnd, &client);
|
|
|
|
POINT pos;
|
|
pos.x = (long)(short)LOWORD(lParam);
|
|
pos.y = (long)(short)HIWORD(lParam);
|
|
|
|
if(regionWindowData.bMovingWindow)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(hwnd, &rc);
|
|
|
|
POINT posOffset = {pos.x-regionWindowData.startPos.x, pos.y-regionWindowData.startPos.y};
|
|
POINT newPos = {rc.left+posOffset.x, rc.top+posOffset.y};
|
|
SIZE windowSize = {rc.right-rc.left, rc.bottom-rc.top};
|
|
|
|
if(newPos.x < regionWindowData.captureRect.left)
|
|
newPos.x = regionWindowData.captureRect.left;
|
|
if(newPos.y < regionWindowData.captureRect.top)
|
|
newPos.y = regionWindowData.captureRect.top;
|
|
|
|
POINT newBottomRight = {rc.right+posOffset.x, rc.bottom+posOffset.y};
|
|
|
|
if(newBottomRight.x > regionWindowData.captureRect.right)
|
|
newPos.x -= (newBottomRight.x-regionWindowData.captureRect.right);
|
|
if(newBottomRight.y > regionWindowData.captureRect.bottom)
|
|
newPos.y -= (newBottomRight.y-regionWindowData.captureRect.bottom);
|
|
|
|
SetWindowPos(hwnd, NULL, newPos.x, newPos.y, 0, 0, SWP_NOSIZE|SWP_NOZORDER);
|
|
}
|
|
else
|
|
{
|
|
BOOL bLeft = (pos.x < 6);
|
|
BOOL bTop = (pos.y < 6);
|
|
BOOL bRight = (!bLeft && (pos.x > client.right-6));
|
|
BOOL bBottom = (!bTop && (pos.y > client.bottom-6));
|
|
|
|
if(bLeft)
|
|
{
|
|
if(bTop)
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENWSE));
|
|
else if(bBottom)
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENESW));
|
|
else
|
|
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
|
}
|
|
else if(bRight)
|
|
{
|
|
if(bTop)
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENESW));
|
|
else if(bBottom)
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENWSE));
|
|
else
|
|
SetCursor(LoadCursor(NULL, IDC_SIZEWE));
|
|
}
|
|
else if(bTop || bBottom)
|
|
SetCursor(LoadCursor(NULL, IDC_SIZENS));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else if(message == WM_LBUTTONDOWN)
|
|
{
|
|
RECT client;
|
|
GetClientRect(hwnd, &client);
|
|
|
|
POINT pos;
|
|
pos.x = (long)LOWORD(lParam);
|
|
pos.y = (long)HIWORD(lParam);
|
|
|
|
BOOL bLeft = (pos.x < 6);
|
|
BOOL bTop = (pos.y < 6);
|
|
BOOL bRight = (!bLeft && (pos.x > client.right-6));
|
|
BOOL bBottom = (!bTop && (pos.y > client.bottom-6));
|
|
|
|
ClientToScreen(hwnd, &pos);
|
|
|
|
POINTS newPos;
|
|
newPos.x = (SHORT)pos.x;
|
|
newPos.y = (SHORT)pos.y;
|
|
|
|
SendMessage(hwnd, WM_MOUSEMOVE, 0, lParam);
|
|
|
|
if(bLeft)
|
|
{
|
|
if(bTop)
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTTOPLEFT, (LPARAM)&newPos);
|
|
else if(bBottom)
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTBOTTOMLEFT, (LPARAM)&newPos);
|
|
else
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTLEFT, (LPARAM)&newPos);
|
|
}
|
|
else if(bRight)
|
|
{
|
|
if(bTop)
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTTOPRIGHT, (LPARAM)&newPos);
|
|
else if(bBottom)
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTBOTTOMRIGHT, (LPARAM)&newPos);
|
|
else
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTRIGHT, (LPARAM)&newPos);
|
|
}
|
|
else if(bTop)
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTTOP, (LPARAM)&newPos);
|
|
else if(bBottom)
|
|
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTBOTTOM, (LPARAM)&newPos);
|
|
else
|
|
{
|
|
regionWindowData.bMovingWindow = true; //can't use HTCAPTION -- if holding down long enough, other windows minimize
|
|
|
|
pos.x = (long)(short)LOWORD(lParam);
|
|
pos.y = (long)(short)HIWORD(lParam);
|
|
mcpy(®ionWindowData.startPos, &pos, sizeof(pos));
|
|
SetCapture(hwnd);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
else if(message == WM_LBUTTONUP)
|
|
{
|
|
if(regionWindowData.bMovingWindow)
|
|
{
|
|
regionWindowData.bMovingWindow = false;
|
|
ReleaseCapture();
|
|
}
|
|
}
|
|
else if(message == WM_PAINT)
|
|
{
|
|
PAINTSTRUCT ps;
|
|
HDC hDC = BeginPaint(hwnd, &ps);
|
|
if(hDC)
|
|
{
|
|
RECT clientRect;
|
|
GetClientRect(hwnd, &clientRect);
|
|
|
|
//-----------------------------------------
|
|
|
|
HPEN oldPen = (HPEN)SelectObject(hDC, GetStockObject(BLACK_PEN));
|
|
|
|
MoveToEx(hDC, 0, 0, NULL);
|
|
LineTo(hDC, clientRect.right-1, 0);
|
|
LineTo(hDC, clientRect.right-1, clientRect.bottom-1);
|
|
LineTo(hDC, 0, clientRect.bottom-1);
|
|
LineTo(hDC, 0, 0);
|
|
|
|
MoveToEx(hDC, 5, 5, NULL);
|
|
LineTo(hDC, clientRect.right-6, 5);
|
|
LineTo(hDC, clientRect.right-6, clientRect.bottom-6);
|
|
LineTo(hDC, 5, clientRect.bottom-6);
|
|
LineTo(hDC, 5, 5);
|
|
|
|
SelectObject(hDC, oldPen);
|
|
|
|
//-----------------------------------------
|
|
|
|
CTSTR lpStr = Str("Sources.SoftwareCaptureSource.RegionWindowText");
|
|
|
|
HFONT hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
|
|
HFONT hfontOld = (HFONT)SelectObject(hDC, hFont);
|
|
|
|
SIZE textExtent;
|
|
GetTextExtentPoint32(hDC, lpStr, slen(lpStr), &textExtent);
|
|
|
|
SetBkMode(hDC, TRANSPARENT);
|
|
SetTextAlign(hDC, TA_CENTER);
|
|
TextOut(hDC, clientRect.right/2, (clientRect.bottom - textExtent.cy)/2, lpStr, slen(lpStr));
|
|
|
|
//-----------------------------------------
|
|
|
|
SelectObject(hDC, hfontOld);
|
|
|
|
EndPaint(hwnd, &ps);
|
|
}
|
|
}
|
|
else if(message == WM_KEYDOWN)
|
|
{
|
|
if(wParam == VK_ESCAPE || wParam == VK_RETURN || wParam == 'Q')
|
|
DestroyWindow(hwnd);
|
|
}
|
|
else if(message == WM_SIZING)
|
|
{
|
|
RECT &rc = *(RECT*)lParam;
|
|
|
|
bool bTop = wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOP;
|
|
bool bLeft = wParam == WMSZ_TOPLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT;
|
|
|
|
if(bLeft)
|
|
{
|
|
if(rc.right-rc.left < 16)
|
|
rc.left = rc.right-16;
|
|
}
|
|
else
|
|
{
|
|
if(rc.right-rc.left < 16)
|
|
rc.right = rc.left+16;
|
|
}
|
|
|
|
if(bTop)
|
|
{
|
|
if(rc.bottom-rc.top < 16)
|
|
rc.top = rc.bottom-16;
|
|
}
|
|
else
|
|
{
|
|
if(rc.bottom-rc.top < 16)
|
|
rc.bottom = rc.top+16;
|
|
}
|
|
|
|
if(regionWindowData.hwndCaptureWindow)
|
|
{
|
|
if(rc.left < regionWindowData.captureRect.left)
|
|
rc.left = regionWindowData.captureRect.left;
|
|
if(rc.top < regionWindowData.captureRect.top)
|
|
rc.top = regionWindowData.captureRect.top;
|
|
|
|
if(rc.right > regionWindowData.captureRect.right)
|
|
rc.right = regionWindowData.captureRect.right;
|
|
if(rc.bottom > regionWindowData.captureRect.bottom)
|
|
rc.bottom = regionWindowData.captureRect.bottom;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
else if(message == WM_SIZE || message == WM_MOVE)
|
|
{
|
|
RECT rc;
|
|
GetWindowRect(hwnd, &rc);
|
|
|
|
if(rc.right-rc.left < 16)
|
|
rc.right = rc.left+16;
|
|
if(rc.bottom-rc.top < 16)
|
|
rc.bottom = rc.top+16;
|
|
|
|
SetWindowText(GetDlgItem(regionWindowData.hwndConfigDialog, IDC_SIZEX), IntString(rc.right-rc.left).Array());
|
|
SetWindowText(GetDlgItem(regionWindowData.hwndConfigDialog, IDC_SIZEY), IntString(rc.bottom-rc.top).Array());
|
|
|
|
if(regionWindowData.hwndCaptureWindow)
|
|
{
|
|
rc.left -= regionWindowData.captureRect.left;
|
|
rc.top -= regionWindowData.captureRect.top;
|
|
}
|
|
|
|
SetWindowText(GetDlgItem(regionWindowData.hwndConfigDialog, IDC_POSX), IntString(rc.left).Array());
|
|
SetWindowText(GetDlgItem(regionWindowData.hwndConfigDialog, IDC_POSY), IntString(rc.top).Array());
|
|
|
|
if(message == WM_SIZE)
|
|
RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT|RDW_ERASE|RDW_INVALIDATE);
|
|
}
|
|
else if(message == WM_ACTIVATE)
|
|
{
|
|
if(wParam == WA_INACTIVE)
|
|
DestroyWindow(hwnd);
|
|
}
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
struct ColorSelectionData
|
|
{
|
|
HDC hdcDesktop;
|
|
HDC hdcDestination;
|
|
HBITMAP hBitmap;
|
|
bool bValid;
|
|
|
|
inline ColorSelectionData() : hdcDesktop(NULL), hdcDestination(NULL), hBitmap(NULL), bValid(false) {}
|
|
inline ~ColorSelectionData() {Clear();}
|
|
|
|
inline bool Init()
|
|
{
|
|
hdcDesktop = GetDC(NULL);
|
|
if(!hdcDesktop)
|
|
return false;
|
|
|
|
hdcDestination = CreateCompatibleDC(hdcDesktop);
|
|
if(!hdcDestination)
|
|
return false;
|
|
|
|
hBitmap = CreateCompatibleBitmap(hdcDesktop, 1, 1);
|
|
if(!hBitmap)
|
|
return false;
|
|
|
|
SelectObject(hdcDestination, hBitmap);
|
|
bValid = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
inline void Clear()
|
|
{
|
|
if(hdcDesktop)
|
|
{
|
|
ReleaseDC(NULL, hdcDesktop);
|
|
hdcDesktop = NULL;
|
|
}
|
|
|
|
if(hdcDestination)
|
|
{
|
|
DeleteDC(hdcDestination);
|
|
hdcDestination = NULL;
|
|
}
|
|
|
|
if(hBitmap)
|
|
{
|
|
DeleteObject(hBitmap);
|
|
hBitmap = NULL;
|
|
}
|
|
|
|
bValid = false;
|
|
}
|
|
|
|
inline DWORD GetColor()
|
|
{
|
|
POINT p;
|
|
if(GetCursorPos(&p))
|
|
{
|
|
BITMAPINFO data;
|
|
zero(&data, sizeof(data));
|
|
|
|
data.bmiHeader.biSize = sizeof(data.bmiHeader);
|
|
data.bmiHeader.biWidth = 1;
|
|
data.bmiHeader.biHeight = 1;
|
|
data.bmiHeader.biPlanes = 1;
|
|
data.bmiHeader.biBitCount = 24;
|
|
data.bmiHeader.biCompression = BI_RGB;
|
|
data.bmiHeader.biSizeImage = 4;
|
|
|
|
if(BitBlt(hdcDestination, 0, 0, 1, 1, hdcDesktop, p.x, p.y, SRCCOPY|CAPTUREBLT))
|
|
{
|
|
DWORD buffer;
|
|
if(GetDIBits(hdcDestination, hBitmap, 0, 1, &buffer, &data, DIB_RGB_COLORS))
|
|
return 0xFF000000|buffer;
|
|
}
|
|
else
|
|
{
|
|
int err = GetLastError();
|
|
nop();
|
|
}
|
|
}
|
|
|
|
return 0xFF000000;
|
|
}
|
|
};
|
|
|
|
INT_PTR CALLBACK ConfigDesktopSourceProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static bool bSelectingColor = false;
|
|
static bool bMouseDown = false;
|
|
static ColorSelectionData colorData;
|
|
HWND hwndTemp;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_INITDIALOG:
|
|
{
|
|
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)lParam);
|
|
LocalizeWindow(hwnd);
|
|
|
|
//--------------------------------------------
|
|
|
|
HWND hwndToolTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, WS_POPUP|TTS_NOPREFIX|TTS_ALWAYSTIP,
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
hwnd, NULL, hinstMain, NULL);
|
|
|
|
TOOLINFO ti;
|
|
zero(&ti, sizeof(ti));
|
|
ti.cbSize = sizeof(ti);
|
|
ti.uFlags = TTF_SUBCLASS|TTF_IDISHWND;
|
|
ti.hwnd = hwnd;
|
|
|
|
if (LocaleIsRTL())
|
|
ti.uFlags |= TTF_RTLREADING;
|
|
|
|
SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 500);
|
|
SendMessage(hwndToolTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, 8000);
|
|
|
|
//-----------------------------------------------------
|
|
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)lParam;
|
|
XElement *data = info->data;
|
|
|
|
if (info->dialogID == IDD_CONFIGUREMONITORCAPTURE || info->dialogID == IDD_CONFIGUREDESKTOPSOURCE) {
|
|
hwndTemp = GetDlgItem(hwnd, IDC_MONITOR);
|
|
for(UINT i=0; i<App->NumMonitors(); i++)
|
|
SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)UIntString(i+1).Array());
|
|
SendMessage(hwndTemp, CB_SETCURSEL, 0, 0);
|
|
|
|
if (OSGetVersion() > 7) {
|
|
EnableWindow(GetDlgItem(hwnd, IDC_CAPTURELAYERED), FALSE);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COMPATIBILITYMODE), (info->dialogID != IDD_CONFIGUREMONITORCAPTURE));
|
|
}
|
|
}
|
|
|
|
UINT captureType = data->GetInt(TEXT("captureType"), 0);
|
|
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE)
|
|
SetDesktopCaptureType(hwnd, captureType);
|
|
|
|
//-----------------------------------------------------
|
|
|
|
if (info->dialogID == IDD_CONFIGUREMONITORCAPTURE || info->dialogID == IDD_CONFIGUREDESKTOPSOURCE) {
|
|
hwndTemp = GetDlgItem(hwnd, IDC_MONITOR);
|
|
UINT monitor = data->GetInt(TEXT("monitor"));
|
|
SendMessage(hwndTemp, CB_SETCURSEL, monitor, 0);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
|
|
if (info->dialogID == IDD_CONFIGUREMONITORCAPTURE) {
|
|
String strWarnings;
|
|
if (OSGetVersion() < 8) {
|
|
strWarnings << Str("Sources.SoftwareCaptureSource.Warning");
|
|
|
|
BOOL bComposition;
|
|
DwmIsCompositionEnabled(&bComposition);
|
|
if (bComposition)
|
|
strWarnings << TEXT("\r\n") << Str("Sources.SoftwareCaptureSource.WarningAero");
|
|
}
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_WARNING), strWarnings);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
|
|
bool bFoundWindow = false;
|
|
if (info->dialogID == IDD_CONFIGUREWINDOWCAPTURE || info->dialogID == IDD_CONFIGUREDESKTOPSOURCE) {
|
|
HWND hwndWindowList = GetDlgItem(hwnd, IDC_WINDOW);
|
|
|
|
CTSTR lpWindowName = data->GetString(TEXT("window"));
|
|
CTSTR lpWindowClass = data->GetString(TEXT("windowClass"));
|
|
BOOL bInnerWindow = (BOOL)data->GetInt(TEXT("innerWindow"), 1);
|
|
|
|
RefreshWindowList(hwndWindowList, info->strClasses);
|
|
|
|
UINT windowID = 0;
|
|
if(lpWindowName)
|
|
windowID = (UINT)SendMessage(hwndWindowList, CB_FINDSTRINGEXACT, -1, (LPARAM)lpWindowName);
|
|
|
|
bFoundWindow = (windowID != CB_ERR);
|
|
if(!bFoundWindow)
|
|
windowID = 0;
|
|
|
|
SendMessage(hwndWindowList, CB_SETCURSEL, windowID, 0);
|
|
|
|
if(bInnerWindow)
|
|
SendMessage(GetDlgItem(hwnd, IDC_INNERWINDOW), BM_SETCHECK, BST_CHECKED, 0);
|
|
else
|
|
SendMessage(GetDlgItem(hwnd, IDC_OUTERWINDOW), BM_SETCHECK, BST_CHECKED, 0);
|
|
}
|
|
|
|
//-----------------------------------------------------
|
|
|
|
bool bMouseCapture = data->GetInt(TEXT("captureMouse"), 1) != 0;
|
|
SendMessage(GetDlgItem(hwnd, IDC_CAPTUREMOUSE), BM_SETCHECK, (bMouseCapture) ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
bool bCaptureLayered = data->GetInt(TEXT("captureLayered"), 0) != 0;
|
|
SendMessage(GetDlgItem(hwnd, IDC_CAPTURELAYERED), BM_SETCHECK, (bCaptureLayered) ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
ti.lpszText = (LPWSTR)Str("Sources.SoftwareCaptureSource.CaptureLayeredTip");
|
|
ti.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_CAPTURELAYERED);
|
|
SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
|
|
|
|
bool bCompatibilityMode = data->GetInt(TEXT("compatibilityMode"), 0) != 0;
|
|
SendMessage(GetDlgItem(hwnd, IDC_COMPATIBILITYMODE), BM_SETCHECK, (bCompatibilityMode) ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
//-----------------------------------------------------
|
|
|
|
bool bRegion = data->GetInt(TEXT("regionCapture")) != FALSE;
|
|
if(captureType == 1 && !bFoundWindow)
|
|
bRegion = false;
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_REGIONCAPTURE), BM_SETCHECK, (bRegion) ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_POSX), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_POSY), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SIZEX), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SIZEY), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECTREGION), bRegion);
|
|
|
|
int posX = 0, posY = 0, sizeX = 32, sizeY = 32;
|
|
if(!data->GetBaseItem(TEXT("captureX")) || !bRegion)
|
|
{
|
|
if(captureType == 0)
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_MONITOR, 0), 0);
|
|
else if(captureType == 1)
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_WINDOW, 0), 0);
|
|
}
|
|
else
|
|
{
|
|
posX = data->GetInt(TEXT("captureX"));
|
|
posY = data->GetInt(TEXT("captureY"));
|
|
sizeX = data->GetInt(TEXT("captureCX"), 32);
|
|
sizeY = data->GetInt(TEXT("captureCY"), 32);
|
|
|
|
if(sizeX < 16)
|
|
sizeX = 16;
|
|
if(sizeY < 16)
|
|
sizeY = 16;
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_POSX), IntString(posX).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_POSY), IntString(posY).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_SIZEX), IntString(sizeX).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_SIZEY), IntString(sizeY).Array());
|
|
}
|
|
|
|
ti.lpszText = (LPWSTR)Str("Sources.SoftwareCaptureSource.SelectRegionTooltip");
|
|
ti.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_SELECTREGION);
|
|
SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
|
|
|
|
//------------------------------------------
|
|
|
|
BOOL bUseColorKey = data->GetInt(TEXT("useColorKey"), 0);
|
|
DWORD keyColor = data->GetInt(TEXT("keyColor"), 0xFFFFFFFF);
|
|
UINT similarity = data->GetInt(TEXT("keySimilarity"), 10);
|
|
UINT blend = data->GetInt(TEXT("keyBlend"), 0);
|
|
|
|
BOOL bUsePointFiltering = data->GetInt(TEXT("usePointFiltering"), 0);
|
|
SendMessage(GetDlgItem(hwnd, IDC_POINTFILTERING), BM_SETCHECK, bUsePointFiltering ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_USECOLORKEY), BM_SETCHECK, bUseColorKey ? BST_CHECKED : BST_UNCHECKED, 0);
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), keyColor);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_SETRANGE32, 0, 100);
|
|
SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_SETPOS32, 0, similarity);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_SETRANGE32, 0, 100);
|
|
SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_SETPOS32, 0, blend);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COLOR), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND), bUseColorKey);
|
|
|
|
//------------------------------------------
|
|
|
|
UINT opacity = data->GetInt(TEXT("opacity"), 100);
|
|
|
|
SendMessage(GetDlgItem(hwnd, IDC_OPACITY2), UDM_SETRANGE32, 0, 100);
|
|
SendMessage(GetDlgItem(hwnd, IDC_OPACITY2), UDM_SETPOS32, 0, opacity);
|
|
|
|
//------------------------------------------
|
|
|
|
int gammaVal = data->GetInt(TEXT("gamma"), 100);
|
|
|
|
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);
|
|
|
|
//--------------------------------------------
|
|
|
|
if (info->dialogID == IDD_CONFIGUREWINDOWCAPTURE)
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_WINDOW, 0), 0);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
case WM_DESTROY:
|
|
if(colorData.bValid)
|
|
{
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
colorData.Clear();
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if(bSelectingColor)
|
|
{
|
|
bMouseDown = true;
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
ConfigDesktopSourceProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COLOR, CCN_CHANGED), (LPARAM)GetDlgItem(hwnd, IDC_COLOR));
|
|
}
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if(bSelectingColor && bMouseDown)
|
|
{
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
ConfigDesktopSourceProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COLOR, CCN_CHANGED), (LPARAM)GetDlgItem(hwnd, IDC_COLOR));
|
|
}
|
|
break;
|
|
|
|
case WM_LBUTTONUP:
|
|
if(bSelectingColor)
|
|
{
|
|
colorData.Clear();
|
|
ReleaseCapture();
|
|
bMouseDown = false;
|
|
bSelectingColor = false;
|
|
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useColorKey"), true);
|
|
}
|
|
break;
|
|
|
|
case WM_CAPTURECHANGED:
|
|
if(bSelectingColor)
|
|
{
|
|
if(colorData.bValid)
|
|
{
|
|
CCSetColor(GetDlgItem(hwnd, IDC_COLOR), colorData.GetColor());
|
|
ConfigDesktopSourceProc(hwnd, WM_COMMAND, MAKEWPARAM(IDC_COLOR, CCN_CHANGED), (LPARAM)GetDlgItem(hwnd, IDC_COLOR));
|
|
colorData.Clear();
|
|
}
|
|
|
|
ReleaseCapture();
|
|
bMouseDown = false;
|
|
bSelectingColor = false;
|
|
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useColorKey"), true);
|
|
}
|
|
break;
|
|
|
|
case WM_HSCROLL:
|
|
{
|
|
if(GetDlgCtrlID((HWND)lParam) == IDC_GAMMA)
|
|
{
|
|
int gamma = SetSliderText(hwnd, IDC_GAMMA, IDC_GAMMAVAL);
|
|
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("gamma"), gamma);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WM_COMMAND:
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_MONITORCAPTURE:
|
|
SetDesktopCaptureType(hwnd, 0);
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_MONITOR, 0), 0);
|
|
|
|
if(OSGetVersion() >= 8)
|
|
{
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COMPATIBILITYMODE), FALSE);
|
|
SendMessage(GetDlgItem(hwnd, IDC_COMPATIBILITYMODE), BM_SETCHECK, BST_UNCHECKED, 0);
|
|
}
|
|
break;
|
|
|
|
case IDC_WINDOWCAPTURE:
|
|
SetDesktopCaptureType(hwnd, 1);
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_WINDOW, 0), 0);
|
|
|
|
if(OSGetVersion() >= 8)
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COMPATIBILITYMODE), TRUE);
|
|
break;
|
|
|
|
case IDC_REGIONCAPTURE:
|
|
{
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
UINT captureType = 0;
|
|
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE)
|
|
captureType = (SendMessage(GetDlgItem(hwnd, IDC_WINDOWCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED) ? 1 : 0;
|
|
else if (info->dialogID == IDD_CONFIGUREMONITORCAPTURE)
|
|
captureType = 0;
|
|
else
|
|
captureType = 1;
|
|
|
|
BOOL bRegion = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_POSX), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_POSY), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SIZEX), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SIZEY), bRegion);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECTREGION), bRegion);
|
|
|
|
if(!bRegion)
|
|
{
|
|
if(captureType == 0)
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_MONITOR, 0), 0);
|
|
else
|
|
PostMessage(hwnd, WM_COMMAND, MAKEWPARAM(IDC_WINDOW, 0), 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_SELECTREGION:
|
|
{
|
|
UINT posX, posY, sizeX, sizeY;
|
|
posX = GetEditText(GetDlgItem(hwnd, IDC_POSX)).ToInt();
|
|
posY = GetEditText(GetDlgItem(hwnd, IDC_POSY)).ToInt();
|
|
sizeX = GetEditText(GetDlgItem(hwnd, IDC_SIZEX)).ToInt();
|
|
sizeY = GetEditText(GetDlgItem(hwnd, IDC_SIZEY)).ToInt();
|
|
|
|
//--------------------------------------------
|
|
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
UINT captureType = 0;
|
|
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE)
|
|
captureType = (SendMessage(GetDlgItem(hwnd, IDC_WINDOWCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED) ? 1 : 0;
|
|
else if (info->dialogID == IDD_CONFIGUREMONITORCAPTURE)
|
|
captureType = 0;
|
|
else
|
|
captureType = 1;
|
|
|
|
BOOL bRegion = (SendMessage(GetDlgItem(hwnd, IDC_REGIONCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
|
|
if(bRegion && captureType == 1)
|
|
{
|
|
UINT windowID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_WINDOW), CB_GETCURSEL, 0, 0);
|
|
if(windowID == CB_ERR) windowID = 0;
|
|
|
|
if(windowID >= info->strClasses.Num())
|
|
{
|
|
OBSMessageBox(hwnd, Str("Sources.SoftwareCaptureSource.WindowNotFound"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
String strWindow = GetCBText(GetDlgItem(hwnd, IDC_WINDOW), windowID);
|
|
|
|
zero(®ionWindowData, sizeof(regionWindowData));
|
|
|
|
regionWindowData.hwndCaptureWindow = FindWindow(info->strClasses[windowID], strWindow);
|
|
if(!regionWindowData.hwndCaptureWindow)
|
|
{
|
|
HWND hwndLastKnownHWND = (HWND)SendMessage(GetDlgItem(hwnd, IDC_WINDOW), CB_GETITEMDATA, windowID, 0);
|
|
if(IsWindow(hwndLastKnownHWND))
|
|
regionWindowData.hwndCaptureWindow = hwndLastKnownHWND;
|
|
else
|
|
regionWindowData.hwndCaptureWindow = FindWindow(info->strClasses[windowID], NULL);
|
|
}
|
|
|
|
if(!regionWindowData.hwndCaptureWindow)
|
|
{
|
|
OBSMessageBox(hwnd, Str("Sources.SoftwareCaptureSource.WindowNotFound"), NULL, 0);
|
|
break;
|
|
}
|
|
else if(IsIconic(regionWindowData.hwndCaptureWindow))
|
|
{
|
|
OBSMessageBox(hwnd, Str("Sources.SoftwareCaptureSource.WindowMinimized"), NULL, 0);
|
|
break;
|
|
}
|
|
|
|
regionWindowData.bInnerWindowRegion = SendMessage(GetDlgItem(hwnd, IDC_INNERWINDOW), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
if(regionWindowData.bInnerWindowRegion)
|
|
{
|
|
GetClientRect(regionWindowData.hwndCaptureWindow, ®ionWindowData.captureRect);
|
|
ClientToScreen(regionWindowData.hwndCaptureWindow, (POINT*)®ionWindowData.captureRect);
|
|
ClientToScreen(regionWindowData.hwndCaptureWindow, (POINT*)®ionWindowData.captureRect.right);
|
|
}
|
|
else
|
|
GetWindowRect(regionWindowData.hwndCaptureWindow, ®ionWindowData.captureRect);
|
|
|
|
posX += regionWindowData.captureRect.left;
|
|
posY += regionWindowData.captureRect.top;
|
|
}
|
|
else
|
|
{
|
|
UINT monitorID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_MONITOR), CB_GETCURSEL, 0, 0);
|
|
if(monitorID == CB_ERR) monitorID = 0;
|
|
|
|
const MonitorInfo &monitor = App->GetMonitor(monitorID);
|
|
regionWindowData.hwndCaptureWindow = NULL;
|
|
mcpy(®ionWindowData.captureRect, &monitor.rect, sizeof(RECT));
|
|
}
|
|
|
|
//--------------------------------------------
|
|
|
|
regionWindowData.hwndConfigDialog = hwnd;
|
|
HWND hwndRegion = CreateWindowEx(WS_EX_TOPMOST, CAPTUREREGIONCLASS, NULL, WS_POPUP|WS_VISIBLE, posX, posY, sizeX, sizeY, hwnd, NULL, hinstMain, NULL);
|
|
|
|
//everyone better thank homeworld for this
|
|
SetWindowLongW(hwndRegion, GWL_EXSTYLE, GetWindowLong(hwndRegion, GWL_EXSTYLE) | WS_EX_LAYERED);
|
|
SetLayeredWindowAttributes(hwndRegion, 0x000000, 0xC0, LWA_ALPHA);
|
|
break;
|
|
}
|
|
|
|
case IDC_SETSTREAMSIZE:
|
|
if (MessageBoxW(hwnd, Str("Sources.SoftwareCaptureSource.ResizeWarning"), Str("Sources.SoftwareCaptureSource.ResizeWarningTitle"), MB_YESNO | MB_ICONWARNING) == IDYES)
|
|
{
|
|
UINT sizeX = (UINT)GetEditText(GetDlgItem(hwnd, IDC_SIZEX)).ToInt();
|
|
UINT sizeY = (UINT)GetEditText(GetDlgItem(hwnd, IDC_SIZEY)).ToInt();
|
|
|
|
if(sizeX < 128)
|
|
sizeX = 128;
|
|
else if(sizeX > 4096)
|
|
sizeX = 4096;
|
|
|
|
if(sizeY < 128)
|
|
sizeY = 128;
|
|
else if(sizeY > 4096)
|
|
sizeY = 4096;
|
|
|
|
AppConfig->SetInt(TEXT("Video"), TEXT("BaseWidth"), sizeX);
|
|
AppConfig->SetInt(TEXT("Video"), TEXT("BaseHeight"), sizeY);
|
|
|
|
if(!App->IsRunning())
|
|
App->ResizeWindow(false);
|
|
}
|
|
break;
|
|
|
|
case IDC_MONITOR:
|
|
{
|
|
UINT id = (UINT)SendMessage(GetDlgItem(hwnd, IDC_MONITOR), CB_GETCURSEL, 0, 0);
|
|
const MonitorInfo &monitor = App->GetMonitor(id);
|
|
|
|
UINT x, y, cx, cy;
|
|
x = monitor.rect.left;
|
|
y = monitor.rect.top;
|
|
cx = monitor.rect.right-monitor.rect.left;
|
|
cy = monitor.rect.bottom-monitor.rect.top;
|
|
|
|
if(cx < 16)
|
|
cx = 16;
|
|
if(cy < 16)
|
|
cy = 16;
|
|
|
|
SetWindowText(GetDlgItem(hwnd, IDC_POSX), IntString(x).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_POSY), IntString(y).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_SIZEX), IntString(cx).Array());
|
|
SetWindowText(GetDlgItem(hwnd, IDC_SIZEY), IntString(cy).Array());
|
|
break;
|
|
}
|
|
|
|
case IDC_WINDOW:
|
|
case IDC_OUTERWINDOW:
|
|
case IDC_INNERWINDOW:
|
|
SelectTargetWindow(hwnd, HIWORD(wParam)==CBN_SELCHANGE);
|
|
break;
|
|
|
|
case IDC_REFRESH:
|
|
{
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
XElement *data = info->data;
|
|
|
|
CTSTR lpWindowName = data->GetString(TEXT("window"));
|
|
|
|
HWND hwndWindowList = GetDlgItem(hwnd, IDC_WINDOW);
|
|
RefreshWindowList(hwndWindowList, info->strClasses);
|
|
|
|
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_INSERTSTRING, 0, (LPARAM)lpWindowName);
|
|
SendMessage(hwndWindowList, CB_SETCURSEL, 0, 0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_USECOLORKEY:
|
|
{
|
|
HWND hwndUseColorKey = (HWND)lParam;
|
|
BOOL bUseColorKey = SendMessage(hwndUseColorKey, BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useColorKey"), bUseColorKey);
|
|
|
|
EnableWindow(GetDlgItem(hwnd, IDC_COLOR), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_SELECT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BASETHRESHOLD), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND_EDIT), bUseColorKey);
|
|
EnableWindow(GetDlgItem(hwnd, IDC_BLEND), bUseColorKey);
|
|
break;
|
|
}
|
|
|
|
case IDC_COLOR:
|
|
{
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
|
|
if(source)
|
|
{
|
|
DWORD color = CCGetColor((HWND)lParam);
|
|
source->SetInt(TEXT("keyColor"), color);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_SELECT:
|
|
{
|
|
if(!bSelectingColor)
|
|
{
|
|
if(colorData.Init())
|
|
{
|
|
bMouseDown = false;
|
|
bSelectingColor = true;
|
|
SetCapture(hwnd);
|
|
HCURSOR hCursor = (HCURSOR)LoadImage(hinstMain, MAKEINTRESOURCE(IDC_COLORPICKER), IMAGE_CURSOR, 32, 32, 0);
|
|
SetCursor(hCursor);
|
|
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
if(source)
|
|
source->SetInt(TEXT("useColorKey"), false);
|
|
}
|
|
else
|
|
colorData.Clear();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IDC_OPACITY_EDIT:
|
|
case IDC_BASETHRESHOLD_EDIT:
|
|
case IDC_BLEND_EDIT:
|
|
if(HIWORD(wParam) == EN_CHANGE)
|
|
{
|
|
ConfigDesktopSourceInfo *configData = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
if(configData)
|
|
{
|
|
ImageSource *source = API->GetSceneImageSource(configData->lpName);
|
|
|
|
if(source)
|
|
{
|
|
HWND hwndVal = NULL;
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_BASETHRESHOLD_EDIT: hwndVal = GetDlgItem(hwnd, IDC_BASETHRESHOLD); break;
|
|
case IDC_BLEND_EDIT: hwndVal = GetDlgItem(hwnd, IDC_BLEND); break;
|
|
case IDC_OPACITY_EDIT: hwndVal = GetDlgItem(hwnd, IDC_OPACITY2); break;
|
|
}
|
|
|
|
int val = (int)SendMessage(hwndVal, UDM_GETPOS32, 0, 0);
|
|
switch(LOWORD(wParam))
|
|
{
|
|
case IDC_BASETHRESHOLD_EDIT: source->SetInt(TEXT("keySimilarity"), val); break;
|
|
case IDC_BLEND_EDIT: source->SetInt(TEXT("keyBlend"), val); break;
|
|
case IDC_OPACITY_EDIT: source->SetInt(TEXT("opacity"), val); break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDOK:
|
|
{
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
|
|
UINT captureType = 0;
|
|
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE) {
|
|
if(SendMessage(GetDlgItem(hwnd, IDC_MONITORCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED)
|
|
captureType = 0;
|
|
else if(SendMessage(GetDlgItem(hwnd, IDC_WINDOWCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED)
|
|
captureType = 1;
|
|
} else if (info->dialogID == IDD_CONFIGUREMONITORCAPTURE) {
|
|
captureType = 0;
|
|
} else { //IDD_CONFIGUREWINDOWCAPTURE
|
|
captureType = 1;
|
|
}
|
|
|
|
BOOL bRegion = (SendMessage(GetDlgItem(hwnd, IDC_REGIONCAPTURE), BM_GETCHECK, 0, 0) == BST_CHECKED);
|
|
|
|
UINT monitorID = 0;
|
|
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE || info->dialogID == IDD_CONFIGUREMONITORCAPTURE)
|
|
monitorID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_MONITOR), CB_GETCURSEL, 0, 0);
|
|
if(monitorID == CB_ERR) monitorID = 0;
|
|
|
|
UINT windowID = 0;
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE || info->dialogID == IDD_CONFIGUREWINDOWCAPTURE)
|
|
windowID = (UINT)SendMessage(GetDlgItem(hwnd, IDC_WINDOW), CB_GETCURSEL, 0, 0);
|
|
if(windowID == CB_ERR) windowID = 0;
|
|
|
|
if(captureType == 1 && windowID >= info->strClasses.Num())
|
|
break;
|
|
|
|
BOOL bInnerWindow = FALSE;
|
|
String strWindow;
|
|
|
|
if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE || info->dialogID == IDD_CONFIGUREWINDOWCAPTURE) {
|
|
strWindow = GetCBText(GetDlgItem(hwnd, IDC_WINDOW), windowID);
|
|
bInnerWindow = SendMessage(GetDlgItem(hwnd, IDC_INNERWINDOW), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
}
|
|
|
|
UINT posX, posY, sizeX, sizeY;
|
|
posX = GetEditText(GetDlgItem(hwnd, IDC_POSX)).ToInt();
|
|
posY = GetEditText(GetDlgItem(hwnd, IDC_POSY)).ToInt();
|
|
sizeX = GetEditText(GetDlgItem(hwnd, IDC_SIZEX)).ToInt();
|
|
sizeY = GetEditText(GetDlgItem(hwnd, IDC_SIZEY)).ToInt();
|
|
|
|
if(sizeX < 16)
|
|
sizeX = 16;
|
|
if(sizeY < 16)
|
|
sizeY = 16;
|
|
|
|
BOOL bCaptureLayered = FALSE;
|
|
BOOL bCaptureMouse = SendMessage(GetDlgItem(hwnd, IDC_CAPTUREMOUSE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
//if (info->dialogID == IDD_CONFIGUREDESKTOPSOURCE || info->dialogID == IDD_CONFIGUREMONITORCAPTURE)
|
|
bCaptureLayered = SendMessage(GetDlgItem(hwnd, IDC_CAPTURELAYERED), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bCompatibilityMode = SendMessage(GetDlgItem(hwnd, IDC_COMPATIBILITYMODE), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
BOOL bUsePointFiltering = SendMessage(GetDlgItem(hwnd, IDC_POINTFILTERING), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
|
|
//---------------------------------
|
|
|
|
XElement *data = info->data;
|
|
|
|
data->SetInt(TEXT("captureType"), captureType);
|
|
|
|
data->SetInt(TEXT("monitor"), monitorID);
|
|
|
|
if(captureType == 1)
|
|
{
|
|
data->SetString(TEXT("window"), strWindow);
|
|
data->SetString(TEXT("windowClass"),info->strClasses[windowID]);
|
|
}
|
|
|
|
data->SetInt(TEXT("innerWindow"), bInnerWindow);
|
|
|
|
data->SetInt(TEXT("regionCapture"), bRegion);
|
|
|
|
data->SetInt(TEXT("captureMouse"), bCaptureMouse);
|
|
|
|
data->SetInt(TEXT("captureLayered"), bCaptureLayered);
|
|
|
|
data->SetInt(TEXT("compatibilityMode"), bCompatibilityMode);
|
|
|
|
data->SetInt(TEXT("usePointFiltering"), bUsePointFiltering);
|
|
|
|
data->SetInt(TEXT("captureX"), posX);
|
|
data->SetInt(TEXT("captureY"), posY);
|
|
data->SetInt(TEXT("captureCX"), sizeX);
|
|
data->SetInt(TEXT("captureCY"), sizeY);
|
|
|
|
//---------------------------------
|
|
|
|
BOOL bUseColorKey = SendMessage(GetDlgItem(hwnd, IDC_USECOLORKEY), BM_GETCHECK, 0, 0) == BST_CHECKED;
|
|
DWORD keyColor = CCGetColor(GetDlgItem(hwnd, IDC_COLOR));
|
|
UINT keySimilarity = (UINT)SendMessage(GetDlgItem(hwnd, IDC_BASETHRESHOLD), UDM_GETPOS32, 0, 0);
|
|
UINT keyBlend = (UINT)SendMessage(GetDlgItem(hwnd, IDC_BLEND), UDM_GETPOS32, 0, 0);
|
|
|
|
data->SetInt(TEXT("useColorKey"), bUseColorKey);
|
|
data->SetInt(TEXT("keyColor"), keyColor);
|
|
data->SetInt(TEXT("keySimilarity"), keySimilarity);
|
|
data->SetInt(TEXT("keyBlend"), keyBlend);
|
|
|
|
//---------------------------------
|
|
|
|
UINT opacity = (UINT)SendMessage(GetDlgItem(hwnd, IDC_OPACITY2), UDM_GETPOS32, 0, 0);
|
|
data->SetInt(TEXT("opacity"), opacity);
|
|
|
|
//---------------------------------
|
|
|
|
int gamma = (int)SendMessage(GetDlgItem(hwnd, IDC_GAMMA), TBM_GETPOS, 0, 0);
|
|
data->SetInt(TEXT("gamma"), gamma);
|
|
|
|
if (captureType == 0 && OSGetVersion() < 8)
|
|
{
|
|
BOOL bComposition;
|
|
DwmIsCompositionEnabled(&bComposition);
|
|
|
|
if (bComposition)
|
|
OBSMessageBox (hwnd, Str("Sources.SoftwareCaptureSource.WarningAero"), TEXT("Warning"), MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
|
|
case IDCANCEL:
|
|
if(LOWORD(wParam) == IDCANCEL)
|
|
{
|
|
ConfigDesktopSourceInfo *info = (ConfigDesktopSourceInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
|
|
ImageSource *source = API->GetSceneImageSource(info->lpName);
|
|
if(source)
|
|
{
|
|
XElement *data = info->data;
|
|
source->SetInt(TEXT("useColorKey"), data->GetInt(TEXT("useColorKey"), 0));
|
|
source->SetInt(TEXT("keyColor"), data->GetInt(TEXT("keyColor"), 0xFFFFFFFF));
|
|
source->SetInt(TEXT("keySimilarity"), data->GetInt(TEXT("keySimilarity"), 10));
|
|
source->SetInt(TEXT("keyBlend"), data->GetInt(TEXT("keyBlend"), 0));
|
|
source->SetInt(TEXT("opacity"), data->GetInt(TEXT("opacity"), 100));
|
|
source->SetInt(TEXT("gamma"), data->GetInt(TEXT("gamma"), 100));
|
|
}
|
|
}
|
|
|
|
EndDialog(hwnd, LOWORD(wParam));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
bool bMadeCaptureRegionClass = false;
|
|
|
|
static bool STDCALL ConfigureDesktopSource2(XElement *element, bool bInitialize, int dialogID)
|
|
{
|
|
if(!bMadeCaptureRegionClass)
|
|
{
|
|
WNDCLASS wc;
|
|
zero(&wc, sizeof(wc));
|
|
wc.hInstance = hinstMain;
|
|
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
wc.lpszClassName = CAPTUREREGIONCLASS;
|
|
wc.lpfnWndProc = (WNDPROC)RegionWindowProc;
|
|
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
|
|
RegisterClass(&wc);
|
|
|
|
bMadeCaptureRegionClass = true;
|
|
}
|
|
|
|
if(!element)
|
|
{
|
|
AppWarning(TEXT("ConfigureDesktopSource: NULL element"));
|
|
return false;
|
|
}
|
|
|
|
XElement *data = element->GetElement(TEXT("data"));
|
|
if(!data)
|
|
data = element->CreateElement(TEXT("data"));
|
|
|
|
ConfigDesktopSourceInfo info;
|
|
info.data = data;
|
|
info.lpName = element->GetName();
|
|
info.dialogID = dialogID;
|
|
|
|
if(OBSDialogBox(hinstMain, MAKEINTRESOURCE(dialogID), hwndMain, ConfigDesktopSourceProc, (LPARAM)&info) == IDOK)
|
|
{
|
|
element->SetInt(TEXT("cx"), data->GetInt(TEXT("captureCX")));
|
|
element->SetInt(TEXT("cy"), data->GetInt(TEXT("captureCY")));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool STDCALL ConfigureDesktopSource(XElement *element, bool bInitialize)
|
|
{
|
|
return ConfigureDesktopSource2(element, bInitialize, IDD_CONFIGUREDESKTOPSOURCE);
|
|
}
|
|
|
|
bool STDCALL ConfigureWindowCaptureSource(XElement *element, bool bInitialize)
|
|
{
|
|
return ConfigureDesktopSource2(element, bInitialize, IDD_CONFIGUREWINDOWCAPTURE);
|
|
}
|
|
|
|
bool STDCALL ConfigureMonitorCaptureSource(XElement *element, bool bInitialize)
|
|
{
|
|
return ConfigureDesktopSource2(element, bInitialize, IDD_CONFIGUREMONITORCAPTURE);
|
|
}
|