summaryrefslogtreecommitdiffstats
path: root/server/Windows
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /server/Windows
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--server/Windows/CMakeLists.txt128
-rw-r--r--server/Windows/ModuleOptions.cmake4
-rw-r--r--server/Windows/cli/CMakeLists.txt60
-rw-r--r--server/Windows/cli/wfreerdp.c171
-rw-r--r--server/Windows/cli/wfreerdp.h25
-rw-r--r--server/Windows/wf_directsound.c219
-rw-r--r--server/Windows/wf_directsound.h13
-rw-r--r--server/Windows/wf_dxgi.c486
-rw-r--r--server/Windows/wf_dxgi.h41
-rw-r--r--server/Windows/wf_info.c402
-rw-r--r--server/Windows/wf_info.h46
-rw-r--r--server/Windows/wf_input.c223
-rw-r--r--server/Windows/wf_input.h36
-rw-r--r--server/Windows/wf_interface.c341
-rw-r--r--server/Windows/wf_interface.h140
-rw-r--r--server/Windows/wf_mirage.c361
-rw-r--r--server/Windows/wf_mirage.h219
-rw-r--r--server/Windows/wf_peer.c414
-rw-r--r--server/Windows/wf_peer.h29
-rw-r--r--server/Windows/wf_rdpsnd.c152
-rw-r--r--server/Windows/wf_rdpsnd.h33
-rw-r--r--server/Windows/wf_settings.c102
-rw-r--r--server/Windows/wf_settings.h28
-rw-r--r--server/Windows/wf_update.c251
-rw-r--r--server/Windows/wf_update.h37
-rw-r--r--server/Windows/wf_wasapi.c333
-rw-r--r--server/Windows/wf_wasapi.h15
27 files changed, 4309 insertions, 0 deletions
diff --git a/server/Windows/CMakeLists.txt b/server/Windows/CMakeLists.txt
new file mode 100644
index 0000000..2744b65
--- /dev/null
+++ b/server/Windows/CMakeLists.txt
@@ -0,0 +1,128 @@
+# 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})
+
+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()
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+set (RC_VERSION_MAJOR ${FREERDP_VERSION_MAJOR})
+set (RC_VERSION_MINOR ${FREERDP_VERSION_MINOR})
+set (RC_VERSION_BUILD ${FREERDP_VERSION_REVISION})
+if(WITH_SERVER_INTERFACE)
+ set (RC_VERSION_FILE "${CMAKE_SHARED_LIBRARY_PREFIX}${MODULE_NAME}${FREERDP_API_VERSION}${CMAKE_SHARED_LIBRARY_SUFFIX}" )
+else()
+ set (RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}" )
+endif()
+
+configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+set (${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+
+if(WITH_SERVER_INTERFACE)
+ add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+ set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME ${MODULE_NAME}${FREERDP_API_VERSION})
+ if (WITH_LIBRARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${FREERDP_VERSION} SOVERSION ${FREERDP_API_VERSION})
+ endif()
+ target_include_directories(${MODULE_NAME} INTERFACE $<INSTALL_INTERFACE:include>)
+else()
+ set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} cli/wfreerdp.c cli/wfreerdp.h)
+ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+ if (WITH_BINARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${MODULE_NAME}${FREERDP_API_VERSION}")
+ endif()
+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})
+
+ if (WITH_DEBUG_SYMBOLS AND MSVC AND BUILD_SHARED_LIBS)
+ install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}${FREERDP_VERSION_MAJOR}.pdb DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT symbols)
+ endif()
+else()
+ install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
+
+ if (WITH_DEBUG_SYMBOLS AND MSVC)
+ install(FILES ${PROJECT_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols)
+ endif()
+endif()
+
+if(WITH_SERVER_INTERFACE)
+ add_subdirectory(cli)
+endif()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Windows")
diff --git a/server/Windows/ModuleOptions.cmake b/server/Windows/ModuleOptions.cmake
new file mode 100644
index 0000000..69d7596
--- /dev/null
+++ b/server/Windows/ModuleOptions.cmake
@@ -0,0 +1,4 @@
+
+set(FREERDP_SERVER_NAME "wfreerdp-server")
+set(FREERDP_SERVER_PLATFORM "Windows")
+set(FREERDP_SERVER_VENDOR "FreeRDP")
diff --git a/server/Windows/cli/CMakeLists.txt b/server/Windows/cli/CMakeLists.txt
new file mode 100644
index 0000000..e125ac3
--- /dev/null
+++ b/server/Windows/cli/CMakeLists.txt
@@ -0,0 +1,60 @@
+# 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)
+
+# On windows create dll version information.
+# Vendor, product and year are already set in top level CMakeLists.txt
+set (RC_VERSION_MAJOR ${FREERDP_VERSION_MAJOR})
+set (RC_VERSION_MINOR ${FREERDP_VERSION_MINOR})
+set (RC_VERSION_BUILD ${FREERDP_VERSION_REVISION})
+set (RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}" )
+
+configure_file(
+ ${PROJECT_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in
+ ${CMAKE_CURRENT_BINARY_DIR}/version.rc
+ @ONLY)
+
+set (${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
+
+add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
+
+if (WITH_BINARY_VERSIONING)
+ set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${OUTPUT_NAME}${FREERDP_API_VERSION}")
+else()
+ set_target_properties(${MODULE_NAME} PROPERTIES OUTPUT_NAME "${OUTPUT_NAME}")
+endif()
+
+set(${MODULE_PREFIX}_LIBS wfreerdp-server)
+
+target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
+
+install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server)
+
+if (WITH_DEBUG_SYMBOLS AND MSVC)
+ install(FILES ${PROJECT_BINARY_DIR}/${OUTPUT_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols)
+endif()
+
+set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Windows")
diff --git a/server/Windows/cli/wfreerdp.c b/server/Windows/cli/wfreerdp.c
new file mode 100644
index 0000000..efc1bf5
--- /dev/null
+++ b/server/Windows/cli/wfreerdp.c
@@ -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] = { 0 };
+ 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(NULL, NULL, 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], NULL, 0);
+
+ if ((errno != 0) || (val > UINT32_MAX))
+ return -1;
+
+ set_screen_id(val);
+ index++;
+ }
+
+ if (index == argc - 1)
+ {
+ UINT32 val = strtoul(argv[index], NULL, 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] = { 0 };
+ 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);
+ WaitForSingleObject(server->thread, INFINITE);
+ WLog_INFO(TAG, "Stopping server");
+ wfreerdp_server_stop(server);
+ wfreerdp_server_free(server);
+ return 0;
+}
diff --git a/server/Windows/cli/wfreerdp.h b/server/Windows/cli/wfreerdp.h
new file mode 100644
index 0000000..017106d
--- /dev/null
+++ b/server/Windows/cli/wfreerdp.h
@@ -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 */
diff --git a/server/Windows/wf_directsound.c b/server/Windows/wf_directsound.c
new file mode 100644
index 0000000..441397a
--- /dev/null
+++ b/server/Windows/wf_directsound.c
@@ -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(NULL, &cap, NULL);
+
+ 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 = NULL;
+
+ hr = cap->lpVtbl->CreateCaptureBuffer(cap, &dscbd, &pDSCB, NULL);
+
+ 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(NULL, 0, wf_rdpsnd_directsound_thread, latestPeer, 0, NULL)))
+ {
+ WLog_ERR(TAG, "Failed to create direct sound thread");
+ return 1;
+ }
+ 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 = NULL;
+ DWORD dwCaptureLength = 0;
+ VOID* pbCaptureData2 = NULL;
+ DWORD dwCaptureLength2 = 0;
+ VOID* pbPlayData = NULL;
+ 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, NULL, &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;
+}
diff --git a/server/Windows/wf_directsound.h b/server/Windows/wf_directsound.h
new file mode 100644
index 0000000..01c1bdd
--- /dev/null
+++ b/server/Windows/wf_directsound.h
@@ -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"
+
+int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
+
+int wf_directsound_activate(RdpsndServerContext* context);
+
+DWORD WINAPI wf_rdpsnd_directsound_thread(LPVOID lpParam);
+
+#endif /* FREERDP_SERVER_WIN_DSOUND_H */
diff --git a/server/Windows/wf_dxgi.c b/server/Windows/wf_dxgi.c
new file mode 100644
index 0000000..899cb55
--- /dev/null
+++ b/server/Windows/wf_dxgi.c
@@ -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 = NULL;
+ID3D11DeviceContext* gContext = NULL;
+IDXGIOutputDuplication* gOutputDuplication = NULL;
+ID3D11Texture2D* gAcquiredDesktopImage = NULL;
+
+IDXGISurface* surf;
+ID3D11Texture2D* sStage;
+
+DXGI_OUTDUPL_FRAME_INFO FrameInfo;
+
+int wf_dxgi_init(wfInfo* wfi)
+{
+ gAcquiredDesktopImage = NULL;
+
+ 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(NULL, DriverTypes[DriverTypeIndex], NULL, 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 = { 0 };
+ IDXGIOutput* pOutput;
+ IDXGIDevice* DxgiDevice = NULL;
+ IDXGIAdapter* DxgiAdapter = NULL;
+ IDXGIOutput* DxgiOutput = NULL;
+ IDXGIOutput1* DxgiOutput1 = NULL;
+
+ 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 = NULL;
+
+ if (FAILED(status))
+ {
+ WLog_ERR(TAG, "Failed to get parent DXGI Adapter");
+ return 1;
+ }
+
+ pOutput = NULL;
+
+ 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 = NULL;
+
+ 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 = NULL;
+
+ 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 = NULL;
+
+ 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 = NULL;
+ }
+
+ if (gOutputDuplication)
+ {
+ gOutputDuplication->lpVtbl->Release(gOutputDuplication);
+ gOutputDuplication = NULL;
+ }
+
+ if (gContext)
+ {
+ gContext->lpVtbl->Release(gContext);
+ gContext = NULL;
+ }
+
+ if (gDevice)
+ {
+ gDevice->lpVtbl->Release(gDevice);
+ gDevice = NULL;
+ }
+
+ return 0;
+}
+
+int wf_dxgi_nextFrame(wfInfo* wfi, UINT timeout)
+{
+ HRESULT status = 0;
+ UINT i = 0;
+ UINT DataBufferSize = 0;
+ BYTE* DataBuffer = NULL;
+ IDXGIResource* DesktopResource = NULL;
+
+ if (wfi->framesWaiting > 0)
+ {
+ wf_dxgi_releasePixelData(wfi);
+ }
+
+ if (gAcquiredDesktopImage)
+ {
+ gAcquiredDesktopImage->lpVtbl->Release(gAcquiredDesktopImage);
+ gAcquiredDesktopImage = NULL;
+ }
+
+ 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 = NULL;
+ }
+
+ if (gOutputDuplication)
+ {
+ gOutputDuplication->lpVtbl->Release(gOutputDuplication);
+ gOutputDuplication = NULL;
+ }
+
+ 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 = NULL;
+
+ 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, NULL, &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 = NULL;
+ sStage->lpVtbl->Release(sStage);
+ sStage = NULL;
+
+ 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 = NULL;
+
+ if (FrameInfo.AccumulatedFrames == 0)
+ {
+ return 1;
+ }
+
+ if (FrameInfo.TotalMetadataBufferSize)
+ {
+
+ if (FrameInfo.TotalMetadataBufferSize > DataBufferSize)
+ {
+ if (DataBuffer)
+ {
+ free(DataBuffer);
+ DataBuffer = NULL;
+ }
+
+ 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
diff --git a/server/Windows/wf_dxgi.h b/server/Windows/wf_dxgi.h
new file mode 100644
index 0000000..560aec0
--- /dev/null
+++ b/server/Windows/wf_dxgi.h
@@ -0,0 +1,41 @@
+/**
+ * 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"
+
+int wf_dxgi_init(wfInfo* context);
+
+int wf_dxgi_createDevice(wfInfo* context);
+
+int wf_dxgi_getDuplication(wfInfo* context);
+
+int wf_dxgi_cleanup(wfInfo* context);
+
+int wf_dxgi_nextFrame(wfInfo* context, UINT timeout);
+
+int wf_dxgi_getPixelData(wfInfo* context, BYTE** data, int* pitch, RECT* invalid);
+
+int wf_dxgi_releasePixelData(wfInfo* context);
+
+int wf_dxgi_getInvalidRegion(RECT* invalid);
+
+#endif /* FREERDP_SERVER_WIN_DXGI_H */
diff --git a/server/Windows/wf_info.c b/server/Windows/wf_info.c
new file mode 100644
index 0000000..7ec754b
--- /dev/null
+++ b/server/Windows/wf_info.c
@@ -0,0 +1,402 @@
+/**
+ * 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 = NULL;
+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 != NULL)
+ {
+ HKEY hKey;
+ LONG status;
+ DWORD dwType;
+ DWORD dwSize;
+ DWORD dwValue;
+ wfi->mutex = CreateMutex(NULL, FALSE, NULL);
+
+ if (wfi->mutex == NULL)
+ {
+ WLog_ERR(TAG, "CreateMutex error: %lu", GetLastError());
+ free(wfi);
+ return NULL;
+ }
+
+ wfi->updateSemaphore = CreateSemaphore(NULL, 0, 32, NULL);
+
+ if (!wfi->updateSemaphore)
+ {
+ WLog_ERR(TAG, "CreateSemaphore error: %lu", GetLastError());
+ CloseHandle(wfi->mutex);
+ free(wfi);
+ return NULL;
+ }
+
+ wfi->updateThread = CreateThread(NULL, 0, wf_update_thread, wfi, CREATE_SUSPENDED, NULL);
+
+ if (!wfi->updateThread)
+ {
+ WLog_ERR(TAG, "Failed to create update thread");
+ CloseHandle(wfi->mutex);
+ CloseHandle(wfi->updateSemaphore);
+ free(wfi);
+ return NULL;
+ }
+
+ 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");
+ CloseHandle(wfi->mutex);
+ CloseHandle(wfi->updateSemaphore);
+ CloseHandle(wfi->updateThread);
+ free(wfi);
+ return NULL;
+ }
+
+ // 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"), NULL, &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"), NULL, &dwType, (BYTE*)&dwValue,
+ &dwSize) == ERROR_SUCCESS)
+ {
+ if (dwValue != 0)
+ wfi->input_disabled = TRUE;
+ }
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ return wfi;
+}
+
+wfInfo* wf_info_get_instance()
+{
+ if (wfInfoInstance == NULL)
+ 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(NULL, TRUE, FALSE, NULL)))
+ goto fail_update_event;
+
+ // get the offset of the top left corner of selected screen
+ EnumDisplayMonitors(NULL, NULL, 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] == NULL)
+ {
+ 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:
+ CloseHandle(context->updateEvent);
+ context->updateEvent = NULL;
+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] = NULL;
+ wfi->peerCount--;
+ 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;
+}
diff --git a/server/Windows/wf_info.h b/server/Windows/wf_info.h
new file mode 100644
index 0000000..82b1781
--- /dev/null
+++ b/server/Windows/wf_info.h
@@ -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
+
+BOOL wf_info_lock(wfInfo* wfi);
+BOOL wf_info_try_lock(wfInfo* wfi, DWORD dwMilliseconds);
+BOOL wf_info_unlock(wfInfo* wfi);
+
+wfInfo* wf_info_get_instance(void);
+BOOL wf_info_peer_register(wfInfo* wfi, wfPeerContext* context);
+void wf_info_peer_unregister(wfInfo* wfi, wfPeerContext* context);
+
+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);
+BOOL wf_info_have_invalid_region(wfInfo* wfi);
+void wf_info_getScreenData(wfInfo* wfi, long* width, long* height, BYTE** pBits, int* pitch);
+BOOL CALLBACK wf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor,
+ LPARAM dwData);
+
+#endif /* FREERDP_SERVER_WIN_INFO_H */
diff --git a/server/Windows/wf_input.c b/server/Windows/wf_input.c
new file mode 100644
index 0000000..a9fdd45
--- /dev/null
+++ b/server/Windows/wf_input.c
@@ -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.ki.wVk = 0;
+ keyboard_event.ki.wScan = code;
+ keyboard_event.ki.dwFlags = KEYEVENTF_SCANCODE;
+ keyboard_event.ki.dwExtraInfo = 0;
+ keyboard_event.ki.time = 0;
+
+ if (flags & KBD_FLAGS_RELEASE)
+ keyboard_event.ki.dwFlags |= KEYEVENTF_KEYUP;
+
+ if (flags & KBD_FLAGS_EXTENDED)
+ keyboard_event.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.ki.wVk = 0;
+ keyboard_event.ki.wScan = code;
+ keyboard_event.ki.dwFlags = KEYEVENTF_UNICODE;
+ keyboard_event.ki.dwExtraInfo = 0;
+ keyboard_event.ki.time = 0;
+
+ if (flags & KBD_FLAGS_RELEASE)
+ keyboard_event.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 = { 0 };
+ float width, height;
+ WINPR_UNUSED(input);
+
+ WINPR_ASSERT(input);
+ mouse_event.type = INPUT_MOUSE;
+
+ if (flags & PTR_FLAGS_WHEEL)
+ {
+ mouse_event.mi.dwFlags = MOUSEEVENTF_WHEEL;
+ mouse_event.mi.mouseData = flags & WheelRotationMask;
+
+ if (flags & PTR_FLAGS_WHEEL_NEGATIVE)
+ mouse_event.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.mi.dx = (LONG)((float)x * (65535.0f / width));
+ mouse_event.mi.dy = (LONG)((float)y * (65535.0f / height));
+ mouse_event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
+
+ if (flags & PTR_FLAGS_MOVE)
+ {
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_MOVE;
+ SendInput(1, &mouse_event, sizeof(INPUT));
+ }
+
+ mouse_event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE;
+
+ if (flags & PTR_FLAGS_BUTTON1)
+ {
+ if (flags & PTR_FLAGS_DOWN)
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_LEFTDOWN;
+ else
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_LEFTUP;
+
+ SendInput(1, &mouse_event, sizeof(INPUT));
+ }
+ else if (flags & PTR_FLAGS_BUTTON2)
+ {
+ if (flags & PTR_FLAGS_DOWN)
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_RIGHTDOWN;
+ else
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_RIGHTUP;
+
+ SendInput(1, &mouse_event, sizeof(INPUT));
+ }
+ else if (flags & PTR_FLAGS_BUTTON3)
+ {
+ if (flags & PTR_FLAGS_DOWN)
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_MIDDLEDOWN;
+ else
+ mouse_event.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 = { 0 };
+ 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.mi.dx = (LONG)((float)x * (65535.0f / width));
+ mouse_event.mi.dy = (LONG)((float)y * (65535.0f / height));
+ mouse_event.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
+ SendInput(1, &mouse_event, sizeof(INPUT));
+ }
+
+ mouse_event.mi.dx = mouse_event.mi.dy = mouse_event.mi.dwFlags = 0;
+
+ if (flags & PTR_XFLAGS_DOWN)
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_XDOWN;
+ else
+ mouse_event.mi.dwFlags |= MOUSEEVENTF_XUP;
+
+ if (flags & PTR_XFLAGS_BUTTON1)
+ mouse_event.mi.mouseData = XBUTTON1;
+ else if (flags & PTR_XFLAGS_BUTTON2)
+ mouse_event.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;
+}
diff --git a/server/Windows/wf_input.h b/server/Windows/wf_input.h
new file mode 100644
index 0000000..8123652
--- /dev/null
+++ b/server/Windows/wf_input.h
@@ -0,0 +1,36 @@
+/**
+ * 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"
+
+BOOL wf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code);
+BOOL wf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code);
+BOOL wf_peer_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
+BOOL wf_peer_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
+
+// dummy versions
+BOOL wf_peer_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT8 code);
+BOOL wf_peer_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code);
+BOOL wf_peer_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
+BOOL wf_peer_extended_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y);
+
+#endif /* FREERDP_SERVER_WIN_INPUT_H */
diff --git a/server/Windows/wf_interface.c b/server/Windows/wf_interface.c
new file mode 100644
index 0000000..37923bf
--- /dev/null
+++ b/server/Windows/wf_interface.c
@@ -0,0 +1,341 @@
+/**
+ * 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 = NULL;
+
+int get_screen_info(int id, _TCHAR* name, size_t length, int* width, int* height, int* bpp)
+{
+ DISPLAY_DEVICE dd = { 0 };
+
+ dd.cb = sizeof(DISPLAY_DEVICE);
+
+ if (EnumDisplayDevices(NULL, id, &dd, 0) != 0)
+ {
+ HDC dc;
+
+ if (name != NULL)
+ _stprintf_s(name, length, _T("%s (%s)"), dd.DeviceName, dd.DeviceString);
+
+ dc = CreateDC(dd.DeviceName, NULL, NULL, NULL);
+ *width = GetDeviceCaps(dc, HORZRES);
+ *height = GetDeviceCaps(dc, VERTRES);
+ *bpp = GetDeviceCaps(dc, BITSPIXEL);
+ // ReleaseDC(NULL, 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] = { 0 };
+ 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, NULL, (UINT16)server->port))
+ return FALSE;
+
+ if (!(server->thread = CreateThread(NULL, 0, wf_server_main_loop, (void*)instance, 0, NULL)))
+ 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 NULL;
+
+ server = (wfServer*)calloc(1, sizeof(wfServer));
+
+ if (server)
+ {
+ server->port = 3389;
+ }
+
+ WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi());
+
+ cbEvent = NULL;
+
+ 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);
+}
diff --git a/server/Windows/wf_interface.h b/server/Windows/wf_interface.h
new file mode 100644
index 0000000..5fcbad4
--- /dev/null
+++ b/server/Windows/wf_interface.h
@@ -0,0 +1,140 @@
+/**
+ * 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 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 BOOL wfreerdp_server_start(wfServer* server);
+FREERDP_API BOOL wfreerdp_server_stop(wfServer* server);
+
+FREERDP_API wfServer* wfreerdp_server_new(void);
+FREERDP_API void wfreerdp_server_free(wfServer* server);
+
+FREERDP_API BOOL wfreerdp_server_is_running(wfServer* server);
+
+FREERDP_API UINT32 wfreerdp_server_num_peers(void);
+FREERDP_API UINT32 wfreerdp_server_get_peer_hostname(int pId, wchar_t* dstStr);
+FREERDP_API BOOL wfreerdp_server_peer_is_local(int pId);
+FREERDP_API BOOL wfreerdp_server_peer_is_connected(int pId);
+FREERDP_API BOOL wfreerdp_server_peer_is_activated(int pId);
+FREERDP_API 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 */
diff --git a/server/Windows/wf_mirage.c b/server/Windows/wf_mirage.c
new file mode 100644
index 0000000..524ff6e
--- /dev/null
+++ b/server/Windows/wf_mirage.c
@@ -0,0 +1,361 @@
+/**
+ * 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(NULL, 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 privleges?");
+
+ return FALSE;
+ }
+
+ dwSize = sizeof(DWORD);
+ status = RegQueryValueEx(hKey, _T("Attach.ToDesktop"), NULL, &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 privleges?");
+
+ 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 privleges?");
+
+ 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->dmPosition.x = wfi->servscreen_xoffset;
+ deviceMode->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, NULL, CDS_UPDATEREGISTRY, NULL);
+ 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, NULL, NULL, NULL);
+
+ if (wfi->driverDC == NULL)
+ {
+ 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,
+ NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0,
+ NULL);
+ // 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 != NULL)
+ {
+ 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;
+ }
+}
diff --git a/server/Windows/wf_mirage.h b/server/Windows/wf_mirage.h
new file mode 100644
index 0000000..a03f0b9
--- /dev/null
+++ b/server/Windows/wf_mirage.h
@@ -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;
+
+BOOL wf_mirror_driver_find_display_device(wfInfo* wfi);
+BOOL wf_mirror_driver_display_device_attach(wfInfo* wfi, DWORD mode);
+BOOL wf_mirror_driver_update(wfInfo* wfi, int mode);
+BOOL wf_mirror_driver_map_memory(wfInfo* wfi);
+BOOL wf_mirror_driver_cleanup(wfInfo* wfi);
+
+BOOL wf_mirror_driver_activate(wfInfo* wfi);
+void wf_mirror_driver_deactivate(wfInfo* wfi);
+
+#endif /* FREERDP_SERVER_WIN_MIRAGE_H */
diff --git a/server/Windows/wf_peer.c b/server/Windows/wf_peer.c
new file mode 100644
index 0000000..4342a3b
--- /dev/null
+++ b/server/Windows/wf_peer.c
@@ -0,0 +1,414 @@
+/**
+ * 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 = NULL;
+ 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, NULL, 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(NULL, 0, wf_peer_main_loop, client, 0, NULL)))
+ return FALSE;
+
+ 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] = { 0 };
+ 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;
+ }
+
+ SetEvent(context->socketEvent);
+ 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 = NULL;
+ 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 = NULL;
+ 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(PrivateKeyFile);
+ 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(NULL, TRUE, FALSE, NULL)))
+ goto fail_socket_event;
+
+ if (!(context->socketSemaphore = CreateSemaphore(NULL, 0, 1, NULL)))
+ goto fail_socket_semaphore;
+
+ if (!(context->socketThread = CreateThread(NULL, 0, wf_peer_socket_listener, client, 0, NULL)))
+ 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);
+
+ ResetEvent(context->updateEvent);
+ ReleaseSemaphore(wfi->updateSemaphore, 1, NULL);
+ }
+
+ if (WaitForSingleObject(context->socketEvent, 0) == 0)
+ {
+ if (client->CheckFileDescriptor(client) != TRUE)
+ {
+ WLog_ERR(TAG, "Failed to check peer file descriptor");
+ context->socketClose = TRUE;
+ }
+
+ ResetEvent(context->socketEvent);
+ ReleaseSemaphore(context->socketSemaphore, 1, NULL);
+
+ 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)
+ {
+ ResetEvent(context->updateEvent);
+ ReleaseSemaphore(wfi->updateSemaphore, 1, NULL);
+ }
+
+ wf_update_peer_deactivate(wfi, context);
+ client->Disconnect(client);
+fail_socket_thread:
+ CloseHandle(context->socketSemaphore);
+ context->socketSemaphore = NULL;
+fail_socket_semaphore:
+ CloseHandle(context->socketEvent);
+ context->socketEvent = NULL;
+fail_socket_event:
+fail_socked_closed:
+fail_client_initialize:
+ freerdp_peer_context_free(client);
+fail_peer_init:
+ freerdp_peer_free(client);
+ return 0;
+}
diff --git a/server/Windows/wf_peer.h b/server/Windows/wf_peer.h
new file mode 100644
index 0000000..19d823c
--- /dev/null
+++ b/server/Windows/wf_peer.h
@@ -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>
+
+BOOL wf_peer_accepted(freerdp_listener* instance, freerdp_peer* client);
+
+#endif /* FREERDP_SERVER_WIN_PEER_H */
diff --git a/server/Windows/wf_rdpsnd.c b/server/Windows/wf_rdpsnd.c
new file mode 100644
index 0000000..b313c35
--- /dev/null
+++ b/server/Windows/wf_rdpsnd.c
@@ -0,0 +1,152 @@
+/**
+ * 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 = NULL;
+ WLog_DBG(TAG, "Client supports the following %d formats:", context->num_client_formats);
+
+ for (size_t i = 0; 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 != NULL)
+ break;
+ }
+
+ if (wfi->agreed_format == NULL)
+ {
+ 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(NULL, FALSE, NULL)))
+ 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;
+}
diff --git a/server/Windows/wf_rdpsnd.h b/server/Windows/wf_rdpsnd.h
new file mode 100644
index 0000000..88e631d
--- /dev/null
+++ b/server/Windows/wf_rdpsnd.h
@@ -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"
+
+int wf_rdpsnd_lock(void);
+int wf_rdpsnd_unlock(void);
+BOOL wf_peer_rdpsnd_init(wfPeerContext* context);
+
+#endif /* FREERDP_SERVER_WIN_RDPSND_H */
diff --git a/server/Windows/wf_settings.c b/server/Windows/wf_settings.c
new file mode 100644
index 0000000..63d2327
--- /dev/null
+++ b/server/Windows/wf_settings.c
@@ -0,0 +1,102 @@
+/**
+ * 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, NULL, &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 = NULL;
+
+ status = RegOpenKeyExA(key, subkey, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
+
+ if (status != ERROR_SUCCESS)
+ return FALSE;
+
+ status = RegQueryValueEx(hKey, name, NULL, &dwType, NULL, &dwSize);
+
+ if (status == ERROR_SUCCESS)
+ {
+ strX = (LPTSTR)malloc(dwSize + sizeof(TCHAR));
+ if (!strX)
+ return FALSE;
+ status = RegQueryValueEx(hKey, name, NULL, &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), NULL, 0, NULL, NULL);
+ strA = (char*)malloc(length + 1);
+ WideCharToMultiByte(CP_UTF8, 0, strX, lstrlenW(strX), strA, length, NULL, NULL);
+ strA[length] = '\0';
+ free(strX);
+#else
+ strA = (char*)strX;
+#endif
+ *value = strA;
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/server/Windows/wf_settings.h b/server/Windows/wf_settings.h
new file mode 100644
index 0000000..40e25aa
--- /dev/null
+++ b/server/Windows/wf_settings.h
@@ -0,0 +1,28 @@
+/**
+ * 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"
+
+BOOL wf_settings_read_dword(HKEY key, LPCSTR subkey, LPTSTR name, DWORD* value);
+BOOL wf_settings_read_string_ascii(HKEY key, LPCSTR subkey, LPTSTR name, char** value);
+
+#endif /* FREERDP_SERVER_WIN_SETTINGS_H */
diff --git a/server/Windows/wf_update.c b/server/Windows/wf_update.c
new file mode 100644
index 0000000..06d60c9
--- /dev/null
+++ b/server/Windows/wf_update.c
@@ -0,0 +1,251 @@
+/**
+ * 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);
+ 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);
+ 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 = NULL;
+ int stride;
+ SURFACE_BITS_COMMAND* cmd;
+ wf_info_find_invalid_region(wfi);
+ cmd = &wfi->cmd;
+ Stream_SetPosition(wfi->s, 0);
+ 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(NULL, 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);
+ }
+}
diff --git a/server/Windows/wf_update.h b/server/Windows/wf_update.h
new file mode 100644
index 0000000..47553af
--- /dev/null
+++ b/server/Windows/wf_update.h
@@ -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);
+
+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 */
diff --git a/server/Windows/wf_wasapi.c b/server/Windows/wf_wasapi.c
new file mode 100644
index 0000000..3925f99
--- /dev/null
+++ b/server/Windows/wf_wasapi.c
@@ -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 = NULL;
+wfPeerContext* latestPeer = NULL;
+
+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 == NULL)
+ {
+ WLog_ERR(TAG, "Failed to match for output device! Disabling rdpsnd.");
+ return 1;
+ }
+
+ WLog_DBG(TAG, "RDPSND (WASAPI) Activated");
+ if (!(hThread = CreateThread(NULL, 0, wf_rdpsnd_wasapi_thread, latestPeer, 0, NULL)))
+ {
+ WLog_ERR(TAG, "CreateThread failed");
+ return 1;
+ }
+ CloseHandle(hThread);
+
+ return 0;
+}
+
+int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr)
+{
+ HRESULT hr;
+ IMMDeviceEnumerator* pEnumerator = NULL;
+ IMMDeviceCollection* pCollection = NULL;
+ IMMDevice* pEndpoint = NULL;
+ IPropertyStore* pProps = NULL;
+ LPWSTR pwszID = NULL;
+ unsigned int count;
+
+ CoInitialize(NULL);
+ hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, 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 ouput 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 = NULL;
+ PropVariantClear(&nameVar);
+
+ pProps->lpVtbl->Release(pProps);
+ pProps = NULL;
+
+ pEndpoint->lpVtbl->Release(pEndpoint);
+ pEndpoint = NULL;
+ }
+
+ pCollection->lpVtbl->Release(pCollection);
+ pCollection = NULL;
+
+ pEnumerator->lpVtbl->Release(pEnumerator);
+ pEnumerator = NULL;
+ CoUninitialize();
+
+ return 0;
+}
+
+DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam)
+{
+ IMMDeviceEnumerator* pEnumerator = NULL;
+ IMMDevice* pDevice = NULL;
+ IAudioClient* pAudioClient = NULL;
+ IAudioCaptureClient* pCaptureClient = NULL;
+ WAVEFORMATEX* pwfx = NULL;
+ 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(NULL);
+ hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, 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, NULL,
+ (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, NULL);
+
+ 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, NULL, NULL);
+ 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 != NULL)
+ pEnumerator->lpVtbl->Release(pEnumerator);
+
+ if (pDevice != NULL)
+ pDevice->lpVtbl->Release(pDevice);
+
+ if (pAudioClient != NULL)
+ pAudioClient->lpVtbl->Release(pAudioClient);
+
+ if (pCaptureClient != NULL)
+ pCaptureClient->lpVtbl->Release(pCaptureClient);
+
+ CoUninitialize();
+
+ return 0;
+}
diff --git a/server/Windows/wf_wasapi.h b/server/Windows/wf_wasapi.h
new file mode 100644
index 0000000..da9c7dc
--- /dev/null
+++ b/server/Windows/wf_wasapi.h
@@ -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"
+
+int wf_rdpsnd_set_latest_peer(wfPeerContext* peer);
+
+int wf_wasapi_activate(RdpsndServerContext* context);
+
+int wf_wasapi_get_device_string(LPWSTR pattern, LPWSTR* deviceStr);
+
+DWORD WINAPI wf_rdpsnd_wasapi_thread(LPVOID lpParam);
+
+#endif /* FREERDP_SERVER_WIN_WASAPI_H */