diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 01:24:41 +0000 |
commit | a9bcc81f821d7c66f623779fa5147e728eb3c388 (patch) | |
tree | 98676963bcdd537ae5908a067a8eb110b93486a6 /server/Mac | |
parent | Initial commit. (diff) | |
download | freerdp3-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/Mac/CMakeLists.txt | 78 | ||||
-rw-r--r-- | server/Mac/ModuleOptions.cmake | 4 | ||||
-rw-r--r-- | server/Mac/mf_audin.c | 64 | ||||
-rw-r--r-- | server/Mac/mf_audin.h | 33 | ||||
-rw-r--r-- | server/Mac/mf_event.c | 219 | ||||
-rw-r--r-- | server/Mac/mf_event.h | 75 | ||||
-rw-r--r-- | server/Mac/mf_info.c | 231 | ||||
-rw-r--r-- | server/Mac/mf_info.h | 49 | ||||
-rw-r--r-- | server/Mac/mf_input.c | 511 | ||||
-rw-r--r-- | server/Mac/mf_input.h | 36 | ||||
-rw-r--r-- | server/Mac/mf_interface.c | 0 | ||||
-rw-r--r-- | server/Mac/mf_interface.h | 106 | ||||
-rw-r--r-- | server/Mac/mf_mountain_lion.c | 269 | ||||
-rw-r--r-- | server/Mac/mf_mountain_lion.h | 38 | ||||
-rw-r--r-- | server/Mac/mf_peer.c | 485 | ||||
-rw-r--r-- | server/Mac/mf_peer.h | 33 | ||||
-rw-r--r-- | server/Mac/mf_rdpsnd.c | 200 | ||||
-rw-r--r-- | server/Mac/mf_rdpsnd.h | 58 | ||||
-rw-r--r-- | server/Mac/mf_types.h | 33 | ||||
-rw-r--r-- | server/Mac/mfreerdp.c | 108 | ||||
-rw-r--r-- | server/Mac/mfreerdp.h | 28 |
21 files changed, 2658 insertions, 0 deletions
diff --git a/server/Mac/CMakeLists.txt b/server/Mac/CMakeLists.txt new file mode 100644 index 0000000..9dddb38 --- /dev/null +++ b/server/Mac/CMakeLists.txt @@ -0,0 +1,78 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP Mac OS X 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 "mfreerdp-server") +set(MODULE_PREFIX "FREERDP_SERVER_MAC") + +FIND_LIBRARY(AUDIO_TOOL AudioToolbox) +FIND_LIBRARY(CORE_AUDIO CoreAudio) +FIND_LIBRARY(CORE_VIDEO CoreVideo) +FIND_LIBRARY(APP_SERVICES ApplicationServices) +FIND_LIBRARY(IOKIT IOKit) +FIND_LIBRARY(IOSURFACE IOSurface) +FIND_LIBRARY(CARBON Carbon) + +set(${MODULE_PREFIX}_SRCS + mfreerdp.c + mfreerdp.h + mf_interface.c + mf_interface.h + mf_event.c + mf_event.h + mf_peer.c + mf_peer.h + mf_info.c + mf_info.h + mf_input.c + mf_input.h + mf_mountain_lion.c + mf_mountain_lion.h) + +if(CHANNEL_AUDIN_SERVER) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} + mf_audin.c + mf_audin.h) +endif() + +if(CHANNEL_RDPSND_SERVER) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} + mf_rdpsnd.c + mf_rdpsnd.h) + +endif() + +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() + +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} + freerdp-server + ${AUDIO_TOOL} + ${CORE_AUDIO} + ${CORE_VIDEO} + ${APP_SERVICES} + ${IOKIT} + ${IOSURFACE} + ${CARBON}) + +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR}) + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/Mac") diff --git a/server/Mac/ModuleOptions.cmake b/server/Mac/ModuleOptions.cmake new file mode 100644 index 0000000..bfd36cb --- /dev/null +++ b/server/Mac/ModuleOptions.cmake @@ -0,0 +1,4 @@ + +set(FREERDP_SERVER_NAME "mfreerdp-server") +set(FREERDP_SERVER_PLATFORM "X11") +set(FREERDP_SERVER_VENDOR "FreeRDP") diff --git a/server/Mac/mf_audin.c b/server/Mac/mf_audin.c new file mode 100644 index 0000000..aaabafa --- /dev/null +++ b/server/Mac/mf_audin.c @@ -0,0 +1,64 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X Server (Audio Input) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com> + * 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 "mfreerdp.h" + +#include "mf_audin.h" +#include "mf_interface.h" + +#include <freerdp/server/server-common.h> +#include <freerdp/log.h> +#define TAG SERVER_TAG("mac") + +static UINT mf_peer_audin_data(audin_server_context* audin, const SNDIN_DATA* data) +{ + /* TODO: Implement */ + WINPR_ASSERT(audin); + WINPR_ASSERT(data); + + WLog_WARN(TAG, "not implemented"); + WLog_DBG(TAG, "receive %" PRIdz " bytes.", Stream_Length(data->Data)); + return CHANNEL_RC_OK; +} + +BOOL mf_peer_audin_init(mfPeerContext* context) +{ + WINPR_ASSERT(context); + + context->audin = audin_server_context_new(context->vcm); + context->audin->rdpcontext = &context->_p; + context->audin->userdata = context; + + context->audin->Data = mf_peer_audin_data; + + return audin_server_set_formats(context->audin, -1, NULL); +} + +void mf_peer_audin_uninit(mfPeerContext* context) +{ + WINPR_ASSERT(context); + + audin_server_context_free(context->audin); + context->audin = NULL; +} diff --git a/server/Mac/mf_audin.h b/server/Mac/mf_audin.h new file mode 100644 index 0000000..31edffa --- /dev/null +++ b/server/Mac/mf_audin.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X Server (Audio Input) + * + * 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. + */ + +#ifndef FREERDP_SERVER_MAC_AUDIN_H +#define FREERDP_SERVER_MAC_AUDIN_H + +#include <freerdp/freerdp.h> +#include <freerdp/listener.h> + +#include "mf_types.h" +#include "mfreerdp.h" + +BOOL mf_peer_audin_init(mfPeerContext* context); +void mf_peer_audin_uninit(mfPeerContext* context); + +#endif /* FREERDP_SERVER_MAC_AUDIN_H */ diff --git a/server/Mac/mf_event.c b/server/Mac/mf_event.c new file mode 100644 index 0000000..e30b9ac --- /dev/null +++ b/server/Mac/mf_event.c @@ -0,0 +1,219 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * OS X Server Event Handling + * + * 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 <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mf_event.h" + +#include <freerdp/log.h> +#define TAG SERVER_TAG("mac") + +int mf_is_event_set(mfEventQueue* event_queue) +{ + fd_set rfds; + int num_set; + struct timeval time = { 0 }; + + FD_ZERO(&rfds); + FD_SET(event_queue->pipe_fd[0], &rfds); + num_set = select(event_queue->pipe_fd[0] + 1, &rfds, 0, 0, &time); + + return (num_set == 1); +} + +void mf_signal_event(mfEventQueue* event_queue) +{ + int length; + + length = write(event_queue->pipe_fd[1], "sig", 4); + + if (length != 4) + WLog_ERR(TAG, "mf_signal_event: error"); +} + +void mf_set_event(mfEventQueue* event_queue) +{ + int length; + + length = write(event_queue->pipe_fd[1], "sig", 4); + + if (length != 4) + WLog_ERR(TAG, "mf_set_event: error"); +} + +void mf_clear_events(mfEventQueue* event_queue) +{ + int length; + + while (mf_is_event_set(event_queue)) + { + length = read(event_queue->pipe_fd[0], &length, 4); + + if (length != 4) + WLog_ERR(TAG, "mf_clear_event: error"); + } +} + +void mf_clear_event(mfEventQueue* event_queue) +{ + int length; + + length = read(event_queue->pipe_fd[0], &length, 4); + + if (length != 4) + WLog_ERR(TAG, "mf_clear_event: error"); +} + +void mf_event_push(mfEventQueue* event_queue, mfEvent* event) +{ + pthread_mutex_lock(&(event_queue->mutex)); + + if (event_queue->count >= event_queue->size) + { + event_queue->size *= 2; + event_queue->events = + (mfEvent**)realloc((void*)event_queue->events, sizeof(mfEvent*) * event_queue->size); + } + + event_queue->events[(event_queue->count)++] = event; + + pthread_mutex_unlock(&(event_queue->mutex)); + + mf_set_event(event_queue); +} + +mfEvent* mf_event_peek(mfEventQueue* event_queue) +{ + mfEvent* event; + + pthread_mutex_lock(&(event_queue->mutex)); + + if (event_queue->count < 1) + event = NULL; + else + event = event_queue->events[0]; + + pthread_mutex_unlock(&(event_queue->mutex)); + + return event; +} + +mfEvent* mf_event_pop(mfEventQueue* event_queue) +{ + mfEvent* event; + + pthread_mutex_lock(&(event_queue->mutex)); + + if (event_queue->count < 1) + return NULL; + + /* remove event signal */ + mf_clear_event(event_queue); + + event = event_queue->events[0]; + (event_queue->count)--; + + memmove(&event_queue->events[0], &event_queue->events[1], event_queue->count * sizeof(void*)); + + pthread_mutex_unlock(&(event_queue->mutex)); + + return event; +} + +mfEventRegion* mf_event_region_new(int x, int y, int width, int height) +{ + mfEventRegion* event_region = malloc(sizeof(mfEventRegion)); + + if (event_region != NULL) + { + event_region->x = x; + event_region->y = y; + event_region->width = width; + event_region->height = height; + } + + return event_region; +} + +void mf_event_region_free(mfEventRegion* event_region) +{ + free(event_region); +} + +mfEvent* mf_event_new(int type) +{ + mfEvent* event = malloc(sizeof(mfEvent)); + if (!event) + return NULL; + event->type = type; + return event; +} + +void mf_event_free(mfEvent* event) +{ + free(event); +} + +mfEventQueue* mf_event_queue_new() +{ + mfEventQueue* event_queue = malloc(sizeof(mfEventQueue)); + + if (event_queue != NULL) + { + event_queue->pipe_fd[0] = -1; + event_queue->pipe_fd[1] = -1; + + event_queue->size = 16; + event_queue->count = 0; + event_queue->events = (mfEvent**)malloc(sizeof(mfEvent*) * event_queue->size); + + if (pipe(event_queue->pipe_fd) < 0) + { + free(event_queue); + return NULL; + } + + pthread_mutex_init(&(event_queue->mutex), NULL); + } + + return event_queue; +} + +void mf_event_queue_free(mfEventQueue* event_queue) +{ + if (event_queue->pipe_fd[0] != -1) + { + close(event_queue->pipe_fd[0]); + event_queue->pipe_fd[0] = -1; + } + + if (event_queue->pipe_fd[1] != -1) + { + close(event_queue->pipe_fd[1]); + event_queue->pipe_fd[1] = -1; + } + + pthread_mutex_destroy(&(event_queue->mutex)); +} diff --git a/server/Mac/mf_event.h b/server/Mac/mf_event.h new file mode 100644 index 0000000..197cfd7 --- /dev/null +++ b/server/Mac/mf_event.h @@ -0,0 +1,75 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * OS X Server Event Handling + * + * 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_MAC_EVENT_H +#define FREERDP_SERVER_MAC_EVENT_H + +typedef struct mf_event mfEvent; +typedef struct mf_event_queue mfEventQueue; +typedef struct mf_event_region mfEventRegion; + +#include <pthread.h> +#include "mfreerdp.h" + +//#include "mf_peer.h" + +enum mf_event_type +{ + FREERDP_SERVER_MAC_EVENT_TYPE_REGION, + FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK +}; + +struct mf_event +{ + int type; +}; + +struct mf_event_queue +{ + int size; + int count; + int pipe_fd[2]; + mfEvent** events; + pthread_mutex_t mutex; +}; + +struct mf_event_region +{ + int type; + + int x; + int y; + int width; + int height; +}; + +void mf_event_push(mfEventQueue* event_queue, mfEvent* event); +mfEvent* mf_event_peek(mfEventQueue* event_queue); +mfEvent* mf_event_pop(mfEventQueue* event_queue); + +mfEventRegion* mf_event_region_new(int x, int y, int width, int height); +void mf_event_region_free(mfEventRegion* event_region); + +mfEvent* mf_event_new(int type); +void mf_event_free(mfEvent* event); + +mfEventQueue* mf_event_queue_new(void); +void mf_event_queue_free(mfEventQueue* event_queue); + +#endif /* FREERDP_SERVER_MAC_EVENT_H */ diff --git a/server/Mac/mf_info.c b/server/Mac/mf_info.c new file mode 100644 index 0000000..ab523e1 --- /dev/null +++ b/server/Mac/mf_info.c @@ -0,0 +1,231 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * FreeRDP Mac OS X 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 <errno.h> + +#include "mf_info.h" +#include "mf_mountain_lion.h" + +#define MF_INFO_DEFAULT_FPS 30 +#define MF_INFO_MAXPEERS 32 + +static mfInfo* mfInfoInstance = NULL; + +int mf_info_lock(mfInfo* mfi) +{ + int status = pthread_mutex_lock(&mfi->mutex); + + switch (status) + { + case 0: + return TRUE; + break; + + default: + return -1; + break; + } + + return 1; +} + +int mf_info_try_lock(mfInfo* mfi, UINT32 ms) +{ + int status = pthread_mutex_trylock(&mfi->mutex); + + switch (status) + { + case 0: + return TRUE; + break; + + case EBUSY: + return FALSE; + break; + + default: + return -1; + break; + } + + return 1; +} + +int mf_info_unlock(mfInfo* mfi) +{ + int status = pthread_mutex_unlock(&mfi->mutex); + + switch (status) + { + case 0: + return TRUE; + break; + + default: + return -1; + break; + } + + return 1; +} + +mfInfo* mf_info_init() +{ + mfInfo* mfi; + + mfi = (mfInfo*)calloc(1, sizeof(mfInfo)); + + if (mfi != NULL) + { + pthread_mutex_init(&mfi->mutex, NULL); + + mfi->peers = (freerdp_peer**)calloc(MF_INFO_MAXPEERS, sizeof(freerdp_peer*)); + if (!mfi->peers) + { + free(mfi); + return NULL; + } + + mfi->framesPerSecond = MF_INFO_DEFAULT_FPS; + mfi->input_disabled = FALSE; + } + + return mfi; +} + +mfInfo* mf_info_get_instance() +{ + if (mfInfoInstance == NULL) + mfInfoInstance = mf_info_init(); + + return mfInfoInstance; +} + +void mf_info_peer_register(mfInfo* mfi, mfPeerContext* context) +{ + if (mf_info_lock(mfi) > 0) + { + int peerId; + + if (mfi->peerCount == MF_INFO_MAXPEERS) + { + mf_info_unlock(mfi); + return; + } + + context->info = mfi; + + if (mfi->peerCount == 0) + { + mf_mlion_display_info(&mfi->servscreen_width, &mfi->servscreen_height, &mfi->scale); + mf_mlion_screen_updates_init(); + mf_mlion_start_getting_screen_updates(); + } + + peerId = 0; + + for (int i = 0; i < MF_INFO_MAXPEERS; ++i) + { + // empty index will be our peer id + if (mfi->peers[i] == NULL) + { + peerId = i; + break; + } + } + + mfi->peers[peerId] = ((rdpContext*)context)->peer; + mfi->peers[peerId]->pId = peerId; + mfi->peerCount++; + + mf_info_unlock(mfi); + } +} + +void mf_info_peer_unregister(mfInfo* mfi, mfPeerContext* context) +{ + if (mf_info_lock(mfi) > 0) + { + int peerId; + + peerId = ((rdpContext*)context)->peer->pId; + mfi->peers[peerId] = NULL; + mfi->peerCount--; + + if (mfi->peerCount == 0) + mf_mlion_stop_getting_screen_updates(); + + mf_info_unlock(mfi); + } +} + +BOOL mf_info_have_updates(mfInfo* mfi) +{ + if (mfi->framesWaiting == 0) + return FALSE; + + return TRUE; +} + +void mf_info_update_changes(mfInfo* mfi) +{ +} + +void mf_info_find_invalid_region(mfInfo* mfi) +{ + mf_mlion_get_dirty_region(&mfi->invalid); +} + +void mf_info_clear_invalid_region(mfInfo* mfi) +{ + mf_mlion_clear_dirty_region(); + mfi->invalid.height = 0; + mfi->invalid.width = 0; +} + +void mf_info_invalidate_full_screen(mfInfo* mfi) +{ + mfi->invalid.x = 0; + mfi->invalid.y = 0; + mfi->invalid.height = mfi->servscreen_height; + mfi->invalid.width = mfi->servscreen_width; +} + +BOOL mf_info_have_invalid_region(mfInfo* mfi) +{ + if (mfi->invalid.width * mfi->invalid.height == 0) + return FALSE; + + return TRUE; +} + +void mf_info_getScreenData(mfInfo* mfi, long* width, long* height, BYTE** pBits, int* pitch) +{ + *width = mfi->invalid.width / mfi->scale; + *height = mfi->invalid.height / mfi->scale; + *pitch = mfi->servscreen_width * mfi->scale * 4; + + mf_mlion_get_pixelData(mfi->invalid.x / mfi->scale, mfi->invalid.y / mfi->scale, *width, + *height, pBits); + + *pBits = *pBits + (mfi->invalid.x * 4) + (*pitch * mfi->invalid.y); +} diff --git a/server/Mac/mf_info.h b/server/Mac/mf_info.h new file mode 100644 index 0000000..8f08982 --- /dev/null +++ b/server/Mac/mf_info.h @@ -0,0 +1,49 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X 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_MAC_INFO_H +#define FREERDP_SERVER_MAC_INFO_H + +#define FREERDP_SERVER_MAC_INFO_DEFAULT_FPS 1 +#define FREERDP_SERVER_MAC_INFO_MAXPEERS 1 + +#include <winpr/wtypes.h> +#include <freerdp/codec/rfx.h> + +#include "mf_interface.h" + +int mf_info_lock(mfInfo* mfi); +int mf_info_try_lock(mfInfo* mfi, UINT32 ms); +int mf_info_unlock(mfInfo* mfi); + +mfInfo* mf_info_get_instance(void); +void mf_info_peer_register(mfInfo* mfi, mfPeerContext* context); +void mf_info_peer_unregister(mfInfo* mfi, mfPeerContext* context); + +BOOL mf_info_have_updates(mfInfo* mfi); +void mf_info_update_changes(mfInfo* mfi); +void mf_info_find_invalid_region(mfInfo* mfi); +void mf_info_clear_invalid_region(mfInfo* mfi); +void mf_info_invalidate_full_screen(mfInfo* mfi); +BOOL mf_info_have_invalid_region(mfInfo* mfi); +void mf_info_getScreenData(mfInfo* mfi, long* width, long* height, BYTE** pBits, int* pitch); +// BOOL CALLBACK mf_info_monEnumCB(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM +// dwData); + +#endif /* FREERDP_SERVER_MAC_INFO_H */ diff --git a/server/Mac/mf_input.c b/server/Mac/mf_input.c new file mode 100644 index 0000000..fd4af85 --- /dev/null +++ b/server/Mac/mf_input.c @@ -0,0 +1,511 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X Server (Input) + * + * 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 <ApplicationServices/ApplicationServices.h> +#include <Carbon/Carbon.h> + +#include <winpr/windows.h> + +#include "mf_input.h" +#include "mf_info.h" + +#include <freerdp/log.h> +#define TAG SERVER_TAG("mac") + +static const CGKeyCode keymap[256] = { + 0xFF, // 0x0 + kVK_Escape, // 0x1 + kVK_ANSI_1, // 0x2 + kVK_ANSI_2, // 0x3 + kVK_ANSI_3, // 0x4 + kVK_ANSI_4, // 0x5 + kVK_ANSI_5, // 0x6 + kVK_ANSI_6, // 0x7 + kVK_ANSI_7, // 0x8 + kVK_ANSI_8, // 0x9 + kVK_ANSI_9, // 0xa + kVK_ANSI_0, // 0xb + kVK_ANSI_Minus, // 0xc + kVK_ANSI_Equal, // 0xd + kVK_Delete, // 0xe + kVK_Tab, // 0xf + kVK_ANSI_Q, // 0x10 + kVK_ANSI_W, // 0x11 + kVK_ANSI_E, // 0x12 + kVK_ANSI_R, // 0x13 + kVK_ANSI_T, // 0x14 + kVK_ANSI_Y, // 0x15 + kVK_ANSI_U, // 0x16 + kVK_ANSI_I, // 0x17 + kVK_ANSI_O, // 0x18 + kVK_ANSI_P, // 0x19 + kVK_ANSI_LeftBracket, // 0x1a + kVK_ANSI_RightBracket, // 0x1b + kVK_Return, // 0x1c + kVK_Control, // 0x1d + kVK_ANSI_A, // 0x1e + kVK_ANSI_S, // 0x1f + kVK_ANSI_D, // 0x20 + kVK_ANSI_F, // 0x21 + kVK_ANSI_G, // 0x22 + kVK_ANSI_H, // 0x23 + kVK_ANSI_J, // 0x24 + kVK_ANSI_K, // 0x25 + kVK_ANSI_L, // 0x26 + kVK_ANSI_Semicolon, // 0x27 + kVK_ANSI_Quote, // 0x28 + kVK_ANSI_Grave, // 0x29 + kVK_Shift, // 0x2a + kVK_ANSI_Backslash, // 0x2b + kVK_ANSI_Z, // 0x2c + kVK_ANSI_X, // 0x2d + kVK_ANSI_C, // 0x2e + kVK_ANSI_V, // 0x2f + kVK_ANSI_B, // 0x30 + kVK_ANSI_N, // 0x31 + kVK_ANSI_M, // 0x32 + kVK_ANSI_Comma, // 0x33 + kVK_ANSI_Period, // 0x34 + kVK_ANSI_Slash, // 0x35 + kVK_Shift, // 0x36 + kVK_ANSI_KeypadMultiply, // 0x37 + kVK_Option, // 0x38 + kVK_Space, // 0x39 + kVK_CapsLock, // 0x3a + kVK_F1, // 0x3b + kVK_F2, // 0x3c + kVK_F3, // 0x3d + kVK_F4, // 0x3e + kVK_F5, // 0x3f + kVK_F6, // 0x40 + kVK_F7, // 0x41 + kVK_F8, // 0x42 + kVK_F9, // 0x43 + kVK_F10, // 0x44 + 0xFF, // 0x45 -- numlock + 0xFF, // 0x46 -- scroll lock + kVK_ANSI_Keypad7, // 0x47 + kVK_ANSI_Keypad8, // 0x48 + kVK_ANSI_Keypad9, // 0x49 + kVK_ANSI_KeypadMinus, // 0x4a + kVK_ANSI_Keypad4, // 0x4b + kVK_ANSI_Keypad5, // 0x4c + kVK_ANSI_Keypad6, // 0x4d + kVK_ANSI_KeypadPlus, // 0x4e + kVK_ANSI_Keypad1, // 0x4f + kVK_ANSI_Keypad2, // 0x50 + kVK_ANSI_Keypad3, // 0x51 + kVK_ANSI_Keypad0, // 0x52 + kVK_ANSI_KeypadDecimal, // 0x53 + 0xFF, // 0x54 + 0xFF, // 0x55 + 0xFF, // 0x56 + kVK_F11, // 0x57 + kVK_F12, // 0x58 + 0xFF, // 0x59 -- pause + 0xFF, // 0x5a + kVK_Control, // 0x5b + kVK_Control, // 0x5c + 0xFF, // 0x5d -- application + 0xFF, // 0x5e -- power + 0xFF, // 0x5f -- sleep + 0xFF, // 0x60 + 0xFF, // 0x61 + 0xFF, // 0x62 + 0xFF, // 0x63 -- wake + 0xFF, // 0x64 + 0xFF, // 0x65 + 0xFF, // 0x66 + 0xFF, // 0x67 + 0xFF, // 0x68 + 0xFF, // 0x69 + 0xFF, // 0x6a + 0xFF, // 0x6b + 0xFF, // 0x6c + 0xFF, // 0x6d + 0xFF, // 0x6e + 0xFF, // 0x6f + 0xFF, // 0x70 + 0xFF, // 0x71 + 0xFF, // 0x72 + 0xFF, // 0x73 + 0xFF, // 0x74 + 0xFF, // 0x75 + 0xFF, // 0x76 + 0xFF, // 0x77 + 0xFF, // 0x78 + 0xFF, // 0x79 + 0xFF, // 0x7a + 0xFF, // 0x7b + 0xFF, // 0x7c + 0xFF, // 0x7d + 0xFF, // 0x7e + 0xFF, // 0x7f + 0xFF, // 0x80 + 0xFF, // 0x81 + 0xFF, // 0x82 + 0xFF, // 0x83 + 0xFF, // 0x84 + 0xFF, // 0x85 + 0xFF, // 0x86 + 0xFF, // 0x87 + 0xFF, // 0x88 + 0xFF, // 0x89 + 0xFF, // 0x8a + 0xFF, // 0x8b + 0xFF, // 0x8c + 0xFF, // 0x8d + 0xFF, // 0x8e + 0xFF, // 0x8f + 0xFF, // 0x90 + 0xFF, // 0x91 + 0xFF, // 0x92 + 0xFF, // 0x93 + 0xFF, // 0x94 + 0xFF, // 0x95 + 0xFF, // 0x96 + 0xFF, // 0x97 + 0xFF, // 0x98 + 0xFF, // 0x99 + 0xFF, // 0x9a + 0xFF, // 0x9b + 0xFF, // 0x9c + 0xFF, // 0x9d + 0xFF, // 0x9e + 0xFF, // 0x9f + 0xFF, // 0xa0 + 0xFF, // 0xa1 + 0xFF, // 0xa2 + 0xFF, // 0xa3 + 0xFF, // 0xa4 + 0xFF, // 0xa5 + 0xFF, // 0xa6 + 0xFF, // 0xa7 + 0xFF, // 0xa8 + 0xFF, // 0xa9 + 0xFF, // 0xaa + 0xFF, // 0xab + 0xFF, // 0xac + 0xFF, // 0xad + 0xFF, // 0xae + 0xFF, // 0xaf + 0xFF, // 0xb0 + 0xFF, // 0xb1 + 0xFF, // 0xb2 + 0xFF, // 0xb3 + 0xFF, // 0xb4 + 0xFF, // 0xb5 + 0xFF, // 0xb6 + 0xFF, // 0xb7 + 0xFF, // 0xb8 + 0xFF, // 0xb9 + 0xFF, // 0xba + 0xFF, // 0xbb + 0xFF, // 0xbc + 0xFF, // 0xbd + 0xFF, // 0xbe + 0xFF, // 0xbf + 0xFF, // 0xc0 + 0xFF, // 0xc1 + 0xFF, // 0xc2 + 0xFF, // 0xc3 + 0xFF, // 0xc4 + 0xFF, // 0xc5 + 0xFF, // 0xc6 + 0xFF, // 0xc7 + 0xFF, // 0xc8 + 0xFF, // 0xc9 + 0xFF, // 0xca + 0xFF, // 0xcb + 0xFF, // 0xcc + 0xFF, // 0xcd + 0xFF, // 0xce + 0xFF, // 0xcf + 0xFF, // 0xd0 + 0xFF, // 0xd1 + 0xFF, // 0xd2 + 0xFF, // 0xd3 + 0xFF, // 0xd4 + 0xFF, // 0xd5 + 0xFF, // 0xd6 + 0xFF, // 0xd7 + 0xFF, // 0xd8 + 0xFF, // 0xd9 + 0xFF, // 0xda + 0xFF, // 0xdb + 0xFF, // 0xdc + 0xFF, // 0xdd + 0xFF, // 0xde + 0xFF, // 0xdf + 0xFF, // 0xe0 + 0xFF, // 0xe1 + 0xFF, // 0xe2 + 0xFF, // 0xe3 + 0xFF, // 0xe4 + 0xFF, // 0xe5 + 0xFF, // 0xe6 + 0xFF, // 0xe7 + 0xFF, // 0xe8 + 0xFF, // 0xe9 + 0xFF, // 0xea + 0xFF, // 0xeb + 0xFF, // 0xec + 0xFF, // 0xed + 0xFF, // 0xee + 0xFF, // 0xef + 0xFF, // 0xf0 + 0xFF, // 0xf1 + 0xFF, // 0xf2 + 0xFF, // 0xf3 + 0xFF, // 0xf4 + 0xFF, // 0xf5 + 0xFF, // 0xf6 + 0xFF, // 0xf7 + 0xFF, // 0xf8 + 0xFF, // 0xf9 + 0xFF, // 0xfa + 0xFF, // 0xfb + 0xFF, // 0xfc + 0xFF, // 0xfd + 0xFF, // 0xfe +}; + +BOOL mf_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code) +{ + CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + BOOL keyDown = TRUE; + CGEventRef kbEvent; + CGKeyCode kCode = 0xFF; + + if (flags & KBD_FLAGS_RELEASE) + { + keyDown = FALSE; + } + + if (flags & KBD_FLAGS_EXTENDED) + { + switch (code) + { + // case 0x52: //insert + case 0x53: + kCode = kVK_ForwardDelete; + break; + + case 0x4B: + kCode = kVK_LeftArrow; + break; + + case 0x47: + kCode = kVK_Home; + break; + + case 0x4F: + kCode = kVK_End; + break; + + case 0x48: + kCode = kVK_UpArrow; + break; + + case 0x50: + kCode = kVK_DownArrow; + break; + + case 0x49: + kCode = kVK_PageUp; + break; + + case 0x51: + kCode = kVK_PageDown; + break; + + case 0x4D: + kCode = kVK_RightArrow; + break; + + default: + break; + } + } + else + { + kCode = keymap[code]; + } + + kbEvent = CGEventCreateKeyboardEvent(source, kCode, keyDown); + CGEventPost(kCGHIDEventTap, kbEvent); + CFRelease(kbEvent); + CFRelease(source); + return TRUE; +} + +BOOL mf_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) +{ + return FALSE; +} + +BOOL mf_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ + float width, height; + CGWheelCount wheelCount = 2; + INT32 scroll_x = 0; + INT32 scroll_y = 0; + + if (flags & (PTR_FLAGS_WHEEL | PTR_FLAGS_HWHEEL)) + { + INT32 scroll = flags & WheelRotationMask; + + if (flags & PTR_FLAGS_WHEEL_NEGATIVE) + scroll = -(flags & WheelRotationMask) / 392; + else + scroll = (flags & WheelRotationMask) / 120; + + if (flags & PTR_FLAGS_WHEEL) + scroll_y = scroll; + else + scroll_x = scroll; + + CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(source, kCGScrollEventUnitLine, + wheelCount, scroll_y, scroll_x); + CGEventPost(kCGHIDEventTap, scrollEvent); + CFRelease(scrollEvent); + CFRelease(source); + } + else + { + mfInfo* mfi; + CGEventSourceRef source = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + CGEventType mouseType = kCGEventNull; + CGMouseButton mouseButton = kCGMouseButtonLeft; + mfi = mf_info_get_instance(); + // width and height of primary screen (even in multimon setups + width = (float)mfi->servscreen_width; + height = (float)mfi->servscreen_height; + x += mfi->servscreen_xoffset; + y += mfi->servscreen_yoffset; + + if (flags & PTR_FLAGS_MOVE) + { + if (mfi->mouse_down_left == TRUE) + { + mouseType = kCGEventLeftMouseDragged; + } + else if (mfi->mouse_down_right == TRUE) + { + mouseType = kCGEventRightMouseDragged; + } + else if (mfi->mouse_down_other == TRUE) + { + mouseType = kCGEventOtherMouseDragged; + } + else + { + mouseType = kCGEventMouseMoved; + } + + CGEventRef move = CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), + mouseButton // ignored for just movement + ); + CGEventPost(kCGHIDEventTap, move); + CFRelease(move); + } + + if (flags & PTR_FLAGS_BUTTON1) + { + mouseButton = kCGMouseButtonLeft; + + if (flags & PTR_FLAGS_DOWN) + { + mouseType = kCGEventLeftMouseDown; + mfi->mouse_down_left = TRUE; + } + else + { + mouseType = kCGEventLeftMouseUp; + mfi->mouse_down_right = FALSE; + } + } + else if (flags & PTR_FLAGS_BUTTON2) + { + mouseButton = kCGMouseButtonRight; + + if (flags & PTR_FLAGS_DOWN) + { + mouseType = kCGEventRightMouseDown; + mfi->mouse_down_right = TRUE; + } + else + { + mouseType = kCGEventRightMouseUp; + mfi->mouse_down_right = FALSE; + } + } + else if (flags & PTR_FLAGS_BUTTON3) + { + mouseButton = kCGMouseButtonCenter; + + if (flags & PTR_FLAGS_DOWN) + { + mouseType = kCGEventOtherMouseDown; + mfi->mouse_down_other = TRUE; + } + else + { + mouseType = kCGEventOtherMouseUp; + mfi->mouse_down_other = FALSE; + } + } + + CGEventRef mouseEvent = + CGEventCreateMouseEvent(source, mouseType, CGPointMake(x, y), mouseButton); + CGEventPost(kCGHIDEventTap, mouseEvent); + CFRelease(mouseEvent); + CFRelease(source); + } + + return TRUE; +} + +BOOL mf_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ + return FALSE; +} + +BOOL mf_input_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code) +{ + return FALSE; +} + +BOOL mf_input_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code) +{ + return FALSE; +} + +BOOL mf_input_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ + return FALSE; +} + +BOOL mf_input_extended_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ + return FALSE; +} diff --git a/server/Mac/mf_input.h b/server/Mac/mf_input.h new file mode 100644 index 0000000..4c039f8 --- /dev/null +++ b/server/Mac/mf_input.h @@ -0,0 +1,36 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X Server (Input) + * + * 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. + */ + +#ifndef FREERDP_SERVER_MAC_INPUT_H +#define FREERDP_SERVER_MAC_INPUT_H + +#include "mf_interface.h" + +BOOL mf_input_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code); +BOOL mf_input_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code); +BOOL mf_input_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y); +BOOL mf_input_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y); + +// dummy versions +BOOL mf_input_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code); +BOOL mf_input_unicode_keyboard_event_dummy(rdpInput* input, UINT16 flags, UINT16 code); +BOOL mf_input_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y); +BOOL mf_input_extended_mouse_event_dummy(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y); + +#endif /* FREERDP_SERVER_MAC_INPUT_H */ diff --git a/server/Mac/mf_interface.c b/server/Mac/mf_interface.c new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/server/Mac/mf_interface.c diff --git a/server/Mac/mf_interface.h b/server/Mac/mf_interface.h new file mode 100644 index 0000000..6ba86f3 --- /dev/null +++ b/server/Mac/mf_interface.h @@ -0,0 +1,106 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * FreeRDP Mac OS X 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_MAC_INTERFACE_H +#define FREERDP_SERVER_MAC_INTERFACE_H + +#include <pthread.h> + +#include <freerdp/config.h> +#include <freerdp/codec/rfx.h> +#include <freerdp/codec/nsc.h> +#include <freerdp/listener.h> +#include <freerdp/freerdp.h> + +#include <winpr/crt.h> + +#ifdef WITH_SERVER_CHANNELS +#include <freerdp/channels/wtsvc.h> +#endif + +#ifdef CHANNEL_RDPSND_SERVER +#include <freerdp/server/rdpsnd.h> +#include "mf_rdpsnd.h" +#endif + +#ifdef CHANNEL_AUDIN_SERVER +#include <freerdp/server/audin.h> +#include "mf_audin.h" +#endif + +#include "mf_types.h" + +struct mf_peer_context +{ + rdpContext _p; + + mfInfo* info; + wStream* s; + BOOL activated; + UINT32 frame_id; + BOOL audin_open; + RFX_CONTEXT* rfx_context; + NSC_CONTEXT* nsc_context; + +#ifdef WITH_SERVER_CHANNELS + HANDLE vcm; +#endif + +#ifdef CHANNEL_AUDIN_SERVER + audin_server_context* audin; +#endif + +#ifdef CHANNEL_RDPSND_SERVER + RdpsndServerContext* rdpsnd; +#endif +}; + +struct mf_info +{ + // STREAM* s; + + // screen and monitor info + UINT32 screenID; + UINT32 virtscreen_width; + UINT32 virtscreen_height; + UINT32 servscreen_width; + UINT32 servscreen_height; + UINT32 servscreen_xoffset; + UINT32 servscreen_yoffset; + + int bitsPerPixel; + int peerCount; + int activePeerCount; + int framesPerSecond; + freerdp_peer** peers; + unsigned int framesWaiting; + UINT32 scale; + + RFX_RECT invalid; + pthread_mutex_t mutex; + + BOOL mouse_down_left; + BOOL mouse_down_right; + BOOL mouse_down_other; + BOOL input_disabled; + BOOL force_all_disconnect; +}; + +#endif /* FREERDP_SERVER_MAC_INTERFACE_H */ diff --git a/server/Mac/mf_mountain_lion.c b/server/Mac/mf_mountain_lion.c new file mode 100644 index 0000000..9db86b8 --- /dev/null +++ b/server/Mac/mf_mountain_lion.c @@ -0,0 +1,269 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * OS X Server Event Handling + * + * 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 <dispatch/dispatch.h> +#include <CoreGraphics/CoreGraphics.h> +#include <CoreVideo/CoreVideo.h> +#include <IOKit/IOKitLib.h> +#include <IOSurface/IOSurface.h> + +#include "mf_mountain_lion.h" + +dispatch_semaphore_t region_sem; +dispatch_semaphore_t data_sem; +dispatch_queue_t screen_update_q; +CGDisplayStreamRef stream; + +CGDisplayStreamUpdateRef lastUpdate = NULL; + +BYTE* localBuf = NULL; + +BOOL ready = FALSE; + +void (^streamHandler)(CGDisplayStreamFrameStatus, uint64_t, IOSurfaceRef, + CGDisplayStreamUpdateRef) = ^(CGDisplayStreamFrameStatus status, + uint64_t displayTime, IOSurfaceRef frameSurface, + CGDisplayStreamUpdateRef updateRef) { + dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER); + + // may need to move this down + if (ready == TRUE) + { + + RFX_RECT rect; + unsigned long offset_beg; + unsigned long stride; + + rect.x = 0; + rect.y = 0; + rect.width = 0; + rect.height = 0; + mf_mlion_peek_dirty_region(&rect); + + // lock surface + IOSurfaceLock(frameSurface, kIOSurfaceLockReadOnly, NULL); + // get pointer + void* baseAddress = IOSurfaceGetBaseAddress(frameSurface); + // copy region + + stride = IOSurfaceGetBytesPerRow(frameSurface); + // memcpy(localBuf, baseAddress + offset_beg, surflen); + for (int i = 0; i < rect.height; i++) + { + offset_beg = (stride * (rect.y + i) + (rect.x * 4)); + memcpy(localBuf + offset_beg, baseAddress + offset_beg, rect.width * 4); + } + + // unlock surface + IOSurfaceUnlock(frameSurface, kIOSurfaceLockReadOnly, NULL); + + ready = FALSE; + dispatch_semaphore_signal(data_sem); + } + + if (status != kCGDisplayStreamFrameStatusFrameComplete) + { + switch (status) + { + case kCGDisplayStreamFrameStatusFrameIdle: + break; + + case kCGDisplayStreamFrameStatusStopped: + break; + + case kCGDisplayStreamFrameStatusFrameBlank: + break; + + default: + break; + } + } + else if (lastUpdate == NULL) + { + CFRetain(updateRef); + lastUpdate = updateRef; + } + else + { + CGDisplayStreamUpdateRef tmpRef; + tmpRef = lastUpdate; + lastUpdate = CGDisplayStreamUpdateCreateMergedUpdate(tmpRef, updateRef); + CFRelease(tmpRef); + } + + dispatch_semaphore_signal(region_sem); +}; + +int mf_mlion_display_info(UINT32* disp_width, UINT32* disp_height, UINT32* scale) +{ + CGDirectDisplayID display_id; + + display_id = CGMainDisplayID(); + + CGDisplayModeRef mode = CGDisplayCopyDisplayMode(display_id); + + size_t pixelWidth = CGDisplayModeGetPixelWidth(mode); + // size_t pixelHeight = CGDisplayModeGetPixelHeight(mode); + + size_t wide = CGDisplayPixelsWide(display_id); + size_t high = CGDisplayPixelsHigh(display_id); + + CGDisplayModeRelease(mode); + + *disp_width = wide; // pixelWidth; + *disp_height = high; // pixelHeight; + *scale = pixelWidth / wide; + + return 0; +} + +int mf_mlion_screen_updates_init() +{ + CGDirectDisplayID display_id; + + display_id = CGMainDisplayID(); + + screen_update_q = dispatch_queue_create("mfreerdp.server.screenUpdate", NULL); + + region_sem = dispatch_semaphore_create(1); + data_sem = dispatch_semaphore_create(1); + + UINT32 pixelWidth; + UINT32 pixelHeight; + UINT32 scale; + + mf_mlion_display_info(&pixelWidth, &pixelHeight, &scale); + + localBuf = malloc(pixelWidth * pixelHeight * 4); + if (!localBuf) + return -1; + + CFDictionaryRef opts; + + void* keys[2]; + void* values[2]; + + keys[0] = (void*)kCGDisplayStreamShowCursor; + values[0] = (void*)kCFBooleanFalse; + + opts = CFDictionaryCreate(kCFAllocatorDefault, (const void**)keys, (const void**)values, 1, + NULL, NULL); + + stream = CGDisplayStreamCreateWithDispatchQueue(display_id, pixelWidth, pixelHeight, 'BGRA', + opts, screen_update_q, streamHandler); + + CFRelease(opts); + + return 0; +} + +int mf_mlion_start_getting_screen_updates() +{ + CGError err; + + err = CGDisplayStreamStart(stream); + + if (err != kCGErrorSuccess) + { + return 1; + } + + return 0; +} +int mf_mlion_stop_getting_screen_updates() +{ + CGError err; + + err = CGDisplayStreamStop(stream); + + if (err != kCGErrorSuccess) + { + return 1; + } + + return 0; +} + +int mf_mlion_get_dirty_region(RFX_RECT* invalid) +{ + dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER); + + if (lastUpdate != NULL) + { + mf_mlion_peek_dirty_region(invalid); + } + + dispatch_semaphore_signal(region_sem); + + return 0; +} + +int mf_mlion_peek_dirty_region(RFX_RECT* invalid) +{ + size_t num_rects; + CGRect dirtyRegion; + + const CGRect* rects = + CGDisplayStreamUpdateGetRects(lastUpdate, kCGDisplayStreamUpdateDirtyRects, &num_rects); + + if (num_rects == 0) + { + return 0; + } + + dirtyRegion = *rects; + for (size_t i = 0; i < num_rects; i++) + { + dirtyRegion = CGRectUnion(dirtyRegion, *(rects + i)); + } + + invalid->x = dirtyRegion.origin.x; + invalid->y = dirtyRegion.origin.y; + invalid->height = dirtyRegion.size.height; + invalid->width = dirtyRegion.size.width; + + return 0; +} + +int mf_mlion_clear_dirty_region() +{ + dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER); + + CFRelease(lastUpdate); + lastUpdate = NULL; + + dispatch_semaphore_signal(region_sem); + + return 0; +} + +int mf_mlion_get_pixelData(long x, long y, long width, long height, BYTE** pxData) +{ + dispatch_semaphore_wait(region_sem, DISPATCH_TIME_FOREVER); + ready = TRUE; + dispatch_semaphore_wait(data_sem, DISPATCH_TIME_FOREVER); + dispatch_semaphore_signal(region_sem); + + // this second wait allows us to block until data is copied... more on this later + dispatch_semaphore_wait(data_sem, DISPATCH_TIME_FOREVER); + *pxData = localBuf; + dispatch_semaphore_signal(data_sem); + + return 0; +} diff --git a/server/Mac/mf_mountain_lion.h b/server/Mac/mf_mountain_lion.h new file mode 100644 index 0000000..c1fbe9b --- /dev/null +++ b/server/Mac/mf_mountain_lion.h @@ -0,0 +1,38 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * OS X Server Event Handling + * + * 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_MAC_MLION_H +#define FREERDP_SERVER_MAC_MLION_H + +#include <freerdp/codec/rfx.h> + +int mf_mlion_display_info(UINT32* disp_width, UINT32* dispHeight, UINT32* scale); + +int mf_mlion_screen_updates_init(void); + +int mf_mlion_start_getting_screen_updates(void); +int mf_mlion_stop_getting_screen_updates(void); + +int mf_mlion_get_dirty_region(RFX_RECT* invalid); +int mf_mlion_peek_dirty_region(RFX_RECT* invalid); +int mf_mlion_clear_dirty_region(void); + +int mf_mlion_get_pixelData(long x, long y, long width, long height, BYTE** pxData); + +#endif /* FREERDP_SERVER_MAC_MLION_H */ diff --git a/server/Mac/mf_peer.c b/server/Mac/mf_peer.c new file mode 100644 index 0000000..0669127 --- /dev/null +++ b/server/Mac/mf_peer.c @@ -0,0 +1,485 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * FreeRDP Mac OS X 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 <freerdp/listener.h> +#include <freerdp/codec/rfx.h> +#include <winpr/stream.h> +#include <freerdp/peer.h> +#include <freerdp/codec/color.h> + +#include <winpr/crt.h> +#include <winpr/assert.h> + +#include "mf_peer.h" +#include "mf_info.h" +#include "mf_input.h" +#include "mf_event.h" +#include "mf_rdpsnd.h" +#include "mf_audin.h" + +#include <mach/clock.h> +#include <mach/mach.h> +#include <dispatch/dispatch.h> + +#include "OpenGL/OpenGL.h" +#include "OpenGL/gl.h" + +#include "CoreVideo/CoreVideo.h" + +#include <freerdp/log.h> +#define TAG SERVER_TAG("mac") + +// refactor these +static int info_last_sec = 0; +static int info_last_nsec = 0; + +static dispatch_source_t info_timer; +static dispatch_queue_t info_queue; + +static mfEventQueue* info_event_queue; + +static CGLContextObj glContext; +static CGContextRef bmp; +static CGImageRef img; + +static void mf_peer_context_free(freerdp_peer* client, rdpContext* context); + +static BOOL mf_peer_get_fds(freerdp_peer* client, void** rfds, int* rcount) +{ + if (info_event_queue->pipe_fd[0] == -1) + return TRUE; + + rfds[*rcount] = (void*)(long)info_event_queue->pipe_fd[0]; + (*rcount)++; + return TRUE; +} + +static void mf_peer_rfx_update(freerdp_peer* client) +{ + // check + mfInfo* mfi = mf_info_get_instance(); + mf_info_find_invalid_region(mfi); + + if (mf_info_have_invalid_region(mfi) == false) + { + return; + } + + long width; + long height; + int pitch; + BYTE* dataBits = NULL; + mf_info_getScreenData(mfi, &width, &height, &dataBits, &pitch); + mf_info_clear_invalid_region(mfi); + // encode + wStream* s; + RFX_RECT rect; + rdpUpdate* update; + mfPeerContext* mfp; + SURFACE_BITS_COMMAND cmd = { 0 }; + + WINPR_ASSERT(client); + + mfp = (mfPeerContext*)client->context; + WINPR_ASSERT(mfp); + + update = client->context->update; + WINPR_ASSERT(update); + + s = mfp->s; + WINPR_ASSERT(s); + + Stream_Clear(s); + Stream_SetPosition(s, 0); + UINT32 x = mfi->invalid.x / mfi->scale; + UINT32 y = mfi->invalid.y / mfi->scale; + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + + rfx_context_reset(mfp->rfx_context, mfi->servscreen_width, mfi->servscreen_height); + + if (!(rfx_compose_message(mfp->rfx_context, s, &rect, 1, (BYTE*)dataBits, rect.width, + rect.height, pitch))) + { + return; + } + + cmd.destLeft = x; + cmd.destTop = y; + cmd.destRight = x + rect.width; + cmd.destBottom = y + rect.height; + cmd.bmp.bpp = 32; + cmd.bmp.codecID = 3; + cmd.bmp.width = rect.width; + cmd.bmp.height = rect.height; + cmd.bmp.bitmapDataLength = Stream_GetPosition(s); + cmd.bmp.bitmapData = Stream_Buffer(s); + // send + update->SurfaceBits(update->context, &cmd); + // clean up... maybe? +} + +static BOOL mf_peer_check_fds(freerdp_peer* client) +{ + mfPeerContext* context = (mfPeerContext*)client->context; + mfEvent* event; + + if (context->activated == FALSE) + return TRUE; + + event = mf_event_peek(info_event_queue); + + if (event != NULL) + { + if (event->type == FREERDP_SERVER_MAC_EVENT_TYPE_REGION) + { + } + else if (event->type == FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK) + { + event = mf_event_pop(info_event_queue); + mf_peer_rfx_update(client); + mf_event_free(event); + } + } + + return TRUE; +} + +/* Called when we have a new peer connecting */ +static BOOL mf_peer_context_new(freerdp_peer* client, rdpContext* context) +{ + rdpSettings* settings; + mfPeerContext* peer = (mfPeerContext*)context; + + WINPR_ASSERT(client); + WINPR_ASSERT(context); + + settings = context->settings; + WINPR_ASSERT(settings); + + if (!(peer->info = mf_info_get_instance())) + return FALSE; + + if (!(peer->rfx_context = rfx_context_new_ex( + TRUE, freerdp_settings_get_uint32(settings, FreeRDP_ThreadingFlags)))) + goto fail; + + rfx_context_reset(peer->rfx_context, + freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth), + freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)); + rfx_context_set_mode(peer->rfx_context, RLGR3); + rfx_context_set_pixel_format(peer->rfx_context, PIXEL_FORMAT_BGRA32); + + if (!(peer->s = Stream_New(NULL, 0xFFFF))) + goto fail; + + peer->vcm = WTSOpenServerA((LPSTR)client->context); + + if (!peer->vcm || (peer->vcm == INVALID_HANDLE_VALUE)) + goto fail; + + mf_info_peer_register(peer->info, peer); + return TRUE; +fail: + mf_peer_context_free(client, context); + return FALSE; +} + +/* Called after a peer disconnects */ +static void mf_peer_context_free(freerdp_peer* client, rdpContext* context) +{ + mfPeerContext* peer = (mfPeerContext*)context; + if (context) + { + mf_info_peer_unregister(peer->info, peer); + dispatch_suspend(info_timer); + Stream_Free(peer->s, TRUE); + rfx_context_free(peer->rfx_context); + // nsc_context_free(peer->nsc_context); +#ifdef CHANNEL_AUDIN_SERVER + + mf_peer_audin_uninit(peer); + +#endif +#ifdef CHANNEL_RDPSND_SERVER + mf_peer_rdpsnd_stop(); + + if (peer->rdpsnd) + rdpsnd_server_context_free(peer->rdpsnd); + +#endif + WTSCloseServer(peer->vcm); + } +} + +/* Called when a new client connects */ +static BOOL mf_peer_init(freerdp_peer* client) +{ + client->ContextSize = sizeof(mfPeerContext); + client->ContextNew = mf_peer_context_new; + client->ContextFree = mf_peer_context_free; + + if (!freerdp_peer_context_new(client)) + return FALSE; + + info_event_queue = mf_event_queue_new(); + info_queue = dispatch_queue_create("FreeRDP.update.timer", DISPATCH_QUEUE_SERIAL); + info_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, info_queue); + + if (info_timer) + { + // DEBUG_WARN( "created timer\n"); + dispatch_source_set_timer(info_timer, DISPATCH_TIME_NOW, 42ull * NSEC_PER_MSEC, + 100ull * NSEC_PER_MSEC); + dispatch_source_set_event_handler(info_timer, ^{ + // DEBUG_WARN( "dispatch\n"); + mfEvent* event = mf_event_new(FREERDP_SERVER_MAC_EVENT_TYPE_FRAME_TICK); + mf_event_push(info_event_queue, (mfEvent*)event); + }); + dispatch_resume(info_timer); + } + + return TRUE; +} + +static BOOL mf_peer_post_connect(freerdp_peer* client) +{ + mfInfo* mfi = mf_info_get_instance(); + + WINPR_ASSERT(client); + + mfPeerContext* context = (mfPeerContext*)client->context; + WINPR_ASSERT(context); + + rdpSettings* settings = client->context->settings; + WINPR_ASSERT(settings); + + mfi->scale = 1; + // mfi->servscreen_width = 2880 / mfi->scale; + // mfi->servscreen_height = 1800 / mfi->scale; + UINT32 bitsPerPixel = 32; + + if ((freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) != mfi->servscreen_width) || + (freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) != mfi->servscreen_height)) + { + } + + if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, mfi->servscreen_width)) + return FALSE; + if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, mfi->servscreen_height)) + return FALSE; + if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, bitsPerPixel)) + return FALSE; + + WINPR_ASSERT(client->context->update); + WINPR_ASSERT(client->context->update->DesktopResize); + client->context->update->DesktopResize(client->context); + + mfi->mouse_down_left = FALSE; + mfi->mouse_down_right = FALSE; + mfi->mouse_down_other = FALSE; +#ifdef CHANNEL_RDPSND_SERVER + + if (WTSVirtualChannelManagerIsChannelJoined(context->vcm, "rdpsnd")) + { + mf_peer_rdpsnd_init(context); /* Audio Output */ + } + +#endif + /* Dynamic Virtual Channels */ +#ifdef CHANNEL_AUDIN_SERVER + mf_peer_audin_init(context); /* Audio Input */ +#endif + return TRUE; +} + +static BOOL mf_peer_activate(freerdp_peer* client) +{ + WINPR_ASSERT(client); + + mfPeerContext* context = (mfPeerContext*)client->context; + WINPR_ASSERT(context); + + rdpSettings* settings = client->context->settings; + WINPR_ASSERT(settings); + + rfx_context_reset(context->rfx_context, + freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth), + freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight)); + context->activated = TRUE; + return TRUE; +} + +static BOOL mf_peer_synchronize_event(rdpInput* input, UINT32 flags) +{ + return TRUE; +} + +static BOOL mf_peer_keyboard_event(rdpInput* input, UINT16 flags, UINT8 code) +{ + bool state_down = FALSE; + + if (flags == KBD_FLAGS_DOWN) + { + state_down = TRUE; + } + return TRUE; +} + +static BOOL mf_peer_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) +{ + return FALSE; +} + +static BOOL mf_peer_suppress_output(rdpContext* context, BYTE allow, const RECTANGLE_16* area) +{ + return FALSE; +} + +static void* mf_peer_main_loop(void* arg) +{ + mfPeerContext* context; + rdpSettings* settings; + rdpInput* input; + rdpUpdate* update; + freerdp_peer* client = (freerdp_peer*)arg; + + if (!mf_peer_init(client)) + goto fail; + + const mf_server_info* info = client->ContextExtra; + WINPR_ASSERT(info); + + WINPR_ASSERT(client->context); + + settings = client->context->settings; + WINPR_ASSERT(settings); + + /* Initialize the real server settings here */ + rdpPrivateKey* key = freerdp_key_new_from_file(info->key); + if (!key) + goto fail; + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerRsaKey, key, 1)) + goto fail; + rdpCertificate* cert = freerdp_certificate_new_from_file(info->cert); + if (!cert) + goto fail; + if (!freerdp_settings_set_pointer_len(settings, FreeRDP_RdpServerCertificate, cert, 1)) + goto fail; + + if (!freerdp_settings_set_bool(settings, FreeRDP_NlaSecurity, FALSE)) + goto fail; + if (!freerdp_settings_set_bool(settings, FreeRDP_RemoteFxCodec, TRUE)) + goto fail; + if (!freerdp_settings_set_uint32(settings, FreeRDP_ColorDepth, 32)) + goto fail; + + if (!freerdp_settings_set_bool(settings, FreeRDP_SuppressOutput, TRUE)) + goto fail; + if (!freerdp_settings_set_bool(settings, FreeRDP_RefreshRect, FALSE)) + goto fail; + + client->PostConnect = mf_peer_post_connect; + client->Activate = mf_peer_activate; + + input = client->context->input; + WINPR_ASSERT(input); + + input->SynchronizeEvent = mf_peer_synchronize_event; + input->KeyboardEvent = mf_input_keyboard_event; // mf_peer_keyboard_event; + input->UnicodeKeyboardEvent = mf_peer_unicode_keyboard_event; + input->MouseEvent = mf_input_mouse_event; + input->ExtendedMouseEvent = mf_input_extended_mouse_event; + + update = client->context->update; + WINPR_ASSERT(update); + + // update->RefreshRect = mf_peer_refresh_rect; + update->SuppressOutput = mf_peer_suppress_output; + + WINPR_ASSERT(client->Initialize); + const BOOL rc = client->Initialize(client); + if (!rc) + goto fail; + context = (mfPeerContext*)client->context; + + while (1) + { + DWORD status; + HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 }; + DWORD count = client->GetEventHandles(client, handles, ARRAYSIZE(handles)); + + if ((count == 0) || (count == MAXIMUM_WAIT_OBJECTS)) + { + WLog_ERR(TAG, "Failed to get FreeRDP file descriptor"); + break; + } + + handles[count++] = WTSVirtualChannelManagerGetEventHandle(context->vcm); + + status = WaitForMultipleObjects(count, handles, FALSE, INFINITE); + if (status == WAIT_FAILED) + { + WLog_ERR(TAG, "WaitForMultipleObjects failed"); + break; + } + + if (client->CheckFileDescriptor(client) != TRUE) + { + break; + } + + if ((mf_peer_check_fds(client)) != TRUE) + { + break; + } + + if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != TRUE) + { + break; + } + } + + client->Disconnect(client); + freerdp_peer_context_free(client); +fail: + freerdp_peer_free(client); + return NULL; +} + +BOOL mf_peer_accepted(freerdp_listener* instance, freerdp_peer* client) +{ + pthread_t th; + + WINPR_ASSERT(instance); + WINPR_ASSERT(client); + + client->ContextExtra = instance->info; + if (pthread_create(&th, 0, mf_peer_main_loop, client) == 0) + { + pthread_detach(th); + return TRUE; + } + + return FALSE; +} diff --git a/server/Mac/mf_peer.h b/server/Mac/mf_peer.h new file mode 100644 index 0000000..e4a966c --- /dev/null +++ b/server/Mac/mf_peer.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X 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_MAC_PEER_H +#define FREERDP_SERVER_MAC_PEER_H + +#include "mf_interface.h" + +typedef struct +{ + const char* cert; + const char* key; +} mf_server_info; + +BOOL mf_peer_accepted(freerdp_listener* instance, freerdp_peer* client); + +#endif /* FREERDP_SERVER_MAC_PEER_H */ diff --git a/server/Mac/mf_rdpsnd.c b/server/Mac/mf_rdpsnd.c new file mode 100644 index 0000000..e1946f2 --- /dev/null +++ b/server/Mac/mf_rdpsnd.c @@ -0,0 +1,200 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X Server (Audio Output) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2015 Thincast Technologies GmbH + * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.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 <freerdp/server/rdpsnd.h> + +#include "mf_info.h" +#include "mf_rdpsnd.h" +#include "mf_interface.h" + +#include <winpr/sysinfo.h> +#include <freerdp/server/server-common.h> +#include <freerdp/log.h> +#define TAG SERVER_TAG("mac") + +AQRecorderState recorderState; + +static void mf_peer_rdpsnd_activated(RdpsndServerContext* context) +{ + OSStatus status; + BOOL formatAgreed = FALSE; + AUDIO_FORMAT* agreedFormat = NULL; + // we should actually loop through the list of client formats here + // and see if we can send the client something that it supports... + WLog_DBG(TAG, "Client supports the following %d formats: ", context->num_client_formats); + + int i = 0; + for (; i < context->num_client_formats; i++) + { + /* TODO: improve the way we agree on a format */ + for (int 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!"); + formatAgreed = TRUE; + agreedFormat = (AUDIO_FORMAT*)&context->server_formats[j]; + break; + } + } + + if (formatAgreed == TRUE) + break; + } + + if (formatAgreed == FALSE) + { + WLog_DBG(TAG, "Could not agree on a audio format with the server"); + return; + } + + context->SelectFormat(context, i); + context->SetVolume(context, 0x7FFF, 0x7FFF); + + switch (agreedFormat->wFormatTag) + { + case WAVE_FORMAT_ALAW: + recorderState.dataFormat.mFormatID = kAudioFormatDVIIntelIMA; + break; + + case WAVE_FORMAT_PCM: + recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM; + break; + + default: + recorderState.dataFormat.mFormatID = kAudioFormatLinearPCM; + break; + } + + recorderState.dataFormat.mSampleRate = agreedFormat->nSamplesPerSec; + recorderState.dataFormat.mFormatFlags = + kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; + recorderState.dataFormat.mBytesPerPacket = 4; + recorderState.dataFormat.mFramesPerPacket = 1; + recorderState.dataFormat.mBytesPerFrame = 4; + recorderState.dataFormat.mChannelsPerFrame = agreedFormat->nChannels; + recorderState.dataFormat.mBitsPerChannel = agreedFormat->wBitsPerSample; + recorderState.snd_context = context; + status = + AudioQueueNewInput(&recorderState.dataFormat, mf_peer_rdpsnd_input_callback, &recorderState, + NULL, kCFRunLoopCommonModes, 0, &recorderState.queue); + + if (status != noErr) + { + WLog_DBG(TAG, "Failed to create a new Audio Queue. Status code: %" PRId32 "", status); + } + + UInt32 dataFormatSize = sizeof(recorderState.dataFormat); + AudioQueueGetProperty(recorderState.queue, kAudioConverterCurrentInputStreamDescription, + &recorderState.dataFormat, &dataFormatSize); + mf_rdpsnd_derive_buffer_size(recorderState.queue, &recorderState.dataFormat, 0.05, + &recorderState.bufferByteSize); + + for (int i = 0; i < SND_NUMBUFFERS; ++i) + { + AudioQueueAllocateBuffer(recorderState.queue, recorderState.bufferByteSize, + &recorderState.buffers[i]); + AudioQueueEnqueueBuffer(recorderState.queue, recorderState.buffers[i], 0, NULL); + } + + recorderState.currentPacket = 0; + recorderState.isRunning = true; + AudioQueueStart(recorderState.queue, NULL); +} + +BOOL mf_peer_rdpsnd_init(mfPeerContext* context) +{ + 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 = mf_peer_rdpsnd_activated; + context->rdpsnd->Initialize(context->rdpsnd, TRUE); + return TRUE; +} + +BOOL mf_peer_rdpsnd_stop() +{ + recorderState.isRunning = false; + AudioQueueStop(recorderState.queue, true); + return TRUE; +} + +void mf_peer_rdpsnd_input_callback(void* inUserData, AudioQueueRef inAQ, + AudioQueueBufferRef inBuffer, const AudioTimeStamp* inStartTime, + UInt32 inNumberPacketDescriptions, + const AudioStreamPacketDescription* inPacketDescs) +{ + OSStatus status; + AQRecorderState* rState; + rState = inUserData; + + if (inNumberPacketDescriptions == 0 && rState->dataFormat.mBytesPerPacket != 0) + { + inNumberPacketDescriptions = + inBuffer->mAudioDataByteSize / rState->dataFormat.mBytesPerPacket; + } + + if (rState->isRunning == 0) + { + return; + } + + rState->snd_context->SendSamples(rState->snd_context, inBuffer->mAudioData, + inBuffer->mAudioDataByteSize / 4, + (UINT16)(GetTickCount() & 0xffff)); + status = AudioQueueEnqueueBuffer(rState->queue, inBuffer, 0, NULL); + + if (status != noErr) + { + WLog_DBG(TAG, "AudioQueueEnqueueBuffer() returned status = %" PRId32 "", status); + } +} + +void mf_rdpsnd_derive_buffer_size(AudioQueueRef audioQueue, + AudioStreamBasicDescription* ASBDescription, Float64 seconds, + UInt32* outBufferSize) +{ + static const int maxBufferSize = 0x50000; + int maxPacketSize = ASBDescription->mBytesPerPacket; + + if (maxPacketSize == 0) + { + UInt32 maxVBRPacketSize = sizeof(maxPacketSize); + AudioQueueGetProperty(audioQueue, kAudioQueueProperty_MaximumOutputPacketSize, + // in Mac OS X v10.5, instead use + // kAudioConverterPropertyMaximumOutputPacketSize + &maxPacketSize, &maxVBRPacketSize); + } + + Float64 numBytesForTime = ASBDescription->mSampleRate * maxPacketSize * seconds; + *outBufferSize = (UInt32)(numBytesForTime < maxBufferSize ? numBytesForTime : maxBufferSize); +} diff --git a/server/Mac/mf_rdpsnd.h b/server/Mac/mf_rdpsnd.h new file mode 100644 index 0000000..28bcb0d --- /dev/null +++ b/server/Mac/mf_rdpsnd.h @@ -0,0 +1,58 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X 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_MAC_RDPSND_H +#define FREERDP_SERVER_MAC_RDPSND_H + +#include <CoreAudio/CoreAudio.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <freerdp/freerdp.h> +#include <freerdp/listener.h> +#include <freerdp/server/rdpsnd.h> + +#include "mf_types.h" +#include "mfreerdp.h" + +void mf_rdpsnd_derive_buffer_size(AudioQueueRef audioQueue, + AudioStreamBasicDescription* ASBDescription, Float64 seconds, + UInt32* outBufferSize); + +void mf_peer_rdpsnd_input_callback(void* inUserData, AudioQueueRef inAQ, + AudioQueueBufferRef inBuffer, const AudioTimeStamp* inStartTime, + UInt32 inNumberPacketDescriptions, + const AudioStreamPacketDescription* inPacketDescs); + +#define SND_NUMBUFFERS 3 +typedef struct +{ + AudioStreamBasicDescription dataFormat; + AudioQueueRef queue; + AudioQueueBufferRef buffers[SND_NUMBUFFERS]; + AudioFileID audioFile; + UInt32 bufferByteSize; + SInt64 currentPacket; + bool isRunning; + RdpsndServerContext* snd_context; +} AQRecorderState; + +BOOL mf_peer_rdpsnd_init(mfPeerContext* context); +BOOL mf_peer_rdpsnd_stop(void); + +#endif /* FREERDP_SERVER_MAC_RDPSND_H */ diff --git a/server/Mac/mf_types.h b/server/Mac/mf_types.h new file mode 100644 index 0000000..e33be83 --- /dev/null +++ b/server/Mac/mf_types.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * FreeRDP Mac OS X Server + * + * Copyright 2023 Armin Novak <anovak@thincst.com> + * Copyright 2023 Thincast Technologies GmbH + * + * 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_MAC_TYPES_H +#define FREERDP_SERVER_MAC_TYPES_H + +#include <pthread.h> + +#include <freerdp/config.h> + +#include <winpr/crt.h> + +typedef struct mf_info mfInfo; +typedef struct mf_peer_context mfPeerContext; + +#endif /* FREERDP_SERVER_MAC_TYPES_H */ diff --git a/server/Mac/mfreerdp.c b/server/Mac/mfreerdp.c new file mode 100644 index 0000000..8a3fffd --- /dev/null +++ b/server/Mac/mfreerdp.c @@ -0,0 +1,108 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X 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 <stdlib.h> +#include <string.h> +#include <errno.h> +#include <pthread.h> +#include <signal.h> +#include <sys/time.h> + +#include <CoreGraphics/CGEvent.h> + +#include <winpr/crt.h> +#include <winpr/wtsapi.h> +#include <winpr/assert.h> + +#include <freerdp/freerdp.h> +#include <freerdp/constants.h> +#include <freerdp/channels/wtsvc.h> +#include <freerdp/channels/channels.h> +#include <freerdp/server/server-common.h> + +#include "mfreerdp.h" +#include "mf_peer.h" + +#include <freerdp/log.h> +#define TAG SERVER_TAG("mac") + +static void mf_server_main_loop(freerdp_listener* instance) +{ + WINPR_ASSERT(instance); + WINPR_ASSERT(instance->GetEventHandles); + WINPR_ASSERT(instance->CheckFileDescriptor); + + while (1) + { + 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) + { + break; + } + } + + instance->Close(instance); +} + +int main(int argc, char* argv[]) +{ + freerdp_server_warn_unmaintained(argc, argv); + mf_server_info info = { .key = "server.key", .cert = "server.crt" }; + + freerdp_listener* instance; + + signal(SIGPIPE, SIG_IGN); + + WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()); + + if (!(instance = freerdp_listener_new())) + return 1; + + instance->info = &info; + instance->PeerAccepted = mf_peer_accepted; + + if (instance->Open(instance, NULL, 3389)) + { + mf_server_main_loop(instance); + } + + freerdp_listener_free(instance); + + return 0; +} diff --git a/server/Mac/mfreerdp.h b/server/Mac/mfreerdp.h new file mode 100644 index 0000000..38fa25b --- /dev/null +++ b/server/Mac/mfreerdp.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Mac OS X 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_MAC_FREERDP_H +#define FREERDP_SERVER_MAC_FREERDP_H + +#include <freerdp/freerdp.h> +#include <freerdp/listener.h> +#include <freerdp/codec/rfx.h> + +#endif /* FREERDP_SERVER_MAC_FREERDP_H */ |