Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,82 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP Windows Server cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "wfreerdp-server")
set(MODULE_PREFIX "FREERDP_SERVER_WINDOWS")
include(WarnUnmaintained)
warn_unmaintained(${MODULE_NAME} "-DWITH_PLATFORM_SERVER=OFF")
include_directories(.)
set(${MODULE_PREFIX}_SRCS
wf_update.c
wf_update.h
wf_dxgi.c
wf_dxgi.h
wf_input.c
wf_input.h
wf_interface.c
wf_interface.h
wf_mirage.c
wf_mirage.h
wf_peer.c
wf_peer.h
wf_settings.c
wf_settings.h
wf_info.c
wf_info.h
)
if(CHANNEL_RDPSND AND NOT WITH_RDPSND_DSOUND)
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} wf_rdpsnd.c wf_rdpsnd.h wf_wasapi.c wf_wasapi.h)
endif()
if(CHANNEL_RDPSND AND WITH_RDPSND_DSOUND)
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} wf_rdpsnd.c wf_rdpsnd.h wf_directsound.c wf_directsound.h)
endif()
if(WITH_SERVER_INTERFACE)
addtargetwithresourcefile(${MODULE_NAME} FALSE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
else()
list(APPEND ${MODULE_PREFIX}_SRCS cli/wfreerdp.c cli/wfreerdp.h)
addtargetwithresourcefile(${MODULE_NAME} TRUE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
endif()
if(NOT CMAKE_WINDOWS_VERSION STREQUAL "WINXP")
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} d3d11 dxgi)
endif()
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} dsound)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-server freerdp)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
if(WITH_SERVER_INTERFACE)
install(TARGETS ${MODULE_NAME} COMPONENT libraries ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
else()
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
endif()
if(WITH_SERVER_INTERFACE)
add_subdirectory(cli)
endif()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Windows")

View File

@@ -0,0 +1,3 @@
set(FREERDP_SERVER_NAME "wfreerdp-server")
set(FREERDP_SERVER_PLATFORM "Windows")
set(FREERDP_SERVER_VENDOR "FreeRDP")

View File

@@ -0,0 +1,34 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP Windows Server (CLI) cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(MODULE_NAME "wfreerdp-server-cli")
set(OUTPUT_NAME "wfreerdp-server")
set(MODULE_PREFIX "FREERDP_SERVER_WINDOWS_CLI")
include_directories(..)
set(${MODULE_PREFIX}_SRCS wfreerdp.c wfreerdp.h)
addtargetwithresourcefile(${MODULE_NAME} TRUE "${FREERDP_VERSION}" ${MODULE_PREFIX}_SRCS)
set(${MODULE_PREFIX}_LIBS wfreerdp-server)
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Windows")

View File

@@ -0,0 +1,171 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <winpr/tchar.h>
#include <winpr/windows.h>
#include "wf_interface.h"
#include "wfreerdp.h"
#include <freerdp/server/server-common.h>
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
int IDcount = 0;
BOOL CALLBACK moncb(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
WLog_DBG(TAG, "%d\t(%ld, %ld), (%ld, %ld)", IDcount, lprcMonitor->left, lprcMonitor->top,
lprcMonitor->right, lprcMonitor->bottom);
IDcount++;
return TRUE;
}
int main(int argc, char* argv[])
{
freerdp_server_warn_unmaintained(argc, argv);
BOOL screen_selected = FALSE;
int index = 1;
wfServer* server;
server = wfreerdp_server_new();
set_screen_id(0);
// handle args
errno = 0;
while (index < argc)
{
// first the args that will cause the program to terminate
if (strcmp("--list-screens", argv[index]) == 0)
{
int width;
int height;
int bpp;
WLog_INFO(TAG, "Detecting screens...");
WLog_INFO(TAG, "ID\tResolution\t\tName (Interface)");
for (int i = 0;; i++)
{
_TCHAR name[128] = WINPR_C_ARRAY_INIT;
if (get_screen_info(i, name, ARRAYSIZE(name), &width, &height, &bpp) != 0)
{
if ((width * height * bpp) == 0)
continue;
WLog_INFO(TAG, "%d\t%dx%dx%d\t", i, width, height, bpp);
WLog_INFO(TAG, "%s", name);
}
else
{
break;
}
}
{
int vscreen_w;
int vscreen_h;
vscreen_w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
vscreen_h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
WLog_INFO(TAG, "");
EnumDisplayMonitors(nullptr, nullptr, moncb, 0);
IDcount = 0;
WLog_INFO(TAG, "Virtual Screen = %dx%d", vscreen_w, vscreen_h);
}
return 0;
}
if (strcmp("--screen", argv[index]) == 0)
{
UINT32 val;
screen_selected = TRUE;
index++;
if (index == argc)
{
WLog_INFO(TAG, "missing screen id parameter");
return 0;
}
val = strtoul(argv[index], nullptr, 0);
if ((errno != 0) || (val > UINT32_MAX))
return -1;
set_screen_id(val);
index++;
}
if (index == argc - 1)
{
UINT32 val = strtoul(argv[index], nullptr, 0);
if ((errno != 0) || (val > UINT32_MAX))
return -1;
server->port = val;
break;
}
}
if (screen_selected == FALSE)
{
int width;
int height;
int bpp;
WLog_INFO(TAG, "screen id not provided. attempting to detect...");
WLog_INFO(TAG, "Detecting screens...");
WLog_INFO(TAG, "ID\tResolution\t\tName (Interface)");
for (int i = 0;; i++)
{
_TCHAR name[128] = WINPR_C_ARRAY_INIT;
if (get_screen_info(i, name, ARRAYSIZE(name), &width, &height, &bpp) != 0)
{
if ((width * height * bpp) == 0)
continue;
WLog_INFO(TAG, "%d\t%dx%dx%d\t", i, width, height, bpp);
WLog_INFO(TAG, "%s", name);
set_screen_id(i);
break;
}
else
{
break;
}
}
}
WLog_INFO(TAG, "Starting server");
wfreerdp_server_start(server);
(void)WaitForSingleObject(server->thread, INFINITE);
WLog_INFO(TAG, "Stopping server");
wfreerdp_server_stop(server);
wfreerdp_server_free(server);
return 0;
}

View File

@@ -0,0 +1,25 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_FREERDP_H
#define FREERDP_SERVER_WIN_FREERDP_H
#include <freerdp/freerdp.h>
#endif /* FREERDP_SERVER_WIN_FREERDP_H */

View File

@@ -0,0 +1,219 @@
#include "wf_directsound.h"
#include "wf_interface.h"
#include "wf_info.h"
#include "wf_rdpsnd.h"
#include <winpr/windows.h>
#define INITGUID
#include <initguid.h>
#include <objbase.h>
#define CINTERFACE 1
#include <mmsystem.h>
#include <dsound.h>
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
IDirectSoundCapture8* cap;
IDirectSoundCaptureBuffer8* capBuf;
DSCBUFFERDESC dscbd;
DWORD lastPos;
wfPeerContext* latestPeer;
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
{
latestPeer = peer;
return 0;
}
int wf_directsound_activate(RdpsndServerContext* context)
{
HRESULT hr;
wfInfo* wfi;
HANDLE hThread;
LPDIRECTSOUNDCAPTUREBUFFER pDSCB;
wfi = wf_info_get_instance();
if (!wfi)
{
WLog_ERR(TAG, "Failed to wfi instance");
return 1;
}
WLog_DBG(TAG, "RDPSND (direct sound) Activated");
hr = DirectSoundCaptureCreate8(nullptr, &cap, nullptr);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to create sound capture device");
return 1;
}
WLog_INFO(TAG, "Created sound capture device");
dscbd.dwSize = sizeof(DSCBUFFERDESC);
dscbd.dwFlags = 0;
dscbd.dwBufferBytes = wfi->agreed_format->nAvgBytesPerSec;
dscbd.dwReserved = 0;
dscbd.lpwfxFormat = wfi->agreed_format;
dscbd.dwFXCount = 0;
dscbd.lpDSCFXDesc = nullptr;
hr = cap->lpVtbl->CreateCaptureBuffer(cap, &dscbd, &pDSCB, nullptr);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to create capture buffer");
}
WLog_INFO(TAG, "Created capture buffer");
hr = pDSCB->lpVtbl->QueryInterface(pDSCB, &IID_IDirectSoundCaptureBuffer8, (LPVOID*)&capBuf);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to QI capture buffer");
}
WLog_INFO(TAG, "Created IDirectSoundCaptureBuffer8");
pDSCB->lpVtbl->Release(pDSCB);
lastPos = 0;
if (!(hThread = CreateThread(nullptr, 0, wf_rdpsnd_directsound_thread, latestPeer, 0, nullptr)))
{
WLog_ERR(TAG, "Failed to create direct sound thread");
return 1;
}
(void)CloseHandle(hThread);
return 0;
}
static DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam)
{
HRESULT hr;
DWORD beg = 0;
DWORD end = 0;
DWORD diff, rate;
wfPeerContext* context;
wfInfo* wfi;
VOID* pbCaptureData = nullptr;
DWORD dwCaptureLength = 0;
VOID* pbCaptureData2 = nullptr;
DWORD dwCaptureLength2 = 0;
VOID* pbPlayData = nullptr;
DWORD dwReadPos = 0;
LONG lLockSize = 0;
wfi = wf_info_get_instance();
if (!wfi)
{
WLog_ERR(TAG, "Failed get instance");
return 1;
}
context = (wfPeerContext*)lpParam;
rate = 1000 / 24;
WLog_INFO(TAG, "Trying to start capture");
hr = capBuf->lpVtbl->Start(capBuf, DSCBSTART_LOOPING);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to start capture");
}
WLog_INFO(TAG, "Capture started");
while (1)
{
end = GetTickCount();
diff = end - beg;
if (diff < rate)
{
Sleep(rate - diff);
}
beg = GetTickCount();
if (wf_rdpsnd_lock() > 0)
{
// check for main exit condition
if (wfi->snd_stop == TRUE)
{
wf_rdpsnd_unlock();
break;
}
hr = capBuf->lpVtbl->GetCurrentPosition(capBuf, nullptr, &dwReadPos);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get read pos");
wf_rdpsnd_unlock();
break;
}
lLockSize = dwReadPos - lastPos; // dscbd.dwBufferBytes;
if (lLockSize < 0)
lLockSize += dscbd.dwBufferBytes;
// WLog_DBG(TAG, "Last, read, lock = [%"PRIu32", %"PRIu32", %"PRId32"]\n", lastPos,
// dwReadPos, lLockSize);
if (lLockSize == 0)
{
wf_rdpsnd_unlock();
continue;
}
hr = capBuf->lpVtbl->Lock(capBuf, lastPos, lLockSize, &pbCaptureData, &dwCaptureLength,
&pbCaptureData2, &dwCaptureLength2, 0L);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to lock sound capture buffer");
wf_rdpsnd_unlock();
break;
}
// fwrite(pbCaptureData, 1, dwCaptureLength, pFile);
// fwrite(pbCaptureData2, 1, dwCaptureLength2, pFile);
// FIXME: frames = bytes/(bytespersample * channels)
context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData, dwCaptureLength / 4,
(UINT16)(beg & 0xffff));
context->rdpsnd->SendSamples(context->rdpsnd, pbCaptureData2, dwCaptureLength2 / 4,
(UINT16)(beg & 0xffff));
hr = capBuf->lpVtbl->Unlock(capBuf, pbCaptureData, dwCaptureLength, pbCaptureData2,
dwCaptureLength2);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to unlock sound capture buffer");
wf_rdpsnd_unlock();
return 0;
}
// TODO keep track of location in buffer
lastPos += dwCaptureLength;
lastPos %= dscbd.dwBufferBytes;
lastPos += dwCaptureLength2;
lastPos %= dscbd.dwBufferBytes;
wf_rdpsnd_unlock();
}
}
WLog_INFO(TAG, "Trying to stop sound capture");
hr = capBuf->lpVtbl->Stop(capBuf);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to stop capture");
}
WLog_INFO(TAG, "Capture stopped");
capBuf->lpVtbl->Release(capBuf);
cap->lpVtbl->Release(cap);
lastPos = 0;
return 0;
}

View File

@@ -0,0 +1,13 @@
#ifndef FREERDP_SERVER_WIN_DSOUND_H
#define FREERDP_SERVER_WIN_DSOUND_H
#include <freerdp/server/rdpsnd.h>
#include "wf_interface.h"
WINPR_ATTR_NODISCARD int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
WINPR_ATTR_NODISCARD int wf_directsound_activate(RdpsndServerContext* context);
WINPR_ATTR_NODISCARD DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam);
#endif /* FREERDP_SERVER_WIN_DSOUND_H */

View File

@@ -0,0 +1,486 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include "wf_interface.h"
#ifdef WITH_DXGI_1_2
#define CINTERFACE
#include <D3D11.h>
#include <dxgi1_2.h>
#include <tchar.h>
#include "wf_dxgi.h"
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
/* Driver types supported */
D3D_DRIVER_TYPE DriverTypes[] = {
D3D_DRIVER_TYPE_HARDWARE,
D3D_DRIVER_TYPE_WARP,
D3D_DRIVER_TYPE_REFERENCE,
};
UINT NumDriverTypes = ARRAYSIZE(DriverTypes);
/* Feature levels supported */
D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_1 };
UINT NumFeatureLevels = ARRAYSIZE(FeatureLevels);
D3D_FEATURE_LEVEL FeatureLevel;
ID3D11Device* gDevice = nullptr;
ID3D11DeviceContext* gContext = nullptr;
IDXGIOutputDuplication* gOutputDuplication = nullptr;
ID3D11Texture2D* gAcquiredDesktopImage = nullptr;
IDXGISurface* surf;
ID3D11Texture2D* sStage;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;
int wf_dxgi_init(wfInfo* wfi)
{
gAcquiredDesktopImage = nullptr;
if (wf_dxgi_createDevice(wfi) != 0)
{
return 1;
}
if (wf_dxgi_getDuplication(wfi) != 0)
{
return 1;
}
return 0;
}
int wf_dxgi_createDevice(wfInfo* wfi)
{
HRESULT status;
UINT DriverTypeIndex;
for (DriverTypeIndex = 0; DriverTypeIndex < NumDriverTypes; ++DriverTypeIndex)
{
status = D3D11CreateDevice(nullptr, DriverTypes[DriverTypeIndex], nullptr, 0, FeatureLevels,
NumFeatureLevels, D3D11_SDK_VERSION, &gDevice, &FeatureLevel,
&gContext);
if (SUCCEEDED(status))
break;
WLog_INFO(TAG, "D3D11CreateDevice returned [%ld] for Driver Type %d", status,
DriverTypes[DriverTypeIndex]);
}
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to create device in InitializeDx");
return 1;
}
return 0;
}
int wf_dxgi_getDuplication(wfInfo* wfi)
{
HRESULT status;
UINT dTop, i = 0;
DXGI_OUTPUT_DESC desc = WINPR_C_ARRAY_INIT;
IDXGIOutput* pOutput;
IDXGIDevice* DxgiDevice = nullptr;
IDXGIAdapter* DxgiAdapter = nullptr;
IDXGIOutput* DxgiOutput = nullptr;
IDXGIOutput1* DxgiOutput1 = nullptr;
status = gDevice->lpVtbl->QueryInterface(gDevice, &IID_IDXGIDevice, (void**)&DxgiDevice);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get QI for DXGI Device");
return 1;
}
status = DxgiDevice->lpVtbl->GetParent(DxgiDevice, &IID_IDXGIAdapter, (void**)&DxgiAdapter);
DxgiDevice->lpVtbl->Release(DxgiDevice);
DxgiDevice = nullptr;
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get parent DXGI Adapter");
return 1;
}
pOutput = nullptr;
while (DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, i, &pOutput) != DXGI_ERROR_NOT_FOUND)
{
DXGI_OUTPUT_DESC* pDesc = &desc;
status = pOutput->lpVtbl->GetDesc(pOutput, pDesc);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get description");
return 1;
}
WLog_INFO(TAG, "Output %u: [%s] [%d]", i, pDesc->DeviceName, pDesc->AttachedToDesktop);
if (pDesc->AttachedToDesktop)
dTop = i;
pOutput->lpVtbl->Release(pOutput);
++i;
}
dTop = wfi->screenID;
status = DxgiAdapter->lpVtbl->EnumOutputs(DxgiAdapter, dTop, &DxgiOutput);
DxgiAdapter->lpVtbl->Release(DxgiAdapter);
DxgiAdapter = nullptr;
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get output");
return 1;
}
status =
DxgiOutput->lpVtbl->QueryInterface(DxgiOutput, &IID_IDXGIOutput1, (void**)&DxgiOutput1);
DxgiOutput->lpVtbl->Release(DxgiOutput);
DxgiOutput = nullptr;
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get IDXGIOutput1");
return 1;
}
status =
DxgiOutput1->lpVtbl->DuplicateOutput(DxgiOutput1, (IUnknown*)gDevice, &gOutputDuplication);
DxgiOutput1->lpVtbl->Release(DxgiOutput1);
DxgiOutput1 = nullptr;
if (FAILED(status))
{
if (status == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
WLog_ERR(
TAG,
"There is already the maximum number of applications using the Desktop Duplication "
"API running, please close one of those applications and then try again.");
return 1;
}
WLog_ERR(TAG, "Failed to get duplicate output. Status = %ld", status);
return 1;
}
return 0;
}
int wf_dxgi_cleanup(wfInfo* wfi)
{
if (wfi->framesWaiting > 0)
{
wf_dxgi_releasePixelData(wfi);
}
if (gAcquiredDesktopImage)
{
gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
gAcquiredDesktopImage = nullptr;
}
if (gOutputDuplication)
{
gOutputDuplication->lpVtbl->Release(gOutputDuplication);
gOutputDuplication = nullptr;
}
if (gContext)
{
gContext->lpVtbl->Release(gContext);
gContext = nullptr;
}
if (gDevice)
{
gDevice->lpVtbl->Release(gDevice);
gDevice = nullptr;
}
return 0;
}
int wf_dxgi_nextFrame(wfInfo* wfi, UINT timeout)
{
HRESULT status = 0;
UINT i = 0;
UINT DataBufferSize = 0;
BYTE* DataBuffer = nullptr;
IDXGIResource* DesktopResource = nullptr;
if (wfi->framesWaiting > 0)
{
wf_dxgi_releasePixelData(wfi);
}
if (gAcquiredDesktopImage)
{
gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
gAcquiredDesktopImage = nullptr;
}
status = gOutputDuplication->lpVtbl->AcquireNextFrame(gOutputDuplication, timeout, &FrameInfo,
&DesktopResource);
if (status == DXGI_ERROR_WAIT_TIMEOUT)
{
return 1;
}
if (FAILED(status))
{
if (status == DXGI_ERROR_ACCESS_LOST)
{
WLog_ERR(TAG, "Failed to acquire next frame with status=%ld", status);
WLog_ERR(TAG, "Trying to reinitialize due to ACCESS LOST...");
if (gAcquiredDesktopImage)
{
gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
gAcquiredDesktopImage = nullptr;
}
if (gOutputDuplication)
{
gOutputDuplication->lpVtbl->Release(gOutputDuplication);
gOutputDuplication = nullptr;
}
wf_dxgi_getDuplication(wfi);
return 1;
}
else
{
WLog_ERR(TAG, "Failed to acquire next frame with status=%ld", status);
status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to release frame with status=%ld", status);
}
return 1;
}
}
status = DesktopResource->lpVtbl->QueryInterface(DesktopResource, &IID_ID3D11Texture2D,
(void**)&gAcquiredDesktopImage);
DesktopResource->lpVtbl->Release(DesktopResource);
DesktopResource = nullptr;
if (FAILED(status))
{
return 1;
}
wfi->framesWaiting = FrameInfo.AccumulatedFrames;
if (FrameInfo.AccumulatedFrames == 0)
{
status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to release frame with status=%ld", status);
}
}
return 0;
}
int wf_dxgi_getPixelData(wfInfo* wfi, BYTE** data, int* pitch, RECT* invalid)
{
HRESULT status;
D3D11_BOX Box;
DXGI_MAPPED_RECT mappedRect;
D3D11_TEXTURE2D_DESC tDesc;
tDesc.Width = (invalid->right - invalid->left);
tDesc.Height = (invalid->bottom - invalid->top);
tDesc.MipLevels = 1;
tDesc.ArraySize = 1;
tDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
tDesc.SampleDesc.Count = 1;
tDesc.SampleDesc.Quality = 0;
tDesc.Usage = D3D11_USAGE_STAGING;
tDesc.BindFlags = 0;
tDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
tDesc.MiscFlags = 0;
Box.top = invalid->top;
Box.left = invalid->left;
Box.right = invalid->right;
Box.bottom = invalid->bottom;
Box.front = 0;
Box.back = 1;
status = gDevice->lpVtbl->CreateTexture2D(gDevice, &tDesc, nullptr, &sStage);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to create staging surface");
exit(1);
return 1;
}
gContext->lpVtbl->CopySubresourceRegion(gContext, (ID3D11Resource*)sStage, 0, 0, 0, 0,
(ID3D11Resource*)gAcquiredDesktopImage, 0, &Box);
status = sStage->lpVtbl->QueryInterface(sStage, &IID_IDXGISurface, (void**)&surf);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to QI staging surface");
exit(1);
return 1;
}
surf->lpVtbl->Map(surf, &mappedRect, DXGI_MAP_READ);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to map staging surface");
exit(1);
return 1;
}
*data = mappedRect.pBits;
*pitch = mappedRect.Pitch;
return 0;
}
int wf_dxgi_releasePixelData(wfInfo* wfi)
{
HRESULT status;
surf->lpVtbl->Unmap(surf);
surf->lpVtbl->Release(surf);
surf = nullptr;
sStage->lpVtbl->Release(sStage);
sStage = nullptr;
status = gOutputDuplication->lpVtbl->ReleaseFrame(gOutputDuplication);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to release frame");
return 1;
}
wfi->framesWaiting = 0;
return 0;
}
int wf_dxgi_getInvalidRegion(RECT* invalid)
{
HRESULT status;
UINT dirty;
UINT BufSize;
RECT* pRect;
BYTE* DirtyRects;
UINT DataBufferSize = 0;
BYTE* DataBuffer = nullptr;
if (FrameInfo.AccumulatedFrames == 0)
{
return 1;
}
if (FrameInfo.TotalMetadataBufferSize)
{
if (FrameInfo.TotalMetadataBufferSize > DataBufferSize)
{
if (DataBuffer)
{
free(DataBuffer);
DataBuffer = nullptr;
}
DataBuffer = (BYTE*)malloc(FrameInfo.TotalMetadataBufferSize);
if (!DataBuffer)
{
DataBufferSize = 0;
WLog_ERR(TAG, "Failed to allocate memory for metadata");
exit(1);
}
DataBufferSize = FrameInfo.TotalMetadataBufferSize;
}
BufSize = FrameInfo.TotalMetadataBufferSize;
status = gOutputDuplication->lpVtbl->GetFrameMoveRects(
gOutputDuplication, BufSize, (DXGI_OUTDUPL_MOVE_RECT*)DataBuffer, &BufSize);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get frame move rects");
return 1;
}
DirtyRects = DataBuffer + BufSize;
BufSize = FrameInfo.TotalMetadataBufferSize - BufSize;
status = gOutputDuplication->lpVtbl->GetFrameDirtyRects(gOutputDuplication, BufSize,
(RECT*)DirtyRects, &BufSize);
if (FAILED(status))
{
WLog_ERR(TAG, "Failed to get frame dirty rects");
return 1;
}
dirty = BufSize / sizeof(RECT);
pRect = (RECT*)DirtyRects;
for (UINT i = 0; i < dirty; ++i)
{
UnionRect(invalid, invalid, pRect);
++pRect;
}
}
return 0;
}
#endif

View File

@@ -0,0 +1,42 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_DXGI_H
#define FREERDP_SERVER_WIN_DXGI_H
#include "wf_interface.h"
WINPR_ATTR_NODISCARD int wf_dxgi_init(wfInfo* context);
WINPR_ATTR_NODISCARD int wf_dxgi_createDevice(wfInfo* context);
WINPR_ATTR_NODISCARD int wf_dxgi_getDuplication(wfInfo* context);
WINPR_ATTR_NODISCARD int wf_dxgi_cleanup(wfInfo* context);
WINPR_ATTR_NODISCARD int wf_dxgi_nextFrame(wfInfo* context, UINT timeout);
WINPR_ATTR_NODISCARD int wf_dxgi_getPixelData(wfInfo* context, BYTE** data, int* pitch,
RECT* invalid);
WINPR_ATTR_NODISCARD int wf_dxgi_releasePixelData(wfInfo* context);
WINPR_ATTR_NODISCARD int wf_dxgi_getInvalidRegion(RECT* invalid);
#endif /* FREERDP_SERVER_WIN_DXGI_H */

View File

@@ -0,0 +1,403 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* FreeRDP Windows Server
*
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <stdlib.h>
#include <freerdp/build-config.h>
#include <winpr/tchar.h>
#include <winpr/windows.h>
#include "wf_info.h"
#include "wf_update.h"
#include "wf_mirage.h"
#include "wf_dxgi.h"
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
#define SERVER_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING "\\Server"
static wfInfo* wfInfoInstance = nullptr;
static int _IDcount = 0;
BOOL wf_info_lock(wfInfo* wfi)
{
DWORD dRes;
dRes = WaitForSingleObject(wfi->mutex, INFINITE);
switch (dRes)
{
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return TRUE;
case WAIT_TIMEOUT:
return FALSE;
case WAIT_FAILED:
WLog_ERR(TAG, "wf_info_lock failed with 0x%08lX", GetLastError());
return FALSE;
}
return FALSE;
}
BOOL wf_info_try_lock(wfInfo* wfi, DWORD dwMilliseconds)
{
DWORD dRes;
dRes = WaitForSingleObject(wfi->mutex, dwMilliseconds);
switch (dRes)
{
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return TRUE;
case WAIT_TIMEOUT:
return FALSE;
case WAIT_FAILED:
WLog_ERR(TAG, "wf_info_try_lock failed with 0x%08lX", GetLastError());
return FALSE;
}
return FALSE;
}
BOOL wf_info_unlock(wfInfo* wfi)
{
if (!ReleaseMutex(wfi->mutex))
{
WLog_ERR(TAG, "wf_info_unlock failed with 0x%08lX", GetLastError());
return FALSE;
}
return TRUE;
}
wfInfo* wf_info_init()
{
wfInfo* wfi;
wfi = (wfInfo*)calloc(1, sizeof(wfInfo));
if (wfi != nullptr)
{
HKEY hKey;
LONG status;
DWORD dwType;
DWORD dwSize;
DWORD dwValue;
wfi->mutex = CreateMutex(nullptr, FALSE, nullptr);
if (wfi->mutex == nullptr)
{
WLog_ERR(TAG, "CreateMutex error: %lu", GetLastError());
free(wfi);
return nullptr;
}
wfi->updateSemaphore = CreateSemaphore(nullptr, 0, 32, nullptr);
if (!wfi->updateSemaphore)
{
WLog_ERR(TAG, "CreateSemaphore error: %lu", GetLastError());
(void)CloseHandle(wfi->mutex);
free(wfi);
return nullptr;
}
wfi->updateThread =
CreateThread(nullptr, 0, wf_update_thread, wfi, CREATE_SUSPENDED, nullptr);
if (!wfi->updateThread)
{
WLog_ERR(TAG, "Failed to create update thread");
(void)CloseHandle(wfi->mutex);
(void)CloseHandle(wfi->updateSemaphore);
free(wfi);
return nullptr;
}
wfi->peers =
(freerdp_peer**)calloc(FREERDP_SERVER_WIN_INFO_MAXPEERS, sizeof(freerdp_peer*));
if (!wfi->peers)
{
WLog_ERR(TAG, "Failed to allocate memory for peer");
(void)CloseHandle(wfi->mutex);
(void)CloseHandle(wfi->updateSemaphore);
(void)CloseHandle(wfi->updateThread);
free(wfi);
return nullptr;
}
// Set FPS
wfi->framesPerSecond = FREERDP_SERVER_WIN_INFO_DEFAULT_FPS;
status =
RegOpenKeyExA(HKEY_LOCAL_MACHINE, SERVER_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
if (status == ERROR_SUCCESS)
{
if (RegQueryValueEx(hKey, _T("FramesPerSecond"), nullptr, &dwType, (BYTE*)&dwValue,
&dwSize) == ERROR_SUCCESS)
wfi->framesPerSecond = dwValue;
}
RegCloseKey(hKey);
// Set input toggle
wfi->input_disabled = FALSE;
status =
RegOpenKeyExA(HKEY_LOCAL_MACHINE, SERVER_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
if (status == ERROR_SUCCESS)
{
if (RegQueryValueEx(hKey, _T("DisableInput"), nullptr, &dwType, (BYTE*)&dwValue,
&dwSize) == ERROR_SUCCESS)
{
if (dwValue != 0)
wfi->input_disabled = TRUE;
}
}
RegCloseKey(hKey);
}
return wfi;
}
wfInfo* wf_info_get_instance()
{
if (wfInfoInstance == nullptr)
wfInfoInstance = wf_info_init();
return wfInfoInstance;
}
BOOL wf_info_peer_register(wfInfo* wfi, wfPeerContext* context)
{
int peerId = 0;
if (!wfi || !context)
return FALSE;
if (!wf_info_lock(wfi))
return FALSE;
if (wfi->peerCount == FREERDP_SERVER_WIN_INFO_MAXPEERS)
goto fail_peer_count;
context->info = wfi;
if (!(context->updateEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
goto fail_update_event;
// get the offset of the top left corner of selected screen
EnumDisplayMonitors(nullptr, nullptr, wf_info_monEnumCB, 0);
_IDcount = 0;
#ifdef WITH_DXGI_1_2
if (wfi->peerCount == 0)
if (wf_dxgi_init(wfi) != 0)
goto fail_driver_init;
#else
if (!wf_mirror_driver_activate(wfi))
goto fail_driver_init;
#endif
// look through the array of peers until an empty slot
for (int i = 0; i < FREERDP_SERVER_WIN_INFO_MAXPEERS; ++i)
{
// empty index will be our peer id
if (wfi->peers[i] == nullptr)
{
peerId = i;
break;
}
}
wfi->peers[peerId] = ((rdpContext*)context)->peer;
wfi->peers[peerId]->pId = peerId;
wfi->peerCount++;
WLog_INFO(TAG, "Registering Peer: id=%d #=%d", peerId, wfi->peerCount);
wf_info_unlock(wfi);
wfreerdp_server_peer_callback_event(peerId, FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_CONNECT);
return TRUE;
fail_driver_init:
(void)CloseHandle(context->updateEvent);
context->updateEvent = nullptr;
fail_update_event:
fail_peer_count:
context->socketClose = TRUE;
wf_info_unlock(wfi);
return FALSE;
}
void wf_info_peer_unregister(wfInfo* wfi, wfPeerContext* context)
{
if (wf_info_lock(wfi))
{
int peerId;
peerId = ((rdpContext*)context)->peer->pId;
wfi->peers[peerId] = nullptr;
wfi->peerCount--;
(void)CloseHandle(context->updateEvent);
WLog_INFO(TAG, "Unregistering Peer: id=%d, #=%d", peerId, wfi->peerCount);
#ifdef WITH_DXGI_1_2
if (wfi->peerCount == 0)
wf_dxgi_cleanup(wfi);
#endif
wf_info_unlock(wfi);
wfreerdp_server_peer_callback_event(peerId,
FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_DISCONNECT);
}
}
BOOL wf_info_have_updates(wfInfo* wfi)
{
#ifdef WITH_DXGI_1_2
if (wfi->framesWaiting == 0)
return FALSE;
#else
if (wfi->nextUpdate == wfi->lastUpdate)
return FALSE;
#endif
return TRUE;
}
void wf_info_update_changes(wfInfo* wfi)
{
#ifdef WITH_DXGI_1_2
wf_dxgi_nextFrame(wfi, wfi->framesPerSecond * 1000);
#else
GETCHANGESBUF* buf;
buf = (GETCHANGESBUF*)wfi->changeBuffer;
wfi->nextUpdate = buf->buffer->counter;
#endif
}
void wf_info_find_invalid_region(wfInfo* wfi)
{
#ifdef WITH_DXGI_1_2
wf_dxgi_getInvalidRegion(&wfi->invalid);
#else
GETCHANGESBUF* buf;
buf = (GETCHANGESBUF*)wfi->changeBuffer;
for (ULONG i = wfi->lastUpdate; i != wfi->nextUpdate; i = (i + 1) % MAXCHANGES_BUF)
{
LPRECT lpR = &buf->buffer->pointrect[i].rect;
// need to make sure we only get updates from the selected screen
if ((lpR->left >= wfi->servscreen_xoffset) &&
(lpR->right <= (wfi->servscreen_xoffset + wfi->servscreen_width)) &&
(lpR->top >= wfi->servscreen_yoffset) &&
(lpR->bottom <= (wfi->servscreen_yoffset + wfi->servscreen_height)))
{
UnionRect(&wfi->invalid, &wfi->invalid, lpR);
}
else
{
continue;
}
}
#endif
if (wfi->invalid.left < 0)
wfi->invalid.left = 0;
if (wfi->invalid.top < 0)
wfi->invalid.top = 0;
if (wfi->invalid.right >= wfi->servscreen_width)
wfi->invalid.right = wfi->servscreen_width - 1;
if (wfi->invalid.bottom >= wfi->servscreen_height)
wfi->invalid.bottom = wfi->servscreen_height - 1;
// WLog_DBG(TAG, "invalid region: (%"PRId32", %"PRId32"), (%"PRId32", %"PRId32")",
// wfi->invalid.left, wfi->invalid.top, wfi->invalid.right, wfi->invalid.bottom);
}
void wf_info_clear_invalid_region(wfInfo* wfi)
{
wfi->lastUpdate = wfi->nextUpdate;
SetRectEmpty(&wfi->invalid);
}
void wf_info_invalidate_full_screen(wfInfo* wfi)
{
SetRect(&wfi->invalid, 0, 0, wfi->servscreen_width, wfi->servscreen_height);
}
BOOL wf_info_have_invalid_region(wfInfo* wfi)
{
return IsRectEmpty(&wfi->invalid);
}
void wf_info_getScreenData(wfInfo* wfi, long* width, long* height, BYTE** pBits, int* pitch)
{
*width = (wfi->invalid.right - wfi->invalid.left);
*height = (wfi->invalid.bottom - wfi->invalid.top);
#ifdef WITH_DXGI_1_2
wf_dxgi_getPixelData(wfi, pBits, pitch, &wfi->invalid);
#else
{
long offset;
GETCHANGESBUF* changes;
changes = (GETCHANGESBUF*)wfi->changeBuffer;
*width += 1;
*height += 1;
offset = (4 * wfi->invalid.left) + (wfi->invalid.top * wfi->virtscreen_width * 4);
*pBits = ((BYTE*)(changes->Userbuffer)) + offset;
*pitch = wfi->virtscreen_width * 4;
}
#endif
}
BOOL CALLBACK wf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor,
LPARAM dwData)
{
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
if (_IDcount == wfi->screenID)
{
wfi->servscreen_xoffset = lprcMonitor->left;
wfi->servscreen_yoffset = lprcMonitor->top;
}
_IDcount++;
return TRUE;
}

View File

@@ -0,0 +1,46 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_INFO_H
#define FREERDP_SERVER_WIN_INFO_H
#include "wf_interface.h"
#define FREERDP_SERVER_WIN_INFO_DEFAULT_FPS 24
#define FREERDP_SERVER_WIN_INFO_MAXPEERS 32
WINPR_ATTR_NODISCARD BOOL wf_info_lock(wfInfo* wfi);
WINPR_ATTR_NODISCARD BOOL wf_info_try_lock(wfInfo* wfi, DWORD dwMilliseconds);
WINPR_ATTR_NODISCARD BOOL wf_info_unlock(wfInfo* wfi);
WINPR_ATTR_NODISCARD wfInfo* wf_info_get_instance(void);
WINPR_ATTR_NODISCARD BOOL wf_info_peer_register(wfInfo* wfi, wfPeerContext* context);
void wf_info_peer_unregister(wfInfo* wfi, wfPeerContext* context);
WINPR_ATTR_NODISCARD BOOL wf_info_have_updates(wfInfo* wfi);
void wf_info_update_changes(wfInfo* wfi);
void wf_info_find_invalid_region(wfInfo* wfi);
void wf_info_clear_invalid_region(wfInfo* wfi);
void wf_info_invalidate_full_screen(wfInfo* wfi);
WINPR_ATTR_NODISCARD BOOL wf_info_have_invalid_region(wfInfo* wfi);
void wf_info_getScreenData(wfInfo* wfi, long* width, long* height, BYTE** pBits, int* pitch);
WINPR_ATTR_NODISCARD BOOL CALLBACK wf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor,
LPRECT lprcMonitor, LPARAM dwData);
#endif /* FREERDP_SERVER_WIN_INFO_H */

View File

@@ -0,0 +1,223 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <winpr/windows.h>
#include "wf_input.h"
#include "wf_info.h"
BOOL wf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code)
{
INPUT keyboard_event;
WINPR_UNUSED(input);
keyboard_event.type = INPUT_KEYBOARD;
keyboard_event.u.ki.wVk = 0;
keyboard_event.u.ki.wScan = code;
keyboard_event.u.ki.dwFlags = KEYEVENTF_SCANCODE;
keyboard_event.u.ki.dwExtraInfo = 0;
keyboard_event.u.ki.time = 0;
if (flags & KBD_FLAGS_RELEASE)
keyboard_event.u.ki.dwFlags |= KEYEVENTF_KEYUP;
if (flags & KBD_FLAGS_EXTENDED)
keyboard_event.u.ki.dwFlags |= KEYEVENTF_EXTENDEDKEY;
SendInput(1, &keyboard_event, sizeof(INPUT));
return TRUE;
}
BOOL wf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
{
INPUT keyboard_event;
WINPR_UNUSED(input);
keyboard_event.type = INPUT_KEYBOARD;
keyboard_event.u.ki.wVk = 0;
keyboard_event.u.ki.wScan = code;
keyboard_event.u.ki.dwFlags = KEYEVENTF_UNICODE;
keyboard_event.u.ki.dwExtraInfo = 0;
keyboard_event.u.ki.time = 0;
if (flags & KBD_FLAGS_RELEASE)
keyboard_event.u.ki.dwFlags |= KEYEVENTF_KEYUP;
SendInput(1, &keyboard_event, sizeof(INPUT));
return TRUE;
}
BOOL wf_peer_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
INPUT mouse_event = WINPR_C_ARRAY_INIT;
float width, height;
WINPR_UNUSED(input);
WINPR_ASSERT(input);
mouse_event.type = INPUT_MOUSE;
if (flags & PTR_FLAGS_WHEEL)
{
mouse_event.u.mi.dwFlags = MOUSEEVENTF_WHEEL;
mouse_event.u.mi.mouseData = flags & WheelRotationMask;
if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
mouse_event.u.mi.mouseData *= -1;
SendInput(1, &mouse_event, sizeof(INPUT));
}
else
{
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
// width and height of primary screen (even in multimon setups
width = (float)GetSystemMetrics(SM_CXSCREEN);
height = (float)GetSystemMetrics(SM_CYSCREEN);
x += wfi->servscreen_xoffset;
y += wfi->servscreen_yoffset;
mouse_event.u.mi.dx = (LONG)((float)x * (65535.0f / width));
mouse_event.u.mi.dy = (LONG)((float)y * (65535.0f / height));
mouse_event.u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
if (flags & PTR_FLAGS_MOVE)
{
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_MOVE;
SendInput(1, &mouse_event, sizeof(INPUT));
}
mouse_event.u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
if (flags & PTR_FLAGS_BUTTON1)
{
if (flags & PTR_FLAGS_DOWN)
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
else
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
SendInput(1, &mouse_event, sizeof(INPUT));
}
else if (flags & PTR_FLAGS_BUTTON2)
{
if (flags & PTR_FLAGS_DOWN)
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
else
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
SendInput(1, &mouse_event, sizeof(INPUT));
}
else if (flags & PTR_FLAGS_BUTTON3)
{
if (flags & PTR_FLAGS_DOWN)
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
else
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_MIDDLEUP;
SendInput(1, &mouse_event, sizeof(INPUT));
}
}
return TRUE;
}
BOOL wf_peer_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
if ((flags & PTR_XFLAGS_BUTTON1) || (flags & PTR_XFLAGS_BUTTON2))
{
INPUT mouse_event = WINPR_C_ARRAY_INIT;
mouse_event.type = INPUT_MOUSE;
if (flags & PTR_FLAGS_MOVE)
{
float width, height;
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
// width and height of primary screen (even in multimon setups
width = (float)GetSystemMetrics(SM_CXSCREEN);
height = (float)GetSystemMetrics(SM_CYSCREEN);
x += wfi->servscreen_xoffset;
y += wfi->servscreen_yoffset;
mouse_event.u.mi.dx = (LONG)((float)x * (65535.0f / width));
mouse_event.u.mi.dy = (LONG)((float)y * (65535.0f / height));
mouse_event.u.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
SendInput(1, &mouse_event, sizeof(INPUT));
}
mouse_event.u.mi.dx = mouse_event.u.mi.dy = mouse_event.u.mi.dwFlags = 0;
if (flags & PTR_XFLAGS_DOWN)
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_XDOWN;
else
mouse_event.u.mi.dwFlags |= MOUSEEVENTF_XUP;
if (flags & PTR_XFLAGS_BUTTON1)
mouse_event.u.mi.mouseData = XBUTTON1;
else if (flags & PTR_XFLAGS_BUTTON2)
mouse_event.u.mi.mouseData = XBUTTON2;
SendInput(1, &mouse_event, sizeof(INPUT));
}
else
{
wf_peer_mouse_event(input, flags, x, y);
}
return TRUE;
}
BOOL wf_peer_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT8 code)
{
WINPR_UNUSED(input);
WINPR_UNUSED(flags);
WINPR_UNUSED(code);
return TRUE;
}
BOOL wf_peer_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code)
{
WINPR_UNUSED(input);
WINPR_UNUSED(flags);
WINPR_UNUSED(code);
return TRUE;
}
BOOL wf_peer_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
WINPR_UNUSED(input);
WINPR_UNUSED(flags);
WINPR_UNUSED(x);
WINPR_UNUSED(y);
return TRUE;
}
BOOL wf_peer_extended_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y)
{
WINPR_UNUSED(input);
WINPR_UNUSED(flags);
WINPR_UNUSED(x);
WINPR_UNUSED(y);
return TRUE;
}

View File

@@ -0,0 +1,41 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_INPUT_H
#define FREERDP_SERVER_WIN_INPUT_H
#include "wf_interface.h"
WINPR_ATTR_NODISCARD BOOL wf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code);
WINPR_ATTR_NODISCARD BOOL wf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags,
UINT16 code);
WINPR_ATTR_NODISCARD BOOL wf_peer_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
WINPR_ATTR_NODISCARD BOOL wf_peer_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
UINT16 y);
// dummy versions
WINPR_ATTR_NODISCARD BOOL wf_peer_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT8 code);
WINPR_ATTR_NODISCARD BOOL wf_peer_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags,
UINT16 code);
WINPR_ATTR_NODISCARD BOOL wf_peer_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x,
UINT16 y);
WINPR_ATTR_NODISCARD BOOL wf_peer_extended_mouse_event_dummy(rdpInput* input, UINT16 flags,
UINT16 x, UINT16 y);
#endif /* FREERDP_SERVER_WIN_INPUT_H */

View File

@@ -0,0 +1,342 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <winpr/tchar.h>
#include <winpr/windows.h>
#include <winpr/winsock.h>
#include <winpr/assert.h>
#include <freerdp/freerdp.h>
#include <freerdp/listener.h>
#include <freerdp/constants.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/channels/channels.h>
#include <freerdp/build-config.h>
#include "wf_peer.h"
#include "wf_settings.h"
#include "wf_info.h"
#include "wf_interface.h"
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
#define SERVER_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING "\\Server"
static cbCallback cbEvent = nullptr;
int get_screen_info(int id, _TCHAR* name, size_t length, int* width, int* height, int* bpp)
{
DISPLAY_DEVICE dd = WINPR_C_ARRAY_INIT;
dd.cb = sizeof(DISPLAY_DEVICE);
if (EnumDisplayDevices(nullptr, id, &dd, 0) != 0)
{
HDC dc;
if (name != nullptr)
_stprintf_s(name, length, _T("%s (%s)"), dd.DeviceName, dd.DeviceString);
dc = CreateDC(dd.DeviceName, nullptr, nullptr, nullptr);
*width = GetDeviceCaps(dc, HORZRES);
*height = GetDeviceCaps(dc, VERTRES);
*bpp = GetDeviceCaps(dc, BITSPIXEL);
// ReleaseDC(nullptr, dc);
DeleteDC(dc);
}
else
{
return 0;
}
return 1;
}
void set_screen_id(int id)
{
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
return;
wfi->screenID = id;
return;
}
static DWORD WINAPI wf_server_main_loop(LPVOID lpParam)
{
freerdp_listener* instance;
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
{
WLog_ERR(TAG, "Failed to get instance");
return -1;
}
wfi->force_all_disconnect = FALSE;
instance = (freerdp_listener*)lpParam;
WINPR_ASSERT(instance);
WINPR_ASSERT(instance->GetEventHandles);
WINPR_ASSERT(instance->CheckFileDescriptor);
while (wfi->force_all_disconnect == FALSE)
{
DWORD status;
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
DWORD count = instance->GetEventHandles(instance, handles, ARRAYSIZE(handles));
if (count == 0)
{
WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
break;
}
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
if (status == WAIT_FAILED)
{
WLog_ERR(TAG, "WaitForMultipleObjects failed");
break;
}
if (instance->CheckFileDescriptor(instance) != TRUE)
{
WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
break;
}
}
WLog_INFO(TAG, "wf_server_main_loop terminating");
instance->Close(instance);
return 0;
}
BOOL wfreerdp_server_start(wfServer* server)
{
freerdp_listener* instance;
server->instance = freerdp_listener_new();
server->instance->PeerAccepted = wf_peer_accepted;
instance = server->instance;
wf_settings_read_dword(HKEY_LOCAL_MACHINE, SERVER_KEY, _T("DefaultPort"), &server->port);
if (!instance->Open(instance, nullptr, (UINT16)server->port))
return FALSE;
if (!(server->thread =
CreateThread(nullptr, 0, wf_server_main_loop, (void*)instance, 0, nullptr)))
return FALSE;
return TRUE;
}
BOOL wfreerdp_server_stop(wfServer* server)
{
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
WLog_INFO(TAG, "Stopping server");
wfi->force_all_disconnect = TRUE;
server->instance->Close(server->instance);
return TRUE;
}
wfServer* wfreerdp_server_new()
{
WSADATA wsaData;
wfServer* server;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
return nullptr;
server = (wfServer*)calloc(1, sizeof(wfServer));
if (server)
{
server->port = 3389;
}
WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
cbEvent = nullptr;
return server;
}
void wfreerdp_server_free(wfServer* server)
{
free(server);
WSACleanup();
}
BOOL wfreerdp_server_is_running(wfServer* server)
{
DWORD tStatus;
BOOL bRet;
bRet = GetExitCodeThread(server->thread, &tStatus);
if (bRet == 0)
{
WLog_ERR(TAG, "Error in call to GetExitCodeThread");
return FALSE;
}
if (tStatus == STILL_ACTIVE)
return TRUE;
return FALSE;
}
UINT32 wfreerdp_server_num_peers()
{
wfInfo* wfi;
wfi = wf_info_get_instance();
if (!wfi)
return -1;
return wfi->peerCount;
}
UINT32 wfreerdp_server_get_peer_hostname(int pId, wchar_t* dstStr)
{
wfInfo* wfi;
freerdp_peer* peer;
wfi = wf_info_get_instance();
if (!wfi)
return 0;
peer = wfi->peers[pId];
if (peer)
{
UINT32 sLen;
sLen = strnlen_s(peer->hostname, 50);
swprintf(dstStr, 50, L"%hs", peer->hostname);
return sLen;
}
else
{
WLog_WARN(TAG, "nonexistent peer id=%d", pId);
return 0;
}
}
BOOL wfreerdp_server_peer_is_local(int pId)
{
wfInfo* wfi;
freerdp_peer* peer;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
peer = wfi->peers[pId];
if (peer)
{
return peer->local;
}
else
{
return FALSE;
}
}
BOOL wfreerdp_server_peer_is_connected(int pId)
{
wfInfo* wfi;
freerdp_peer* peer;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
peer = wfi->peers[pId];
if (peer)
{
return peer->connected;
}
else
{
return FALSE;
}
}
BOOL wfreerdp_server_peer_is_activated(int pId)
{
wfInfo* wfi;
freerdp_peer* peer;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
peer = wfi->peers[pId];
if (peer)
{
return peer->activated;
}
else
{
return FALSE;
}
}
BOOL wfreerdp_server_peer_is_authenticated(int pId)
{
wfInfo* wfi;
freerdp_peer* peer;
wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
peer = wfi->peers[pId];
if (peer)
{
return peer->authenticated;
}
else
{
return FALSE;
}
}
void wfreerdp_server_register_callback_event(cbCallback cb)
{
cbEvent = cb;
}
void wfreerdp_server_peer_callback_event(int pId, UINT32 eType)
{
if (cbEvent)
cbEvent(pId, eType);
}

View File

@@ -0,0 +1,141 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_INTERFACE_H
#define FREERDP_SERVER_WIN_INTERFACE_H
#include <winpr/windows.h>
#include <freerdp/api.h>
#include <freerdp/freerdp.h>
#include <freerdp/listener.h>
#include <freerdp/freerdp.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/server/rdpsnd.h>
#if _WIN32_WINNT >= 0x0602
#define WITH_DXGI_1_2 1
#endif
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_CONNECT 1
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_DISCONNECT 2
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_ACTIVATE 4
#define FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_AUTH 8
typedef struct wf_info wfInfo;
typedef struct wf_peer_context wfPeerContext;
struct wf_info
{
wStream* s;
// screen and monitor info
int screenID;
int virtscreen_width;
int virtscreen_height;
int servscreen_width;
int servscreen_height;
int servscreen_xoffset;
int servscreen_yoffset;
int frame_idx;
int bitsPerPixel;
HDC driverDC;
int peerCount;
int activePeerCount;
void* changeBuffer;
int framesPerSecond;
LPTSTR deviceKey;
TCHAR deviceName[32];
freerdp_peer** peers;
BOOL mirrorDriverActive;
UINT framesWaiting;
HANDLE snd_mutex;
BOOL snd_stop;
AUDIO_FORMAT* agreed_format;
RECT invalid;
HANDLE mutex;
BOOL updatePending;
HANDLE updateEvent;
HANDLE updateThread;
HANDLE updateSemaphore;
RFX_CONTEXT* rfx_context;
unsigned long lastUpdate;
unsigned long nextUpdate;
SURFACE_BITS_COMMAND cmd;
BOOL input_disabled;
BOOL force_all_disconnect;
};
struct wf_peer_context
{
rdpContext _p;
wfInfo* info;
int frame_idx;
HANDLE updateEvent;
BOOL socketClose;
HANDLE socketEvent;
HANDLE socketThread;
HANDLE socketSemaphore;
HANDLE vcm;
RdpsndServerContext* rdpsnd;
};
struct wf_server
{
DWORD port;
HANDLE thread;
freerdp_listener* instance;
};
typedef struct wf_server wfServer;
typedef void(__stdcall* cbCallback)(int, UINT32);
FREERDP_API WINPR_ATTR_NODISCARD int get_screen_info(int id, _TCHAR* name, size_t length, int* w,
int* h, int* b);
FREERDP_API void set_screen_id(int id);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_start(wfServer* server);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_stop(wfServer* server);
FREERDP_API WINPR_ATTR_NODISCARD wfServer* wfreerdp_server_new(void);
FREERDP_API void wfreerdp_server_free(wfServer* server);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_is_running(wfServer* server);
FREERDP_API WINPR_ATTR_NODISCARD UINT32 wfreerdp_server_num_peers(void);
FREERDP_API WINPR_ATTR_NODISCARD UINT32 wfreerdp_server_get_peer_hostname(int pId, wchar_t* dstStr);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_local(int pId);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_connected(int pId);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_activated(int pId);
FREERDP_API WINPR_ATTR_NODISCARD BOOL wfreerdp_server_peer_is_authenticated(int pId);
FREERDP_API void wfreerdp_server_register_callback_event(cbCallback cb);
void wfreerdp_server_peer_callback_event(int pId, UINT32 eType);
#endif /* FREERDP_SERVER_WIN_INTERFACE_H */

View File

@@ -0,0 +1,362 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012-2013 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <winpr/tchar.h>
#include <winpr/windows.h>
#include "wf_mirage.h"
#include <freerdp/log.h>
#define TAG SERVER_TAG("Windows.mirror")
#define DEVICE_KEY_PREFIX _T("\\Registry\\Machine\\")
/*
This function will iterate over the loaded display devices until it finds
the mirror device we want to load. If found, it will then copy the registry
key corresponding to the device to the wfi and returns TRUE. Otherwise
the function returns FALSE.
*/
BOOL wf_mirror_driver_find_display_device(wfInfo* wfi)
{
BOOL result;
BOOL devFound;
DWORD deviceNumber;
DISPLAY_DEVICE deviceInfo;
devFound = FALSE;
deviceNumber = 0;
deviceInfo.cb = sizeof(deviceInfo);
while (result = EnumDisplayDevices(nullptr, deviceNumber, &deviceInfo, 0))
{
if (_tcscmp(deviceInfo.DeviceString, _T("Mirage Driver")) == 0)
{
int deviceKeyLength;
int deviceKeyPrefixLength;
deviceKeyPrefixLength = _tcslen(DEVICE_KEY_PREFIX);
if (_tcsnicmp(deviceInfo.DeviceKey, DEVICE_KEY_PREFIX, deviceKeyPrefixLength) == 0)
{
deviceKeyLength = _tcslen(deviceInfo.DeviceKey) - deviceKeyPrefixLength;
wfi->deviceKey = (LPTSTR)malloc((deviceKeyLength + 1) * sizeof(TCHAR));
if (!wfi->deviceKey)
return FALSE;
_tcsncpy_s(wfi->deviceKey, deviceKeyLength + 1,
&deviceInfo.DeviceKey[deviceKeyPrefixLength], deviceKeyLength);
}
_tcsncpy_s(wfi->deviceName, 32, deviceInfo.DeviceName, _tcslen(deviceInfo.DeviceName));
return TRUE;
}
deviceNumber++;
}
return FALSE;
}
/**
* This function will attempt to access the the windows registry using the device
* key stored in the current wfi. It will attempt to read the value of the
* "Attach.ToDesktop" subkey and will return TRUE if the value is already set to
* val. If unable to read the subkey, this function will return FALSE. If the
* subkey is not set to val it will then attempt to set it to val and return TRUE. If
* unsuccessful or an unexpected value is encountered, the function returns
* FALSE.
*/
BOOL wf_mirror_driver_display_device_attach(wfInfo* wfi, DWORD mode)
{
HKEY hKey;
LONG status;
DWORD dwType;
DWORD dwSize;
DWORD dwValue;
status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, wfi->deviceKey, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY,
&hKey);
if (status != ERROR_SUCCESS)
{
WLog_DBG(TAG, "Error opening RegKey: status=0x%08lX", status);
if (status == ERROR_ACCESS_DENIED)
WLog_DBG(TAG, "access denied. Do you have admin privileges?");
return FALSE;
}
dwSize = sizeof(DWORD);
status =
RegQueryValueEx(hKey, _T("Attach.ToDesktop"), nullptr, &dwType, (BYTE*)&dwValue, &dwSize);
if (status != ERROR_SUCCESS)
{
WLog_DBG(TAG, "Error querying RegKey: status=0x%08lX", status);
if (status == ERROR_ACCESS_DENIED)
WLog_DBG(TAG, "access denied. Do you have admin privileges?");
return FALSE;
}
if (dwValue ^ mode) // only if we want to change modes
{
dwValue = mode;
dwSize = sizeof(DWORD);
status = RegSetValueEx(hKey, _T("Attach.ToDesktop"), 0, REG_DWORD, (BYTE*)&dwValue, dwSize);
if (status != ERROR_SUCCESS)
{
WLog_DBG(TAG, "Error writing registry key: %ld", status);
if (status == ERROR_ACCESS_DENIED)
WLog_DBG(TAG, "access denied. Do you have admin privileges?");
WLog_DBG(TAG, "");
return FALSE;
}
}
return TRUE;
}
void wf_mirror_driver_print_display_change_status(LONG status)
{
TCHAR disp_change[64];
switch (status)
{
case DISP_CHANGE_SUCCESSFUL:
_tcscpy(disp_change, _T("DISP_CHANGE_SUCCESSFUL"));
break;
case DISP_CHANGE_BADDUALVIEW:
_tcscpy(disp_change, _T("DISP_CHANGE_BADDUALVIEW"));
break;
case DISP_CHANGE_BADFLAGS:
_tcscpy(disp_change, _T("DISP_CHANGE_BADFLAGS"));
break;
case DISP_CHANGE_BADMODE:
_tcscpy(disp_change, _T("DISP_CHANGE_BADMODE"));
break;
case DISP_CHANGE_BADPARAM:
_tcscpy(disp_change, _T("DISP_CHANGE_BADPARAM"));
break;
case DISP_CHANGE_FAILED:
_tcscpy(disp_change, _T("DISP_CHANGE_FAILED"));
break;
case DISP_CHANGE_NOTUPDATED:
_tcscpy(disp_change, _T("DISP_CHANGE_NOTUPDATED"));
break;
case DISP_CHANGE_RESTART:
_tcscpy(disp_change, _T("DISP_CHANGE_RESTART"));
break;
default:
_tcscpy(disp_change, _T("DISP_CHANGE_UNKNOWN"));
break;
}
if (status != DISP_CHANGE_SUCCESSFUL)
WLog_ERR(TAG, "ChangeDisplaySettingsEx() failed with %s (%ld)", disp_change, status);
else
WLog_INFO(TAG, "ChangeDisplaySettingsEx() succeeded with %s (%ld)", disp_change, status);
}
/**
* This function will attempt to apply the currently configured display settings
* in the registry to the display driver. It will return TRUE if successful
* otherwise it returns FALSE.
* If mode is MIRROR_UNLOAD then the the driver will be asked to remove itself.
*/
BOOL wf_mirror_driver_update(wfInfo* wfi, int mode)
{
BOOL status;
DWORD* extHdr;
WORD drvExtraSaved;
DEVMODE* deviceMode;
LONG disp_change_status;
DWORD dmf_devmodewext_magic_sig = 0xDF20C0DE;
if ((mode != MIRROR_LOAD) && (mode != MIRROR_UNLOAD))
{
WLog_DBG(TAG, "Invalid mirror mode!");
return FALSE;
}
deviceMode = (DEVMODE*)malloc(sizeof(DEVMODE) + EXT_DEVMODE_SIZE_MAX);
if (!deviceMode)
return FALSE;
deviceMode->dmDriverExtra = 2 * sizeof(DWORD);
extHdr = (DWORD*)((BYTE*)&deviceMode + sizeof(DEVMODE));
extHdr[0] = dmf_devmodewext_magic_sig;
extHdr[1] = 0;
drvExtraSaved = deviceMode->dmDriverExtra;
memset(deviceMode, 0, sizeof(DEVMODE) + EXT_DEVMODE_SIZE_MAX);
deviceMode->dmSize = sizeof(DEVMODE);
deviceMode->dmDriverExtra = drvExtraSaved;
if (mode == MIRROR_LOAD)
{
wfi->virtscreen_width = GetSystemMetrics(SM_CXVIRTUALSCREEN);
wfi->virtscreen_height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
deviceMode->dmPelsWidth = wfi->virtscreen_width;
deviceMode->dmPelsHeight = wfi->virtscreen_height;
deviceMode->dmBitsPerPel = wfi->bitsPerPixel;
deviceMode->u.s2.dmPosition.x = wfi->servscreen_xoffset;
deviceMode->u.s2.dmPosition.y = wfi->servscreen_yoffset;
}
deviceMode->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION;
_tcsncpy_s(deviceMode->dmDeviceName, 32, wfi->deviceName, _tcslen(wfi->deviceName));
disp_change_status =
ChangeDisplaySettingsEx(wfi->deviceName, deviceMode, nullptr, CDS_UPDATEREGISTRY, nullptr);
status = (disp_change_status == DISP_CHANGE_SUCCESSFUL) ? TRUE : FALSE;
if (!status)
wf_mirror_driver_print_display_change_status(disp_change_status);
return status;
}
BOOL wf_mirror_driver_map_memory(wfInfo* wfi)
{
int status;
wfi->driverDC = CreateDC(wfi->deviceName, nullptr, nullptr, nullptr);
if (wfi->driverDC == nullptr)
{
WLog_ERR(TAG, "Could not create device driver context!");
{
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf,
0, nullptr);
// Display the error message and exit the process
WLog_ERR(TAG, "CreateDC failed on device [%s] with error %lu: %s", wfi->deviceName, dw,
lpMsgBuf);
LocalFree(lpMsgBuf);
}
return FALSE;
}
wfi->changeBuffer = calloc(1, sizeof(GETCHANGESBUF));
if (!wfi->changeBuffer)
return FALSE;
status = ExtEscape(wfi->driverDC, dmf_esc_usm_pipe_map, 0, 0, sizeof(GETCHANGESBUF),
(LPSTR)wfi->changeBuffer);
if (status <= 0)
{
WLog_ERR(TAG, "Failed to map shared memory from the driver! code %d", status);
return FALSE;
}
return TRUE;
}
/* Unmap the shared memory and release the DC */
BOOL wf_mirror_driver_cleanup(wfInfo* wfi)
{
int status;
status = ExtEscape(wfi->driverDC, dmf_esc_usm_pipe_unmap, sizeof(GETCHANGESBUF),
(LPSTR)wfi->changeBuffer, 0, 0);
if (status <= 0)
{
WLog_ERR(TAG, "Failed to unmap shared memory from the driver! code %d", status);
}
if (wfi->driverDC != nullptr)
{
status = DeleteDC(wfi->driverDC);
if (status == 0)
{
WLog_ERR(TAG, "Failed to release DC!");
}
}
free(wfi->changeBuffer);
return TRUE;
}
BOOL wf_mirror_driver_activate(wfInfo* wfi)
{
if (!wfi->mirrorDriverActive)
{
WLog_DBG(TAG, "Activating Mirror Driver");
if (wf_mirror_driver_find_display_device(wfi) == FALSE)
{
WLog_DBG(TAG, "Could not find dfmirage mirror driver! Is it installed?");
return FALSE;
}
if (wf_mirror_driver_display_device_attach(wfi, 1) == FALSE)
{
WLog_DBG(TAG, "Could not attach display device!");
return FALSE;
}
if (wf_mirror_driver_update(wfi, MIRROR_LOAD) == FALSE)
{
WLog_DBG(TAG, "could not update system with new display settings!");
return FALSE;
}
if (wf_mirror_driver_map_memory(wfi) == FALSE)
{
WLog_DBG(TAG, "Unable to map memory for mirror driver!");
return FALSE;
}
wfi->mirrorDriverActive = TRUE;
}
return TRUE;
}
void wf_mirror_driver_deactivate(wfInfo* wfi)
{
if (wfi->mirrorDriverActive)
{
WLog_DBG(TAG, "Deactivating Mirror Driver");
wf_mirror_driver_cleanup(wfi);
wf_mirror_driver_display_device_attach(wfi, 0);
wf_mirror_driver_update(wfi, MIRROR_UNLOAD);
wfi->mirrorDriverActive = FALSE;
}
}

View File

@@ -0,0 +1,219 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012-2013 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_MIRAGE_H
#define FREERDP_SERVER_WIN_MIRAGE_H
#include "wf_interface.h"
enum
{
MIRROR_LOAD = 0,
MIRROR_UNLOAD = 1
};
enum
{
DMF_ESCAPE_BASE_1_VB = 1030,
DMF_ESCAPE_BASE_2_VB = 1026,
DMF_ESCAPE_BASE_3_VB = 24
};
#ifdef _WIN64
#define CLIENT_64BIT 0x8000
enum
{
DMF_ESCAPE_BASE_1 = CLIENT_64BIT | DMF_ESCAPE_BASE_1_VB,
DMF_ESCAPE_BASE_2 = CLIENT_64BIT | DMF_ESCAPE_BASE_2_VB,
DMF_ESCAPE_BASE_3 = CLIENT_64BIT | DMF_ESCAPE_BASE_3_VB,
};
#else
enum
{
DMF_ESCAPE_BASE_1 = DMF_ESCAPE_BASE_1_VB,
DMF_ESCAPE_BASE_2 = DMF_ESCAPE_BASE_2_VB,
DMF_ESCAPE_BASE_3 = DMF_ESCAPE_BASE_3_VB,
};
#endif
typedef enum
{
dmf_esc_qry_ver_info = DMF_ESCAPE_BASE_2 + 0,
dmf_esc_usm_pipe_map = DMF_ESCAPE_BASE_1 + 0,
dmf_esc_usm_pipe_unmap = DMF_ESCAPE_BASE_1 + 1,
dmf_esc_test = DMF_ESCAPE_BASE_1 + 20,
dmf_esc_usm_pipe_mapping_test = DMF_ESCAPE_BASE_1 + 21,
dmf_esc_pointer_shape_get = DMF_ESCAPE_BASE_3,
} dmf_escape;
#define CLIP_LIMIT 50
#define MAXCHANGES_BUF 20000
typedef enum
{
dmf_dfo_IGNORE = 0,
dmf_dfo_FROM_SCREEN = 1,
dmf_dfo_FROM_DIB = 2,
dmf_dfo_TO_SCREEN = 3,
dmf_dfo_SCREEN_SCREEN = 11,
dmf_dfo_BLIT = 12,
dmf_dfo_SOLIDFILL = 13,
dmf_dfo_BLEND = 14,
dmf_dfo_TRANS = 15,
dmf_dfo_PLG = 17,
dmf_dfo_TEXTOUT = 18,
dmf_dfo_Ptr_Shape = 19,
dmf_dfo_Ptr_Engage = 48,
dmf_dfo_Ptr_Avert = 49,
dmf_dfn_assert_on = 64,
dmf_dfn_assert_off = 65,
} dmf_UpdEvent;
#define NOCACHE 1
#define OLDCACHE 2
#define NEWCACHE 3
typedef struct
{
ULONG type;
RECT rect;
#ifndef DFMIRAGE_LEAN
RECT origrect;
POINT point;
ULONG color;
ULONG refcolor;
#endif
} CHANGES_RECORD;
typedef CHANGES_RECORD* PCHANGES_RECORD;
typedef struct
{
ULONG counter;
CHANGES_RECORD pointrect[MAXCHANGES_BUF];
} CHANGES_BUF;
#define EXT_DEVMODE_SIZE_MAX 3072
#define DMF_PIPE_SEC_SIZE_DEFAULT ALIGN64K(sizeof(CHANGES_BUF))
typedef struct
{
CHANGES_BUF* buffer;
PVOID Userbuffer;
} GETCHANGESBUF;
#define dmf_sprb_ERRORMASK 0x07FF
#define dmf_sprb_STRICTSESSION_AFF 0x1FFF
typedef enum
{
dmf_sprb_internal_error = 0x0001,
dmf_sprb_miniport_gen_error = 0x0004,
dmf_sprb_memory_alloc_failed = 0x0008,
dmf_sprb_pipe_buff_overflow = 0x0010,
dmf_sprb_pipe_buff_insufficient = 0x0020,
dmf_sprb_pipe_not_ready = 0x0040,
dmf_sprb_gdi_err = 0x0100,
dmf_sprb_owner_died = 0x0400,
dmf_sprb_tgtwnd_gone = 0x0800,
dmf_sprb_pdev_detached = 0x2000,
} dmf_session_prob_status;
#define DMF_ESC_RET_FAILF 0x80000000
#define DMF_ESC_RET_SSTMASK 0x0000FFFF
#define DMF_ESC_RET_IMMMASK 0x7FFF0000
typedef enum
{
dmf_escret_generic_ok = 0x00010000,
dmf_escret_bad_state = 0x00100000,
dmf_escret_access_denied = 0x00200000,
dmf_escret_bad_buffer_size = 0x00400000,
dmf_escret_internal_err = 0x00800000,
dmf_escret_out_of_memory = 0x02000000,
dmf_escret_already_connected = 0x04000000,
dmf_escret_oh_boy_too_late = 0x08000000,
dmf_escret_bad_window = 0x10000000,
dmf_escret_drv_ver_higher = 0x20000000,
dmf_escret_drv_ver_lower = 0x40000000,
} dmf_esc_retcode;
typedef struct
{
ULONG cbSize;
ULONG app_actual_version;
ULONG display_minreq_version;
ULONG connect_options;
} Esc_dmf_Qvi_IN;
enum
{
esc_qvi_prod_name_max = 16,
};
#define ESC_QVI_PROD_MIRAGE "MIRAGE"
#define ESC_QVI_PROD_QUASAR "QUASAR"
typedef struct
{
ULONG cbSize;
ULONG display_actual_version;
ULONG miniport_actual_version;
ULONG app_minreq_version;
ULONG display_buildno;
ULONG miniport_buildno;
char prod_name[esc_qvi_prod_name_max];
} Esc_dmf_Qvi_OUT;
typedef struct
{
ULONG cbSize;
char* pDstBmBuf;
ULONG nDstBmBufSize;
} Esc_dmf_pointer_shape_get_IN;
typedef struct
{
ULONG cbSize;
POINTL BmSize;
char* pMaskBm;
ULONG nMaskBmSize;
char* pColorBm;
ULONG nColorBmSize;
char* pColorBmPal;
ULONG nColorBmPalEntries;
} Esc_dmf_pointer_shape_get_OUT;
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_find_display_device(wfInfo* wfi);
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_display_device_attach(wfInfo* wfi, DWORD mode);
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_update(wfInfo* wfi, int mode);
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_map_memory(wfInfo* wfi);
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_cleanup(wfInfo* wfi);
WINPR_ATTR_NODISCARD BOOL wf_mirror_driver_activate(wfInfo* wfi);
void wf_mirror_driver_deactivate(wfInfo* wfi);
#endif /* FREERDP_SERVER_WIN_MIRAGE_H */

View File

@@ -0,0 +1,415 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <winpr/assert.h>
#include <winpr/tchar.h>
#include <winpr/stream.h>
#include <winpr/windows.h>
#include <freerdp/listener.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/build-config.h>
#include <freerdp/crypto/certificate.h>
#include "wf_info.h"
#include "wf_input.h"
#include "wf_mirage.h"
#include "wf_update.h"
#include "wf_settings.h"
#include "wf_rdpsnd.h"
#include "wf_peer.h"
#include <freerdp/peer.h>
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
#define SERVER_KEY "Software\\" FREERDP_VENDOR_STRING "\\" FREERDP_PRODUCT_STRING
static DWORD WINAPI wf_peer_main_loop(LPVOID lpParam);
static BOOL wf_peer_context_new(freerdp_peer* client, rdpContext* ctx)
{
wfPeerContext* context = (wfPeerContext*)ctx;
WINPR_ASSERT(context);
if (!(context->info = wf_info_get_instance()))
return FALSE;
context->vcm = WTSOpenServerA((LPSTR)client->context);
if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
return FALSE;
if (!wf_info_peer_register(context->info, context))
{
WTSCloseServer(context->vcm);
context->vcm = nullptr;
return FALSE;
}
return TRUE;
}
static void wf_peer_context_free(freerdp_peer* client, rdpContext* ctx)
{
wfPeerContext* context = (wfPeerContext*)ctx;
WINPR_ASSERT(context);
wf_info_peer_unregister(context->info, context);
if (context->rdpsnd)
{
wf_rdpsnd_lock();
context->info->snd_stop = TRUE;
rdpsnd_server_context_free(context->rdpsnd);
wf_rdpsnd_unlock();
}
WTSCloseServer(context->vcm);
}
static BOOL wf_peer_init(freerdp_peer* client)
{
client->ContextSize = sizeof(wfPeerContext);
client->ContextNew = wf_peer_context_new;
client->ContextFree = wf_peer_context_free;
return freerdp_peer_context_new(client);
}
static BOOL wf_peer_post_connect(freerdp_peer* client)
{
wfInfo* wfi;
rdpSettings* settings;
wfPeerContext* context;
WINPR_ASSERT(client);
context = (wfPeerContext*)client->context;
WINPR_ASSERT(context);
wfi = context->info;
WINPR_ASSERT(wfi);
settings = client->context->settings;
WINPR_ASSERT(settings);
if ((get_screen_info(wfi->screenID, nullptr, 0, &wfi->servscreen_width, &wfi->servscreen_height,
&wfi->bitsPerPixel) == 0) ||
(wfi->servscreen_width == 0) || (wfi->servscreen_height == 0) || (wfi->bitsPerPixel == 0))
{
WLog_ERR(TAG, "postconnect: error getting screen info for screen %d", wfi->screenID);
WLog_ERR(TAG, "\t%dx%dx%d", wfi->servscreen_height, wfi->servscreen_width,
wfi->bitsPerPixel);
return FALSE;
}
if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) != wfi->servscreen_width) ||
(freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) != wfi->servscreen_height))
{
/*
WLog_DBG(TAG, "Client requested resolution %"PRIu32"x%"PRIu32", but will resize to %dx%d",
freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight), wfi->servscreen_width,
wfi->servscreen_height);
*/
if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, wfi->servscreen_width) ||
!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, wfi->servscreen_height) ||
!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, wfi->bitsPerPixel))
return FALSE;
WINPR_ASSERT(client->context->update);
WINPR_ASSERT(client->context->update->DesktopResize);
client->context->update->DesktopResize(client->context);
}
if (WTSVirtualChannelManagerIsChannelJoined(context->vcm, "rdpsnd"))
{
wf_peer_rdpsnd_init(context); /* Audio Output */
}
return TRUE;
}
static BOOL wf_peer_activate(freerdp_peer* client)
{
wfInfo* wfi;
wfPeerContext* context = (wfPeerContext*)client->context;
wfi = context->info;
client->activated = TRUE;
wf_update_peer_activate(wfi, context);
wfreerdp_server_peer_callback_event(((rdpContext*)context)->peer->pId,
FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_ACTIVATE);
return TRUE;
}
static BOOL wf_peer_logon(freerdp_peer* client, const SEC_WINNT_AUTH_IDENTITY* identity,
BOOL automatic)
{
wfreerdp_server_peer_callback_event(((rdpContext*)client->context)->peer->pId,
FREERDP_SERVER_WIN_SRV_CALLBACK_EVENT_AUTH);
return TRUE;
}
static BOOL wf_peer_synchronize_event(rdpInput* input, UINT32 flags)
{
return TRUE;
}
BOOL wf_peer_accepted(freerdp_listener* instance, freerdp_peer* client)
{
HANDLE hThread;
if (!(hThread = CreateThread(nullptr, 0, wf_peer_main_loop, client, 0, nullptr)))
return FALSE;
(void)CloseHandle(hThread);
return TRUE;
}
static DWORD WINAPI wf_peer_socket_listener(LPVOID lpParam)
{
wfPeerContext* context;
freerdp_peer* client = (freerdp_peer*)lpParam;
WINPR_ASSERT(client);
WINPR_ASSERT(client->GetEventHandles);
WINPR_ASSERT(client->CheckFileDescriptor);
context = (wfPeerContext*)client->context;
WINPR_ASSERT(context);
while (1)
{
DWORD status;
HANDLE handles[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
DWORD count = client->GetEventHandles(client, handles, ARRAYSIZE(handles));
if (count == 0)
{
WLog_ERR(TAG, "Failed to get FreeRDP file descriptor");
break;
}
status = WaitForMultipleObjects(count, handles, FALSE, INFINITE);
if (status == WAIT_FAILED)
{
WLog_ERR(TAG, "WaitForMultipleObjects failed");
break;
}
(void)SetEvent(context->socketEvent);
(void)WaitForSingleObject(context->socketSemaphore, INFINITE);
if (context->socketClose)
break;
}
return 0;
}
static BOOL wf_peer_read_settings(freerdp_peer* client)
{
rdpSettings* settings;
WINPR_ASSERT(client);
WINPR_ASSERT(client->context);
settings = client->context->settings;
WINPR_ASSERT(settings);
char* CertificateFile = nullptr;
if (!wf_settings_read_string_ascii(HKEY_LOCAL_MACHINE, SERVER_KEY, _T("CertificateFile"),
&(CertificateFile)))
CertificateFile = _strdup("server.crt");
rdpCertificate* cert = freerdp_certificate_new_from_file(CertificateFile);
free(CertificateFile);
if (!cert)
return FALSE;
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1))
return FALSE;
char* PrivateKeyFile = nullptr;
if (!wf_settings_read_string_ascii(HKEY_LOCAL_MACHINE, SERVER_KEY, _T("PrivateKeyFile"),
&(PrivateKeyFile)))
PrivateKeyFile = _strdup("server.key");
rdpPrivateKey* key = freerdp_key_new_from_file_enc(PrivateKeyFile, nullptr);
free(PrivateKeyFile);
if (!key)
return FALSE;
if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1))
return FALSE;
return TRUE;
}
DWORD WINAPI wf_peer_main_loop(LPVOID lpParam)
{
wfInfo* wfi;
DWORD nCount;
DWORD status;
HANDLE handles[32];
rdpSettings* settings;
wfPeerContext* context;
freerdp_peer* client = (freerdp_peer*)lpParam;
if (!wf_peer_init(client))
goto fail_peer_init;
WINPR_ASSERT(client->context);
settings = client->context->settings;
WINPR_ASSERT(settings);
if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE))
goto fail_peer_init;
if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32))
goto fail_peer_init;
if (!freerdp_settings_set_bool(settings, FreeRDP_NSCodec, FALSE))
goto fail_peer_init;
if (!freerdp_settings_set_bool(settings, FreeRDP_JpegCodec, FALSE))
goto fail_peer_init;
if (!wf_peer_read_settings(client))
goto fail_peer_init;
client->PostConnect = wf_peer_post_connect;
client->Activate = wf_peer_activate;
client->Logon = wf_peer_logon;
WINPR_ASSERT(client->context->input);
client->context->input->SynchronizeEvent = wf_peer_synchronize_event;
client->context->input->KeyboardEvent = wf_peer_keyboard_event;
client->context->input->UnicodeKeyboardEvent = wf_peer_unicode_keyboard_event;
client->context->input->MouseEvent = wf_peer_mouse_event;
client->context->input->ExtendedMouseEvent = wf_peer_extended_mouse_event;
WINPR_ASSERT(client->Initialize);
if (!client->Initialize(client))
goto fail_client_initialize;
context = (wfPeerContext*)client->context;
if (context->socketClose)
goto fail_socked_closed;
wfi = context->info;
if (wfi->input_disabled)
{
WLog_INFO(TAG, "client input is disabled");
client->context->input->KeyboardEvent = wf_peer_keyboard_event_dummy;
client->context->input->UnicodeKeyboardEvent = wf_peer_unicode_keyboard_event_dummy;
client->context->input->MouseEvent = wf_peer_mouse_event_dummy;
client->context->input->ExtendedMouseEvent = wf_peer_extended_mouse_event_dummy;
}
if (!(context->socketEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
goto fail_socket_event;
if (!(context->socketSemaphore = CreateSemaphore(nullptr, 0, 1, nullptr)))
goto fail_socket_semaphore;
if (!(context->socketThread =
CreateThread(nullptr, 0, wf_peer_socket_listener, client, 0, nullptr)))
goto fail_socket_thread;
WLog_INFO(TAG, "We've got a client %s", client->local ? "(local)" : client->hostname);
nCount = 0;
handles[nCount++] = context->updateEvent;
handles[nCount++] = context->socketEvent;
while (1)
{
status = WaitForMultipleObjects(nCount, handles, FALSE, INFINITE);
if ((status == WAIT_FAILED) || (status == WAIT_TIMEOUT))
{
WLog_ERR(TAG, "WaitForMultipleObjects failed");
break;
}
if (WaitForSingleObject(context->updateEvent, 0) == 0)
{
if (client->activated)
wf_update_peer_send(wfi, context);
(void)ResetEvent(context->updateEvent);
ReleaseSemaphore(wfi->updateSemaphore, 1, nullptr);
}
if (WaitForSingleObject(context->socketEvent, 0) == 0)
{
if (client->CheckFileDescriptor(client) != TRUE)
{
WLog_ERR(TAG, "Failed to check peer file descriptor");
context->socketClose = TRUE;
}
(void)ResetEvent(context->socketEvent);
ReleaseSemaphore(context->socketSemaphore, 1, nullptr);
if (context->socketClose)
break;
}
// force disconnect
if (wfi->force_all_disconnect == TRUE)
{
WLog_INFO(TAG, "Forcing Disconnect -> ");
break;
}
/* FIXME: we should wait on this, instead of calling it every time */
if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE)
break;
}
WLog_INFO(TAG, "Client %s disconnected.", client->local ? "(local)" : client->hostname);
if (WaitForSingleObject(context->updateEvent, 0) == 0)
{
(void)ResetEvent(context->updateEvent);
ReleaseSemaphore(wfi->updateSemaphore, 1, nullptr);
}
wf_update_peer_deactivate(wfi, context);
client->Disconnect(client);
fail_socket_thread:
(void)CloseHandle(context->socketSemaphore);
context->socketSemaphore = nullptr;
fail_socket_semaphore:
(void)CloseHandle(context->socketEvent);
context->socketEvent = nullptr;
fail_socket_event:
fail_socked_closed:
fail_client_initialize:
freerdp_peer_context_free(client);
fail_peer_init:
freerdp_peer_free(client);
return 0;
}

View File

@@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_PEER_H
#define FREERDP_SERVER_WIN_PEER_H
#include "wf_interface.h"
#include <freerdp/listener.h>
WINPR_ATTR_NODISCARD BOOL wf_peer_accepted(freerdp_listener* instance, freerdp_peer* client);
#endif /* FREERDP_SERVER_WIN_PEER_H */

View File

@@ -0,0 +1,153 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server (Audio Output)
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2013 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <winpr/windows.h>
#include <freerdp/server/server-common.h>
#include "wf_rdpsnd.h"
#include "wf_info.h"
#ifdef WITH_RDPSND_DSOUND
#include "wf_directsound.h"
#else
#include "wf_wasapi.h"
#endif
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
static void wf_peer_rdpsnd_activated(RdpsndServerContext* context)
{
wfInfo* wfi;
wfi = wf_info_get_instance();
wfi->agreed_format = nullptr;
WLog_DBG(TAG, "Client supports the following %d formats:", context->num_client_formats);
size_t i = 0;
for (; i < context->num_client_formats; i++)
{
// TODO: improve the way we agree on a format
for (size_t j = 0; j < context->num_server_formats; j++)
{
if ((context->client_formats[i].wFormatTag == context->server_formats[j].wFormatTag) &&
(context->client_formats[i].nChannels == context->server_formats[j].nChannels) &&
(context->client_formats[i].nSamplesPerSec ==
context->server_formats[j].nSamplesPerSec))
{
WLog_DBG(TAG, "agreed on format!");
wfi->agreed_format = (AUDIO_FORMAT*)&context->server_formats[j];
break;
}
}
if (wfi->agreed_format != nullptr)
break;
}
if (wfi->agreed_format == nullptr)
{
WLog_ERR(TAG, "Could not agree on a audio format with the server");
return;
}
context->SelectFormat(context, i);
context->SetVolume(context, 0x7FFF, 0x7FFF);
#ifdef WITH_RDPSND_DSOUND
wf_directsound_activate(context);
#else
wf_wasapi_activate(context);
#endif
}
int wf_rdpsnd_lock()
{
DWORD dRes;
wfInfo* wfi;
wfi = wf_info_get_instance();
dRes = WaitForSingleObject(wfi->snd_mutex, INFINITE);
switch (dRes)
{
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return TRUE;
break;
case WAIT_TIMEOUT:
return FALSE;
break;
case WAIT_FAILED:
WLog_ERR(TAG, "wf_rdpsnd_lock failed with 0x%08lX", GetLastError());
return -1;
break;
}
return -1;
}
int wf_rdpsnd_unlock()
{
wfInfo* wfi;
wfi = wf_info_get_instance();
if (ReleaseMutex(wfi->snd_mutex) == 0)
{
WLog_DBG(TAG, "wf_rdpsnd_unlock failed with 0x%08lX", GetLastError());
return -1;
}
return TRUE;
}
BOOL wf_peer_rdpsnd_init(wfPeerContext* context)
{
wfInfo* wfi = wf_info_get_instance();
if (!wfi)
return FALSE;
if (!(wfi->snd_mutex = CreateMutex(nullptr, FALSE, nullptr)))
return FALSE;
context->rdpsnd = rdpsnd_server_context_new(context->vcm);
context->rdpsnd->rdpcontext = &context->_p;
context->rdpsnd->data = context;
context->rdpsnd->num_server_formats =
server_rdpsnd_get_formats(&context->rdpsnd->server_formats);
if (context->rdpsnd->num_server_formats > 0)
context->rdpsnd->src_format = &context->rdpsnd->server_formats[0];
context->rdpsnd->Activated = wf_peer_rdpsnd_activated;
context->rdpsnd->Initialize(context->rdpsnd, TRUE);
wf_rdpsnd_set_latest_peer(context);
wfi->snd_stop = FALSE;
return TRUE;
}

View File

@@ -0,0 +1,33 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server (Audio Output)
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_RDPSND_H
#define FREERDP_SERVER_WIN_RDPSND_H
#include <freerdp/freerdp.h>
#include <freerdp/listener.h>
#include <freerdp/server/rdpsnd.h>
#include "wf_interface.h"
WINPR_ATTR_NODISCARD int wf_rdpsnd_lock(void);
WINPR_ATTR_NODISCARD int wf_rdpsnd_unlock(void);
WINPR_ATTR_NODISCARD BOOL wf_peer_rdpsnd_init(wfPeerContext* context);
#endif /* FREERDP_SERVER_WIN_RDPSND_H */

View File

@@ -0,0 +1,103 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <winpr/tchar.h>
#include <winpr/windows.h>
#include "wf_settings.h"
BOOL wf_settings_read_dword(HKEY key, LPCSTR subkey, LPTSTR name, DWORD* value)
{
HKEY hKey;
LONG status;
DWORD dwType;
DWORD dwSize;
DWORD dwValue;
status = RegOpenKeyExA(key, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
if (status == ERROR_SUCCESS)
{
dwSize = sizeof(DWORD);
status = RegQueryValueEx(hKey, name, nullptr, &dwType, (BYTE*)&dwValue, &dwSize);
if (status == ERROR_SUCCESS)
*value = dwValue;
RegCloseKey(hKey);
return (status == ERROR_SUCCESS) ? TRUE : FALSE;
}
return FALSE;
}
BOOL wf_settings_read_string_ascii(HKEY key, LPCSTR subkey, LPTSTR name, char** value)
{
HKEY hKey;
int length;
LONG status;
DWORD dwType;
DWORD dwSize;
char* strA;
TCHAR* strX = nullptr;
status = RegOpenKeyExA(key, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
if (status != ERROR_SUCCESS)
return FALSE;
status = RegQueryValueEx(hKey, name, nullptr, &dwType, nullptr, &dwSize);
if (status == ERROR_SUCCESS)
{
strX = (LPTSTR)malloc(dwSize + sizeof(TCHAR));
if (!strX)
return FALSE;
status = RegQueryValueEx(hKey, name, nullptr, &dwType, (BYTE*)strX, &dwSize);
if (status != ERROR_SUCCESS)
{
free(strX);
RegCloseKey(hKey);
return FALSE;
}
}
if (strX)
{
#ifdef UNICODE
length =
WideCharToMultiByte(CP_UTF8, 0, strX, lstrlenW(strX), nullptr, 0, nullptr, nullptr);
strA = (char*)malloc(length + 1);
WideCharToMultiByte(CP_UTF8, 0, strX, lstrlenW(strX), strA, length, nullptr, nullptr);
strA[length] = '\0';
free(strX);
#else
strA = (char*)strX;
#endif
*value = strA;
return TRUE;
}
return FALSE;
}

View File

@@ -0,0 +1,30 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_SETTINGS_H
#define FREERDP_SERVER_WIN_SETTINGS_H
#include "wf_interface.h"
WINPR_ATTR_NODISCARD BOOL wf_settings_read_dword(HKEY key, LPCSTR subkey, LPTSTR name,
DWORD* value);
WINPR_ATTR_NODISCARD BOOL wf_settings_read_string_ascii(HKEY key, LPCSTR subkey, LPTSTR name,
char** value);
#endif /* FREERDP_SERVER_WIN_SETTINGS_H */

View File

@@ -0,0 +1,252 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2012 Corey Clayton <can.of.tuna@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/config.h>
#include <stdio.h>
#include <winpr/windows.h>
#include <freerdp/freerdp.h>
#include <freerdp/listener.h>
#include "wf_peer.h"
#include "wf_info.h"
#include "wf_mirage.h"
#include "wf_update.h"
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
DWORD WINAPI wf_update_thread(LPVOID lpParam)
{
DWORD fps;
wfInfo* wfi;
DWORD beg, end;
DWORD diff, rate;
wfi = (wfInfo*)lpParam;
fps = wfi->framesPerSecond;
rate = 1000 / fps;
while (1)
{
beg = GetTickCount();
if (wf_info_lock(wfi) > 0)
{
if (wfi->activePeerCount > 0)
{
wf_info_update_changes(wfi);
if (wf_info_have_updates(wfi))
{
wf_update_encode(wfi);
// WLog_DBG(TAG, "Start of parallel sending");
int index = 0;
for (int peerindex = 0; peerindex < wfi->peerCount; peerindex++)
{
for (; index < FREERDP_SERVER_WIN_INFO_MAXPEERS; index++)
{
if (wfi->peers[index] && wfi->peers[index]->activated)
{
// WLog_DBG(TAG, "Setting event for %d of %d", index + 1,
// wfi->activePeerCount);
(void)SetEvent(
((wfPeerContext*)wfi->peers[index]->context)->updateEvent);
}
}
}
for (int index = 0; index < wfi->activePeerCount; index++)
{
// WLog_DBG(TAG, "Waiting for %d of %d", index + 1, wfi->activePeerCount);
// WaitForSingleObject(wfi->updateSemaphore, INFINITE);
(void)WaitForSingleObject(wfi->updateSemaphore, 1000);
}
// WLog_DBG(TAG, "End of parallel sending");
wf_info_clear_invalid_region(wfi);
}
}
wf_info_unlock(wfi);
}
end = GetTickCount();
diff = end - beg;
if (diff < rate)
{
Sleep(rate - diff);
}
}
// WLog_DBG(TAG, "Exiting Update Thread");
return 0;
}
void wf_update_encode(wfInfo* wfi)
{
RFX_RECT rect;
long height, width;
BYTE* pDataBits = nullptr;
int stride;
SURFACE_BITS_COMMAND* cmd;
wf_info_find_invalid_region(wfi);
cmd = &wfi->cmd;
Stream_ResetPosition(wfi->s);
wf_info_getScreenData(wfi, &width, &height, &pDataBits, &stride);
rect.x = 0;
rect.y = 0;
rect.width = (UINT16)width;
rect.height = (UINT16)height;
// WLog_DBG(TAG, "x:%"PRId32" y:%"PRId32" w:%ld h:%ld", wfi->invalid.left, wfi->invalid.top,
// width, height);
Stream_Clear(wfi->s);
if (!(rfx_compose_message(wfi->rfx_context, wfi->s, &rect, 1, pDataBits, width, height,
stride)))
{
return;
}
wfi->frame_idx = rfx_context_get_frame_idx(wfi->rfx_context);
cmd->destLeft = wfi->invalid.left;
cmd->destTop = wfi->invalid.top;
cmd->destRight = wfi->invalid.left + width;
cmd->destBottom = wfi->invalid.top + height;
cmd->bmp.bpp = 32;
cmd->bmp.codecID = 3;
cmd->bmp.width = width;
cmd->bmp.height = height;
cmd->bmp.bitmapDataLength = Stream_GetPosition(wfi->s);
cmd->bmp.bitmapData = Stream_Buffer(wfi->s);
}
void wf_update_peer_send(wfInfo* wfi, wfPeerContext* context)
{
freerdp_peer* client;
WINPR_ASSERT(wfi);
WINPR_ASSERT(context);
client = ((rdpContext*)context)->peer;
WINPR_ASSERT(client);
/* This happens when the RemoteFX encoder state is reset */
if (wfi->frame_idx == 1)
context->frame_idx = 0;
/*
* When a new client connects, it is possible that old frames from
* from a previous encoding state remain. Those frames should be discarded
* as they will cause an error condition in mstsc.
*/
if ((context->frame_idx + 1) != wfi->frame_idx)
{
/* This frame is meant to be discarded */
if (context->frame_idx == 0)
return;
/* This is an unexpected error condition */
WLog_DBG(TAG, "Unexpected Frame Index: Actual: %d Expected: %d", wfi->frame_idx,
context->frame_idx + 1);
}
WINPR_ASSERT(client->context);
WINPR_ASSERT(client->context->settings);
WINPR_ASSERT(client->context->update);
WINPR_ASSERT(client->context->update->SurfaceBits);
wfi->cmd.bmp.codecID =
freerdp_settings_get_uint32(client->context->settings, FreeRDP_RemoteFxCodecId);
client->context->update->SurfaceBits(client->context, &wfi->cmd);
context->frame_idx++;
}
void wf_update_encoder_reset(wfInfo* wfi)
{
if (wf_info_lock(wfi) > 0)
{
WLog_DBG(TAG, "Resetting encoder");
if (wfi->rfx_context)
{
rfx_context_reset(wfi->rfx_context, wfi->servscreen_width, wfi->servscreen_height);
}
else
{
/* TODO: pass ThreadingFlags somehow */
wfi->rfx_context = rfx_context_new(TRUE);
rfx_context_set_mode(wfi->rfx_context, RLGR3);
rfx_context_reset(wfi->rfx_context, wfi->servscreen_width, wfi->servscreen_height);
rfx_context_set_pixel_format(wfi->rfx_context, PIXEL_FORMAT_BGRA32);
wfi->s = Stream_New(nullptr, 0xFFFF);
}
wf_info_invalidate_full_screen(wfi);
wf_info_unlock(wfi);
}
}
void wf_update_peer_activate(wfInfo* wfi, wfPeerContext* context)
{
if (wf_info_lock(wfi) > 0)
{
if (wfi->activePeerCount < 1)
{
#ifndef WITH_DXGI_1_2
wf_mirror_driver_activate(wfi);
#endif
ResumeThread(wfi->updateThread);
}
wf_update_encoder_reset(wfi);
wfi->activePeerCount++;
WLog_DBG(TAG, "Activating Peer Updates: %d", wfi->activePeerCount);
wf_info_unlock(wfi);
}
}
void wf_update_peer_deactivate(wfInfo* wfi, wfPeerContext* context)
{
if (wf_info_lock(wfi) > 0)
{
freerdp_peer* client = ((rdpContext*)context)->peer;
if (client->activated)
{
if (wfi->activePeerCount <= 1)
{
wf_mirror_driver_deactivate(wfi);
}
client->activated = FALSE;
wfi->activePeerCount--;
WLog_DBG(TAG, "Deactivating Peer Updates: %d", wfi->activePeerCount);
}
wf_info_unlock(wfi);
}
}

View File

@@ -0,0 +1,37 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Windows Server
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_WIN_UPDATE_H
#define FREERDP_SERVER_WIN_UPDATE_H
#include "wf_interface.h"
void wf_update_encode(wfInfo* wfi);
void wf_update_send(wfInfo* wfi);
WINPR_ATTR_NODISCARD DWORD WINAPI wf_update_thread(LPVOID lpParam);
void wf_update_begin(wfInfo* wfi);
void wf_update_peer_send(wfInfo* wfi, wfPeerContext* context);
void wf_update_end(wfInfo* wfi);
void wf_update_peer_activate(wfInfo* wfi, wfPeerContext* context);
void wf_update_peer_deactivate(wfInfo* wfi, wfPeerContext* context);
#endif /* FREERDP_SERVER_WIN_UPDATE_H */

View File

@@ -0,0 +1,333 @@
#include "wf_wasapi.h"
#include "wf_info.h"
#include <initguid.h>
#include <mmdeviceapi.h>
#include <functiondiscoverykeys_devpkey.h>
#include <audioclient.h>
#include <freerdp/log.h>
#define TAG SERVER_TAG("windows")
//#define REFTIMES_PER_SEC 10000000
//#define REFTIMES_PER_MILLISEC 10000
#define REFTIMES_PER_SEC 100000
#define REFTIMES_PER_MILLISEC 100
//#define REFTIMES_PER_SEC 50000
//#define REFTIMES_PER_MILLISEC 50
#ifndef __MINGW32__
DEFINE_GUID(CLSID_MMDeviceEnumerator, 0xBCDE0395, 0xE52F, 0x467C, 0x8E, 0x3D, 0xC4, 0x57, 0x92,
0x91, 0x69, 0x2E);
DEFINE_GUID(IID_IMMDeviceEnumerator, 0xA95664D2, 0x9614, 0x4F35, 0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36,
0x17, 0xE6);
DEFINE_GUID(IID_IAudioClient, 0x1cb9ad4c, 0xdbfa, 0x4c32, 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03,
0xb2);
DEFINE_GUID(IID_IAudioCaptureClient, 0xc8adbd64, 0xe71e, 0x48a0, 0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c,
0xd3, 0x17);
#endif
LPWSTR devStr = nullptr;
wfPeerContext* latestPeer = nullptr;
int wf_rdpsnd_set_latest_peer(wfPeerContext* peer)
{
latestPeer = peer;
return 0;
}
int wf_wasapi_activate(RdpsndServerContext* context)
{
wchar_t* pattern = L"Stereo Mix";
HANDLE hThread;
wf_wasapi_get_device_string(pattern, &devStr);
if (devStr == nullptr)
{
WLog_ERR(TAG, "Failed to match for output device! Disabling rdpsnd.");
return 1;
}
WLog_DBG(TAG, "RDPSND (WASAPI) Activated");
if (!(hThread = CreateThread(nullptr, 0, wf_rdpsnd_wasapi_thread, latestPeer, 0, nullptr)))
{
WLog_ERR(TAG, "CreateThread failed");
return 1;
}
(void)CloseHandle(hThread);
return 0;
}
int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr)
{
HRESULT hr;
IMMDeviceEnumerator* pEnumerator = nullptr;
IMMDeviceCollection* pCollection = nullptr;
IMMDevice* pEndpoint = nullptr;
IPropertyStore* pProps = nullptr;
LPWSTR pwszID = nullptr;
unsigned int count;
CoInitialize(nullptr);
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to cocreate device enumerator");
exit(1);
}
hr = pEnumerator->lpVtbl->EnumAudioEndpoints(pEnumerator, eCapture, DEVICE_STATE_ACTIVE,
&pCollection);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to create endpoint collection");
exit(1);
}
pCollection->lpVtbl->GetCount(pCollection, &count);
WLog_INFO(TAG, "Num endpoints: %u", count);
if (count == 0)
{
WLog_ERR(TAG, "No endpoints!");
exit(1);
}
for (unsigned int i = 0; i < count; ++i)
{
PROPVARIANT nameVar;
PropVariantInit(&nameVar);
hr = pCollection->lpVtbl->Item(pCollection, i, &pEndpoint);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get endpoint %u", i);
exit(1);
}
hr = pEndpoint->lpVtbl->GetId(pEndpoint, &pwszID);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get endpoint ID");
exit(1);
}
hr = pEndpoint->lpVtbl->OpenPropertyStore(pEndpoint, STGM_READ, &pProps);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to open property store");
exit(1);
}
hr = pProps->lpVtbl->GetValue(pProps, &PKEY_Device_FriendlyName, &nameVar);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get device friendly name");
exit(1);
}
// do this a more reliable way
if (wcscmp(pattern, nameVar.pwszVal) < 0)
{
unsigned int devStrLen;
WLog_INFO(TAG, "Using sound output endpoint: [%s] (%s)", nameVar.pwszVal, pwszID);
// WLog_INFO(TAG, "matched %d characters", wcscmp(pattern, nameVar.pwszVal);
devStrLen = wcslen(pwszID);
*deviceStr = (LPWSTR)calloc(devStrLen + 1, 2);
if (!deviceStr)
return -1;
wcscpy_s(*deviceStr, devStrLen + 1, pwszID);
}
CoTaskMemFree(pwszID);
pwszID = nullptr;
PropVariantClear(&nameVar);
pProps->lpVtbl->Release(pProps);
pProps = nullptr;
pEndpoint->lpVtbl->Release(pEndpoint);
pEndpoint = nullptr;
}
pCollection->lpVtbl->Release(pCollection);
pCollection = nullptr;
pEnumerator->lpVtbl->Release(pEnumerator);
pEnumerator = nullptr;
CoUninitialize();
return 0;
}
DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam)
{
IMMDeviceEnumerator* pEnumerator = nullptr;
IMMDevice* pDevice = nullptr;
IAudioClient* pAudioClient = nullptr;
IAudioCaptureClient* pCaptureClient = nullptr;
WAVEFORMATEX* pwfx = nullptr;
HRESULT hr;
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferFrameCount;
UINT32 numFramesAvailable;
UINT32 packetLength = 0;
UINT32 dCount = 0;
BYTE* pData;
wfPeerContext* context;
wfInfo* wfi;
wfi = wf_info_get_instance();
context = (wfPeerContext*)lpParam;
CoInitialize(nullptr);
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, nullptr, CLSCTX_ALL, &IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to cocreate device enumerator");
exit(1);
}
hr = pEnumerator->lpVtbl->GetDevice(pEnumerator, devStr, &pDevice);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to cocreate get device");
exit(1);
}
hr = pDevice->lpVtbl->Activate(pDevice, &IID_IAudioClient, CLSCTX_ALL, nullptr,
(void**)&pAudioClient);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to activate audio client");
exit(1);
}
hr = pAudioClient->lpVtbl->GetMixFormat(pAudioClient, &pwfx);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get mix format");
exit(1);
}
pwfx->wFormatTag = wfi->agreed_format->wFormatTag;
pwfx->nChannels = wfi->agreed_format->nChannels;
pwfx->nSamplesPerSec = wfi->agreed_format->nSamplesPerSec;
pwfx->nAvgBytesPerSec = wfi->agreed_format->nAvgBytesPerSec;
pwfx->nBlockAlign = wfi->agreed_format->nBlockAlign;
pwfx->wBitsPerSample = wfi->agreed_format->wBitsPerSample;
pwfx->cbSize = wfi->agreed_format->cbSize;
hr = pAudioClient->lpVtbl->Initialize(pAudioClient, AUDCLNT_SHAREMODE_SHARED, 0,
hnsRequestedDuration, 0, pwfx, nullptr);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to initialize the audio client");
exit(1);
}
hr = pAudioClient->lpVtbl->GetBufferSize(pAudioClient, &bufferFrameCount);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get buffer size");
exit(1);
}
hr = pAudioClient->lpVtbl->GetService(pAudioClient, &IID_IAudioCaptureClient,
(void**)&pCaptureClient);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get the capture client");
exit(1);
}
hnsActualDuration = (UINT32)REFTIMES_PER_SEC * bufferFrameCount / pwfx->nSamplesPerSec;
hr = pAudioClient->lpVtbl->Start(pAudioClient);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to start capture");
exit(1);
}
dCount = 0;
while (wfi->snd_stop == FALSE)
{
DWORD flags;
Sleep(hnsActualDuration / REFTIMES_PER_MILLISEC / 2);
hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get packet length");
exit(1);
}
while (packetLength != 0)
{
hr = pCaptureClient->lpVtbl->GetBuffer(pCaptureClient, &pData, &numFramesAvailable,
&flags, nullptr, nullptr);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get buffer");
exit(1);
}
// Here we are writing the audio data
// not sure if this flag is ever set by the system; msdn is not clear about it
if (!(flags & AUDCLNT_BUFFERFLAGS_SILENT))
context->rdpsnd->SendSamples(context->rdpsnd, pData, packetLength,
(UINT16)(GetTickCount() & 0xffff));
hr = pCaptureClient->lpVtbl->ReleaseBuffer(pCaptureClient, numFramesAvailable);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to release buffer");
exit(1);
}
hr = pCaptureClient->lpVtbl->GetNextPacketSize(pCaptureClient, &packetLength);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to get packet length");
exit(1);
}
}
}
pAudioClient->lpVtbl->Stop(pAudioClient);
if (FAILED(hr))
{
WLog_ERR(TAG, "Failed to stop audio client");
exit(1);
}
CoTaskMemFree(pwfx);
if (pEnumerator != nullptr)
pEnumerator->lpVtbl->Release(pEnumerator);
if (pDevice != nullptr)
pDevice->lpVtbl->Release(pDevice);
if (pAudioClient != nullptr)
pAudioClient->lpVtbl->Release(pAudioClient);
if (pCaptureClient != nullptr)
pCaptureClient->lpVtbl->Release(pCaptureClient);
CoUninitialize();
return 0;
}

View File

@@ -0,0 +1,15 @@
#ifndef FREERDP_SERVER_WIN_WASAPI_H
#define FREERDP_SERVER_WIN_WASAPI_H
#include <freerdp/server/rdpsnd.h>
#include "wf_interface.h"
WINPR_ATTR_NODISCARD int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
WINPR_ATTR_NODISCARD int wf_wasapi_activate(RdpsndServerContext* context);
WINPR_ATTR_NODISCARD int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr);
WINPR_ATTR_NODISCARD DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam);
#endif /* FREERDP_SERVER_WIN_WASAPI_H */