Initial push for the native Mac FreeRDP client

This commit is contained in:
Laxmikant Rashinkar 2012-05-02 20:15:27 -07:00
parent 17d5e65940
commit 1cbf3dab21
11 changed files with 2129 additions and 1 deletions

View File

@ -111,6 +111,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)

View File

@ -37,3 +37,7 @@ if(WITH_PULSEAUDIO)
add_subdirectory(pulse)
endif()
if(WITH_MACAUDIO)
add_subdirectory(mac_audio)
endif()

View File

@ -0,0 +1,36 @@
# FreeRDP: A Remote Desktop Protocol Client
# FreeRDP cmake build script
#
# Copyright 2012 Laxmikant Rashinkar <LK.Rashinkar@gmail.com>
# Copyright 2011 O.S. Systems Software Ltda.
# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set(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})

View File

@ -0,0 +1,241 @@
/**
* FreeRDP: A Remote Desktop Protocol client.
* Audio Output Virtual Channel
*
* Copyright 2012 Laxmikant Rashinkar <LK.Rashinkar@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Use AudioQueue to implement audio redirection
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <freerdp/types.h>
#include <freerdp/utils/memory.h>
#include <freerdp/utils/dsp.h>
#include <freerdp/utils/svc_plugin.h>
#include <AudioToolbox/AudioToolbox.h>
#include <AudioToolbox/AudioQueue.h>
#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;
}

View File

@ -525,7 +525,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)

25
client/Mac/MRDPCursor.h Normal file
View File

@ -0,0 +1,25 @@
//
// MRDPCursor.h
// MacFreeRDP
//
// Created by Laxmikant Rashinkar on 3/28/12.
// Copyright (c) 2012 FreeRDP.org All rights reserved.
//
#import <Foundation/Foundation.h>
#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

12
client/Mac/MRDPCursor.m Normal file
View File

@ -0,0 +1,12 @@
//
// MRDPCursor.m
// MacFreeRDP
//
// Created by Laxmikant Rashinkar on 3/28/12.
// Copyright (c) 2012 FreeRDP.org All rights reserved.
//
#import "MRDPCursor.h"
@implementation MRDPCursor
@end

144
client/Mac/MRDPView.h Normal file
View File

@ -0,0 +1,144 @@
//
// MRDPView.h
// MacFreeRDP
//
// Created by Laxmikant Rashinkar on 3/28/12.
// Copyright (c) 2012 FreeRDP.org All rights reserved.
//
#import <Cocoa/Cocoa.h>
typedef int boolean;
#include "freerdp/freerdp.h"
#include "freerdp/types.h"
#include "freerdp/channels/channels.h"
#include "freerdp/gdi/gdi.h"
#include "freerdp/graphics.h"
#include "freerdp/utils/event.h"
#include "freerdp/plugins/cliprdr.h"
#include "freerdp/utils/args.h"
@interface MRDPView : NSView
{
CFRunLoopSourceRef run_loop_src;
CFRunLoopSourceRef run_loop_src_channels;
NSBitmapImageRep *bmiRep;
NSMutableArray *cursors;
NSTimer *pasteboard_timer;
NSRect rect;
freerdp *rdp_instance;
rdpContext *rdp_context;
boolean mouseInClientArea;
char *pixel_data;
int width;
int height;
int argc;
char **argv;
// 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) saveStateInfo :(freerdp *) instance :(rdpContext *) context;
- (BOOL) eventIsInClientArea :(NSEvent *) event :(int *) xptr :(int *) yptr;
- (void) onPasteboardTimerFired :(NSTimer *) timer;
- (void) my_draw_rect :(void *) context;
- (void) releaseResources;
@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);
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);
/* LK_TODO
int freerdp_parse_args(rdpSettings* settings, int argc, char** argv,
ProcessPluginArgs plugin_callback, void* plugin_user_data,
ProcessUIArgs ui_callback, void* ui_user_data);
*/
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;
};

1519
client/Mac/MRDPView.m Normal file

File diff suppressed because it is too large Load Diff

140
client/Mac/README.txt Normal file
View File

@ -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="</path/to/your/staging/dir>"
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 <Cocoa/Cocoa.h>
#import "MRDPView.h"
@interface AppDelegate : NSObject <NSApplicationDelegate>
@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

View File

@ -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 \