diff --git a/.gitignore b/.gitignore index 00fb8eddc..73c0bf88c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,10 +39,12 @@ Debug *.dylib cunit/test_freerdp client/X11/xfreerdp +client/Mac/xcode client/test/freerdp-test client/DirectFB/dfreerdp server/test/tfreerdp-server server/X11/xfreerdp-server +xcode # Other *~ diff --git a/CMakeLists.txt b/CMakeLists.txt index bd5c7266a..d74dbd1fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -114,6 +114,7 @@ endif() if(NOT WIN32) find_required_package(ZLIB) find_optional_package(PulseAudio) + find_optional_package(MacAudio) find_optional_package(PCSC) find_suggested_package(Cups) diff --git a/channels/CMakeLists.txt b/channels/CMakeLists.txt index 2cbc568b3..1951fd8f1 100644 --- a/channels/CMakeLists.txt +++ b/channels/CMakeLists.txt @@ -17,13 +17,13 @@ # See the License for the specific language governing permissions and # limitations under the License. +add_subdirectory(cliprdr) add_subdirectory(drdynvc) +add_subdirectory(rail) add_subdirectory(rdpdbg) add_subdirectory(skel) if(NOT WIN32) add_subdirectory(rdpdr) - add_subdirectory(cliprdr) - add_subdirectory(rail) add_subdirectory(rdpsnd) endif() diff --git a/channels/rail/rail_main.c b/channels/rail/rail_main.c index 58d1de40e..3b2c63a1d 100644 --- a/channels/rail/rail_main.c +++ b/channels/rail/rail_main.c @@ -128,7 +128,7 @@ static void rail_recv_set_sysparams_event(rdpRailOrder* rail_order, RDP_EVENT* e while (data && data->size > 0) { rail_process_plugin_data(rail_order, data); - data = (RDP_PLUGIN_DATA*)(((void*) data) + data->size); + data = (RDP_PLUGIN_DATA*)((char *)(data) + data->size); } } diff --git a/channels/rdpsnd/CMakeLists.txt b/channels/rdpsnd/CMakeLists.txt index 3065ac63e..c7d6a7054 100644 --- a/channels/rdpsnd/CMakeLists.txt +++ b/channels/rdpsnd/CMakeLists.txt @@ -37,3 +37,7 @@ if(WITH_PULSEAUDIO) add_subdirectory(pulse) endif() +if(WITH_MACAUDIO) + add_subdirectory(mac_audio) +endif() + diff --git a/channels/rdpsnd/mac_audio/CMakeLists.txt b/channels/rdpsnd/mac_audio/CMakeLists.txt new file mode 100644 index 000000000..63cb82b95 --- /dev/null +++ b/channels/rdpsnd/mac_audio/CMakeLists.txt @@ -0,0 +1,36 @@ +# FreeRDP: A Remote Desktop Protocol Client +# FreeRDP cmake build script +# +# Copyright 2012 Laxmikant Rashinkar +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# 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(RDPSND_MACAUDIO_SRCS + rdpsnd_audio_q.c +) + +include_directories(..) +include_directories(${MACAUDIO_INCLUDE_DIRS}) + +add_library(rdpsnd_macaudio ${RDPSND_MACAUDIO_SRCS}) +set_target_properties(rdpsnd_macaudio PROPERTIES PREFIX "") + +target_link_libraries(rdpsnd_macaudio freerdp-utils) +target_link_libraries(rdpsnd_macaudio ${MAC_AUDIOTOOLBOX_LIBRARY_PATH}) +target_link_libraries(rdpsnd_macaudio ${MAC_COREFOUNDATION_LIBRARY_PATH}) + +install(TARGETS rdpsnd_macaudio DESTINATION ${FREERDP_PLUGIN_PATH}) + diff --git a/channels/rdpsnd/mac_audio/rdpsnd_audio_q.c b/channels/rdpsnd/mac_audio/rdpsnd_audio_q.c new file mode 100644 index 000000000..b982e54c9 --- /dev/null +++ b/channels/rdpsnd/mac_audio/rdpsnd_audio_q.c @@ -0,0 +1,241 @@ +/** + * FreeRDP: A Remote Desktop Protocol client. + * Audio Output Virtual Channel + * + * Copyright 2012 Laxmikant Rashinkar + * + * 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. + */ + +/** + * Use AudioQueue to implement audio redirection + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rdpsnd_main.h" + +#define GOT_HERE printf(">>>>> got here: %s %s %d\n", __FILE__, __func__, __LINE__) + +#define AQ_NUM_BUFFERS 10 +#define AQ_BUF_SIZE (32 * 1024) + +static void aq_playback_cb(void *user_data, + AudioQueueRef aq_ref, + AudioQueueBufferRef aq_buf_ref + ); + +struct rdpsnd_audio_q_plugin +{ + rdpsndDevicePlugin device; + + /* audio queue player state */ + int is_open; // true when audio_q has been inited + char * device_name; + int is_playing; + int buf_index; + + AudioStreamBasicDescription data_format; + AudioQueueRef aq_ref; + AudioQueueBufferRef buffers[AQ_NUM_BUFFERS]; +}; +typedef struct rdpsnd_audio_q_plugin rdpsndAudioQPlugin; + +static void rdpsnd_audio_close(rdpsndDevicePlugin* device) +{ + rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin*) device; + + GOT_HERE; + + AudioQueueStop(aq_plugin_p->aq_ref, 0); + aq_plugin_p->is_open = 0; +} + +static void rdpsnd_audio_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency) +{ + int rv; + int i; + + GOT_HERE; + + rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device; + if (aq_plugin_p->is_open) { + return; + } + + aq_plugin_p->buf_index = 0; + + // setup AudioStreamBasicDescription + aq_plugin_p->data_format.mSampleRate = 44100; + aq_plugin_p->data_format.mFormatID = kAudioFormatLinearPCM; + aq_plugin_p->data_format.mFormatFlags = kAudioFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; + + // until we know better, assume that one packet = one frame + // one frame = bytes_per_sample x number_of_channels + aq_plugin_p->data_format.mBytesPerPacket = 4; + aq_plugin_p->data_format.mFramesPerPacket = 1; + aq_plugin_p->data_format.mBytesPerFrame = 4; + aq_plugin_p->data_format.mChannelsPerFrame = 2; + aq_plugin_p->data_format.mBitsPerChannel = 16; + + rv = AudioQueueNewOutput(&aq_plugin_p->data_format, // audio stream basic desc + aq_playback_cb, // callback when more data is required + aq_plugin_p, // data to pass to callback + CFRunLoopGetCurrent(), // The current run loop, and the one on + // which the audio queue playback callback + // will be invoked + kCFRunLoopCommonModes, // run loop modes in which callbacks can + // be invoked + 0, // flags - reserved + &aq_plugin_p->aq_ref + ); + if (rv != 0) { + printf("rdpsnd_audio_open: AudioQueueNewOutput() failed with error %d\n", rv); + aq_plugin_p->is_open = 1; + return; + } + + for (i = 0; i < AQ_NUM_BUFFERS; i++) + { + rv = AudioQueueAllocateBuffer(aq_plugin_p->aq_ref, AQ_BUF_SIZE, &aq_plugin_p->buffers[i]); + } + + aq_plugin_p->is_open = 1; +} + +static void rdpsnd_audio_free(rdpsndDevicePlugin* device) +{ + GOT_HERE; +} + +static boolean rdpsnd_audio_format_supported(rdpsndDevicePlugin* device, rdpsndFormat* format) +{ + GOT_HERE; + + switch (format->wFormatTag) + { + case 1: /* PCM */ + if (format->cbSize == 0 && + (format->nSamplesPerSec <= 48000) && + (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) && + (format->nChannels == 1 || format->nChannels == 2)) + { + return 1; + } + break; + } + return 0; +} + +static void rdpsnd_audio_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency) +{ + GOT_HERE; +} + +static void rdpsnd_audio_set_volume(rdpsndDevicePlugin* device, uint32 value) +{ + GOT_HERE; +} + +static void rdpsnd_audio_play(rdpsndDevicePlugin* device, uint8* data, int size) +{ + rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device; + AudioQueueBufferRef aq_buf_ref; + int len; + + GOT_HERE; + + if (!aq_plugin_p->is_open) { + return; + } + + /* get next empty buffer */ + aq_buf_ref = aq_plugin_p->buffers[aq_plugin_p->buf_index]; + + // fill aq_buf_ref with audio data + len = size > AQ_BUF_SIZE ? AQ_BUF_SIZE : size; + + memcpy(aq_buf_ref->mAudioData, (char *) data, len); + aq_buf_ref->mAudioDataByteSize = len; + + // add buffer to audioqueue + AudioQueueEnqueueBuffer(aq_plugin_p->aq_ref, aq_buf_ref, 0, 0); + + // update buf_index + aq_plugin_p->buf_index++; + if (aq_plugin_p->buf_index >= AQ_NUM_BUFFERS) { + aq_plugin_p->buf_index = 0; + } +} + +static void rdpsnd_audio_start(rdpsndDevicePlugin* device) +{ + GOT_HERE; + + rdpsndAudioQPlugin* aq_plugin_p = (rdpsndAudioQPlugin *) device; + + AudioQueueStart(aq_plugin_p->aq_ref, NULL); +} + +/** + * AudioQueue Playback callback + * + * our job here is to fill aq_buf_ref with audio data and enqueue it + */ + +static void aq_playback_cb(void *user_data, + AudioQueueRef aq_ref, + AudioQueueBufferRef aq_buf_ref + ) +{ + GOT_HERE; +} + +int FreeRDPRdpsndDeviceEntry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints) +{ + rdpsndAudioQPlugin* aqPlugin; + RDP_PLUGIN_DATA* data; + + GOT_HERE; + + aqPlugin = xnew(rdpsndAudioQPlugin); + + aqPlugin->device.Open = rdpsnd_audio_open; + aqPlugin->device.FormatSupported = rdpsnd_audio_format_supported; + aqPlugin->device.SetFormat = rdpsnd_audio_set_format; + aqPlugin->device.SetVolume = rdpsnd_audio_set_volume; + aqPlugin->device.Play = rdpsnd_audio_play; + aqPlugin->device.Start = rdpsnd_audio_start; + aqPlugin->device.Close = rdpsnd_audio_close; + aqPlugin->device.Free = rdpsnd_audio_free; + + data = pEntryPoints->plugin_data; + + if (data && strcmp((char *)data->data[0], "macaudio") == 0) { + if(strlen((char *)data->data[1]) > 0) + aqPlugin->device_name = strdup((char *)data->data[1]); + else + aqPlugin->device_name = NULL; + } + pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)aqPlugin); + return 0; +} + diff --git a/channels/rdpsnd/rdpsnd_main.c b/channels/rdpsnd/rdpsnd_main.c index c6b4a75cb..c5a738316 100644 --- a/channels/rdpsnd/rdpsnd_main.c +++ b/channels/rdpsnd/rdpsnd_main.c @@ -505,7 +505,12 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin) { default_data[0].data[0] = "alsa"; default_data[0].data[1] = "default"; - rdpsnd_load_device_plugin(rdpsnd, "alsa", default_data); + if (!rdpsnd_load_device_plugin(rdpsnd, "alsa", default_data)) + { + default_data[0].data[0] = "macaudio"; + default_data[0].data[1] = "default"; + rdpsnd_load_device_plugin(rdpsnd, "macaudio", default_data); + } } } if (rdpsnd->device == NULL) diff --git a/client/Mac/AppDelegate.h b/client/Mac/AppDelegate.h new file mode 100644 index 000000000..5af060d1c --- /dev/null +++ b/client/Mac/AppDelegate.h @@ -0,0 +1,19 @@ +// +// AppDelegate.h +// MacFreeRDP +// +// Created by Thomas Goddard on 5/8/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import +#import "MRDPView.h" + +@interface AppDelegate : NSObject + +@property (assign) IBOutlet MRDPView *mrdpView; +@property (assign) IBOutlet NSWindow *window; + + +int rdp_connect(); +@end diff --git a/client/Mac/AppDelegate.m b/client/Mac/AppDelegate.m new file mode 100644 index 000000000..3b7658d80 --- /dev/null +++ b/client/Mac/AppDelegate.m @@ -0,0 +1,26 @@ +// +// AppDelegate.m +// MacFreeRDP +// +// Created by Thomas Goddard on 5/8/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "AppDelegate.h" + +@implementation AppDelegate + +//@synthesize window = _window; +//@synthesize mrdpView; + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + rdp_connect(); +} + +- (void) applicationWillTerminate:(NSNotification *)notification +{ + //[mrdpView releaseResources]; +} + +@end diff --git a/client/Mac/CMakeLists.txt b/client/Mac/CMakeLists.txt new file mode 100644 index 000000000..870a65950 --- /dev/null +++ b/client/Mac/CMakeLists.txt @@ -0,0 +1,149 @@ + +cmake_minimum_required (VERSION 2.8) +project (MacFreeRDP) +set(CMAKE_COLOR_MAKEFILE ON) + +include(CheckIncludeFiles) +include(CheckLibraryExists) +include(CheckStructHasMember) +include(FindPkgConfig) +include(TestBigEndian) + +# Include our extra modules +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/../../cmake/) + +include(AutoVersioning) +include(ConfigOptions) +include(FindOptionalPackage) +include(CheckCCompilerFlag) +include(GNUInstallDirsWrapper) + +# Default to debug build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# Default to build shared libs +if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) +endif() + +# Compiler-specific flags +if(CMAKE_COMPILER_IS_GNUCC) + if(CMAKE_BUILD_TYPE STREQUAL "Release") + set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2") + endif() + if(WITH_SSE2_TARGET) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2") + endif() +endif() + +# Libraries that we have a hard dependency on +if(NOT DEFINED OPENSSL_INCLUDE_DIR OR NOT DEFINED OPENSSL_LIBRARIES) + find_required_package(OpenSSL) +endif() + +# Mac OS X +if(APPLE) + # Set the include files for FreeRDP to the relative path + set(FREERDP_INCLUDE_PATH ${CMAKE_SOURCE_DIR}/../../include/) + set(FRAMEWORK_HEADERS_PATH /System/Library/Frameworks/Cocoa.framework/Versions/A/Headers/) + include_directories (${FREERDP_INCLUDE_PATH} ${FRAMEWORK_HEADERS_PATH} /System/Library/Frameworks) + + # set(CMAKE_OSX_SYSROOT MacOSX10.7.sdk) # uncomment to specify SDK version + set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -mmacosx-version-min=10.4") + set(GUI_TYPE MACOSX_BUNDLE) + + # Import libraries + find_library(FOUNDATION_LIBRARY Foundation) + message("+ Using foundation library ${FOUNDATION_LIBRARY}") + find_library(COCOA_LIBRARY Cocoa) + message("+ Using cocoa library ${COCOA_LIBRARY}") + find_library(APPKIT_LIBRARY AppKit) + message("+ Using appkit library ${APPKIT_LIBRARY}") + + message(" Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") + # Set the OS X Bundle specific CMake variables which will be used to populate the plist for + # the application bundle + set(MACOSX_BUNDLE_INFO_STRING "${PROJECT_NAME}") + set(MACOSX_BUNDLE_GUI_IDENTIFIER "com.freerdp.mac") + set(MACOSX_BUNDLE_BUNDLE_IDENTIFIER "FreeRDP.Mac") + set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_NAME} Version 1.0.1") + set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_NAME}) + set(MACOSX_BUNDLE_SHORT_VERSION_STRING 1.0.1) + set(MACOSX_BUNDLE_BUNDLE_VERSION 1.0.1) + set(MACOSX_BUNDLE_COPYRIGHT "Copyright 2012. All Rights Reserved.") + + # Specific plist and NOT standard CMake variables + set(MACOSX_BUNDLE_NSMAIN_NIB_FILE "MainMenu") + set(MACOSX_BUNDLE_NSPRINCIPAL_CLASS "NSApplication") + + mark_as_advanced(COCOA_LIBRARY + FOUNDATION_LIBRARY + APPKIT_LIBRARY) + set(EXTRA_LIBS ${COCOA_LIBRARY} ${FOUNDATION_LIBRARY} ${APPKIT_LIBRARY}) + set(APP_TYPE MACOSX_BUNDLE) +endif() + +# OS X Interface Builder files +set (MacFreeRDP_XIBS + MainMenu.xib + Credits.rtf +) + +# Headers +set (MacFreeRDP_Headers + MRDPCursor.h + MRDPView.h + AppDelegate.h) + +# Source +set (MacFreeRDP_Source + MRDPCursor.m + MRDPView.m + AppDelegate.m + main.m) + +add_executable(MacFreeRDP + ${APP_TYPE} + ${MacFreeRDP_Headers} + ${MacFreeRDP_Source} + ${MacFreeRDP_XIBS}) + +# This is necessary for the xib file part below +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Info.plist ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) + +# This allows for automatic xib to nib ibitool +set_target_properties(MacFreeRDP PROPERTIES RESOURCE "${MacFreeRDP_XIBS}") + +# Automatic ref counting +set_target_properties(MacFreeRDP PROPERTIES XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES) + +# Support for automatic reference counting requires non-fragile abi. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fobjc-nonfragile-abi") + +# XCode project architecture to native architecture of build machine +# ----------------------------------------------------------------------------------------------------- +# Issue: Had some issues with FreeRDP project building only 64 bit and +# MacFreeRDP attempting to link to both 32 and 64 for dual target. +# In the future the FreeRDP Xcode project should be pulled in for a couple of reasons: +# 1) better step-into debugging 2) automatic dependency compilation and multi-arch compilation + linkage +# If you know the solutions for 1 and 2, please add below. +set_target_properties(MacFreeRDP PROPERTIES XCODE_ATTRIBUTE_ARCHS "$(NATIVE_ARCH_ACTUAL)") + +# Set the info plist to the custom instance +set_target_properties(MacFreeRDP PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) + +if(NOT WIN32) + find_optional_package(MacAudio) +endif() + +# Add all libraries +target_link_libraries(MacFreeRDP ${EXTRA_LIBS}) +target_link_libraries(MacFreeRDP ${CMAKE_SOURCE_DIR}/../../xcode/libfreerdp-core/Debug/libfreerdp-core.dylib) +target_link_libraries(MacFreeRDP ${CMAKE_SOURCE_DIR}/../../xcode/libfreerdp-channels/Debug/libfreerdp-channels.dylib) +target_link_libraries(MacFreeRDP ${CMAKE_SOURCE_DIR}/../../xcode/libfreerdp-cache/Debug/libfreerdp-cache.dylib) +target_link_libraries(MacFreeRDP ${CMAKE_SOURCE_DIR}/../../xcode/libfreerdp-gdi/Debug/libfreerdp-gdi.dylib) +target_link_libraries(MacFreeRDP ${CMAKE_SOURCE_DIR}/../../xcode/libfreerdp-utils/Debug/libfreerdp-utils.dylib) +target_link_libraries(MacFreeRDP ${CMAKE_SOURCE_DIR}/../../xcode/libfreerdp-codec/Debug/libfreerdp-codec.dylib) diff --git a/client/Mac/Credits.rtf b/client/Mac/Credits.rtf new file mode 100755 index 000000000..41b9f4014 --- /dev/null +++ b/client/Mac/Credits.rtf @@ -0,0 +1,21 @@ +{\rtf1\ansi\ansicpg1252\cocoartf1138\cocoasubrtf320 +{\fonttbl\f0\fswiss\fcharset0 Helvetica;} +{\colortbl;\red255\green255\blue255;} +\vieww9600\viewh8400\viewkind0 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720 + +\f0\b\fs24 \cf0 Engineering: +\b0 \ + Jay sorg\ + Marc-Andre Moreau\ + Vic Lee\ + Otvaio Salvador \ + Laxmikant Rashinkar\ + and others\ +\ + +\b Human Interface Design: +\b0 \ + Laxmikant Rashinkar\ + Jay Sorg\ +} \ No newline at end of file diff --git a/client/Mac/Info.plist b/client/Mac/Info.plist new file mode 100644 index 000000000..530ecb9a7 --- /dev/null +++ b/client/Mac/Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + FreeRDP.Mac + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2012 __MyCompanyName__. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/client/Mac/MRDPCursor.h b/client/Mac/MRDPCursor.h new file mode 100644 index 000000000..98a2a08aa --- /dev/null +++ b/client/Mac/MRDPCursor.h @@ -0,0 +1,25 @@ +// +// MRDPCursor.h +// MacFreeRDP +// +// Created by Laxmikant Rashinkar +// Copyright (c) 2012 FreeRDP.org All rights reserved. +// + +#import + +#define boolean int + +#include "freerdp/graphics.h" + +@interface MRDPCursor : NSObject +{ +@public + rdpPointer *pointer; + uint8 *cursor_data; // bitmapped pixel data + NSBitmapImageRep *bmiRep; + NSCursor *nsCursor; + NSImage *nsImage; +} + +@end diff --git a/client/Mac/MRDPCursor.m b/client/Mac/MRDPCursor.m new file mode 100644 index 000000000..29a93c808 --- /dev/null +++ b/client/Mac/MRDPCursor.m @@ -0,0 +1,12 @@ +// +// MRDPCursor.m +// MacFreeRDP +// +// Created by Laxmikant Rashinkar +// Copyright (c) 2012 FreeRDP.org All rights reserved. +// + +#import "MRDPCursor.h" + +@implementation MRDPCursor +@end diff --git a/client/Mac/MRDPRailView.h b/client/Mac/MRDPRailView.h new file mode 100644 index 000000000..17a7cf4a4 --- /dev/null +++ b/client/Mac/MRDPRailView.h @@ -0,0 +1,51 @@ +#import + +#define boolean int + +#import "freerdp/gdi/gdi.h" +#import "freerdp/rail/rail.h" + +@interface MRDPRailView : NSView +{ + freerdp * rdp_instance; + rdpContext * context; + NSBitmapImageRep * bmiRep; + NSPoint savedDragLocation; + char * pixelData; + boolean mouseInClientArea; + boolean titleBarClicked; + int width; + int height; + int savedWindowId; + + // store state info for some keys + int kdlshift; + int kdrshift; + int kdlctrl; + int kdrctrl; + int kdlalt; + int kdralt; + int kdlmeta; + int kdrmeta; + int kdcapslock; + + @public + boolean isMoveSizeInProgress; + boolean saveInitialDragLoc; + boolean skipMoveWindowOnce; + int localMoveType; +} + +- (void) updateDisplay; +- (void) setRdpInstance:(freerdp *) instance width:(int) w andHeight:(int) h windowID:(int) windowID; +- (BOOL) eventIsInClientArea :(NSEvent *) event :(int *) xptr :(int *) yptr; +- (void) setupBmiRep:(int) width :(int) height; + +void mac_rail_MoveWindow(rdpRail *rail, rdpWindow *window); +void apple_to_windowMove(NSRect * r, RAIL_WINDOW_MOVE_ORDER * windowMove); +void mac_send_rail_client_event(rdpChannels *channels, uint16 event_type, void *param); +void windows_to_apple_cords(NSRect * r); +void rail_MoveWindow(rdpRail * rail, rdpWindow * window); + +@end + diff --git a/client/Mac/MRDPRailView.m b/client/Mac/MRDPRailView.m new file mode 100644 index 000000000..7b69cf1e5 --- /dev/null +++ b/client/Mac/MRDPRailView.m @@ -0,0 +1,749 @@ +#include "MRDPRailView.h" + +#define USE_RAIL_CVT + +@implementation MRDPRailView + +MRDPRailView * g_mrdpRailView; + +struct kkey +{ + int key_code; + int flags; +}; + +extern struct kkey g_keys[]; + +- (void) updateDisplay +{ + boolean moveWindow = NO; + NSRect srcRectOuter; + NSRect destRectOuter; + + rdpGdi * gdi; + + if ((context == 0) || (context->gdi == 0)) + return; + + if (context->gdi->primary->hdc->hwnd->invalid->null) + return; + + if (context->gdi->drawing != context->gdi->primary) + return; + + gdi = context->gdi; + + srcRectOuter = NSMakeRect(0, 0, self->width, self->height); + destRectOuter = [[self window] frame]; + + // cannot be bigger than our current screen size + NSRect screenSize = [[NSScreen mainScreen] frame]; + if (destRectOuter.size.width > screenSize.size.width) { + destRectOuter.size.width = screenSize.size.width; + moveWindow = YES; + } + + if (destRectOuter.size.height > screenSize.size.height) { + destRectOuter.size.height = screenSize.size.height; + moveWindow = YES; + } + + if (destRectOuter.origin.x + destRectOuter.size.width > width) + destRectOuter.size.width = width - destRectOuter.origin.x; + + [self setupBmiRep:destRectOuter.size.width :destRectOuter.size.height]; + + if (moveWindow) { + moveWindow = NO; + RAIL_WINDOW_MOVE_ORDER newWndLoc; + apple_to_windowMove(&destRectOuter, &newWndLoc); + newWndLoc.windowId = savedWindowId; + //skipMoveWindowOnce = TRUE; + //mac_send_rail_client_event(g_mrdpRailView->rdp_instance->context->channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &newWndLoc); + } + + destRectOuter.origin.y = height - destRectOuter.origin.y - destRectOuter.size.height; + rail_convert_color_space(pixelData, (char *) gdi->primary_buffer, + &destRectOuter, self->width, self->height); + + if (moveWindow) + [self setNeedsDisplayInRect:destRectOuter]; + else + [self setNeedsDisplayInRect:[self frame]]; + + gdi->primary->hdc->hwnd->ninvalid = 0; +} + +/** ********************************************************************* + * called when our view needs to be redrawn + ***********************************************************************/ + +- (void) drawRect:(NSRect)dirtyRect +{ + [bmiRep drawInRect:dirtyRect fromRect:dirtyRect operation:NSCompositeCopy fraction:1.0 respectFlipped:NO hints:nil]; + + if (pixelData) { + free(pixelData); + pixelData = NULL; + } + bmiRep = nil; +} + +/** ********************************************************************* + * become first responder so we can get keyboard and mouse events + ***********************************************************************/ + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +/** ********************************************************************* + * called when a mouse move event occurrs + * + * ideally we want to be called when the mouse moves over NSView client area, + * but in reality we get called any time the mouse moves anywhere on the screen; + * we could use NSTrackingArea class to handle this but this class is available + * on Mac OS X v10.5 and higher; since we want to be compatible with older + * versions, we do this manually. + * + * TODO: here is how it can be done using legacy methods + * http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/MouseTrackingEvents/MouseTrackingEvents.html#//apple_ref/doc/uid/10000060i-CH11-SW1 + ***********************************************************************/ + +- (void) mouseMoved:(NSEvent *)event +{ + [super mouseMoved:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + + y = height - y; + + // send mouse motion event to RDP server + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_MOVE, x, y); +} + +/** ********************************************************************* + * called when left mouse button is pressed down + ***********************************************************************/ + +- (void)mouseDown:(NSEvent *) event +{ + [super mouseDown:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + int yPos = (int) (winFrame.size.height - loc.y); + + y = height - y; + + + if ((yPos >= 4) && (yPos <= 20)) + titleBarClicked = YES; + else + titleBarClicked = NO; + + savedDragLocation.x = loc.x; + savedDragLocation.y = loc.y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1, x, y); +} + +/** ********************************************************************* + * called when left mouse button is released + ***********************************************************************/ + +- (void) mouseUp:(NSEvent *) event +{ + [super mouseUp:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_BUTTON1, x, y); + titleBarClicked = NO; +} + +/** ********************************************************************* + * called when right mouse button is pressed down + ***********************************************************************/ + +- (void) rightMouseDown:(NSEvent *)event +{ + [super rightMouseDown:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2, x, y); +} + +/** ********************************************************************* + * called when right mouse button is released + ***********************************************************************/ + +- (void) rightMouseUp:(NSEvent *)event +{ + [super rightMouseUp:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_BUTTON2, x, y); +} + +/** ********************************************************************* + * called when middle mouse button is pressed + ***********************************************************************/ + +- (void) otherMouseDown:(NSEvent *)event +{ + [super otherMouseDown:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3, x, y); +} + +/** ********************************************************************* + * called when middle mouse button is released + ***********************************************************************/ + +- (void) otherMouseUp:(NSEvent *)event +{ + [super otherMouseUp:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_BUTTON3, x, y); +} + +- (void) scrollWheel:(NSEvent *)event +{ + uint16 flags; + + [super scrollWheel:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) (winFrame.origin.x + loc.x); + int y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + flags = PTR_FLAGS_WHEEL; + if ([event scrollingDeltaY] < 0) { + flags |= PTR_FLAGS_WHEEL_NEGATIVE | 0x0088; + } + else { + flags |= 0x0078; + } + x += (int) [event scrollingDeltaX]; + y += (int) [event scrollingDeltaY]; + + rdp_instance->input->MouseEvent(rdp_instance->input, flags, x, y); +} + +/** ********************************************************************* + * called when mouse is moved with left button pressed + * note: invocation order is: mouseDown, mouseDragged, mouseUp + ***********************************************************************/ +- (void) mouseDragged:(NSEvent *)event +{ + [super mouseDragged:event]; + + NSRect winFrame = [[self window] frame]; + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + if (titleBarClicked) { + // window is being dragged to a new location + int newX = x - savedDragLocation.x; + int newY = y - savedDragLocation.y; + + if ((newX == 0) && (newY == 0)) + return; + + winFrame.origin.x += newX; + winFrame.origin.y += newY; + + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_LEFT) { + // left border resize taking place + int diff = (int) (loc.x - savedDragLocation.x); + if (diff == 0) + return; + + if (diff < 0) { + diff = abs(diff); + winFrame.origin.x -= diff; + winFrame.size.width += diff; + } + else { + winFrame.origin.x += diff; + winFrame.size.width -= diff; + } + + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_RIGHT) { + // right border resize taking place + int diff = (int) (loc.x - savedDragLocation.x); + if (diff == 0) + return; + + savedDragLocation.x = loc.x; + savedDragLocation.y = loc.y; + + winFrame.size.width += diff; + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_TOP) { + // top border resize taking place + int diff = (int) (loc.y - savedDragLocation.y); + if (diff == 0) + return; + + savedDragLocation.x = loc.x; + savedDragLocation.y = loc.y; + + winFrame.size.height += diff; + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_BOTTOM) { + // bottom border resize taking place + int diff = (int) (loc.y - savedDragLocation.y); + if (diff == 0) + return; + + if (diff < 0) { + diff = abs(diff); + winFrame.origin.y -= diff; + winFrame.size.height += diff; + } + else { + winFrame.origin.y += diff; + winFrame.size.height -= diff; + } + + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_TOPLEFT) { + // top left border resize taking place + int diff = (int) (loc.x - savedDragLocation.x); + if (diff != 0) { + if (diff < 0) { + diff = abs(diff); + winFrame.origin.x -= diff; + winFrame.size.width += diff; + } + else { + winFrame.origin.x += diff; + winFrame.size.width -= diff; + } + } + + diff = (int) (loc.y - savedDragLocation.y); + if (diff != 0) { + savedDragLocation.y = loc.y; + winFrame.size.height += diff; + } + + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_TOPRIGHT) { + // top right border resize taking place + int diff = (int) (loc.x - savedDragLocation.x); + if (diff != 0) { + winFrame.size.width += diff; + } + + diff = (int) (loc.y - savedDragLocation.y); + if (diff != 0) { + winFrame.size.height += diff; + } + + savedDragLocation.x = loc.x; + savedDragLocation.y = loc.y; + + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_BOTTOMLEFT) { + // bottom left border resize taking place + int diff = (int) (loc.x - savedDragLocation.x); + if (diff != 0) { + if (diff < 0) { + diff = abs(diff); + winFrame.origin.x -= diff; + winFrame.size.width += diff; + } + else { + winFrame.origin.x += diff; + winFrame.size.width -= diff; + } + } + + diff = (int) (loc.y - savedDragLocation.y); + if (diff != 0) { + if (diff < 0) { + diff = abs(diff); + winFrame.origin.y -= diff; + winFrame.size.height += diff; + } + else { + winFrame.origin.y += diff; + winFrame.size.height -= diff; + } + } + + [[self window] setFrame:winFrame display:YES]; + return; + } + + if (localMoveType == RAIL_WMSZ_BOTTOMRIGHT) { + // bottom right border resize taking place + int diff = (int) (loc.x - savedDragLocation.x); + if (diff != 0) { + savedDragLocation.x = loc.x; + //savedDragLocation.y = loc.y; + winFrame.size.width += diff; + } + + diff = (int) (loc.y - savedDragLocation.y); + if (diff != 0) { + if (diff < 0) { + diff = abs(diff); + winFrame.origin.y -= diff; + winFrame.size.height += diff; + } + else { + winFrame.origin.y += diff; + winFrame.size.height -= diff; + } + } + + [[self window] setFrame:winFrame display:YES]; + return; + } + + x = (int) (winFrame.origin.x + loc.x); + y = (int) (winFrame.origin.y + loc.y); + y = height - y; + + // send mouse motion event to RDP server + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_MOVE, x, y); +} + +/** ********************************************************************* + * called when a key is pressed + ***********************************************************************/ + +- (void) keyDown:(NSEvent *) event +{ + int key; + + key = [event keyCode]; + rdp_instance->input->KeyboardEvent(rdp_instance->input, g_keys[key].flags | KBD_FLAGS_DOWN, g_keys[key].key_code); +} + +/** ********************************************************************* + * called when a key is released + ***********************************************************************/ + +- (void) keyUp:(NSEvent *) event +{ + int key; + + key = [event keyCode]; + rdp_instance->input->KeyboardEvent(rdp_instance->input, g_keys[key].flags | KBD_FLAGS_RELEASE, g_keys[key].key_code); +} + +/** ********************************************************************* + * called when shift, control, alt and meta keys are pressed/released + ***********************************************************************/ + +- (void) flagsChanged:(NSEvent *) event +{ + NSUInteger mf = [event modifierFlags]; + + // caps lock + if (mf == 0x10100) { + printf("TODO: caps lock is on\n"); + kdcapslock = 1; + } + if (kdcapslock && (mf == 0x100)) { + kdcapslock = 0; + printf("TODO: caps lock is off\n"); + } + // left shift + if ((kdlshift == 0) && ((mf & 2) != 0)) { + // left shift went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x2a); + kdlshift = 1; + } + if ((kdlshift != 0) && ((mf & 2) == 0)) { + // left shift went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x2a); + kdlshift = 0; + } + + // right shift + if ((kdrshift == 0) && ((mf & 4) != 0)) { + // right shift went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x36); + kdrshift = 1; + } + if ((kdrshift != 0) && ((mf & 4) == 0)) { + // right shift went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x36); + kdrshift = 0; + } + + // left ctrl + if ((kdlctrl == 0) && ((mf & 1) != 0)) { + // left ctrl went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x1d); + kdlctrl = 1; + } + if ((kdlctrl != 0) && ((mf & 1) == 0)) { + // left ctrl went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x1d); + kdlctrl = 0; + } + + // right ctrl + if ((kdrctrl == 0) && ((mf & 0x2000) != 0)) { + // right ctrl went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x1d); + kdrctrl = 1; + } + if ((kdrctrl != 0) && ((mf & 0x2000) == 0)) { + // right ctrl went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x1d); + kdrctrl = 0; + } + + // left alt + if ((kdlalt == 0) && ((mf & 0x20) != 0)) { + // left alt went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x38); + kdlalt = 1; + } + if ((kdlalt != 0) && ((mf & 0x20) == 0)) { + // left alt went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x38); + kdlalt = 0; + } + + // right alt + if ((kdralt == 0) && ((mf & 0x40) != 0)) { + // right alt went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x38); + kdralt = 1; + } + if ((kdralt != 0) && ((mf & 0x40) == 0)) { + // right alt went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x38); + kdralt = 0; + } + + // left meta + if ((kdlmeta == 0) && ((mf & 0x08) != 0)) { + // left meta went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x5b); + kdlmeta = 1; + } + if ((kdlmeta != 0) && ((mf & 0x08) == 0)) { + // left meta went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x5b); + kdlmeta = 0; + } + + // right meta + if ((kdrmeta == 0) && ((mf & 0x10) != 0)) { + // right meta went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x5c); + kdrmeta = 1; + } + if ((kdrmeta != 0) && ((mf & 0x10) == 0)) { + // right meta went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x5c); + kdrmeta = 0; + } +} + +- (void) setRdpInstance:(freerdp *) instance width:(int) w andHeight:(int) h windowID:(int) windowID +{ + rdp_instance = instance; + context = instance->context; + width = w; + height = h; + savedWindowId = windowID; + + NSRect tr = NSMakeRect(0, 0, + [[NSScreen mainScreen] frame].size.width, + [[NSScreen mainScreen] frame].size.height); + + NSTrackingArea * trackingArea = [[NSTrackingArea alloc] initWithRect:tr options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingCursorUpdate | NSTrackingEnabledDuringMouseDrag | NSTrackingActiveAlways owner:self userInfo:nil]; + + [self addTrackingArea:trackingArea]; + + g_mrdpRailView = self; + + [self becomeFirstResponder]; +} + +- (void) setupBmiRep:(int) frameWidth :(int) frameHeight +{ + struct rgba_data + { + char red; + char green; + char blue; + char alpha; + }; + + if (pixelData) + free(pixelData); + + pixelData = (char *) malloc(frameWidth * frameHeight * sizeof(struct rgba_data)); + bmiRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **) &pixelData + pixelsWide:frameWidth + pixelsHigh:frameHeight + bitsPerSample:8 + samplesPerPixel:sizeof(struct rgba_data) + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:0 + bytesPerRow:frameWidth * sizeof(struct rgba_data) + bitsPerPixel:0]; +} + +void rail_cvt_from_rect(char *dest, char *src, NSRect destRect, int destWidth, int destHeight, NSRect srcRect) +{ +} + +/** ********************************************************************* + * color space conversion used specifically in RAIL + ***********************************************************************/ +void rail_convert_color_space(char *destBuf, char * srcBuf, + NSRect * destRect, int width, int height) +{ + int i; + int j; + int numRows; + int srcX; + int srcY; + int destX; + int destY; + int pixelsPerRow; + int pixel; + int pixel1; + int pixel2; + int * src32; + int * dest32; + + int destWidth = destRect->size.width; + int destHeight = destRect->size.height; + + if ((!destBuf) || (!srcBuf)) { + return; + } + + numRows = (destRect->origin.y + destHeight > height) ? height - destRect->origin.y : destHeight; + pixelsPerRow = destWidth; + + srcX = destRect->origin.x; + srcY = destRect->origin.y; + destX = 0; + destY = 0; + + for (i = 0; i < numRows; i++) + { + src32 = (int *) (srcBuf + ((srcY + i) * width + srcX) * 4); + dest32 = (int *) (destBuf + ((destY + i) * destWidth + destX) * 4); + + for (j = 0; j < pixelsPerRow; j++) + { + pixel = *src32; + pixel1 = (pixel & 0x00ff0000) >> 16; + pixel2 = (pixel & 0x000000ff) << 16; + pixel = (pixel & 0xff00ff00) | pixel1 | pixel2; + + *dest32 = pixel; + + src32++; + dest32++; + } + } + + destRect->origin.y = destHeight - destRect->origin.y - destRect->size.height; + return; +} + +/** + * let RDP server know that window has moved + */ + +void rail_MoveWindow(rdpRail * rail, rdpWindow * window) +{ + if (g_mrdpRailView->isMoveSizeInProgress) { + return; + } + + if (g_mrdpRailView->skipMoveWindowOnce) { + g_mrdpRailView->skipMoveWindowOnce = NO; + return; + } + + // this rect is based on Windows co-ordinates... + NSRect r; + r.origin.x = window->windowOffsetX; + r.origin.y = window->windowOffsetY; + r.size.width = window->windowWidth; + r.size.height = window->windowHeight; + + windows_to_apple_cords(&r); + [[g_mrdpRailView window] setFrame:r display:YES]; +} + +@end diff --git a/client/Mac/MRDPRailWindow.h b/client/Mac/MRDPRailWindow.h new file mode 100644 index 000000000..629dc819c --- /dev/null +++ b/client/Mac/MRDPRailWindow.h @@ -0,0 +1,13 @@ +// +// MRDPRailWindow.h +// Mac +// +// Created by Laxmikant Rashinkar +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +@interface MRDPRailWindow : NSWindow + +@end diff --git a/client/Mac/MRDPRailWindow.m b/client/Mac/MRDPRailWindow.m new file mode 100644 index 000000000..9cab180f4 --- /dev/null +++ b/client/Mac/MRDPRailWindow.m @@ -0,0 +1,18 @@ +// +// MRDPRailWindow.m +// Mac +// +// Created by Laxmikant Rashinkar +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import "MRDPRailWindow.h" + +@implementation MRDPRailWindow + +- (BOOL) canBecomeKeyWindow +{ + return YES; +} + +@end diff --git a/client/Mac/MRDPView.h b/client/Mac/MRDPView.h new file mode 100644 index 000000000..fe8b8cbbf --- /dev/null +++ b/client/Mac/MRDPView.h @@ -0,0 +1,177 @@ +// +// MRDPView.h +// MacFreeRDP +// +// Created by Laxmikant Rashinkar +// Copyright (c) 2012 FreeRDP.org All rights reserved. +// + +#import + +typedef int boolean; + +#import "MRDPWindow.h" +#import "freerdp/freerdp.h" +#import "freerdp/types.h" +#import "freerdp/channels/channels.h" +#import "freerdp/gdi/gdi.h" +#import "freerdp/graphics.h" +#import "freerdp/utils/event.h" +#import "freerdp/plugins/cliprdr.h" +#import "freerdp/utils/args.h" +#import "freerdp/rail/rail.h" +#import "freerdp/rail.h" +#import "freerdp/utils/rail.h" + +@interface MRDPView : NSView +{ + CFRunLoopSourceRef run_loop_src; + CFRunLoopSourceRef run_loop_src_channels; + NSBitmapImageRep *bmiRep; + NSMutableArray *cursors; + NSMutableArray *windows; + NSTimer *pasteboard_timer; + NSRect rect; + NSRect prevWinPosition; + freerdp *rdp_instance; + rdpContext *rdp_context; + char *pixel_data; + int width; + int height; + int argc; + int titleBarHeight; + char **argv; + + // RAIL stuff + MRDPWindow *currentWindow; + NSPoint savedDragLocation; + boolean mouseInClientArea; + boolean isRemoteApp; + boolean firstCreateWindow; + boolean isMoveSizeInProgress; + boolean skipResizeOnce; + boolean saveInitialDragLoc; + boolean skipMoveWindowOnce; + + // store state info for some keys + int kdlshift; + int kdrshift; + int kdlctrl; + int kdrctrl; + int kdlalt; + int kdralt; + int kdlmeta; + int kdrmeta; + int kdcapslock; + +@public + NSWindow *ourMainWindow; + NSPasteboard *pasteboard_rd; // for reading from clipboard + NSPasteboard *pasteboard_wr; // for writing to clipboard + int pasteboard_changecount; + int pasteboard_format; + int is_connected; // true when connected to RDP server +} + +- (void) rdpConnectEror; +- (void) rdpRemoteAppError; +- (void) saveStateInfo :(freerdp *) instance :(rdpContext *) context; +- (void) onPasteboardTimerFired :(NSTimer *) timer; +- (void) my_draw_rect :(void *) context; +- (void) releaseResources; +- (void) setViewSize : (int) width : (int) height; + +@property (assign) int is_connected; + +@end + +/* Pointer Flags */ +#define PTR_FLAGS_WHEEL 0x0200 +#define PTR_FLAGS_WHEEL_NEGATIVE 0x0100 +#define PTR_FLAGS_MOVE 0x0800 +#define PTR_FLAGS_DOWN 0x8000 +#define PTR_FLAGS_BUTTON1 0x1000 +#define PTR_FLAGS_BUTTON2 0x2000 +#define PTR_FLAGS_BUTTON3 0x4000 +#define WheelRotationMask 0x01FF + +void pointer_new(rdpContext* context, rdpPointer* pointer); +void pointer_free(rdpContext* context, rdpPointer* pointer); +void pointer_set(rdpContext* context, rdpPointer* pointer); +void pointer_setNull(rdpContext* context); +void pointer_setDefault(rdpContext* context); +int rdp_connect(); +boolean mac_pre_connect(freerdp *inst); +boolean mac_post_connect(freerdp *inst); +void mac_context_new(freerdp *inst, rdpContext *context); +void mac_context_free(freerdp *inst, rdpContext *context); +void mac_set_bounds(rdpContext *context, rdpBounds *bounds); +void mac_bitmap_update(rdpContext *context, BITMAP_UPDATE *bitmap); +void mac_begin_paint(rdpContext *context); +void mac_end_paint(rdpContext* context); +void mac_save_state_info(freerdp *inst, rdpContext *context); +void skt_activity_cb(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info); +void channel_activity_cb(CFSocketRef s, CFSocketCallBackType callbackType, CFDataRef address, const void *data, void *info); +int register_fds(int *fds, int count, void *inst); +int invoke_draw_rect(rdpContext *context); +int process_plugin_args(rdpSettings* settings, const char* name, RDP_PLUGIN_DATA* plugin_data, void* user_data); +int receive_channel_data(freerdp *inst, int chan_id, uint8 *data, int size, int flags, int total_size); +void process_cliprdr_event(freerdp *inst, RDP_EVENT *event); +void cliprdr_process_cb_format_list_event(freerdp *inst, RDP_CB_FORMAT_LIST_EVENT* event); +void cliprdr_send_data_request(freerdp *inst, uint32 format); +void cliprdr_process_cb_monitor_ready_event(freerdp* inst); +void cliprdr_process_cb_data_response_event(freerdp *inst, RDP_CB_DATA_RESPONSE_EVENT *event); +void cliprdr_process_text(freerdp *inst, uint8 *data, int len); +void cliprdr_send_supported_format_list(freerdp *inst); +int register_channel_fds(int *fds, int count, void *inst); + +void mac_process_rail_event(freerdp *inst, RDP_EVENT *event); +void mac_rail_register_callbacks(freerdp *inst, rdpRail *rail); +void mac_rail_CreateWindow(rdpRail *rail, rdpWindow *window); +void mac_rail_MoveWindow(rdpRail *rail, rdpWindow *window); +void mac_rail_ShowWindow(rdpRail *rail, rdpWindow *window, uint8 state); +void mac_rail_SetWindowText(rdpRail *rail, rdpWindow *window); +void mac_rail_SetWindowIcon(rdpRail *rail, rdpWindow *window, rdpIcon *icon); +void mac_rail_SetWindowRects(rdpRail *rail, rdpWindow *window); +void mac_rail_SetWindowVisibilityRects(rdpRail *rail, rdpWindow *window); +void mac_rail_DestroyWindow(rdpRail *rail, rdpWindow *window); +void mac_process_rail_get_sysparams_event(rdpChannels *channels, RDP_EVENT *event); +void mac_send_rail_client_event(rdpChannels *channels, uint16 event_type, void *param); +void mac_on_free_rail_client_event(RDP_EVENT* event); +void mac_process_rail_server_sysparam_event(rdpChannels* channels, RDP_EVENT* event); +void mac_process_rail_exec_result_event(rdpChannels* channels, RDP_EVENT* event); +void mac_rail_enable_remoteapp_mode(); +void mac_process_rail_server_minmaxinfo_event(rdpChannels* channels, RDP_EVENT* event); +void mac_process_rail_server_localmovesize_event(freerdp *inst, RDP_EVENT *event); +void apple_center_window(NSRect * r); +void apple_to_windowMove(NSRect * r, RAIL_WINDOW_MOVE_ORDER * windowMove); + +struct mac_context +{ + // *must* have this - do not delete + rdpContext _p; +}; + +struct cursor +{ + rdpPointer *pointer; + uint8 *cursor_data; // bitmapped pixel data + void *bmiRep; // NSBitmapImageRep + void *nsCursor; // NSCursor + void *nsImage; // NSImage +}; + +struct rgba_data +{ + char red; + char green; + char blue; + char alpha; +}; + +struct kkey +{ + int key_code; + int flags; +}; + diff --git a/client/Mac/MRDPView.m b/client/Mac/MRDPView.m new file mode 100644 index 000000000..1e0c2b724 --- /dev/null +++ b/client/Mac/MRDPView.m @@ -0,0 +1,2314 @@ +// +// MRDPView.m +// MacFreeRDP +// +// Created by Laxmikant Rashinkar +// Copyright (c) 2012 FreeRDP.org All rights reserved. +// + +/* + * TODO + * + provide a UI for configuring optional parameters, but keep cmd line args + * + audio redirection is delayed considerably + * + caps lock key needs to be sent in func flagsChanged() + * + libfreerdp-utils.1.0.dylib needs to be installed to /usr/local/lib + * + * - MRDPView implementation is incomplete + * - all variables should have consistent nameing scheme - camel case + * - all funcs same as above + * - PolygonSc seems to create a transparent rect + * - ensure mouse cursor changes are working ok after moving to NSTracking area + * - RAIL: + * - + * - + * - tool tips to be correctly positioned + * - dragging is slightly of + * - resize after dragging not working + * - dragging app from macbook to monitor gives exec/access err + * - unable to drag rect out of monitor boundaries + * - + * - + * - + */ + +#import "MRDPView.h" +#import "MRDPCursor.h" + +// Should be defined in Makefile +// #define RUN_IN_XCODE + +// LK_TODO +#define GOT_HERE //printf("### got here: %s : %s() : %d\n", __FILE__, __func__, __LINE__) + +// RAIL_TODO DELETE WHEN DONE TESTING +#define MRDP_DRAW_INDIVIDUAL_RECTS + +@implementation MRDPView + +MRDPView *g_mrdpview; + +@synthesize is_connected; + +struct kkey g_keys[]; +void convert_color_space(char *dest, char *src, NSRect* drawRect, int width, int height); + +const char* error_code_names[] = +{ + "RAIL_EXEC_S_OK", + "RAIL_EXEC_E_HOOK_NOT_LOADED", + "RAIL_EXEC_E_DECODE_FAILED", + "RAIL_EXEC_E_NOT_IN_ALLOWLIST", + "RAIL_EXEC_E_FILE_NOT_FOUND", + "RAIL_EXEC_E_FAIL", + "RAIL_EXEC_E_SESSION_LOCKED" +}; + +struct kkey g_keys[256] = +{ + { 0x1e, 0 }, // a 0 + { 0x1f, 0 }, // s + { 0x20, 0 }, // d + { 0x21, 0 }, // f + { 0x23, 0 }, // h + { 0x22, 0 }, // g + { 0x2c, 0 }, // z + { 0x2d, 0 }, // x + { 0x2e, 0 }, // c + { 0x2f, 0 }, // v + { 0x00, 0 }, // 10 + { 0x30, 0 }, // b + { 0x10, 0 }, // q + { 0x11, 0 }, // w + { 0x12, 0 }, // e + { 0x13, 0 }, // r + { 0x15, 0 }, // y + { 0x14, 0 }, // t + { 0x02, 0 }, // 1 + { 0x03, 0 }, // 2 + { 0x04, 0 }, // 3 20 + { 0x05, 0 }, // 4 + { 0x07, 0 }, // 6 + { 0x06, 0 }, // 5 + { 0x0d, 0 }, // = or + + { 0x0a, 0 }, // 9 + { 0x08, 0 }, // 7 + { 0x0c, 0 }, // - or _ + { 0x09, 0 }, // 8 + { 0x0b, 0 }, // 0 + { 0x1b, 0 }, // ] or } 30 + { 0x18, 0 }, // o + { 0x16, 0 }, // u + { 0x1a, 0 }, // [ or { + { 0x17, 0 }, // i + { 0x19, 0 }, // p + { 0x1c, 0 }, // enter + { 0x26, 0 }, // l + { 0x24, 0 }, // j + { 0x28, 0 }, // ' or " + { 0x25, 0 }, // k 40 + { 0x27, 0 }, // ; or : + { 0x2b, 0 }, // \ or | + { 0x33, 0 }, // , or < + { 0x35, 0 }, // / or ? + { 0x31, 0 }, // n + { 0x32, 0 }, // m + { 0x34, 0 }, // . or > + { 0x0f, 0 }, // tab + { 0x39, 0 }, // space + { 0x29, 0 }, // ` or ~ 50 + { 0x0e, 0 }, // backspace + { 0x00, 0 }, // + { 0x01, 0 }, // esc + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, // 60 + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x53, 0 }, // KP. + { 0x00, 0 }, + { 0x37, 0 }, // KP* + { 0x00, 0 }, + { 0x4e, 0 }, // KP+ + { 0x00, 0 }, // 70 + { 0x45, 0 }, // num lock + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x35, 1 }, // KP/ + { 0x1c, 1 }, // KPEnter + { 0x00, 0 }, + { 0x4a, 0 }, // KP- + { 0x00, 0 }, + { 0x00, 0 }, // 80 + { 0x00, 0 }, + { 0x52, 0 }, // KP0 + { 0x4f, 0 }, // KP1 + { 0x50, 0 }, // KP2 + { 0x51, 0 }, // KP3 + { 0x4b, 0 }, // KP4 + { 0x4c, 0 }, // KP5 + { 0x4d, 0 }, // KP6 + { 0x47, 0 }, // KP7 + { 0x00, 0 }, // 90 + { 0x48, 0 }, // KP8 + { 0x49, 0 }, // KP9 + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, // 100 + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x5d, 1 }, // menu 110 + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x52, 1 }, // Insert + { 0x47, 1 }, // Home + { 0x49, 1 }, // PgUp + { 0x53, 1 }, // Delete + { 0x00, 0 }, + { 0x4f, 1 }, // End + { 0x00, 0 }, // 120 + { 0x51, 1 }, // PgDown + { 0x3b, 0 }, // f1 + { 0x4b, 1 }, // left + { 0x4d, 1 }, // right + { 0x50, 1 }, // down + { 0x48, 1 }, // up + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, + { 0x00, 0 }, +}; + +/************************************************************************ + methods we override +************************************************************************/ + +/** ********************************************************************* + * create MRDPView with specified rectangle + ***********************************************************************/ + +- (id)initWithFrame:(NSRect)frame +{ + self = [super initWithFrame:frame]; + if (self) { + // Initialization code here. + } + + return self; +} + +/** ********************************************************************* + * called when MRDPView has been successfully created from the NIB + ***********************************************************************/ + +- (void) awakeFromNib +{ + g_mrdpview = self; + + // store our window dimensions + width = [self frame].size.width; + height = [self frame].size.height; + titleBarHeight = 22; + + [[self window] becomeFirstResponder]; + [[self window] setAcceptsMouseMovedEvents:YES]; + + cursors = [[NSMutableArray alloc] initWithCapacity:10]; + + firstCreateWindow = TRUE; + skipResizeOnce = YES; + windows = [[NSMutableArray alloc] initWithCapacity:10]; + + // setup a mouse tracking area + NSTrackingArea * trackingArea = [[NSTrackingArea alloc] initWithRect:[self visibleRect] options:NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingCursorUpdate | NSTrackingEnabledDuringMouseDrag | NSTrackingActiveWhenFirstResponder owner:self userInfo:nil]; + + //[self addTrackingArea:trackingArea]; + + // windows in RemoteApp (RAIL) mode cannot have title bars + NSArray * args = [[NSProcessInfo processInfo] arguments]; + for (NSString * str in args) + { + if ([str compare:@"--app"] == NSOrderedSame) { + isRemoteApp = TRUE; + break; + } + } + + if (!isRemoteApp) + [self addTrackingArea:trackingArea]; + + mouseInClientArea = YES; +} + +/** ********************************************************************* + * become first responder so we can get keyboard and mouse events + ***********************************************************************/ + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +/** ********************************************************************* + * called when a mouse move event occurrs + * + * ideally we want to be called when the mouse moves over NSView client area, + * but in reality we get called any time the mouse moves anywhere on the screen; + * we could use NSTrackingArea class to handle this but this class is available + * on Mac OS X v10.5 and higher; since we want to be compatible with older + * versions, we do this manually. + * + * TODO: here is how it can be done using legacy methods + * http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/MouseTrackingEvents/MouseTrackingEvents.html#//apple_ref/doc/uid/10000060i-CH11-SW1 + ***********************************************************************/ + + - (void) mouseMoved:(NSEvent *)event +{ + [super mouseMoved:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + // send mouse motion event to RDP server + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_MOVE, x, y); +} + +/** ********************************************************************* + * called when left mouse button is pressed down + ***********************************************************************/ + +- (void)mouseDown:(NSEvent *) event +{ + [super mouseDown:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON1, x, y); +} + +/** ********************************************************************* + * called when left mouse button is released + ***********************************************************************/ + +- (void) mouseUp:(NSEvent *) event +{ + [super mouseUp:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_BUTTON1, x, y); +} + +/** ********************************************************************* + * called when right mouse button is pressed down + ***********************************************************************/ + +- (void) rightMouseDown:(NSEvent *)event +{ + [super rightMouseDown:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON2, x, y); +} + +/** ********************************************************************* + * called when right mouse button is released + ***********************************************************************/ + +- (void) rightMouseUp:(NSEvent *)event +{ + [super rightMouseUp:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_BUTTON2, x, y); +} + +/** ********************************************************************* + * called when middle mouse button is pressed + ***********************************************************************/ + +- (void) otherMouseDown:(NSEvent *)event +{ + [super otherMouseDown:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_DOWN | PTR_FLAGS_BUTTON3, x, y); +} + +/** ********************************************************************* + * called when middle mouse button is released + ***********************************************************************/ + +- (void) otherMouseUp:(NSEvent *)event +{ + [super otherMouseUp:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_BUTTON3, x, y); +} + +- (void) scrollWheel:(NSEvent *)event +{ + uint16 flags; + + [super scrollWheel:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + y = height - y; + + flags = PTR_FLAGS_WHEEL; + if ([event deltaY] < 0) { + flags |= PTR_FLAGS_WHEEL_NEGATIVE | 0x0088; + } + else { + flags |= 0x0078; + } + x += (int) [event deltaX]; + y += (int) [event deltaY]; + rdp_instance->input->MouseEvent(rdp_instance->input, flags, x, y); +} + +/** ********************************************************************* + * called when mouse is moved with left button pressed + * note: invocation order is: mouseDown, mouseDragged, mouseUp + ***********************************************************************/ + +- (void) mouseDragged:(NSEvent *)event +{ + [super mouseDragged:event]; + + if (!is_connected) + return; + + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + // RAIL_TODO delete this if not reqd + if ((isRemoteApp) && (isMoveSizeInProgress)) { + if (saveInitialDragLoc) { + saveInitialDragLoc = NO; + savedDragLocation.x = x; + savedDragLocation.y = y; + return; + } + + int newX = x - savedDragLocation.x; + int newY = y - savedDragLocation.y; + + NSRect r = [[self window] frame]; + r.origin.x += newX; + r.origin.y += newY; + [[g_mrdpview window] setFrame:r display:YES]; + } + + y = height - y; + + // send mouse motion event to RDP server + rdp_instance->input->MouseEvent(rdp_instance->input, PTR_FLAGS_MOVE, x, y); +} + +/** ********************************************************************* + * called when a key is pressed + ***********************************************************************/ + +- (void) keyDown:(NSEvent *) event +{ + int key; + + if (!is_connected) { + return; + } + + key = [event keyCode]; + rdp_instance->input->KeyboardEvent(rdp_instance->input, g_keys[key].flags | KBD_FLAGS_DOWN, g_keys[key].key_code); +} + +/** ********************************************************************* + * called when a key is released + ***********************************************************************/ + +- (void) keyUp:(NSEvent *) event +{ + int key; + + if (!is_connected) { + return; + } + + key = [event keyCode]; + rdp_instance->input->KeyboardEvent(rdp_instance->input, g_keys[key].flags | KBD_FLAGS_RELEASE, g_keys[key].key_code); +} + +/** ********************************************************************* + * called when shift, control, alt and meta keys are pressed/released + ***********************************************************************/ + +- (void) flagsChanged:(NSEvent *) event +{ + NSUInteger mf = [event modifierFlags]; + + if (!is_connected) { + return; + } + + // caps lock + if (mf == 0x10100) { + printf("TODO: caps lock is on\n"); + kdcapslock = 1; + } + if (kdcapslock && (mf == 0x100)) { + kdcapslock = 0; + printf("TODO: caps lock is off\n"); + } + // left shift + if ((kdlshift == 0) && ((mf & 2) != 0)) { + // left shift went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x2a); + kdlshift = 1; + } + if ((kdlshift != 0) && ((mf & 2) == 0)) { + // left shift went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x2a); + kdlshift = 0; + } + + // right shift + if ((kdrshift == 0) && ((mf & 4) != 0)) { + // right shift went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x36); + kdrshift = 1; + } + if ((kdrshift != 0) && ((mf & 4) == 0)) { + // right shift went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x36); + kdrshift = 0; + } + + // left ctrl + if ((kdlctrl == 0) && ((mf & 1) != 0)) { + // left ctrl went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x1d); + kdlctrl = 1; + } + if ((kdlctrl != 0) && ((mf & 1) == 0)) { + // left ctrl went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x1d); + kdlctrl = 0; + } + + // right ctrl + if ((kdrctrl == 0) && ((mf & 0x2000) != 0)) { + // right ctrl went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x1d); + kdrctrl = 1; + } + if ((kdrctrl != 0) && ((mf & 0x2000) == 0)) { + // right ctrl went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x1d); + kdrctrl = 0; + } + + // left alt + if ((kdlalt == 0) && ((mf & 0x20) != 0)) { + // left alt went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_DOWN, 0x38); + kdlalt = 1; + } + if ((kdlalt != 0) && ((mf & 0x20) == 0)) { + // left alt went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, KBD_FLAGS_RELEASE, 0x38); + kdlalt = 0; + } + + // right alt + if ((kdralt == 0) && ((mf & 0x40) != 0)) { + // right alt went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x38); + kdralt = 1; + } + if ((kdralt != 0) && ((mf & 0x40) == 0)) { + // right alt went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x38); + kdralt = 0; + } + + // left meta + if ((kdlmeta == 0) && ((mf & 0x08) != 0)) { + // left meta went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x5b); + kdlmeta = 1; + } + if ((kdlmeta != 0) && ((mf & 0x08) == 0)) { + // left meta went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x5b); + kdlmeta = 0; + } + + // right meta + if ((kdrmeta == 0) && ((mf & 0x10) != 0)) { + // right meta went down + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_DOWN, 0x5c); + kdrmeta = 1; + } + if ((kdrmeta != 0) && ((mf & 0x10) == 0)) { + // right meta went up + rdp_instance->input->KeyboardEvent(rdp_instance->input, 1 | KBD_FLAGS_RELEASE, 0x5c); + kdrmeta = 0; + } +} + +- (void) releaseResources +{ + int i; + + for (i = 0; i < argc; i++) + { + if (argv[i]) + free(argv[i]); + } + + for (MRDPWindow * w in windows) + { + [w setWindow:nil]; + [w setView:nil]; + } + + if (!is_connected) + return; + + freerdp_channels_global_uninit(); + + if (pixel_data) + free(pixel_data); + + if (run_loop_src != 0) + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_src_channels, kCFRunLoopDefaultMode); + + if (run_loop_src != 0) + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), run_loop_src, kCFRunLoopDefaultMode); +} + +/** ********************************************************************* + * called when our view needs refreshing + ***********************************************************************/ + +- (void) drawRect:(NSRect)dirtyRect +{ + if (!rdp_context) + return; + + if (g_mrdpview->isRemoteApp && g_mrdpview->currentWindow) { + return; + } + + if (!bmiRep) { + pixel_data = (char *) malloc(width * height * sizeof(struct rgba_data)); + bmiRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **) &pixel_data + pixelsWide:width + pixelsHigh:height + bitsPerSample:8 + samplesPerPixel:sizeof(struct rgba_data) + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:0 //NSAlphaFirstBitmapFormat + bytesPerRow:width * sizeof(struct rgba_data) + bitsPerPixel:0]; + } + + [bmiRep drawInRect:dirtyRect fromRect:dirtyRect operation:NSCompositeCopy fraction:1.0 respectFlipped:NO hints:nil]; +} + +/************************************************************************ + instance methods +************************************************************************/ + +/** ********************************************************************* + * called when RDP server wants us to update a rect with new data + ***********************************************************************/ + +- (void) my_draw_rect:(void *)context +{ + int w; + int h; + + rdpContext * ctx = (rdpContext *) context; + + struct rgba_data + { + char red; + char green; + char blue; + char alpha; + }; + + if (isRemoteApp && currentWindow) { + NSRect vrect = [ [currentWindow view] frame]; + [[currentWindow view] setNeedsDisplayInRect:vrect]; + // actual drawing will be done in MRDPRailView:drawRect() + return; + } + + w = width; + h = height; + rect.origin.x = 0; + rect.origin.y = 0; + rect.size.width = w; + rect.size.height = h; + + if (!bmiRep) { + pixel_data = (char *) malloc(w * h * sizeof(struct rgba_data)); + bmiRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **) &pixel_data + pixelsWide:w + pixelsHigh:h + bitsPerSample:8 + samplesPerPixel:sizeof(struct rgba_data) + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:0 //NSAlphaFirstBitmapFormat + bytesPerRow:w * sizeof(struct rgba_data) + bitsPerPixel:0]; + } + +#ifdef MRDP_DRAW_INDIVIDUAL_RECTS + [self setNeedsDisplayInRect:rect]; + return; +#endif + + convert_color_space(pixel_data, (char *) ctx->gdi->primary_buffer, &rect, w, h); + [self setNeedsDisplayInRect:rect]; +} + +/** ********************************************************************* + * save state info for use by other methods later on + ***********************************************************************/ + +- (void) saveStateInfo:(freerdp *) instance:(rdpContext *) context +{ + rdp_instance = instance; + rdp_context = context; +} + +/** ********************************************************************* + * double check that a mouse event occurred in our client view + ***********************************************************************/ + +- (BOOL) eventIsInClientArea :(NSEvent *) event :(int *) xptr :(int *) yptr +{ + NSPoint loc = [event locationInWindow]; + int x = (int) loc.x; + int y = (int) loc.y; + + if ((x < 0) || (y < 0)) { + if (mouseInClientArea) { + // set default cursor before leaving client area + mouseInClientArea = NO; + NSCursor *cur = [NSCursor arrowCursor]; + [cur set]; + } + return NO; + } + if ((x > width) || (y > height)) { + if (mouseInClientArea) { + // set default cursor before leaving client area + mouseInClientArea = NO; + NSCursor *cur = [NSCursor arrowCursor]; + [cur set]; + } + return NO; + } + + // on Mac origin is at lower left, but we want it on upper left + y = height - y; + + *xptr = x; + *yptr = y; + mouseInClientArea = YES; + return YES; +} + +/** ********************************************************************* + * called when we fail to connect to a RDP server + ***********************************************************************/ + +- (void) rdpConnectError +{ + + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Error connecting to server"]; + [alert beginSheetModalForWindow:[g_mrdpview window] + modalDelegate:g_mrdpview + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:nil]; +} + +/** ********************************************************************* + * called when we fail to launch remote app on RDP server + ***********************************************************************/ + +- (void) rdpRemoteAppError +{ + NSAlert *alert = [[NSAlert alloc] init]; + [alert setMessageText:@"Error starting remote app on specified server"]; + [alert beginSheetModalForWindow:[g_mrdpview window] + modalDelegate:g_mrdpview + didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) + contextInfo:nil]; +} + +/** ********************************************************************* + * just a terminate selector for above call + ***********************************************************************/ + +- (void) alertDidEnd:(NSAlert *)a returnCode:(NSInteger)rc contextInfo:(void *)ci +{ + [NSApp terminate:nil]; +} + +- (void) onPasteboardTimerFired :(NSTimer *) timer +{ + NSArray *types; + int i; + + i = (int) [pasteboard_rd changeCount]; + if (i != pasteboard_changecount) + { + pasteboard_changecount = i; + types = [NSArray arrayWithObject:NSStringPboardType]; + NSString *str = [pasteboard_rd availableTypeFromArray:types]; + if (str != nil) + { + cliprdr_send_supported_format_list(rdp_instance); + } + } +} + +- (void) setViewSize : (int) w : (int) h +{ + // store current dimensions + width = w; + height = h; + + // compute difference between window and client area + NSRect outerRect = [[g_mrdpview window] frame]; + NSRect innerRect = [g_mrdpview frame]; + + int widthDiff = outerRect.size.width - innerRect.size.width; + int heightDiff = outerRect.size.height - innerRect.size.height; + + if (!g_mrdpview->isRemoteApp) { + // we are not in RemoteApp mode, disable resizing + outerRect.size.width = w + widthDiff; + outerRect.size.height = h + heightDiff; + [[g_mrdpview window] setMaxSize:outerRect.size]; + [[g_mrdpview window] setMinSize:outerRect.size]; + [[g_mrdpview window] setFrame:outerRect display:YES]; + + // set client area to specified dimensions + innerRect.size.width = w; + innerRect.size.height = h; + [g_mrdpview setFrame:innerRect]; + + return; + } + + // we are in RemoteApp mode + + outerRect.origin.x = 0; + outerRect.origin.y = 0; + outerRect.size.width = width + widthDiff; + outerRect.size.height = height + heightDiff; + [[g_mrdpview window] setFrame:outerRect display:YES]; + + // set client area to specified dimensions + innerRect.size.width = width; + innerRect.size.height = height; + [g_mrdpview setFrame:innerRect]; + + // main window displays desktop background - hide it + [[g_mrdpview window] orderOut:g_mrdpview]; +} + +// RAIL_TODO is this func required +- (void) windowDidResize:(NSNotification *) notification +{ + RAIL_WINDOW_MOVE_ORDER windowMove; + + printf("RAIL_TODO: MRDPView: windowDidResize() - not yet implemented\n"); + + return; + + // window resize valid only in RemoteApp mode + if (!g_mrdpview->isRemoteApp) + return; + + // window has resized, let server know + + NSRect r = [[g_mrdpview window] frame]; + printf("----- LK_TODO: MRDPView:windowDidResize (%d,%d %dx%d)\n", + (int) r.origin.x, (int) r.origin.y, + (int) r.size.width, (int) r.size.height); + + + windowMove.windowId = [currentWindow windowID]; + + windowMove.left = (uint16) r.origin.x; // x-cordinate of top left corner + windowMove.right = (uint16) (windowMove.left + r.size.width); // x-cordinate of bottom right corner + windowMove.top = (uint16) r.origin.y; // y-cordinate of top left corner + windowMove.bottom = (uint16) (windowMove.top + r.size.height); // y-cordinate of bottom right corner + + printf("----- LK_TODO: MRDPView:windowDidResize windowID=%d left=%d top=%d right=%d bottom=x%d width=%f height=%f\n", + [currentWindow windowID], windowMove.left, windowMove.top, windowMove.right, windowMove.bottom, r.size.width, r.size.height); + + //mac_send_rail_client_event(g_mrdpview->rdp_instance->context->channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &windowMove); +} + +/************************************************************************ + * * + * C functions * + * * + ***********************************************************************/ + +/** ********************************************************************* + * connect to RDP server + * + * @return 0 on success, -1 on failure + ***********************************************************************/ + +int rdp_connect() +{ + freerdp *inst; + int status; + + freerdp_channels_global_init(); + + inst = freerdp_new(); + inst->PreConnect = mac_pre_connect; + inst->PostConnect = mac_post_connect; + inst->context_size = sizeof(struct mac_context); + inst->ContextNew = mac_context_new; + inst->ContextFree = mac_context_free; + inst->ReceiveChannelData = receive_channel_data; + freerdp_context_new(inst); + + status = freerdp_connect(inst); + if(status) { + freerdp_check_fds(inst); + [g_mrdpview setIs_connected:1]; + return 0; + } + [g_mrdpview setIs_connected:0]; + [g_mrdpview rdpConnectError]; + return -1; +} + +/** ********************************************************************* + * a callback given to freerdp_connect() to process the pre-connect operations. + * + * @param inst - pointer to a rdp_freerdp struct that contains the connection's parameters, and + * will be filled with the appropriate informations. + * + * @return true if successful. false otherwise. + ************************************************************************/ + +boolean mac_pre_connect(freerdp *inst) +{ + char *cptr; + int len; + int i; + + inst->settings->offscreen_bitmap_cache = false; + inst->settings->glyph_cache = true; + inst->settings->glyphSupportLevel = GLYPH_SUPPORT_FULL; + inst->settings->order_support[NEG_GLYPH_INDEX_INDEX] = true; + inst->settings->order_support[NEG_FAST_GLYPH_INDEX] = false; + inst->settings->order_support[NEG_FAST_INDEX_INDEX] = false; + inst->settings->order_support[NEG_SCRBLT_INDEX] = true; + inst->settings->order_support[NEG_SAVEBITMAP_INDEX] = false; + + inst->settings->bitmap_cache = true; + inst->settings->order_support[NEG_MEMBLT_INDEX] = true; + inst->settings->order_support[NEG_MEMBLT_V2_INDEX] = true; + inst->settings->order_support[NEG_MEM3BLT_INDEX] = false; + inst->settings->order_support[NEG_MEM3BLT_V2_INDEX] = false; + inst->settings->bitmapCacheV2NumCells = 3; // 5; + inst->settings->bitmapCacheV2CellInfo[0].numEntries = 0x78; // 600; + inst->settings->bitmapCacheV2CellInfo[0].persistent = false; + inst->settings->bitmapCacheV2CellInfo[1].numEntries = 0x78; // 600; + inst->settings->bitmapCacheV2CellInfo[1].persistent = false; + inst->settings->bitmapCacheV2CellInfo[2].numEntries = 0x150; // 2048; + inst->settings->bitmapCacheV2CellInfo[2].persistent = false; + inst->settings->bitmapCacheV2CellInfo[3].numEntries = 0; // 4096; + inst->settings->bitmapCacheV2CellInfo[3].persistent = false; + inst->settings->bitmapCacheV2CellInfo[4].numEntries = 0; // 2048; + inst->settings->bitmapCacheV2CellInfo[4].persistent = false; + + inst->settings->order_support[NEG_MULTIDSTBLT_INDEX] = false; + inst->settings->order_support[NEG_MULTIPATBLT_INDEX] = false; + inst->settings->order_support[NEG_MULTISCRBLT_INDEX] = false; + inst->settings->order_support[NEG_MULTIOPAQUERECT_INDEX] = false; + inst->settings->order_support[NEG_POLYLINE_INDEX] = false; + inst->settings->color_depth = 24; + inst->settings->sw_gdi = 1; + + // setup callbacks + inst->update->BeginPaint = mac_begin_paint; + inst->update->EndPaint = mac_end_paint; + inst->update->SetBounds = mac_set_bounds; + inst->update->BitmapUpdate = mac_bitmap_update; + + NSArray *args = [[NSProcessInfo processInfo] arguments]; + +#ifdef RUN_IN_XCODE + g_mrdpview->argc = 30; +#else + g_mrdpview->argc = (int) [args count]; +#endif + + g_mrdpview->argv = malloc(sizeof(char *) * g_mrdpview->argc); + if (g_mrdpview->argv == NULL) { + return false; + } + +#ifdef RUN_IN_XCODE + + // create our own cmd line args + i = 0; + + NSString *sptr = [args objectAtIndex:0]; + len = [sptr length] + 1; + cptr = (char *) malloc(len); + strcpy(cptr, [sptr UTF8String]); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "-g"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "1280x800"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "--sec"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "rdp"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "--plugin"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "cliprdr"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "--plugin"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "rdpsnd"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "-u"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "lk"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "-p"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "abc@@@123"); + g_mrdpview->argv[i++] = cptr; + +#if 1 + // for RemoteApp (RAIL) testing + cptr = (char *)malloc(80); + strcpy(cptr, "--app"); + g_mrdpview->argv[i++] = cptr; + g_mrdpview->isRemoteApp = TRUE; + + cptr = (char *)malloc(80); + strcpy(cptr, "--plugin"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "rail"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "--data"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "||WordPad"); + g_mrdpview->argv[i++] = cptr; + + cptr = (char *)malloc(80); + strcpy(cptr, "--"); + g_mrdpview->argv[i++] = cptr; +#endif + + cptr = (char *)malloc(80); + strcpy(cptr, "192.168.1.69:45990"); + g_mrdpview->argv[i++] = cptr; + + g_mrdpview->argc = i; + +#else + // MacFreeRDP was not run in Xcode + i = 0; + + // in RemoteApp (RAIL) mode, we connect to RDP server at max screen resolution; + // in order to achieve this, we need to modify the cmd line args entered by the user; + + if (g_mrdpview->isRemoteApp) { + boolean gotGeometry = NO; + + // get dimensions of screen that has keyboard focus; + // we use these dimensions when connecting to RDP server + inst->settings->width = [[NSScreen mainScreen] frame].size.width; + inst->settings->height = [[NSScreen mainScreen] frame].size.height - g_mrdpview->titleBarHeight; + g_mrdpview->width = inst->settings->width; + g_mrdpview->height = inst->settings->height; + + for (NSString * str in args) + { + if (gotGeometry) { + gotGeometry = NO; + cptr = (char *) malloc(20); + sprintf(cptr, "%dx%d", g_mrdpview->width, g_mrdpview->height); + g_mrdpview->argv[i++] = cptr; + continue; + } + + len = (int) ([str length] + 1); + cptr = (char *) malloc(len); + strcpy(cptr, [str UTF8String]); + g_mrdpview->argv[i++] = cptr; + + // -g is the cmd line arg to specify screen resolution/geometry + if ([str compare:@"-g"] == NSOrderedSame) { + gotGeometry = YES; + } + } + } + else { + for (NSString * str in args) + { + len = (int) ([str length] + 1); + cptr = (char *) malloc(len); + strcpy(cptr, [str UTF8String]); + g_mrdpview->argv[i++] = cptr; + } + } +#endif + + freerdp_parse_args(inst->settings, g_mrdpview->argc, g_mrdpview->argv, process_plugin_args, inst->context->channels, NULL, NULL); + if ((strcmp(g_mrdpview->argv[1], "-h") == 0) || (strcmp(g_mrdpview->argv[1], "--help") == 0)) { + [NSApp terminate:nil]; + return true; + } + + [g_mrdpview setViewSize:inst->settings->width :inst->settings->height]; + + freerdp_channels_pre_connect(inst->context->channels, inst); + return true; +} + +/** ********************************************************************* + * a callback registered with freerdp_connect() to perform post-connection operations. + * we get called only if the connection was initialized properly, and will continue + * the initialization based on the newly created connection. + * + * @param inst - pointer to a rdp_freerdp struct + * + * @return true on success, false on failure + * + ************************************************************************/ + +boolean mac_post_connect(freerdp *inst) +{ + uint32 flags; + rdpPointer rdp_pointer; + void *rd_fds[32]; + void *wr_fds[32]; + int rd_count = 0; + int wr_count = 0; + int index; + int fds[32]; + + memset(&rdp_pointer, 0, sizeof(rdpPointer)); + rdp_pointer.size = sizeof(rdpPointer); + rdp_pointer.New = pointer_new; + rdp_pointer.Free = pointer_free; + rdp_pointer.Set = pointer_set; + rdp_pointer.SetNull = pointer_setNull; + rdp_pointer.SetDefault = pointer_setDefault; + + flags = CLRCONV_ALPHA; + flags |= CLRBUF_32BPP; + + gdi_init(inst, flags, NULL); + pointer_cache_register_callbacks(inst->update); + graphics_register_pointer(inst->context->graphics, &rdp_pointer); + + // register file descriptors with the RunLoop + if (!freerdp_get_fds(inst, rd_fds, &rd_count, 0, 0)) + { + printf("mac_post_connect: freerdp_get_fds() failed!\n"); + } + + for (index = 0; index < rd_count; index++) + { + fds[index] = (int)(long)rd_fds[index]; + } + register_fds(fds, rd_count, inst); + + // register channel manager file descriptors with the RunLoop + if (!freerdp_channels_get_fds(inst->context->channels, inst, rd_fds, &rd_count, wr_fds, &wr_count)) + { + printf("ERROR: freerdp_channels_get_fds() failed\n"); + } + for (index = 0; index < rd_count; index++) + { + fds[index] = (int)(long)rd_fds[index]; + } + register_channel_fds(fds, rd_count, inst); + freerdp_channels_post_connect(inst->context->channels, inst); + + // setup RAIL (remote app) + inst->context->rail = rail_new(inst->settings); + rail_register_update_callbacks(inst->context->rail, inst->update); + mac_rail_register_callbacks(inst, inst->context->rail); + + // setup pasteboard (aka clipboard) for copy operations (write only) + g_mrdpview->pasteboard_wr = [NSPasteboard generalPasteboard]; + + // setup pasteboard for read operations + g_mrdpview->pasteboard_rd = [NSPasteboard generalPasteboard]; + g_mrdpview->pasteboard_changecount = (int) [g_mrdpview->pasteboard_rd changeCount]; + g_mrdpview->pasteboard_timer = [NSTimer scheduledTimerWithTimeInterval:0.5 target:g_mrdpview selector:@selector(onPasteboardTimerFired:) userInfo:nil repeats:YES]; + + // we want to be notified when window resizes + [[NSNotificationCenter defaultCenter] addObserver:g_mrdpview selector:@selector(windowDidResize:) name:NSWindowDidResizeNotification object:nil]; + + return true; +} + +/** ********************************************************************* + * create a new mouse cursor + * + * @param context our context state + * @param pointer information about the cursor to create + * + ************************************************************************/ + +void pointer_new(rdpContext* context, rdpPointer* pointer) +{ + MRDPCursor *mrdpCursor = [[MRDPCursor alloc] init]; + uint8 *cursor_data; + + NSRect rect; + rect.size.width = pointer->width; + rect.size.height = pointer->height; + rect.origin.x = pointer->xPos; + rect.origin.y = pointer->yPos; + + cursor_data = (uint8 *) malloc(rect.size.width * rect.size.height * 4); + mrdpCursor->cursor_data = cursor_data; + + freerdp_alpha_cursor_convert(cursor_data, pointer->xorMaskData, pointer->andMaskData, + pointer->width, pointer->height, pointer->xorBpp, context->gdi->clrconv); + + // TODO if xorBpp is > 24 need to call freerdp_image_swap_color_order + // see file df_graphics.c + + // store cursor bitmap image in representation - required by NSImage + NSBitmapImageRep *bmiRep; + bmiRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(unsigned char **) &cursor_data + pixelsWide:rect.size.width + pixelsHigh:rect.size.height + bitsPerSample:8 + samplesPerPixel:sizeof(struct rgba_data) + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bitmapFormat:0 + bytesPerRow:rect.size.width * 4 + bitsPerPixel:0]; + mrdpCursor->bmiRep = bmiRep; + + // create an image using above representation + NSImage *image = [[NSImage alloc] initWithSize:[bmiRep size]]; + [image addRepresentation: bmiRep]; + [image setFlipped:NO]; + mrdpCursor->nsImage = image; + + // need hotspot to create cursor + NSPoint hotSpot; + hotSpot.x = pointer->xPos; + hotSpot.y = pointer->yPos; + + NSCursor *cursor = [[NSCursor alloc] initWithImage: image hotSpot:hotSpot]; + mrdpCursor->nsCursor = cursor; + mrdpCursor->pointer = pointer; + + // save cursor for later use in pointer_set() + NSMutableArray *ma = g_mrdpview->cursors; + [ma addObject:mrdpCursor]; +} + +/** ********************************************************************* + * release resources on specified cursor + ************************************************************************/ + +void pointer_free(rdpContext* context, rdpPointer* pointer) +{ + NSMutableArray *ma = g_mrdpview->cursors; + + for (MRDPCursor *cursor in ma) + { + if (cursor->pointer == pointer) { + cursor->nsImage = nil; + cursor->nsCursor = nil; + cursor->bmiRep = nil; + free(cursor->cursor_data); + [ma removeObject:cursor]; + return; + } + } +} + +/** ********************************************************************* + * set specified cursor as the current cursor + ************************************************************************/ + +void pointer_set(rdpContext* context, rdpPointer* pointer) +{ + NSMutableArray *ma = g_mrdpview->cursors; + + if (!g_mrdpview->mouseInClientArea) + { + return; // not in client area + } + + for (MRDPCursor *cursor in ma) + { + if (cursor->pointer == pointer) { + [cursor->nsCursor set]; + return; + } + } +} + +/** ********************************************************************* + * do not display any mouse cursor + ***********************************************************************/ + +void pointer_setNull(rdpContext* context) +{ +} + +/** ********************************************************************* + * display default mouse cursor + ***********************************************************************/ + +void pointer_setDefault(rdpContext* context) +{ +} + +/** ********************************************************************* + * create a new context - but all we really need to do is save state info + ***********************************************************************/ + +void mac_context_new(freerdp *inst, rdpContext *context) +{ + [g_mrdpview saveStateInfo:inst :context]; + context->channels = freerdp_channels_new(); +} + +/** ********************************************************************* + * we don't do much over here + ***********************************************************************/ + +void mac_context_free(freerdp *inst, rdpContext *context) +{ +} + +/** ********************************************************************* + * clip drawing surface so nothing is drawn outside specified bounds + ***********************************************************************/ + +void mac_set_bounds(rdpContext *context, rdpBounds *bounds) +{ +} + +/** ********************************************************************* + * we don't do much over here + ***********************************************************************/ + +void mac_bitmap_update(rdpContext *context, BITMAP_UPDATE *bitmap) +{ +} + +/** ********************************************************************* + * we don't do much over here + ***********************************************************************/ + +void mac_begin_paint(rdpContext *context) +{ + rdpGdi* gdi = context->gdi; + gdi->primary->hdc->hwnd->invalid->null = 1; +} + +/** ********************************************************************* + * RDP server wants us to draw new data in the view + ***********************************************************************/ + +void mac_end_paint(rdpContext* context) +{ + int i; + NSRect drawRect; + rdpGdi * gdi; + + if ((context == 0) || (context->gdi == 0)) + return; + + if (context->gdi->primary->hdc->hwnd->invalid->null) + return; + + if (context->gdi->drawing != context->gdi->primary) + return; + + gdi = g_mrdpview->rdp_context->gdi; + + if (g_mrdpview->isRemoteApp && g_mrdpview->currentWindow) { + [[g_mrdpview->currentWindow view] updateDisplay]; + return; + } + + for (i = 0; i < gdi->primary->hdc->hwnd->ninvalid; i++) + { + drawRect.origin.x = gdi->primary->hdc->hwnd->cinvalid[i].x; + drawRect.origin.y = gdi->primary->hdc->hwnd->cinvalid[i].y; + drawRect.size.width = gdi->primary->hdc->hwnd->cinvalid[i].w; + drawRect.size.height = gdi->primary->hdc->hwnd->cinvalid[i].h; + + convert_color_space(g_mrdpview->pixel_data, (char *) gdi->primary_buffer, &drawRect, g_mrdpview->width, g_mrdpview->height); + + [g_mrdpview setNeedsDisplayInRect:drawRect]; + } + gdi->primary->hdc->hwnd->ninvalid = 0; +} + +/** ********************************************************************* + * called when data is available on a socket + ***********************************************************************/ + +void skt_activity_cb( + CFSocketRef s, + CFSocketCallBackType callbackType, + CFDataRef address, + const void *data, + void *info + ) +{ + if (!freerdp_check_fds(info)) { + // lost connection or did not connect + [NSApp terminate:nil]; + } +} + +/** ********************************************************************* + * called when data is available on a virtual channel + ***********************************************************************/ + +void channel_activity_cb( + CFSocketRef s, + CFSocketCallBackType callbackType, + CFDataRef address, + const void *data, + void *info + ) +{ + freerdp *inst = (freerdp *) info; + RDP_EVENT* event; + + GOT_HERE; + + freerdp_channels_check_fds(inst->context->channels, inst); + event = freerdp_channels_pop_event(inst->context->channels); + if (event) { + switch (event->event_class) + { + case RDP_EVENT_CLASS_RAIL: + mac_process_rail_event(inst, event); + break; + + case RDP_EVENT_CLASS_CLIPRDR: + process_cliprdr_event(inst, event); + break; + } + } +} + +/** ********************************************************************* + * setup callbacks for data availability on sockets + ***********************************************************************/ + +int register_fds(int *fds, int count, void *inst) +{ + int i; + CFSocketRef skt_ref; + CFSocketContext skt_context = { 0, inst, NULL, NULL, NULL }; + + for (i = 0; i < count; i++) + { + skt_ref = CFSocketCreateWithNative(NULL, fds[i], kCFSocketReadCallBack, skt_activity_cb, &skt_context); + g_mrdpview->run_loop_src = CFSocketCreateRunLoopSource(NULL, skt_ref, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), g_mrdpview->run_loop_src, kCFRunLoopDefaultMode); + CFRelease(skt_ref); + } + return 0; +} + +/** ********************************************************************* + * setup callbacks for data availability on channels + ***********************************************************************/ + +int register_channel_fds(int *fds, int count, void *inst) +{ + int i; + CFSocketRef skt_ref; + CFSocketContext skt_context = { 0, inst, NULL, NULL, NULL }; + + for (i = 0; i < count; i++) + { + skt_ref = CFSocketCreateWithNative(NULL, fds[i], kCFSocketReadCallBack, channel_activity_cb, &skt_context); + g_mrdpview->run_loop_src_channels = CFSocketCreateRunLoopSource(NULL, skt_ref, 0); + CFRunLoopAddSource(CFRunLoopGetCurrent(), g_mrdpview->run_loop_src_channels, kCFRunLoopDefaultMode); + CFRelease(skt_ref); + } + return 0; +} + +/** ********************************************************************* + * called when channel data is available + ***********************************************************************/ + +int receive_channel_data(freerdp *inst, int chan_id, uint8 *data, int size, int flags, int total_size) +{ + return freerdp_channels_data(inst, chan_id, data, size, flags, total_size); +} + +/** ********************************************************************* + * convert an array containing ARGB data to RGBA + ***********************************************************************/ + +void convert_color_space(char *dest, char *src, NSRect* drawRect, int width, int height) +{ + int i; + int j; + int x; + int y; + int cx; + int cy; + int pixel; + int pixel1; + int pixel2; + int* src32; + int* dst32; + + if ((!dest) || (!src)) { + return; + } + + x = drawRect->origin.x; + y = drawRect->origin.y; + cx = drawRect->size.width; + cy = drawRect->size.height; + + for (j = 0; j < cy; j++) + { + src32 = (int*)(src + ((y + j) * width + x) * 4); + dst32 = (int*)(dest + ((y + j) * width + x) * 4); + for (i = 0; i < cx; i++) + { + pixel = *src32; + pixel1 = (pixel & 0x00ff0000) >> 16; + pixel2 = (pixel & 0x000000ff) << 16; + pixel = (pixel & 0xff00ff00) | pixel1 | pixel2; + *dst32 = pixel; + src32++; + dst32++; + } + } + + drawRect->origin.y = height - drawRect->origin.y - drawRect->size.height; +} + +/** + * Used to load plugins based on the commandline parameters. + * This function is provided as a parameter to freerdp_parse_args(), that will call it + * each time a plugin name is found on the command line. + * This function just calls freerdp_channels_load_plugin() for the given plugin, and always returns 1. + * + * @param settings + * @param name + * @param plugin_data + * @param user_data + * + * @return 1 + */ + +int process_plugin_args(rdpSettings* settings, const char* name, RDP_PLUGIN_DATA* plugin_data, void* user_data) +{ + rdpChannels* channels = (rdpChannels*) user_data; + + freerdp_channels_load_plugin(channels, settings, name, plugin_data); + return 1; +} + +/* + * stuff related to clipboard redirection + */ + +/** + * remote system has requested clipboard data from local system + */ + +void cliprdr_process_cb_data_request_event(freerdp *inst) +{ + RDP_CB_DATA_RESPONSE_EVENT *event; + NSArray *types; + int len; + + event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_DATA_RESPONSE, NULL, NULL); + types = [NSArray arrayWithObject:NSStringPboardType]; + NSString *str = [g_mrdpview->pasteboard_rd availableTypeFromArray:types]; + if (str == nil) + { + event->data = NULL; + event->size = 0; + } + else + { + NSString *data = [g_mrdpview->pasteboard_rd stringForType:NSStringPboardType]; + len = (int) ([data length] * 2 + 2); + event->data = malloc(len); + [data getCString:(char *) event->data maxLength:len encoding:NSUnicodeStringEncoding]; + event->size = len; + } + freerdp_channels_send_event(inst->context->channels, (RDP_EVENT*) event); +} + +void cliprdr_send_data_request(freerdp *inst, uint32 format) +{ + RDP_CB_DATA_REQUEST_EVENT* event; + + event = (RDP_CB_DATA_REQUEST_EVENT *) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_DATA_REQUEST, NULL, NULL); + event->format = format; + freerdp_channels_send_event(inst->context->channels, (RDP_EVENT*) event); +} + +/** + * at the moment, only the following formats are supported + * CB_FORMAT_TEXT + * CB_FORMAT_UNICODETEXT + */ + +void cliprdr_process_cb_data_response_event(freerdp *inst, RDP_CB_DATA_RESPONSE_EVENT *event) +{ + NSString *str; + NSArray *types; + + if (event->size == 0) { + return; + } + + if (g_mrdpview->pasteboard_format == CB_FORMAT_TEXT || g_mrdpview->pasteboard_format == CB_FORMAT_UNICODETEXT) { + str = [[NSString alloc] initWithCharacters:(unichar *) event->data length:event->size / 2]; + types = [[NSArray alloc] initWithObjects:NSStringPboardType, nil]; + [g_mrdpview->pasteboard_wr declareTypes:types owner:g_mrdpview]; + [g_mrdpview->pasteboard_wr setString:str forType:NSStringPboardType]; + } +} + +void cliprdr_process_cb_monitor_ready_event(freerdp* inst) +{ + RDP_EVENT* event; + RDP_CB_FORMAT_LIST_EVENT* format_list_event; + + event = freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR, RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL); + + format_list_event = (RDP_CB_FORMAT_LIST_EVENT*)event; + format_list_event->num_formats = 0; + + freerdp_channels_send_event(inst->context->channels, event); +} + +/** + * list of supported clipboard formats; currently only the following are supported + * CB_FORMAT_TEXT + * CB_FORMAT_UNICODETEXT + */ + +void cliprdr_process_cb_format_list_event(freerdp *inst, RDP_CB_FORMAT_LIST_EVENT* event) +{ + int i; + + if (event->num_formats == 0) { + return; + } + + for (i = 0; i < event->num_formats; i++) + { + switch (event->formats[i]) + { + case CB_FORMAT_RAW: + printf("CB_FORMAT_RAW: not yet supported\n"); + break; + + case CB_FORMAT_TEXT: + case CB_FORMAT_UNICODETEXT: + g_mrdpview->pasteboard_format = CB_FORMAT_UNICODETEXT; + cliprdr_send_data_request(inst, CB_FORMAT_UNICODETEXT); + return; + break; + + case CB_FORMAT_DIB: + printf("CB_FORMAT_DIB: not yet supported\n"); + break; + + case CB_FORMAT_HTML: + printf("CB_FORMAT_HTML\n"); + break; + + case CB_FORMAT_PNG: + printf("CB_FORMAT_PNG: not yet supported\n"); + break; + + case CB_FORMAT_JPEG: + printf("CB_FORMAT_JPEG: not yet supported\n"); + break; + + case CB_FORMAT_GIF: + printf("CB_FORMAT_GIF: not yet supported\n"); + break; + } + } +} + +void process_cliprdr_event(freerdp *inst, RDP_EVENT *event) +{ + if (event) { + switch (event->event_type) + { + // Monitor Ready PDU is sent by server to indicate that it has been + // inited and is ready. This PDU is transmitted by the server after it has sent + // Clipboard Capabilities PDU + case RDP_EVENT_TYPE_CB_MONITOR_READY: + cliprdr_process_cb_monitor_ready_event(inst); + break; + + // The Format List PDU is sent either by the client or the server when its + // local system clipboard is updated with new clipboard data. This PDU + // contains the Clipboard Format ID and name pairs of the new Clipboard + // Formats on the clipboard + case RDP_EVENT_TYPE_CB_FORMAT_LIST: + cliprdr_process_cb_format_list_event(inst, (RDP_CB_FORMAT_LIST_EVENT*) event); + break; + + // The Format Data Request PDU is sent by the receipient of the Format List PDU. + // It is used to request the data for one of the formats that was listed in the + // Format List PDU + case RDP_EVENT_TYPE_CB_DATA_REQUEST: + cliprdr_process_cb_data_request_event(inst); + break; + + // The Format Data Response PDU is sent as a reply to the Format Data Request PDU. + // It is used to indicate whether processing of the Format Data Request PDU + // was successful. If the processing was successful, the Format Data Response PDU + // includes the contents of the requested clipboard data + case RDP_EVENT_TYPE_CB_DATA_RESPONSE: + cliprdr_process_cb_data_response_event(inst, (RDP_CB_DATA_RESPONSE_EVENT*) event); + break; + + default: + printf("process_cliprdr_event: unknown event type %d\n", event->event_type); + break; + } + freerdp_event_free(event); + } +} + +void cliprdr_send_supported_format_list(freerdp *inst) +{ + RDP_CB_FORMAT_LIST_EVENT* event; + + event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR, + RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL); + + event->formats = (uint32 *) malloc(sizeof(uint32) * 1); + event->num_formats = 1; + event->formats[0] = CB_FORMAT_UNICODETEXT; + freerdp_channels_send_event(inst->context->channels, (RDP_EVENT*) event); +} + +/**************************************************************************************** + * * + * * + * RemoteApp (RAIL) related stuff goes here * + * * + * * + ****************************************************************************************/ + +void mac_process_rail_event(freerdp *inst, RDP_EVENT *event) +{ + switch (event->event_type) + { + case RDP_EVENT_TYPE_RAIL_CHANNEL_GET_SYSPARAMS: + mac_process_rail_get_sysparams_event(inst->context->channels, event); + break; + + case RDP_EVENT_TYPE_RAIL_CHANNEL_EXEC_RESULTS: + mac_process_rail_exec_result_event(inst->context->channels, event); + break; + + case RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_SYSPARAM: + mac_process_rail_server_sysparam_event(inst->context->channels, event); + break; + + case RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_MINMAXINFO: + mac_process_rail_server_minmaxinfo_event(inst->context->channels, event); + break; + + case RDP_EVENT_TYPE_RAIL_CHANNEL_SERVER_LOCALMOVESIZE: + mac_process_rail_server_localmovesize_event(inst, event); + break; + + case RDP_EVENT_TYPE_RAIL_CHANNEL_APPID_RESP: + GOT_HERE; + //xf_process_rail_appid_resp_event(xfi, channels, event); + break; + + case RDP_EVENT_TYPE_RAIL_CHANNEL_LANGBARINFO: + GOT_HERE; + //xf_process_rail_langbarinfo_event(xfi, channels, event); + break; + } +} + +void mac_rail_CreateWindow(rdpRail *rail, rdpWindow *window) +{ + boolean centerWindow = NO; + boolean moveWindow = NO; + boolean displayAsModal = NO; + NSMutableArray * ma = g_mrdpview->windows; + + // make sure window fits resolution + if (window->windowWidth > g_mrdpview->width) + window->windowWidth = g_mrdpview->width; + + if (window->windowHeight > g_mrdpview->height) + window->windowHeight = g_mrdpview->height; + + // center main window, which is the first to be created + if ([ma count] == 0) { + centerWindow = YES; + moveWindow = YES; + } + + if ((window->extendedStyle & WS_EX_TOPMOST) || (window->extendedStyle & WS_EX_TOOLWINDOW)) { + [g_mrdpview->currentWindow view]->skipMoveWindowOnce = TRUE; + moveWindow = YES; + } + else if (window->style & WS_POPUP) { + centerWindow = YES; + moveWindow = YES; + } + else { + } + + // create NSWindow + NSRect winFrame = NSMakeRect(window->windowOffsetX, window->windowOffsetY, + window->windowWidth, window->windowHeight); + if (centerWindow) + apple_center_window(&winFrame); + + MRDPRailWindow * newWindow = [[MRDPRailWindow alloc] initWithContentRect:winFrame + styleMask:NSTitledWindowMask | NSResizableWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + + // this does not work if specified during window creation in above code + [newWindow setStyleMask:NSBorderlessWindowMask]; + + if (moveWindow) { + // let RDP server know that window has moved + RAIL_WINDOW_MOVE_ORDER windowMove; + apple_to_windowMove(&winFrame, &windowMove); + windowMove.windowId = window->windowId; + mac_send_rail_client_event(g_mrdpview->rdp_instance->context->channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &windowMove); + } + + // create MRDPRailView and add to above window + NSRect viewFrame = NSMakeRect(window->clientOffsetX, window->clientOffsetY, + window->clientAreaWidth, window->clientAreaHeight); + + MRDPRailView * newView = [[MRDPRailView alloc] initWithFrame:viewFrame]; + [newView setRdpInstance:g_mrdpview->rdp_instance width:g_mrdpview->width andHeight:g_mrdpview->height windowID: window->windowId]; + [newWindow setContentView:newView]; + + // save new window + MRDPWindow * mrdpWindow = [[MRDPWindow alloc] init]; + [mrdpWindow setWindowID:window->windowId]; + [mrdpWindow setWindow:newWindow]; + [mrdpWindow setView:newView]; + + // add to list of windows + [ma addObject:mrdpWindow]; + + // make new window current + g_mrdpview->currentWindow = mrdpWindow; + + if (displayAsModal) { + // display as modal window + NSModalSession session = [NSApp beginModalSessionForWindow:newWindow]; + while (1) + { + if ([NSApp runModalSession:session] != NSRunContinuesResponse) + break; + } + [NSApp endModalSession:session]; + } + else { + [newWindow makeKeyAndOrderFront:NSApp]; + [[g_mrdpview window] resignFirstResponder]; + [g_mrdpview resignFirstResponder]; + [[g_mrdpview window] setNextResponder:newWindow]; + } + + return; +} + +void mac_rail_MoveWindow(rdpRail *rail, rdpWindow *window) +{ + if (g_mrdpview->currentWindow) { + rail_MoveWindow(rail, window); + return; + } +} + +void mac_rail_ShowWindow(rdpRail *rail, rdpWindow *window, uint8 state) +{ +} + +void mac_rail_SetWindowText(rdpRail *rail, rdpWindow *window) +{ +} + +void mac_rail_SetWindowIcon(rdpRail *rail, rdpWindow *window, rdpIcon *icon) +{ +} + +void mac_rail_SetWindowRects(rdpRail *rail, rdpWindow *window) +{ +} + +void mac_rail_SetWindowVisibilityRects(rdpRail *rail, rdpWindow *window) +{ +} + +/** ********************************************************************* + * destroy window created in mac_rail_CreateWindow() + ***********************************************************************/ + +void mac_rail_DestroyWindow(rdpRail *rail, rdpWindow *window) +{ + int count = 0; + + for (MRDPWindow * win in g_mrdpview->windows) + { + if ([win windowID] == window->windowId) { + //[[win window] close]; + [win setView:nil]; + [win setWindow:nil]; + [g_mrdpview->windows removeObject:win]; + count = [g_mrdpview->windows count]; + if (count) { + g_mrdpview->currentWindow = [g_mrdpview->windows objectAtIndex:count - 1]; + //[[g_mrdpview window] makeKeyAndOrderFront:[g_mrdpview->currentWindow window]]; + [[g_mrdpview->currentWindow window] makeKeyAndOrderFront:NSApp]; + } + else { + g_mrdpview->currentWindow = nil; + // RAIL_TODO [[g_mrdpview window] makeKeyAndOrderFront:[g_mrdpview window]]; + [NSApp terminate:nil]; + } + return; + } + } +} + +void mac_rail_register_callbacks(freerdp *inst, rdpRail *rail) +{ + GOT_HERE; + + rail->extra = (void *) inst; + rail->rail_CreateWindow = mac_rail_CreateWindow; + rail->rail_MoveWindow = mac_rail_MoveWindow; + rail->rail_ShowWindow = mac_rail_ShowWindow; + rail->rail_SetWindowText = mac_rail_SetWindowText; + rail->rail_SetWindowIcon = mac_rail_SetWindowIcon; + rail->rail_SetWindowRects = mac_rail_SetWindowRects; + rail->rail_SetWindowVisibilityRects = mac_rail_SetWindowVisibilityRects; + rail->rail_DestroyWindow = mac_rail_DestroyWindow; +} + +/** ********************************************************************* + * set work area size, which is the portion of the screen not obscured + * by the system taskbar or by application desktop toolbars + ************************************************************************/ + +void mac_process_rail_get_sysparams_event(rdpChannels *channels, RDP_EVENT *event) +{ + RAIL_SYSPARAM_ORDER * sysparam; + sysparam = (RAIL_SYSPARAM_ORDER *) event->user_data; + + sysparam->workArea.left = 0; + sysparam->workArea.top = 22; + sysparam->workArea.right = g_mrdpview->width; + sysparam->workArea.bottom = g_mrdpview->height - 22; + + sysparam->taskbarPos.left = 0; + sysparam->taskbarPos.top = 0; + sysparam->taskbarPos.right = 0; + sysparam->taskbarPos.bottom = 0; + + sysparam->dragFullWindows = false; + + mac_send_rail_client_event(channels, RDP_EVENT_TYPE_RAIL_CLIENT_SET_SYSPARAMS, sysparam); +} + +void mac_process_rail_server_sysparam_event(rdpChannels* channels, RDP_EVENT* event) +{ + RAIL_SYSPARAM_ORDER* sysparam = (RAIL_SYSPARAM_ORDER*) event->user_data; + + GOT_HERE; + + switch (sysparam->param) + { + case SPI_SET_SCREEN_SAVE_ACTIVE: + break; + + case SPI_SET_SCREEN_SAVE_SECURE: + break; + } +} + +/** ********************************************************************* + * server returned result of exec'ing remote app on server + ************************************************************************/ + +void mac_process_rail_exec_result_event(rdpChannels* channels, RDP_EVENT* event) +{ + RAIL_EXEC_RESULT_ORDER* exec_result; + + GOT_HERE; + + exec_result = (RAIL_EXEC_RESULT_ORDER*) event->user_data; + + if (exec_result->execResult != RAIL_EXEC_S_OK) { + printf("RAIL exec error: execResult=%s NtError=0x%X\n", + error_code_names[exec_result->execResult], exec_result->rawResult); + g_mrdpview->is_connected = FALSE; + [g_mrdpview rdpRemoteAppError]; + } + else { + mac_rail_enable_remoteapp_mode(); + } +} + +/** ********************************************************************* + * sent by server when a window move or resize on the server is being + * initiated. this PDU contains info about the min and max extents + * to which the window can be moved or sized + ************************************************************************/ + +void mac_process_rail_server_minmaxinfo_event(rdpChannels* channels, RDP_EVENT* event) +{ + RAIL_MINMAXINFO_ORDER * minmax = (RAIL_MINMAXINFO_ORDER*) event->user_data; + +#if 0 + printf("minmax_info: maxPosX=%d maxPosY=%d maxWidth=%d maxHeight=%d minTrackWidth=%d minTrackHeight=%d maxTrackWidth=%d maxTrackHeight=%d\n", + minmax->maxPosX, minmax->maxPosY, minmax->maxWidth, minmax->maxHeight, + minmax->minTrackWidth, minmax->minTrackHeight, minmax->maxTrackWidth, minmax->maxTrackHeight); +#endif +} + +/** ********************************************************************* + * sent by the server when a window on the server is beginning a move or + * resize; use this info to initiate a local move or resize of the + * corresponding local window + ************************************************************************/ + +void mac_process_rail_server_localmovesize_event(freerdp *inst, RDP_EVENT *event) +{ + RAIL_LOCALMOVESIZE_ORDER * moveSize = (RAIL_LOCALMOVESIZE_ORDER *) event->user_data; + RAIL_WINDOW_MOVE_ORDER windowMove; + + switch (moveSize->moveSizeType) { + case RAIL_WMSZ_LEFT: + printf("!!!! RAIL_WMSZ_LEFT\n"); + break; + + case RAIL_WMSZ_RIGHT: + printf("!!!! RAIL_WMSZ_RIGHT\n"); + break; + + case RAIL_WMSZ_TOP: + printf("!!!! RAIL_WMSZ_TOP\n"); + break; + + case RAIL_WMSZ_TOPLEFT: + printf("!!!! RAIL_WMSZ_TOPLEFT\n"); + break; + + case RAIL_WMSZ_TOPRIGHT: + printf("!!!! RAIL_WMSZ_TOPRIGHT\n"); + break; + + case RAIL_WMSZ_BOTTOM: + printf("!!!! RAIL_WMSZ_BOTTOM\n"); + break; + + case RAIL_WMSZ_BOTTOMLEFT: + printf("!!!! RAIL_WMSZ_BOTTOMLEFT\n"); + break; + + case RAIL_WMSZ_BOTTOMRIGHT: + printf("!!!! RAIL_WMSZ_BOTTOMRIGHT\n"); + break; + + case RAIL_WMSZ_MOVE: + if (moveSize->isMoveSizeStart) { + // local window move in progress + [g_mrdpview->currentWindow view]->isMoveSizeInProgress = YES; + [g_mrdpview->currentWindow view]->saveInitialDragLoc = YES; + + return; + } + + // local move has completed + [g_mrdpview->currentWindow view]->isMoveSizeInProgress = NO; + [g_mrdpview->currentWindow view]->saveInitialDragLoc = NO; + + //NSRect rect = [[g_mrdpview->currentWindow view] frame]; + NSRect rect = [[[g_mrdpview->currentWindow view] window] frame]; + + // let RDP server know where this window is located + mac_send_rail_client_event(inst->context->channels, RDP_EVENT_TYPE_RAIL_CLIENT_WINDOW_MOVE, &windowMove); + + // the event we just sent will cause an extra MoveWindow() to be invoked which we need to ignore + [g_mrdpview->currentWindow view]->skipMoveWindowOnce = YES; + + break; + + case RAIL_WMSZ_KEYMOVE: + printf("!!!! RAIL_WMSZ_KEYMOVE\n"); + break; + + case RAIL_WMSZ_KEYSIZE: + printf("!!!! RAIL_WMSZ_KEYSIZE\n"); + break; + + default: + break; + } + return; +} + +void mac_send_rail_client_event(rdpChannels *channels, uint16 event_type, void *param) +{ + RDP_EVENT *out_event = NULL; + void *payload = NULL; + + GOT_HERE; + + payload = rail_clone_order(event_type, param); + if (payload != NULL) { + out_event = freerdp_event_new(RDP_EVENT_CLASS_RAIL, event_type, + mac_on_free_rail_client_event, payload); + freerdp_channels_send_event(channels, out_event); + } +} + +void mac_on_free_rail_client_event(RDP_EVENT* event) +{ + GOT_HERE; + + if (event->event_class == RDP_EVENT_CLASS_RAIL) + { + rail_free_cloned_order(event->event_type, event->user_data); + } +} + +void mac_rail_enable_remoteapp_mode() +{ + GOT_HERE; + + if (!g_mrdpview->isRemoteApp) + g_mrdpview->isRemoteApp = TRUE; +} + +/** + * given a rect with 0,0 at the bottom left (apple cords) + * convert it to a rect with 0,0 at the top left (windows cords) + */ + +void apple_to_windows_cords(NSRect * r) +{ + r->origin.y = g_mrdpview->height - (r->origin.y + r->size.height); +} + +/** + * given a rect with 0,0 at the top left (windows cords) + * convert it to a rect with 0,0 at the bottom left (apple cords) + */ + +void windows_to_apple_cords(NSRect * r) +{ + r->origin.y = g_mrdpview->height - (r->origin.y + r->size.height); +} + +void apple_center_window(NSRect * r) +{ + r->origin.x = (g_mrdpview->width - r->size.width) / 2; + r->origin.y = (g_mrdpview->height - r->size.height) / 2; +} + +void apple_to_windowMove(NSRect * r, RAIL_WINDOW_MOVE_ORDER * windowMove) +{ + windowMove->left = (uint16) r->origin.x; // x-cord of top left corner + windowMove->top = (uint16) g_mrdpview->height - (r->origin.y + r->size.height); // y-cord of top left corner + windowMove->right = (uint16) (windowMove->left + r->size.width); // x-cord of bottom right corner + windowMove->bottom = (uint16) (windowMove->top + r->size.height); // y-cord of bottom right corner +} + +@end diff --git a/client/Mac/MRDPWindow.h b/client/Mac/MRDPWindow.h new file mode 100644 index 000000000..cef1ab09e --- /dev/null +++ b/client/Mac/MRDPWindow.h @@ -0,0 +1,15 @@ + +#import +#import "MRDPRailView.h" +#import "MRDPRailWindow.h" + +@interface MRDPWindow : NSObject +{ +} + +@property (assign) int windowID; +@property (retain) MRDPRailWindow * window; +@property (retain) MRDPRailView * view; + +@end + diff --git a/client/Mac/MRDPWindow.m b/client/Mac/MRDPWindow.m new file mode 100644 index 000000000..c99217692 --- /dev/null +++ b/client/Mac/MRDPWindow.m @@ -0,0 +1,11 @@ + +#include "MRDPWindow.h" + +@implementation MRDPWindow + +@synthesize windowID; +@synthesize window; +@synthesize view; + +@end + diff --git a/client/Mac/MainMenu.xib b/client/Mac/MainMenu.xib new file mode 100755 index 000000000..bff95a9d5 --- /dev/null +++ b/client/Mac/MainMenu.xib @@ -0,0 +1,796 @@ + + + + 1070 + 11D50b + 2177 + 1138.32 + 568.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 2177 + + + NSView + NSMenu + NSWindowTemplate + NSMenuItem + NSCustomView + IBNSLayoutConstraint + NSCustomObject + + + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + + + FreeRDP + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + FreeRDP + + + + About FreeRDP + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide FreeRDP + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit FreeRDP + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + + + + Edit + + 1048576 + 2147483647 + + + + + + Format + + 2147483647 + + + + + + View + + 1048576 + 2147483647 + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + + + Mac Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 15 + 2 + {{163, 10}, {1024, 768}} + 1954021376 + FreeRDP + NSWindow + + + {1024, 768} + {1024, 768} + + + 256 + + + + 268 + {1024, 768} + + + _NS:9 + MRDPView + + + {1024, 768} + + + + + {{0, 0}, {1366, 746}} + {1024, 790} + {1024, 790} + 128 + YES + + + AppDelegate + + + NSFontManager + + + + + + + terminate: + + + + 449 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + delegate + + + + 568 + + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + performZoom: + + + + 240 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + unhideAllApplications: + + + + 370 + + + + showHelp: + + + + 493 + + + + mrdpView + + + + 565 + + + + window + + + + 567 + + + + + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + + + + + + + + + + + + 19 + + + + + + + + 56 + + + + + + + + 217 + + + + + + 83 + + + + + + 57 + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 236 + + + + + 149 + + + + + 145 + + + + + 24 + + + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + + + + 371 + + + + + + + + 372 + + + + + + 6 + 0 + + 6 + 1 + + 0.0 + + 1000 + 8 + 29 + 3 + + + + + 5 + 0 + + 5 + 1 + + 0.0 + + 1000 + 8 + 29 + 3 + + + + + 4 + 0 + + 4 + 1 + + 0.0 + + 1000 + 8 + 29 + 3 + + + + + 3 + 0 + + 3 + 1 + + 0.0 + + 1000 + 8 + 29 + 3 + + + + + + + 375 + + + + + + 420 + + + + + 490 + + + + + + + + 491 + + + + + + + + 492 + + + + + 494 + + + + + 551 + + + + + 561 + + + + + 562 + + + + + 563 + + + + + 564 + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + com.apple.InterfaceBuilder.CocoaPlugin + {{380, 496}, {480, 360}} + + + + + + + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + + 568 + + + + + AppDelegate + NSObject + + MRDPView + NSWindow + + + + mrdpView + MRDPView + + + window + NSWindow + + + + IBProjectSource + ./Classes/AppDelegate.h + + + + MRDPView + NSView + + IBProjectSource + ./Classes/MRDPView.h + + + + NSLayoutConstraint + NSObject + + IBProjectSource + ./Classes/NSLayoutConstraint.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + YES + 3 + + {11, 11} + {10, 3} + + YES + + diff --git a/client/Mac/README.txt b/client/Mac/README.txt new file mode 100644 index 000000000..e523d02b4 --- /dev/null +++ b/client/Mac/README.txt @@ -0,0 +1,140 @@ + +------------------------------------------------------------------------- + Building FreeRDP on Mac OS X +------------------------------------------------------------------------- + +Platform: Lion with Xcode 4.3.2 + +------------------ + installing cmake +------------------ + +first install macports by googling for it, the run the following command +sudo port install cmake + +---------------- + installing gcc +---------------- +Click on Xcode->Preferences->Downloads +Click on Components +Click on Install Command line tools + +You will be prompted for your Apple Developer userid and password + +---------------------------------------- + download FreeRDP source code using git +---------------------------------------- + +mkdir ~/projects/A8 +cd ~/projects/A8 +git clone git://github.com/FreeRDP/FreeRDP.git + +------------------ + building FreeRDP +------------------ + +cd ~projects/A8/FreeRDP +cmake -DWITH_MACAUDIO=ON -DCMAKE_INSTALL_PREFIX="" +make +make install + +------------------------ + creating Xcode project +------------------------ + +Start xcode +Select 'Create a new xcode project' +In 'Choose a template for your new project', click on Mac OS X -> application +Click on 'Cocoa Application' +Click on next +I used the following: +Product Name: Mac +Company Identifier: com.freerdp +Check 'Automatic Reference Counting' +Create the project in your directory of choice + +------------------------------- + Adding files to your projects +------------------------------- + +Add the following files to your project: + +cd ~/projects/A8/FreeRDP/client/Mac/MRDPCursor.h +cd ~/projects/A8/FreeRDP/client/Mac/MRDPCursor.m +cd ~/projects/A8/FreeRDP/client/Mac/MRDPView.h +cd ~/projects/A8/FreeRDP/client/Mac/MRDPView.m + +This is what your AppDelegate.h file should like like + +#import +#import "MRDPView.h" + +@interface AppDelegate : NSObject + +@property (assign) IBOutlet NSWindow *window; +@property (assign) IBOutlet MRDPView *mrdpView; + +int rdp_connect(); + +@end + +This is what your AppDelegate.m file should like like + +#import "AppDelegate.h" + +@implementation AppDelegate + +@synthesize window = _window; +@synthesize mrdpView; + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + rdp_connect(); +} + +- (void) applicationWillTerminate:(NSNotification *)notification +{ + [mrdpView releaseResources]; +} + +@end + +---------------------------------- + Modifying your MainMenu.xib file +---------------------------------- + +In your project select MainMenu.xib and drag a NSView object intot the main window +Name the class MRDPView +In Interface Builder, select the Application Delegate and tie the mrdpview outlet to the NSView +Set the default size of the main window to 1024x768. This is FreeRDP's default resolution + +---------------------------- + Configuring build settings +---------------------------- + +In Project Navigator, click on Mac +Click on Targets -> Mac +Click on Build Phases +Click on 'Link Binary With Libraries' and click on the + button, then click on the 'Add Other' button to add the following dynamic libraries +~/projects/A8/FreeRDP/libfreerdp-core/libfreerdp-core.dylib +~/projects/A8/FreeRDP/libfreerdp-channels/libfreerdp-channels.dylilb +~/projects/A8/FreeRDP/libfreerdp-utils/libfreerdp-utils.dylib +~/projects/A8/FreeRDP/libfreerdp-codec/libfreerdp-codec.dylib +~/projects/A8/FreeRDP/libfreerdp-cache/libfreerdp-cache.dylib +~/projects/A8/FreeRDP/libfreerdp-gdi/libfreerdp-gdi.dylib + +Click on 'Build Settings' +In 'Search Paths -> Library Search Paths' set the following + Header Search Path Debug: ~/projects/A8/FreeRDP/include + Header Search Path Release: ~/projects/A8/FreeRDP/include + +TODO: in build settings, set strip build product to yes when done debugging + +--------------------------- + To deploy the application +--------------------------- + +in xcode, click on Product->Archive +Click on Distribute button +Select Export As -> application + diff --git a/client/Mac/main.m b/client/Mac/main.m new file mode 100644 index 000000000..a7ee86852 --- /dev/null +++ b/client/Mac/main.m @@ -0,0 +1,14 @@ +// +// main.m +// MacFreeRDP +// +// Created by Thomas Goddard on 5/8/12. +// Copyright (c) 2012 __MyCompanyName__. All rights reserved. +// + +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **)argv); +} diff --git a/client/Windows/CMakeLists.txt b/client/Windows/CMakeLists.txt index 68b46713f..e239f206a 100644 --- a/client/Windows/CMakeLists.txt +++ b/client/Windows/CMakeLists.txt @@ -1,36 +1,42 @@ -# FreeRDP: A Remote Desktop Protocol Client -# FreeRDP Windows cmake build script -# -# Copyright 2011 O.S. Systems Software Ltda. -# Copyright 2011 Otavio Salvador -# Copyright 2011 Marc-Andre Moreau -# -# 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. - -add_executable(wfreerdp WIN32 - wf_gdi.c - wf_gdi.h - wf_event.c - wf_event.h - wf_graphics.c - wf_graphics.h - wfreerdp.c - wfreerdp.h) - -target_link_libraries(wfreerdp freerdp-core) -target_link_libraries(wfreerdp freerdp-gdi) -target_link_libraries(wfreerdp freerdp-utils) -target_link_libraries(wfreerdp freerdp-codec) -target_link_libraries(wfreerdp freerdp-channels) - -install(TARGETS wfreerdp DESTINATION ${CMAKE_INSTALL_BINDIR}) +# FreeRDP: A Remote Desktop Protocol Client +# FreeRDP Windows cmake build script +# +# Copyright 2011 O.S. Systems Software Ltda. +# Copyright 2011 Otavio Salvador +# Copyright 2011 Marc-Andre Moreau +# +# 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. + +add_executable(wfreerdp WIN32 + wf_gdi.c + wf_gdi.h + wf_event.c + wf_event.h + wf_graphics.c + wf_graphics.h + wf_cliprdr.c + wf_cliprdr.h + wf_window.c + wf_window.h + wf_rail.c + wf_rail.h + wfreerdp.c + wfreerdp.h) + +target_link_libraries(wfreerdp freerdp-core) +target_link_libraries(wfreerdp freerdp-gdi) +target_link_libraries(wfreerdp freerdp-utils) +target_link_libraries(wfreerdp freerdp-codec) +target_link_libraries(wfreerdp freerdp-channels) + +install(TARGETS wfreerdp DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/client/Windows/wf_cliprdr.c b/client/Windows/wf_cliprdr.c new file mode 100644 index 000000000..5ce19004e --- /dev/null +++ b/client/Windows/wf_cliprdr.c @@ -0,0 +1,106 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Windows Clipboard Redirection + * + * Copyright 2012 Jason Champion + * + * 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 +#include +#include +#include + +#include "wf_cliprdr.h" + +void wf_cliprdr_init(wfInfo* wfi, rdpChannels* chanman) +{ + +} + +void wf_cliprdr_uninit(wfInfo* wfi) +{ + +} + +static void wf_cliprdr_process_cb_monitor_ready_event(wfInfo* wfi) +{ + +} + +static void wf_cliprdr_process_cb_data_request_event(wfInfo* wfi, RDP_CB_DATA_REQUEST_EVENT* event) +{ + +} + +static void wf_cliprdr_process_cb_format_list_event(wfInfo* wfi, RDP_CB_FORMAT_LIST_EVENT* event) +{ + +} + +static void wf_cliprdr_process_cb_data_response_event(wfInfo* wfi, RDP_CB_DATA_RESPONSE_EVENT* event) +{ + +} + +void wf_process_cliprdr_event(wfInfo* wfi, RDP_EVENT* event) +{ + switch (event->event_type) + { + case RDP_EVENT_TYPE_CB_MONITOR_READY: + wf_cliprdr_process_cb_monitor_ready_event(wfi); + break; + + case RDP_EVENT_TYPE_CB_FORMAT_LIST: + wf_cliprdr_process_cb_format_list_event(wfi, (RDP_CB_FORMAT_LIST_EVENT*) event); + break; + + case RDP_EVENT_TYPE_CB_DATA_REQUEST: + wf_cliprdr_process_cb_data_request_event(wfi, (RDP_CB_DATA_REQUEST_EVENT*) event); + break; + + case RDP_EVENT_TYPE_CB_DATA_RESPONSE: + wf_cliprdr_process_cb_data_response_event(wfi, (RDP_CB_DATA_RESPONSE_EVENT*) event); + break; + + default: + break; + } +} + +boolean wf_cliprdr_process_selection_notify(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + return true; +} + +boolean wf_cliprdr_process_selection_request(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + return true; +} + +boolean wf_cliprdr_process_selection_clear(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + return true; +} + +boolean wf_cliprdr_process_property_notify(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) +{ + return true; +} + +void wf_cliprdr_check_owner(wfInfo* wfi) +{ + +} + diff --git a/client/Windows/wf_cliprdr.h b/client/Windows/wf_cliprdr.h new file mode 100644 index 000000000..99e38b63f --- /dev/null +++ b/client/Windows/wf_cliprdr.h @@ -0,0 +1,33 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Windows Clipboard Redirection + * + * Copyright 2012 Jason Champion + * + * 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 __WF_CLIPRDR_H +#define __WF_CLIPRDR_H + +#include "wfreerdp.h" + +void wf_cliprdr_init(wfInfo* wfi, rdpChannels* chanman); +void wf_cliprdr_uninit(wfInfo* wfi); +void wf_process_cliprdr_event(wfInfo* wfi, RDP_EVENT* event); +boolean wf_cliprdr_process_selection_notify(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +boolean wf_cliprdr_process_selection_request(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +boolean wf_cliprdr_process_selection_clear(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +boolean wf_cliprdr_process_property_notify(wfInfo* wfi, HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam); +void wf_cliprdr_check_owner(wfInfo* wfi); + +#endif /* __WF_CLIPRDR_H */ diff --git a/client/Windows/wf_rail.c b/client/Windows/wf_rail.c new file mode 100644 index 000000000..6a831c9ba --- /dev/null +++ b/client/Windows/wf_rail.c @@ -0,0 +1,54 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Windows RAIL + * + * Copyright 2012 Jason Champion + * + * 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 +#include +#include +#include + +#include "wf_window.h" +#include "wf_rail.h" + +void wf_rail_paint(wfInfo* wfi, rdpRail* rail, sint32 uleft, sint32 utop, uint32 uright, uint32 ubottom) +{ +} + +void wf_rail_register_callbacks(wfInfo* wfi, rdpRail* rail) +{ +} + +void wf_rail_send_client_system_command(wfInfo* wfi, uint32 windowId, uint16 command) +{ +} + +void wf_rail_send_activate(wfInfo* wfi, HWND window, boolean enabled) +{ +} + +void wf_process_rail_event(wfInfo* wfi, rdpChannels* chanman, RDP_EVENT* event) +{ +} + +void wf_rail_adjust_position(wfInfo* wfi, rdpWindow *window) +{ +} + +void wf_rail_end_local_move(wfInfo* wfi, rdpWindow *window) +{ +} diff --git a/client/Windows/wf_rail.h b/client/Windows/wf_rail.h new file mode 100644 index 000000000..82683ef42 --- /dev/null +++ b/client/Windows/wf_rail.h @@ -0,0 +1,32 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Windows RAIL + * + * Copyright 2012 Jason Champion + * + * 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 __WF_RAIL_H +#define __WF_RAIL_H + +#include "wfreerdp.h" + +void wf_rail_paint(wfInfo* wfi, rdpRail* rail, sint32 uleft, sint32 utop, uint32 uright, uint32 ubottom); +void wf_rail_register_callbacks(wfInfo* wfi, rdpRail* rail); +void wf_rail_send_client_system_command(wfInfo* wfi, uint32 windowId, uint16 command); +void wf_rail_send_activate(wfInfo* wfi, HWND window, boolean enabled); +void wf_process_rail_event(wfInfo* wfi, rdpChannels* chanman, RDP_EVENT* event); +void wf_rail_adjust_position(wfInfo* wfi, rdpWindow *window); +void wf_rail_end_local_move(wfInfo* wfi, rdpWindow *window); + +#endif \ No newline at end of file diff --git a/client/Windows/wf_window.c b/client/Windows/wf_window.c new file mode 100644 index 000000000..670ddc1e0 --- /dev/null +++ b/client/Windows/wf_window.c @@ -0,0 +1,20 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Windows RAIL + * + * Copyright 2012 Jason Champion + * + * 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 "wf_window.h" \ No newline at end of file diff --git a/client/Windows/wf_window.h b/client/Windows/wf_window.h new file mode 100644 index 000000000..aeb54f4c6 --- /dev/null +++ b/client/Windows/wf_window.h @@ -0,0 +1,27 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Windows RAIL + * + * Copyright 2012 Jason Champion + * + * 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 __WF_WINDOW_H +#define __WF_WINDOW_H + +#include +#include + +#include "wfreerdp.h" + +#endif \ No newline at end of file diff --git a/client/Windows/wfreerdp.c b/client/Windows/wfreerdp.c index 2354aaecc..e1a8741cd 100644 --- a/client/Windows/wfreerdp.c +++ b/client/Windows/wfreerdp.c @@ -41,6 +41,7 @@ #include "wf_gdi.h" #include "wf_graphics.h" +#include "wf_cliprdr.h" #include "wfreerdp.h" @@ -383,6 +384,8 @@ boolean wf_post_connect(freerdp* instance) freerdp_channels_post_connect(instance->context->channels, instance); + wf_cliprdr_init(wfi, instance->context->channels); + return true; } diff --git a/client/X11/xf_graphics.c b/client/X11/xf_graphics.c index cf16820ac..a97fc86d1 100644 --- a/client/X11/xf_graphics.c +++ b/client/X11/xf_graphics.c @@ -25,6 +25,8 @@ #endif #include +#include +#include #include "xf_graphics.h" @@ -106,9 +108,17 @@ void xf_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap) } void xf_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, - uint8* data, int width, int height, int bpp, int length, boolean compressed) + uint8* data, int width, int height, int bpp, int length, + boolean compressed, int codec_id) { uint16 size; + RFX_MESSAGE* msg; + uint8* src; + uint8* dst; + int yindex; + int xindex; + xfInfo* xfi; + boolean status; size = width * height * (bpp + 7) / 8; @@ -117,20 +127,57 @@ void xf_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, else bitmap->data = (uint8*) xrealloc(bitmap->data, size); - if (compressed) + switch (codec_id) { - boolean status; + case CODEC_ID_NSCODEC: + printf("xf_Bitmap_Decompress: nsc not done\n"); + break; + case CODEC_ID_REMOTEFX: + xfi = ((xfContext*)context)->xfi; + rfx_context_set_pixel_format(xfi->rfx_context, RDP_PIXEL_FORMAT_B8G8R8A8); + msg = rfx_process_message(xfi->rfx_context, data, length); + if (msg == NULL) + { + printf("xf_Bitmap_Decompress: rfx Decompression Failed\n"); + } + else + { + for (yindex = 0; yindex < height; yindex++) + { + src = msg->tiles[0]->data + yindex * 64 * 4; + dst = bitmap->data + yindex * width * 3; + for (xindex = 0; xindex < width; xindex++) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } + } + rfx_message_free(xfi->rfx_context, msg); + } + break; + case CODEC_ID_JPEG: + if (!jpeg_decompress(data, bitmap->data, width, height, length, bpp)) + { + printf("xf_Bitmap_Decompress: jpeg Decompression Failed\n"); + } + break; + default: + if (compressed) + { + status = bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp); - status = bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp); - - if (status != true) - { - printf("Bitmap Decompression Failed\n"); - } - } - else - { - freerdp_image_flip(data, bitmap->data, width, height, bpp); + if (status == false) + { + printf("xf_Bitmap_Decompress: Bitmap Decompression Failed\n"); + } + } + else + { + freerdp_image_flip(data, bitmap->data, width, height, bpp); + } + break; } bitmap->compressed = false; diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 0e1951674..1f9f7d6da 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -3,6 +3,7 @@ option(WITH_NEON "Enable NEON optimization for rfx decoder" OFF) option(WITH_PROFILER "Compile profiler." OFF) option(WITH_SSE2_TARGET "Allow compiler to generate SSE2 instructions." OFF) option(WITH_SSE2 "Use SSE2 optimization." OFF) +option(WITH_JPEG "Use JPEG decoding." OFF) option(WITH_DEBUG_CERTIFICATE "Print certificate related debug messages." OFF) option(WITH_DEBUG_CHANNELS "Print channel manager debug messages." OFF) diff --git a/config.h.in b/config.h.in index 1dced6c30..2f19ea9d7 100644 --- a/config.h.in +++ b/config.h.in @@ -26,6 +26,7 @@ #cmakedefine WITH_SSE2 #cmakedefine WITH_NEON #cmakedefine WITH_NATIVE_SSPI +#cmakedefine WITH_JPEG /* Debug */ #cmakedefine WITH_DEBUG_CERTIFICATE diff --git a/freerdp.spec b/freerdp.spec index 369a283a7..445f132d8 100644 --- a/freerdp.spec +++ b/freerdp.spec @@ -97,6 +97,7 @@ cmake \ -DWITH_CUPS:BOOL=ON \ -DWITH_PCSC:BOOL=ON \ -DWITH_PULSEAUDIO:BOOL=ON \ + -DWITH_MACAUDIO:BOOL=ON \ -DWITH_X11:BOOL=ON \ -DWITH_XCURSOR:BOOL=ON \ -DWITH_XEXT:BOOL=ON \ diff --git a/include/freerdp/codec/jpeg.h b/include/freerdp/codec/jpeg.h new file mode 100644 index 000000000..36540d922 --- /dev/null +++ b/include/freerdp/codec/jpeg.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Compressed Bitmap + * + * Copyright 2012 Jay Sorg + * + * 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 __JPEG_H +#define __JPEG_H + +#include + +boolean +jpeg_decompress(uint8* input, uint8* output, int width, int height, int size, int bpp); + +#endif /* __BITMAP_H */ diff --git a/include/freerdp/constants.h b/include/freerdp/constants.h index 88b3c321f..c9bcf67c7 100644 --- a/include/freerdp/constants.h +++ b/include/freerdp/constants.h @@ -27,6 +27,7 @@ enum RDP_CODEC_ID { CODEC_ID_NONE = 0x00, CODEC_ID_NSCODEC = 0x01, + CODEC_ID_JPEG = 0x02, CODEC_ID_REMOTEFX = 0x03 }; diff --git a/include/freerdp/graphics.h b/include/freerdp/graphics.h index 033ff3a23..578feae3b 100644 --- a/include/freerdp/graphics.h +++ b/include/freerdp/graphics.h @@ -35,7 +35,8 @@ typedef void (*pBitmap_New)(rdpContext* context, rdpBitmap* bitmap); typedef void (*pBitmap_Free)(rdpContext* context, rdpBitmap* bitmap); typedef void (*pBitmap_Paint)(rdpContext* context, rdpBitmap* bitmap); typedef void (*pBitmap_Decompress)(rdpContext* context, rdpBitmap* bitmap, - uint8* data, int width, int height, int bpp, int length, boolean compressed); + uint8* data, int width, int height, int bpp, int length, + boolean compressed, int codec_id); typedef void (*pBitmap_SetSurface)(rdpContext* context, rdpBitmap* bitmap, boolean primary); struct rdp_bitmap diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 362037ff0..ab37336ef 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -398,8 +398,12 @@ struct rdp_settings ALIGN64 uint32 rfx_codec_id; /* 282 */ ALIGN64 uint32 ns_codec_id; /* 283 */ ALIGN64 uint32 rfx_codec_mode; /* 284 */ - ALIGN64 uint32 frame_acknowledge; /* 285 */ - ALIGN64 uint64 paddingM[296 - 286]; /* 286 */ + ALIGN64 boolean frame_acknowledge; /* 285 */ + ALIGN64 boolean jpeg_codec; /* 286 */ + ALIGN64 uint32 jpeg_codec_id; /* 287 */ + ALIGN64 uint32 jpeg_quality; /* 288 */ + ALIGN64 uint32 v3_codec_id; /* 289 */ + ALIGN64 uint64 paddingM[296 - 290]; /* 290 */ /* Recording */ ALIGN64 boolean dump_rfx; /* 296 */ diff --git a/libfreerdp-cache/bitmap.c b/libfreerdp-cache/bitmap.c index a382bfe0c..a87e9b550 100644 --- a/libfreerdp-cache/bitmap.c +++ b/libfreerdp-cache/bitmap.c @@ -18,6 +18,7 @@ */ #include +#include #include #include @@ -74,7 +75,8 @@ void update_gdi_cache_bitmap(rdpContext* context, CACHE_BITMAP_ORDER* cache_bitm bitmap->Decompress(context, bitmap, cache_bitmap->bitmapDataStream, cache_bitmap->bitmapWidth, cache_bitmap->bitmapHeight, - cache_bitmap->bitmapBpp, cache_bitmap->bitmapLength, cache_bitmap->compressed); + cache_bitmap->bitmapBpp, cache_bitmap->bitmapLength, + cache_bitmap->compressed, CODEC_ID_NONE); bitmap->New(context, bitmap); @@ -104,7 +106,8 @@ void update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cach bitmap->Decompress(context, bitmap, cache_bitmap_v2->bitmapDataStream, cache_bitmap_v2->bitmapWidth, cache_bitmap_v2->bitmapHeight, - cache_bitmap_v2->bitmapBpp, cache_bitmap_v2->bitmapLength, cache_bitmap_v2->compressed); + cache_bitmap_v2->bitmapBpp, cache_bitmap_v2->bitmapLength, + cache_bitmap_v2->compressed, CODEC_ID_NONE); bitmap->New(context, bitmap); @@ -116,6 +119,38 @@ void update_gdi_cache_bitmap_v2(rdpContext* context, CACHE_BITMAP_V2_ORDER* cach bitmap_cache_put(cache->bitmap, cache_bitmap_v2->cacheId, cache_bitmap_v2->cacheIndex, bitmap); } +void update_gdi_cache_bitmap_v3(rdpContext* context, CACHE_BITMAP_V3_ORDER* cache_bitmap_v3) +{ + rdpBitmap* bitmap; + rdpBitmap* prevBitmap; + rdpCache* cache = context->cache; + BITMAP_DATA_EX* bitmapData = &cache_bitmap_v3->bitmapData; + + bitmap = Bitmap_Alloc(context); + + Bitmap_SetDimensions(context, bitmap, bitmapData->width, bitmapData->height); + + if (cache_bitmap_v3->bitmapData.bpp == 0) + { + /* Workaround for Windows 8 bug where bitmapBpp is not set */ + cache_bitmap_v3->bitmapData.bpp = context->instance->settings->color_depth; + } + + bitmap->Decompress(context, bitmap, + bitmapData->data, bitmap->width, bitmap->height, + bitmapData->bpp, bitmapData->length, true, + bitmapData->codecID); + + bitmap->New(context, bitmap); + + prevBitmap = bitmap_cache_get(cache->bitmap, cache_bitmap_v3->cacheId, cache_bitmap_v3->cacheIndex); + + if (prevBitmap != NULL) + Bitmap_Free(context, prevBitmap); + + bitmap_cache_put(cache->bitmap, cache_bitmap_v3->cacheId, cache_bitmap_v3->cacheIndex, bitmap); +} + void update_gdi_bitmap_update(rdpContext* context, BITMAP_UPDATE* bitmap_update) { int i; @@ -149,7 +184,8 @@ void update_gdi_bitmap_update(rdpContext* context, BITMAP_UPDATE* bitmap_update) bitmap->Decompress(context, bitmap, bitmap_data->bitmapDataStream, bitmap_data->width, bitmap_data->height, - bitmap_data->bitsPerPixel, bitmap_data->bitmapLength, bitmap_data->compressed); + bitmap_data->bitsPerPixel, bitmap_data->bitmapLength, + bitmap_data->compressed, CODEC_ID_NONE); if (reused) bitmap->Free(context, bitmap); @@ -220,6 +256,7 @@ void bitmap_cache_register_callbacks(rdpUpdate* update) update->secondary->CacheBitmap = update_gdi_cache_bitmap; update->secondary->CacheBitmapV2 = update_gdi_cache_bitmap_v2; + update->secondary->CacheBitmapV3 = update_gdi_cache_bitmap_v3; update->BitmapUpdate = update_gdi_bitmap_update; } diff --git a/libfreerdp-cache/offscreen.c b/libfreerdp-cache/offscreen.c index cbc485f0c..526368a49 100644 --- a/libfreerdp-cache/offscreen.c +++ b/libfreerdp-cache/offscreen.c @@ -137,7 +137,7 @@ rdpOffscreenCache* offscreen_cache_new(rdpSettings* settings) offscreen_cache->currentSurface = SCREEN_BITMAP_SURFACE; offscreen_cache->maxSize = 7680; - offscreen_cache->maxEntries = 100; + offscreen_cache->maxEntries = 2000; settings->offscreen_bitmap_cache_size = offscreen_cache->maxSize; settings->offscreen_bitmap_cache_entries = offscreen_cache->maxEntries; diff --git a/libfreerdp-codec/CMakeLists.txt b/libfreerdp-codec/CMakeLists.txt index 4408ac2c3..69194daf8 100644 --- a/libfreerdp-codec/CMakeLists.txt +++ b/libfreerdp-codec/CMakeLists.txt @@ -43,7 +43,8 @@ set(FREERDP_CODEC_SRCS nsc_encode.h nsc_types.h mppc_dec.c - mppc_enc.c) + mppc_enc.c + jpeg.c) set(FREERDP_CODEC_SSE2_SRCS rfx_sse2.c @@ -72,10 +73,13 @@ if(WITH_NEON) set_property(SOURCE rfx_neon.c PROPERTY COMPILE_FLAGS "-mfpu=neon -mfloat-abi=softfp") endif() +if(WITH_JPEG) + set(FREERDP_JPEG_LIBS jpeg) +endif() + add_library(freerdp-codec ${FREERDP_CODEC_SRCS}) set_target_properties(freerdp-codec PROPERTIES VERSION ${FREERDP_VERSION_FULL} SOVERSION ${FREERDP_VERSION} PREFIX "lib") -target_link_libraries(freerdp-codec freerdp-utils) +target_link_libraries(freerdp-codec freerdp-utils ${FREERDP_JPEG_LIBS}) install(TARGETS freerdp-codec DESTINATION ${CMAKE_INSTALL_LIBDIR}) - diff --git a/libfreerdp-codec/jpeg.c b/libfreerdp-codec/jpeg.c new file mode 100644 index 000000000..eec5eb614 --- /dev/null +++ b/libfreerdp-codec/jpeg.c @@ -0,0 +1,154 @@ +/** + * FreeRDP: A Remote Desktop Protocol Client + * Compressed jpeg + * + * Copyright 2012 Jay Sorg + * + * 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 "config.h" +#include +#include +#include + +#ifdef WITH_JPEG + +#define HAVE_BOOLEAN +#include + +struct mydata_decomp +{ + char* data; + int data_bytes; +}; + +/*****************************************************************************/ +static void my_init_source(j_decompress_ptr cinfo) +{ +} + +/*****************************************************************************/ +static boolean my_fill_input_buffer(j_decompress_ptr cinfo) +{ + struct mydata_decomp* md; + + md = (struct mydata_decomp*)(cinfo->client_data); + cinfo->src->next_input_byte = (unsigned char*)(md->data); + cinfo->src->bytes_in_buffer = md->data_bytes; + return 1; +} + +/*****************************************************************************/ +static void my_skip_input_data(j_decompress_ptr cinfo, long num_bytes) +{ +} + +/*****************************************************************************/ +static boolean my_resync_to_restart(j_decompress_ptr cinfo, int desired) +{ + return 1; +} + +/*****************************************************************************/ +static void my_term_source(j_decompress_ptr cinfo) +{ +} + +/*****************************************************************************/ +static int +do_decompress(char* comp_data, int comp_data_bytes, + int* width, int* height, int* bpp, + char* decomp_data, int* decomp_data_bytes) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + struct jpeg_source_mgr src_mgr; + struct mydata_decomp md; + JSAMPROW row_pointer[1]; + + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + memset(&src_mgr, 0, sizeof(src_mgr)); + cinfo.src = &src_mgr; + src_mgr.init_source = my_init_source; + src_mgr.fill_input_buffer = my_fill_input_buffer; + src_mgr.skip_input_data = my_skip_input_data; + src_mgr.resync_to_restart = my_resync_to_restart; + src_mgr.term_source = my_term_source; + + memset(&md, 0, sizeof(md)); + md.data = comp_data; + md.data_bytes = comp_data_bytes; + cinfo.client_data = &md; + + jpeg_read_header(&cinfo, 1); + + cinfo.out_color_space = JCS_RGB; + + *width = cinfo.image_width; + *height = cinfo.image_height; + *bpp = cinfo.num_components * 8; + + jpeg_start_decompress(&cinfo); + + while(cinfo.output_scanline < cinfo.image_height) + { + row_pointer[0] = (JSAMPROW) decomp_data; + jpeg_read_scanlines(&cinfo, row_pointer, 1); + decomp_data += cinfo.image_width * cinfo.num_components; + } + *decomp_data_bytes = cinfo.output_width * + cinfo.output_height * cinfo.num_components; + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return 0; +} + +/* jpeg decompress */ +boolean +jpeg_decompress(uint8 * input, uint8 * output, int width, int height, int size, int bpp) +{ + int lwidth; + int lheight; + int lbpp; + int ldecomp_data_bytes; + + if (bpp != 24) + { + return 0; + } + if (do_decompress((char*)input, size, + &lwidth, &lheight, &lbpp, + (char*)output, &ldecomp_data_bytes) != 0) + { + return 0; + } + if (lwidth != width || lheight != height || lbpp != bpp) + { + return 0; + } + return 1; +} + +#else + +boolean +jpeg_decompress(uint8 * input, uint8 * output, int width, int height, int size, int bpp) +{ + return 0; +} + +#endif diff --git a/libfreerdp-core/capabilities.c b/libfreerdp-core/capabilities.c index 0a1811f9c..85480f02c 100644 --- a/libfreerdp-core/capabilities.c +++ b/libfreerdp-core/capabilities.c @@ -62,6 +62,9 @@ static const char* const CAPSET_TYPE_STRINGS[] = /* CODEC_GUID_NSCODEC 0xCA8D1BB9000F154F589FAE2D1A87E2D6 */ #define CODEC_GUID_NSCODEC "\xb9\x1b\x8d\xca\x0f\x00\x4f\x15\x58\x9f\xae\x2d\x1a\x87\xe2\xd6" +/* CODEC_GUID_JPEG 0x430C9EED1BAF4CE6869ACB8B37B66237*/ +#define CODEC_GUID_JPEG "\xE6\x4C\xAF\x1B\xED\x9E\x0C\x43\x86\x9A\xCB\x8B\x37\xB6\x62\x37" + void rdp_read_capability_set_header(STREAM* s, uint16* length, uint16* type) { stream_read_uint16(s, *type); /* capabilitySetType */ @@ -1521,6 +1524,12 @@ void rdp_write_nsc_client_capability_container(STREAM* s, rdpSettings* settings) stream_write_uint8(s, 3); /* colorLossLevel */ } +void rdp_write_jpeg_client_capability_container(STREAM* s, rdpSettings* settings) +{ + stream_write_uint16(s, 1); /* codecPropertiesLength */ + stream_write_uint8(s, settings->jpeg_quality); +} + /** * Write RemoteFX Server Capability Container.\n * @param s stream @@ -1532,6 +1541,12 @@ void rdp_write_rfx_server_capability_container(STREAM* s, rdpSettings* settings) stream_write_uint32(s, 0); /* reserved */ } +void rdp_write_jpeg_server_capability_container(STREAM* s, rdpSettings* settings) +{ + stream_write_uint16(s, 1); /* codecPropertiesLength */ + stream_write_uint8(s, 75); +} + /** * Write NSCODEC Server Capability Container.\n * @param s stream @@ -1563,6 +1578,8 @@ void rdp_write_bitmap_codecs_capability_set(STREAM* s, rdpSettings* settings) bitmapCodecCount++; if (settings->ns_codec) bitmapCodecCount++; + if (settings->jpeg_codec) + bitmapCodecCount++; stream_write_uint8(s, bitmapCodecCount); @@ -1595,6 +1612,20 @@ void rdp_write_bitmap_codecs_capability_set(STREAM* s, rdpSettings* settings) rdp_write_nsc_client_capability_container(s, settings); } } + if (settings->jpeg_codec) + { + stream_write(s, CODEC_GUID_JPEG, 16); + if (settings->server_mode) + { + stream_write_uint8(s, 0); /* codecID is defined by the client */ + rdp_write_jpeg_server_capability_container(s, settings); + } + else + { + stream_write_uint8(s, CODEC_ID_JPEG); /* codecID */ + rdp_write_jpeg_client_capability_container(s, settings); + } + } rdp_capability_set_finish(s, header, CAPSET_TYPE_BITMAP_CODECS); } @@ -1616,6 +1647,21 @@ void rdp_read_frame_acknowledge_capability_set(STREAM* s, uint16 length, rdpSett } } +void rdp_read_bitmap_cache_v3_codec_id_capability_set(STREAM* s, uint16 length, rdpSettings* settings) +{ + stream_seek_uint8(s); /* (1 byte) */ +} + +void rdp_write_bitmap_cache_v3_codec_id_capability_set(STREAM* s, rdpSettings* settings) +{ + uint8* header; + + header = rdp_capability_set_start(s); + stream_write_uint8(s, settings->v3_codec_id); + rdp_capability_set_finish(s, header, 6); +} + + /** * Write frame acknowledge capability set.\n * @param s stream @@ -1768,6 +1814,10 @@ boolean rdp_read_capability_sets(STREAM* s, rdpSettings* settings, uint16 number rdp_read_frame_acknowledge_capability_set(s, length, settings); break; + case 6: + rdp_read_bitmap_cache_v3_codec_id_capability_set(s, length, settings); + break; + default: printf("unknown capability type %d\n", type); break; @@ -2076,6 +2126,15 @@ void rdp_write_confirm_active(STREAM* s, rdpSettings* settings) } } + if (settings->received_caps[6]) + { + if (settings->v3_codec_id != 0) + { + numberCapabilities++; + rdp_write_bitmap_cache_v3_codec_id_capability_set(s, settings); + } + } + stream_get_mark(s, em); stream_set_mark(s, lm); /* go back to lengthCombinedCapabilities */ @@ -2098,4 +2157,3 @@ boolean rdp_send_confirm_active(rdpRdp* rdp) return rdp_send_pdu(rdp, s, PDU_TYPE_CONFIRM_ACTIVE, rdp->mcs->user_id); } - diff --git a/libfreerdp-core/settings.c b/libfreerdp-core/settings.c index 56f01f0e1..071398fcc 100644 --- a/libfreerdp-core/settings.c +++ b/libfreerdp-core/settings.c @@ -240,7 +240,7 @@ rdpSettings* settings_new(void* instance) settings->offscreen_bitmap_cache = true; settings->offscreen_bitmap_cache_size = 7680; - settings->offscreen_bitmap_cache_entries = 100; + settings->offscreen_bitmap_cache_entries = 2000; settings->draw_nine_grid_cache_size = 2560; settings->draw_nine_grid_cache_entries = 256; diff --git a/libfreerdp-gdi/graphics.c b/libfreerdp-gdi/graphics.c index 04816334c..565ff46ea 100644 --- a/libfreerdp-gdi/graphics.c +++ b/libfreerdp-gdi/graphics.c @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -87,9 +89,17 @@ void gdi_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap) } void gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, - uint8* data, int width, int height, int bpp, int length, boolean compressed) + uint8* data, int width, int height, int bpp, int length, + boolean compressed, int codec_id) { uint16 size; + RFX_MESSAGE* msg; + uint8* src; + uint8* dst; + int yindex; + int xindex; + rdpGdi* gdi; + boolean status; size = width * height * (bpp + 7) / 8; @@ -98,21 +108,59 @@ void gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, else bitmap->data = (uint8*) xrealloc(bitmap->data, size); - if (compressed) + switch (codec_id) { - boolean status; - - status = bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp); - - if (status != true) - { - printf("Bitmap Decompression Failed\n"); - } - } - else - { - freerdp_image_flip(data, bitmap->data, width, height, bpp); + case CODEC_ID_NSCODEC: + printf("gdi_Bitmap_Decompress: nsc not done\n"); + break; + case CODEC_ID_REMOTEFX: + gdi = context->gdi; + rfx_context_set_pixel_format(gdi->rfx_context, RDP_PIXEL_FORMAT_B8G8R8A8); + msg = rfx_process_message(gdi->rfx_context, data, length); + if (msg == NULL) + { + printf("gdi_Bitmap_Decompress: rfx Decompression Failed\n"); + } + else + { + for (yindex = 0; yindex < height; yindex++) + { + src = msg->tiles[0]->data + yindex * 64 * 4; + dst = bitmap->data + yindex * width * 3; + for (xindex = 0; xindex < width; xindex++) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } + } + rfx_message_free(gdi->rfx_context, msg); + } + break; + case CODEC_ID_JPEG: +#ifdef WITH_JPEG + if (!jpeg_decompress(data, bitmap->data, width, height, length, bpp)) + { + printf("gdi_Bitmap_Decompress: jpeg Decompression Failed\n"); + } +#endif + break; + default: + if (compressed) + { + status = bitmap_decompress(data, bitmap->data, width, height, length, bpp, bpp); + if (status == false) + { + printf("gdi_Bitmap_Decompress: Bitmap Decompression Failed\n"); + } + } + else + { + freerdp_image_flip(data, bitmap->data, width, height, bpp); + } + break; } bitmap->width = width; @@ -236,4 +284,3 @@ void gdi_register_graphics(rdpGraphics* graphics) graphics_register_glyph(graphics, glyph); xfree(glyph); } - diff --git a/libfreerdp-gdi/graphics.h b/libfreerdp-gdi/graphics.h index 3940a44be..930c5e860 100644 --- a/libfreerdp-gdi/graphics.h +++ b/libfreerdp-gdi/graphics.h @@ -28,7 +28,8 @@ HGDI_BITMAP gdi_create_bitmap(rdpGdi* gdi, int width, int height, int bpp, uint8 void gdi_Bitmap_New(rdpContext* context, rdpBitmap* bitmap); void gdi_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap); void gdi_Bitmap_Decompress(rdpContext* context, rdpBitmap* bitmap, - uint8* data, int width, int height, int bpp, int length, boolean compressed); + uint8* data, int width, int height, int bpp, int length, + boolean compressed, int codec_id); void gdi_register_graphics(rdpGraphics* graphics); #endif /* __GDI_GRAPHICS_H */ diff --git a/libfreerdp-utils/args.c b/libfreerdp-utils/args.c index 89a1aa1ba..87df89fb1 100644 --- a/libfreerdp-utils/args.c +++ b/libfreerdp-utils/args.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -112,11 +113,16 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, " --gdi: graphics rendering (hw, sw)\n" " --no-osb: disable offscreen bitmaps\n" " --no-bmp-cache: disable bitmap cache\n" + " --bcv3: codec for bitmap cache v3 (rfx, nsc, jpeg)\n" " --plugin: load a virtual channel plugin\n" " --rfx: enable RemoteFX\n" " --rfx-mode: RemoteFX operational flags (v[ideo], i[mage]), default is video\n" " --frame-ack: number of frames pending to be acknowledged, default is 2 (disable with 0)\n" " --nsc: enable NSCodec (experimental)\n" +#ifdef WITH_JPEG + " --jpeg: enable jpeg codec, uses 75 quality\n" + " --jpegex: enable jpeg and set quality(1..99)\n" +#endif " --disable-wallpaper: disables wallpaper\n" " --composition: enable desktop composition\n" " --disable-full-window-drag: disables full window drag\n" @@ -385,6 +391,61 @@ int freerdp_parse_args(rdpSettings* settings, int argc, char** argv, return FREERDP_ARGS_PARSE_FAILURE; } } + else if (strcmp("--bcv3", argv[index]) == 0) + { + index++; + if (index == argc) + { + printf("missing codec name\n"); + return FREERDP_ARGS_PARSE_FAILURE; + } + settings->bitmap_cache_v3 = true; + if (strcmp("rfx", argv[index]) == 0) + { + printf("setting rfx\n"); + settings->v3_codec_id = CODEC_ID_REMOTEFX; + settings->rfx_codec = true; + } + else if (strcmp("nsc", argv[index]) == 0) + { + printf("setting codec nsc\n"); + settings->v3_codec_id = CODEC_ID_NSCODEC; + settings->ns_codec = true; + } +#ifdef WITH_JPEG + else if (strcmp("jpeg", argv[index]) == 0) + { + printf("setting codec jpeg\n"); + settings->v3_codec_id = CODEC_ID_JPEG; + settings->jpeg_codec = true; + if (settings->jpeg_quality == 0) + settings->jpeg_quality = 75; + } +#endif + else + { + printf("bad codec name\n"); + return FREERDP_ARGS_PARSE_FAILURE; + } + } +#ifdef WITH_JPEG + else if (strcmp("--jpeg", argv[index]) == 0) + { + settings->jpeg_codec = true; + settings->jpeg_quality = 75; + } + else if (strcmp("--jpegex", argv[index]) == 0) + { + index++; + if (index == argc) + { + printf("missing codec name\n"); + return FREERDP_ARGS_PARSE_FAILURE; + } + settings->jpeg_codec = true; + settings->jpeg_quality = atoi(argv[index]); + } +#endif else if (strcmp("--rfx", argv[index]) == 0) { settings->rfx_codec = true; diff --git a/xcode.sh b/xcode.sh new file mode 100755 index 000000000..eebfed050 --- /dev/null +++ b/xcode.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# Xcode generated files directory +XCODE_PROJ_DIR=xcode +# MacFreeRDP client directory +CLIENT_MAC_DIR=./client/Mac/ +pushd . + +GEN='Xcode' + +# Build settings +ARCH=-DCMAKE_OSX_ARCHITECTURES="${CMAKE_OSX_ARCHITECTURES:-i386;x86_64}" +BUILDTYPE=-DCMAKE_BUILD_TYPE="${CMAKE_BUILD_TYPE:Debug}" +MANPAGES=-DWITH_MANPAGES="${WITHMANPAGES:NO}" + +# Run cmake for FreeRDP and MacFreeRDP +mkdir ${XCODE_PROJ_DIR} >/dev/null 2>&1 +pushd ${XCODE_PROJ_DIR} +cmake ${BUILDTYPE} -G "$GEN" ${ARCH} ../ +popd +mkdir ${CLIENT_MAC_DIR}/${XCODE_PROJ_DIR} >/dev/null 2>&1 +pushd ${CLIENT_MAC_DIR}/${XCODE_PROJ_DIR} +cmake ${BUILDTYPE} -G "$GEN" ${ARCH} ../ +popd + +# Check for errors; otherwise, ask for compile. +if [ "$?" -ne 0 ]; then + echo "CMake failed. Please check error messages" + popd > /dev/null + exit +else + popd + while true + do + echo -n "Compile FreeRDP? (y or n) - (y recommended for MacFreeRDP compilation):" + read CONFIRM + case $CONFIRM in + y|Y|YES|yes|Yes) + pushd ./${XCODE_PROJ_DIR} + xcodebuild + popd + break ;; + n|N|no|NO|No) + echo OK - you entered $CONFIRM + break + ;; + *) echo Please enter only y or n + esac + done + + echo "SUCCESS!" + while true + do + echo -n "Open Xcode projects now? (y or n):" + read CONFIRM + case $CONFIRM in + y|Y|YES|yes|Yes) + open ${CLIENT_MAC_DIR}/${XCODE_PROJ_DIR}/MacFreeRDP.xcodeproj + open ./${XCODE_PROJ_DIR}/FreeRDP.xcodeproj + break ;; + n|N|no|NO|No) + echo OK - $CONFIRM + break + ;; + *) echo Please enter only y or n + esac + done + + echo -n "NOTE: Dragging FreeRDP project from finder onto the MacFreeRDP project in Xcode + will enable code stepping from MacFreeRDP into FreeRDP. +" +fi \ No newline at end of file