diff options
Diffstat (limited to 'channels/rdpemsc')
-rw-r--r-- | channels/rdpemsc/CMakeLists.txt | 22 | ||||
-rw-r--r-- | channels/rdpemsc/ChannelOptions.cmake | 12 | ||||
-rw-r--r-- | channels/rdpemsc/server/CMakeLists.txt | 28 | ||||
-rw-r--r-- | channels/rdpemsc/server/mouse_cursor_main.c | 727 |
4 files changed, 789 insertions, 0 deletions
diff --git a/channels/rdpemsc/CMakeLists.txt b/channels/rdpemsc/CMakeLists.txt new file mode 100644 index 0000000..0b5f46b --- /dev/null +++ b/channels/rdpemsc/CMakeLists.txt @@ -0,0 +1,22 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de> +# +# 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. + +define_channel("rdpemsc") + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/rdpemsc/ChannelOptions.cmake b/channels/rdpemsc/ChannelOptions.cmake new file mode 100644 index 0000000..d89f37e --- /dev/null +++ b/channels/rdpemsc/ChannelOptions.cmake @@ -0,0 +1,12 @@ + +set(OPTION_DEFAULT ON) +set(OPTION_CLIENT_DEFAULT OFF) +set(OPTION_SERVER_DEFAULT ON) + +define_channel_options(NAME "rdpemsc" TYPE "dynamic" + DESCRIPTION "Mouse Cursor Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPEMSC]" + DEFAULT ${OPTION_DEFAULT}) + +define_channel_server_options(${OPTION_SERVER_DEFAULT}) + diff --git a/channels/rdpemsc/server/CMakeLists.txt b/channels/rdpemsc/server/CMakeLists.txt new file mode 100644 index 0000000..3384d70 --- /dev/null +++ b/channels/rdpemsc/server/CMakeLists.txt @@ -0,0 +1,28 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de> +# +# 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. + +define_channel_server("rdpemsc") + +set(${MODULE_PREFIX}_SRCS + mouse_cursor_main.c +) + +set(${MODULE_PREFIX}_LIBS + freerdp +) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") diff --git a/channels/rdpemsc/server/mouse_cursor_main.c b/channels/rdpemsc/server/mouse_cursor_main.c new file mode 100644 index 0000000..d7d2f78 --- /dev/null +++ b/channels/rdpemsc/server/mouse_cursor_main.c @@ -0,0 +1,727 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Mouse Cursor Virtual Channel Extension + * + * Copyright 2023 Pascal Nowack <Pascal.Nowack@gmx.de> + * + * 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 <freerdp/freerdp.h> +#include <freerdp/channels/log.h> +#include <freerdp/server/rdpemsc.h> + +#define TAG CHANNELS_TAG("rdpemsc.server") + +typedef enum +{ + MOUSE_CURSOR_INITIAL, + MOUSE_CURSOR_OPENED, +} eMouseCursorChannelState; + +typedef struct +{ + MouseCursorServerContext context; + + HANDLE stopEvent; + + HANDLE thread; + void* mouse_cursor_channel; + + DWORD SessionId; + + BOOL isOpened; + BOOL externalThread; + + /* Channel state */ + eMouseCursorChannelState state; + + wStream* buffer; +} mouse_cursor_server; + +static UINT mouse_cursor_server_initialize(MouseCursorServerContext* context, BOOL externalThread) +{ + UINT error = CHANNEL_RC_OK; + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + + WINPR_ASSERT(mouse_cursor); + + if (mouse_cursor->isOpened) + { + WLog_WARN(TAG, "Application error: Mouse Cursor channel already initialized, " + "calling in this state is not possible!"); + return ERROR_INVALID_STATE; + } + + mouse_cursor->externalThread = externalThread; + + return error; +} + +static UINT mouse_cursor_server_open_channel(mouse_cursor_server* mouse_cursor) +{ + MouseCursorServerContext* context = NULL; + DWORD Error = ERROR_SUCCESS; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + UINT32 channelId = 0; + BOOL status = TRUE; + + WINPR_ASSERT(mouse_cursor); + context = &mouse_cursor->context; + WINPR_ASSERT(context); + + if (WTSQuerySessionInformationA(mouse_cursor->context.vcm, WTS_CURRENT_SESSION, WTSSessionId, + (LPSTR*)&pSessionId, &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + return ERROR_INTERNAL_ERROR; + } + + mouse_cursor->SessionId = (DWORD)*pSessionId; + WTSFreeMemory(pSessionId); + + mouse_cursor->mouse_cursor_channel = WTSVirtualChannelOpenEx( + mouse_cursor->SessionId, RDPEMSC_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + if (!mouse_cursor->mouse_cursor_channel) + { + Error = GetLastError(); + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed with error %" PRIu32 "!", Error); + return Error; + } + + channelId = WTSChannelGetIdByHandle(mouse_cursor->mouse_cursor_channel); + + IFCALLRET(context->ChannelIdAssigned, status, context, channelId); + if (!status) + { + WLog_ERR(TAG, "context->ChannelIdAssigned failed!"); + return ERROR_INTERNAL_ERROR; + } + + return Error; +} + +static BOOL read_cap_set(wStream* s, wArrayList* capsSets) +{ + RDP_MOUSE_CURSOR_CAPSET* capsSet = NULL; + UINT32 signature = 0; + RDP_MOUSE_CURSOR_CAPVERSION version = RDP_MOUSE_CURSOR_CAPVERSION_INVALID; + UINT32 size = 0; + size_t capsDataSize = 0; + + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return FALSE; + + Stream_Read_UINT32(s, signature); + Stream_Read_UINT32(s, version); + Stream_Read_UINT32(s, size); + + if (size < 12) + { + WLog_ERR(TAG, "Size of caps set is invalid: %u", size); + return FALSE; + } + + capsDataSize = size - 12; + if (!Stream_CheckAndLogRequiredLength(TAG, s, capsDataSize)) + return FALSE; + + switch (version) + { + case RDP_MOUSE_CURSOR_CAPVERSION_1: + { + RDP_MOUSE_CURSOR_CAPSET_VERSION1* capsSetV1 = NULL; + + capsSetV1 = calloc(1, sizeof(RDP_MOUSE_CURSOR_CAPSET_VERSION1)); + if (!capsSetV1) + return FALSE; + + capsSet = (RDP_MOUSE_CURSOR_CAPSET*)capsSetV1; + break; + } + default: + WLog_WARN(TAG, "Received caps set with unknown version %u", version); + Stream_Seek(s, capsDataSize); + return TRUE; + } + WINPR_ASSERT(capsSet); + + capsSet->signature = signature; + capsSet->version = version; + capsSet->size = size; + + if (!ArrayList_Append(capsSets, capsSet)) + { + WLog_ERR(TAG, "Failed to append caps set to arraylist"); + free(capsSet); + return FALSE; + } + + // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): ArrayList_Append owns capsSet + return TRUE; +} + +static UINT mouse_cursor_server_recv_cs_caps_advertise(MouseCursorServerContext* context, + wStream* s, + const RDP_MOUSE_CURSOR_HEADER* header) +{ + RDP_MOUSE_CURSOR_CAPS_ADVERTISE_PDU pdu = { 0 }; + UINT error = CHANNEL_RC_OK; + + WINPR_ASSERT(context); + WINPR_ASSERT(s); + WINPR_ASSERT(header); + + pdu.header = *header; + + /* There must be at least one capability set present */ + if (!Stream_CheckAndLogRequiredLength(TAG, s, 12)) + return ERROR_NO_DATA; + + pdu.capsSets = ArrayList_New(FALSE); + if (!pdu.capsSets) + { + WLog_ERR(TAG, "Failed to allocate arraylist"); + return ERROR_NOT_ENOUGH_MEMORY; + } + + wObject* aobj = ArrayList_Object(pdu.capsSets); + WINPR_ASSERT(aobj); + aobj->fnObjectFree = free; + + while (Stream_GetRemainingLength(s) > 0) + { + if (!read_cap_set(s, pdu.capsSets)) + { + ArrayList_Free(pdu.capsSets); + return ERROR_INVALID_DATA; + } + } + + IFCALLRET(context->CapsAdvertise, error, context, &pdu); + if (error) + WLog_ERR(TAG, "context->CapsAdvertise failed with error %" PRIu32 "", error); + + ArrayList_Free(pdu.capsSets); + + return error; +} + +static UINT mouse_cursor_process_message(mouse_cursor_server* mouse_cursor) +{ + BOOL rc = 0; + UINT error = ERROR_INTERNAL_ERROR; + ULONG BytesReturned = 0; + RDP_MOUSE_CURSOR_HEADER header = { 0 }; + wStream* s = NULL; + + WINPR_ASSERT(mouse_cursor); + WINPR_ASSERT(mouse_cursor->mouse_cursor_channel); + + s = mouse_cursor->buffer; + WINPR_ASSERT(s); + + Stream_SetPosition(s, 0); + rc = WTSVirtualChannelRead(mouse_cursor->mouse_cursor_channel, 0, NULL, 0, &BytesReturned); + if (!rc) + goto out; + + if (BytesReturned < 1) + { + error = CHANNEL_RC_OK; + goto out; + } + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out; + } + + if (WTSVirtualChannelRead(mouse_cursor->mouse_cursor_channel, 0, (PCHAR)Stream_Buffer(s), + (ULONG)Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + goto out; + } + + Stream_SetLength(s, BytesReturned); + if (!Stream_CheckAndLogRequiredLength(TAG, s, RDPEMSC_HEADER_SIZE)) + return ERROR_NO_DATA; + + Stream_Read_UINT8(s, header.pduType); + Stream_Read_UINT8(s, header.updateType); + Stream_Read_UINT16(s, header.reserved); + + switch (header.pduType) + { + case PDUTYPE_CS_CAPS_ADVERTISE: + error = mouse_cursor_server_recv_cs_caps_advertise(&mouse_cursor->context, s, &header); + break; + default: + WLog_ERR(TAG, "mouse_cursor_process_message: unknown or invalid pduType %" PRIu8 "", + header.pduType); + break; + } + +out: + if (error) + WLog_ERR(TAG, "Response failed with error %" PRIu32 "!", error); + + return error; +} + +static UINT mouse_cursor_server_context_poll_int(MouseCursorServerContext* context) +{ + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + UINT error = ERROR_INTERNAL_ERROR; + + WINPR_ASSERT(mouse_cursor); + + switch (mouse_cursor->state) + { + case MOUSE_CURSOR_INITIAL: + error = mouse_cursor_server_open_channel(mouse_cursor); + if (error) + WLog_ERR(TAG, "mouse_cursor_server_open_channel failed with error %" PRIu32 "!", + error); + else + mouse_cursor->state = MOUSE_CURSOR_OPENED; + break; + case MOUSE_CURSOR_OPENED: + error = mouse_cursor_process_message(mouse_cursor); + break; + } + + return error; +} + +static HANDLE mouse_cursor_server_get_channel_handle(mouse_cursor_server* mouse_cursor) +{ + void* buffer = NULL; + DWORD BytesReturned = 0; + HANDLE ChannelEvent = NULL; + + WINPR_ASSERT(mouse_cursor); + + if (WTSVirtualChannelQuery(mouse_cursor->mouse_cursor_channel, WTSVirtualEventHandle, &buffer, + &BytesReturned) == TRUE) + { + if (BytesReturned == sizeof(HANDLE)) + CopyMemory(&ChannelEvent, buffer, sizeof(HANDLE)); + + WTSFreeMemory(buffer); + } + + return ChannelEvent; +} + +static DWORD WINAPI mouse_cursor_server_thread_func(LPVOID arg) +{ + DWORD nCount = 0; + HANDLE events[2] = { 0 }; + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)arg; + UINT error = CHANNEL_RC_OK; + DWORD status = 0; + + WINPR_ASSERT(mouse_cursor); + + nCount = 0; + events[nCount++] = mouse_cursor->stopEvent; + + while ((error == CHANNEL_RC_OK) && (WaitForSingleObject(events[0], 0) != WAIT_OBJECT_0)) + { + switch (mouse_cursor->state) + { + case MOUSE_CURSOR_INITIAL: + error = mouse_cursor_server_context_poll_int(&mouse_cursor->context); + if (error == CHANNEL_RC_OK) + { + events[1] = mouse_cursor_server_get_channel_handle(mouse_cursor); + nCount = 2; + } + break; + case MOUSE_CURSOR_OPENED: + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + switch (status) + { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: + case WAIT_TIMEOUT: + error = mouse_cursor_server_context_poll_int(&mouse_cursor->context); + break; + + case WAIT_FAILED: + default: + error = ERROR_INTERNAL_ERROR; + break; + } + break; + } + } + + WTSVirtualChannelClose(mouse_cursor->mouse_cursor_channel); + mouse_cursor->mouse_cursor_channel = NULL; + + if (error && mouse_cursor->context.rdpcontext) + setChannelError(mouse_cursor->context.rdpcontext, error, + "mouse_cursor_server_thread_func reported an error"); + + ExitThread(error); + return error; +} + +static UINT mouse_cursor_server_open(MouseCursorServerContext* context) +{ + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + + WINPR_ASSERT(mouse_cursor); + + if (!mouse_cursor->externalThread && (mouse_cursor->thread == NULL)) + { + mouse_cursor->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!mouse_cursor->stopEvent) + { + WLog_ERR(TAG, "CreateEvent failed!"); + return ERROR_INTERNAL_ERROR; + } + + mouse_cursor->thread = + CreateThread(NULL, 0, mouse_cursor_server_thread_func, mouse_cursor, 0, NULL); + if (!mouse_cursor->thread) + { + WLog_ERR(TAG, "CreateThread failed!"); + CloseHandle(mouse_cursor->stopEvent); + mouse_cursor->stopEvent = NULL; + return ERROR_INTERNAL_ERROR; + } + } + mouse_cursor->isOpened = TRUE; + + return CHANNEL_RC_OK; +} + +static UINT mouse_cursor_server_close(MouseCursorServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + + WINPR_ASSERT(mouse_cursor); + + if (!mouse_cursor->externalThread && mouse_cursor->thread) + { + SetEvent(mouse_cursor->stopEvent); + + if (WaitForSingleObject(mouse_cursor->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error); + return error; + } + + CloseHandle(mouse_cursor->thread); + CloseHandle(mouse_cursor->stopEvent); + mouse_cursor->thread = NULL; + mouse_cursor->stopEvent = NULL; + } + if (mouse_cursor->externalThread) + { + if (mouse_cursor->state != MOUSE_CURSOR_INITIAL) + { + WTSVirtualChannelClose(mouse_cursor->mouse_cursor_channel); + mouse_cursor->mouse_cursor_channel = NULL; + mouse_cursor->state = MOUSE_CURSOR_INITIAL; + } + } + mouse_cursor->isOpened = FALSE; + + return error; +} + +static UINT mouse_cursor_server_context_poll(MouseCursorServerContext* context) +{ + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + + WINPR_ASSERT(mouse_cursor); + + if (!mouse_cursor->externalThread) + return ERROR_INTERNAL_ERROR; + + return mouse_cursor_server_context_poll_int(context); +} + +static BOOL mouse_cursor_server_context_handle(MouseCursorServerContext* context, HANDLE* handle) +{ + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + + WINPR_ASSERT(mouse_cursor); + WINPR_ASSERT(handle); + + if (!mouse_cursor->externalThread) + return FALSE; + if (mouse_cursor->state == MOUSE_CURSOR_INITIAL) + return FALSE; + + *handle = mouse_cursor_server_get_channel_handle(mouse_cursor); + + return TRUE; +} + +static wStream* mouse_cursor_server_packet_new(size_t size, RDP_MOUSE_CURSOR_PDUTYPE pduType, + const RDP_MOUSE_CURSOR_HEADER* header) +{ + wStream* s = NULL; + + /* Allocate what we need plus header bytes */ + s = Stream_New(NULL, size + RDPEMSC_HEADER_SIZE); + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + return NULL; + } + + Stream_Write_UINT8(s, pduType); + Stream_Write_UINT8(s, header->updateType); + Stream_Write_UINT16(s, header->reserved); + + return s; +} + +static UINT mouse_cursor_server_packet_send(MouseCursorServerContext* context, wStream* s) +{ + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + UINT error = CHANNEL_RC_OK; + ULONG written = 0; + + WINPR_ASSERT(mouse_cursor); + WINPR_ASSERT(s); + + if (!WTSVirtualChannelWrite(mouse_cursor->mouse_cursor_channel, (PCHAR)Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + error = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written, + Stream_GetPosition(s)); + } + +out: + Stream_Free(s, TRUE); + return error; +} + +static UINT +mouse_cursor_server_send_sc_caps_confirm(MouseCursorServerContext* context, + const RDP_MOUSE_CURSOR_CAPS_CONFIRM_PDU* capsConfirm) +{ + RDP_MOUSE_CURSOR_CAPSET* capsetHeader = NULL; + RDP_MOUSE_CURSOR_PDUTYPE pduType = PDUTYPE_EMSC_RESERVED; + size_t caps_size = 0; + wStream* s = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(capsConfirm); + + capsetHeader = capsConfirm->capsSet; + WINPR_ASSERT(capsetHeader); + + caps_size = 12; + switch (capsetHeader->version) + { + case RDP_MOUSE_CURSOR_CAPVERSION_1: + break; + default: + WINPR_ASSERT(FALSE); + break; + } + + pduType = PDUTYPE_SC_CAPS_CONFIRM; + s = mouse_cursor_server_packet_new(caps_size, pduType, &capsConfirm->header); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + Stream_Write_UINT32(s, capsetHeader->signature); + Stream_Write_UINT32(s, capsetHeader->version); + Stream_Write_UINT32(s, capsetHeader->size); + + /* Write capsData */ + switch (capsetHeader->version) + { + case RDP_MOUSE_CURSOR_CAPVERSION_1: + break; + default: + WINPR_ASSERT(FALSE); + break; + } + + return mouse_cursor_server_packet_send(context, s); +} + +static void write_point16(wStream* s, const TS_POINT16* point16) +{ + WINPR_ASSERT(s); + WINPR_ASSERT(point16); + + Stream_Write_UINT16(s, point16->xPos); + Stream_Write_UINT16(s, point16->yPos); +} + +static UINT mouse_cursor_server_send_sc_mouseptr_update( + MouseCursorServerContext* context, const RDP_MOUSE_CURSOR_MOUSEPTR_UPDATE_PDU* mouseptrUpdate) +{ + TS_POINT16* position = NULL; + TS_POINTERATTRIBUTE* pointerAttribute = NULL; + TS_LARGEPOINTERATTRIBUTE* largePointerAttribute = NULL; + RDP_MOUSE_CURSOR_PDUTYPE pduType = PDUTYPE_EMSC_RESERVED; + size_t update_size = 0; + wStream* s = NULL; + + WINPR_ASSERT(context); + WINPR_ASSERT(mouseptrUpdate); + + position = mouseptrUpdate->position; + pointerAttribute = mouseptrUpdate->pointerAttribute; + largePointerAttribute = mouseptrUpdate->largePointerAttribute; + + switch (mouseptrUpdate->header.updateType) + { + case TS_UPDATETYPE_MOUSEPTR_SYSTEM_NULL: + case TS_UPDATETYPE_MOUSEPTR_SYSTEM_DEFAULT: + update_size = 0; + break; + case TS_UPDATETYPE_MOUSEPTR_POSITION: + WINPR_ASSERT(position); + update_size = 4; + break; + case TS_UPDATETYPE_MOUSEPTR_CACHED: + WINPR_ASSERT(mouseptrUpdate->cachedPointerIndex); + update_size = 2; + break; + case TS_UPDATETYPE_MOUSEPTR_POINTER: + WINPR_ASSERT(pointerAttribute); + update_size = 2 + 2 + 4 + 2 + 2 + 2 + 2; + update_size += pointerAttribute->lengthAndMask; + update_size += pointerAttribute->lengthXorMask; + break; + case TS_UPDATETYPE_MOUSEPTR_LARGE_POINTER: + WINPR_ASSERT(largePointerAttribute); + update_size = 2 + 2 + 4 + 2 + 2 + 4 + 4; + update_size += largePointerAttribute->lengthAndMask; + update_size += largePointerAttribute->lengthXorMask; + break; + default: + WINPR_ASSERT(FALSE); + break; + } + + pduType = PDUTYPE_SC_MOUSEPTR_UPDATE; + s = mouse_cursor_server_packet_new(update_size, pduType, &mouseptrUpdate->header); + if (!s) + return ERROR_NOT_ENOUGH_MEMORY; + + switch (mouseptrUpdate->header.updateType) + { + case TS_UPDATETYPE_MOUSEPTR_SYSTEM_NULL: + case TS_UPDATETYPE_MOUSEPTR_SYSTEM_DEFAULT: + break; + case TS_UPDATETYPE_MOUSEPTR_POSITION: + write_point16(s, position); + break; + case TS_UPDATETYPE_MOUSEPTR_CACHED: + Stream_Write_UINT16(s, *mouseptrUpdate->cachedPointerIndex); + break; + case TS_UPDATETYPE_MOUSEPTR_POINTER: + Stream_Write_UINT16(s, pointerAttribute->xorBpp); + Stream_Write_UINT16(s, pointerAttribute->cacheIndex); + write_point16(s, &pointerAttribute->hotSpot); + Stream_Write_UINT16(s, pointerAttribute->width); + Stream_Write_UINT16(s, pointerAttribute->height); + Stream_Write_UINT16(s, pointerAttribute->lengthAndMask); + Stream_Write_UINT16(s, pointerAttribute->lengthXorMask); + Stream_Write(s, pointerAttribute->xorMaskData, pointerAttribute->lengthXorMask); + Stream_Write(s, pointerAttribute->andMaskData, pointerAttribute->lengthAndMask); + break; + case TS_UPDATETYPE_MOUSEPTR_LARGE_POINTER: + Stream_Write_UINT16(s, largePointerAttribute->xorBpp); + Stream_Write_UINT16(s, largePointerAttribute->cacheIndex); + write_point16(s, &largePointerAttribute->hotSpot); + Stream_Write_UINT16(s, largePointerAttribute->width); + Stream_Write_UINT16(s, largePointerAttribute->height); + Stream_Write_UINT32(s, largePointerAttribute->lengthAndMask); + Stream_Write_UINT32(s, largePointerAttribute->lengthXorMask); + Stream_Write(s, largePointerAttribute->xorMaskData, + largePointerAttribute->lengthXorMask); + Stream_Write(s, largePointerAttribute->andMaskData, + largePointerAttribute->lengthAndMask); + break; + default: + WINPR_ASSERT(FALSE); + break; + } + + return mouse_cursor_server_packet_send(context, s); +} + +MouseCursorServerContext* mouse_cursor_server_context_new(HANDLE vcm) +{ + mouse_cursor_server* mouse_cursor = + (mouse_cursor_server*)calloc(1, sizeof(mouse_cursor_server)); + + if (!mouse_cursor) + return NULL; + + mouse_cursor->context.vcm = vcm; + mouse_cursor->context.Initialize = mouse_cursor_server_initialize; + mouse_cursor->context.Open = mouse_cursor_server_open; + mouse_cursor->context.Close = mouse_cursor_server_close; + mouse_cursor->context.Poll = mouse_cursor_server_context_poll; + mouse_cursor->context.ChannelHandle = mouse_cursor_server_context_handle; + + mouse_cursor->context.CapsConfirm = mouse_cursor_server_send_sc_caps_confirm; + mouse_cursor->context.MouseptrUpdate = mouse_cursor_server_send_sc_mouseptr_update; + + mouse_cursor->buffer = Stream_New(NULL, 4096); + if (!mouse_cursor->buffer) + goto fail; + + return &mouse_cursor->context; +fail: + WINPR_PRAGMA_DIAG_PUSH + WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC + mouse_cursor_server_context_free(&mouse_cursor->context); + WINPR_PRAGMA_DIAG_POP + return NULL; +} + +void mouse_cursor_server_context_free(MouseCursorServerContext* context) +{ + mouse_cursor_server* mouse_cursor = (mouse_cursor_server*)context; + + if (mouse_cursor) + { + mouse_cursor_server_close(context); + Stream_Free(mouse_cursor->buffer, TRUE); + } + + free(mouse_cursor); +} |