Skip to content

Commit

Permalink
Merge pull request #19 from semvis123/master
Browse files Browse the repository at this point in the history
added MacOS native gui version
  • Loading branch information
Plutoberth committed Feb 11, 2021
2 parents 36e8d90 + 2fe3c80 commit c03ccd4
Show file tree
Hide file tree
Showing 15 changed files with 1,087 additions and 2 deletions.
4 changes: 4 additions & 0 deletions Client/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ inline constexpr char END_MARKER{ 60 };
inline constexpr auto MAC_ADDR_STR_SIZE = 17;

inline constexpr auto SERVICE_UUID = "96CC203E-5068-46ad-B32D-E316F5E069BA";
inline unsigned char SERVICE_UUID_IN_BYTES[] = { // this is the SERVICE_UUID but in bytes
0x96, 0xcc, 0x20, 0x3e, 0x50, 0x68, 0x46, 0xad,
0xb3, 0x2d, 0xe3, 0x16, 0xf5, 0xe0, 0x69, 0xba
};

inline constexpr auto APP_NAME_W = L"Sony Headphones App";

Expand Down
17 changes: 17 additions & 0 deletions Client/macos/AppDelegate.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// AppDelegate.h
// SonyHeadphonesClient
//
// Created by Sem Visscher on 01/12/2020.
//

#import <Cocoa/Cocoa.h>
#import <stdio.h>
#import <IOBluetoothUI/IOBluetoothUI.h>
#import "BluetoothWrapper.h"
#import "MacOSBluetoothConnector.h"

@interface AppDelegate : NSObject <NSApplicationDelegate>
@property (weak) NSWindow* window;

@end
35 changes: 35 additions & 0 deletions Client/macos/AppDelegate.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// AppDelegate.m
// SonyHeadphonesClient
//
// Created by Sem Visscher on 01/12/2020.
//

#import "AppDelegate.h"

@interface AppDelegate ()


@end

@implementation AppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
_window = [[[NSApplication sharedApplication] windows] firstObject];
}

- (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)flag
{
if (flag) {
return NO;
}
else {
[_window makeKeyAndOrderFront:self];
return YES;
}
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
// Insert code here to tear down your application
}

@end
35 changes: 35 additions & 0 deletions Client/macos/MacOSBluetoothConnector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once
#include <stdio.h>
#include "../IBluetoothConnector.h"
#include "IOBluetooth/IOBluetooth.h"
#include "Constants.h"
#include <thread>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <deque>

class MacOSBluetoothConnector final : public IBluetoothConnector
{
public:
MacOSBluetoothConnector();
~MacOSBluetoothConnector();
static void connectToMac(MacOSBluetoothConnector* MacOSBluetoothConnector) noexcept(false);
virtual void connect(const std::string& addrStr) noexcept(false);
virtual int send(char* buf, size_t length) noexcept(false);
virtual int recv(char* buf, size_t length) noexcept(false);
virtual void disconnect() noexcept;
virtual bool isConnected() noexcept;
virtual void closeConnection();

virtual std::vector<BluetoothDevice> getConnectedDevices() noexcept(false);
std::deque<std::vector<unsigned char>> receivedBytes;
std::mutex receiveDataMutex;
std::condition_variable receiveDataConditionVariable;
std::atomic<bool> running = false;

private:
void *rfcommDevice;
void *rfcommchannel;
std::thread* uthread = NULL;
};
158 changes: 158 additions & 0 deletions Client/macos/MacOSBluetoothConnector.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include "MacOSBluetoothConnector.h"

MacOSBluetoothConnector::MacOSBluetoothConnector()
{

}
MacOSBluetoothConnector::~MacOSBluetoothConnector()
{
// onclose event
if (isConnected()){
disconnect();
}
}

@interface AsyncCommDelegate : NSObject <IOBluetoothRFCOMMChannelDelegate> {
@public
MacOSBluetoothConnector* delegateCPP;
}
@end

@implementation AsyncCommDelegate {
}
- (void)rfcommChannelClosed:(IOBluetoothRFCOMMChannel *)rfcommChannel{
delegateCPP->disconnect();
}

-(void)rfcommChannelData:(IOBluetoothRFCOMMChannel *)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength
{
std::lock_guard<std::mutex> g(delegateCPP->receiveDataMutex);

unsigned char* buffer = (unsigned char*)dataPointer;
std::vector<unsigned char> vectorBuffer(buffer, buffer+dataLength);

delegateCPP->receivedBytes.push_back(vectorBuffer);
delegateCPP->receiveDataConditionVariable.notify_one();
}


@end

int MacOSBluetoothConnector::send(char* buf, size_t length)
{
[(__bridge IOBluetoothRFCOMMChannel*)rfcommchannel writeSync:(void*)buf length:length];
return (int)length;
}


void MacOSBluetoothConnector::connectToMac(MacOSBluetoothConnector* macOSBluetoothConnector)
{
// get device
IOBluetoothDevice *device = (__bridge IOBluetoothDevice *)macOSBluetoothConnector->rfcommDevice;
// create new channel
IOBluetoothRFCOMMChannel *channel = [[IOBluetoothRFCOMMChannel alloc] init];
// create sppServiceid
IOBluetoothSDPUUID *sppServiceUUID = [IOBluetoothSDPUUID uuidWithBytes:(void*)SERVICE_UUID_IN_BYTES length: 16];
// get sppServiceRecord
IOBluetoothSDPServiceRecord *sppServiceRecord = [device getServiceRecordForUUID:sppServiceUUID];
// get rfcommChannelID from sppServiceRecord
UInt8 rfcommChannelID;
[sppServiceRecord getRFCOMMChannelID:&rfcommChannelID];
// setup delegate
AsyncCommDelegate* asyncCommDelegate = [[AsyncCommDelegate alloc] init];
asyncCommDelegate->delegateCPP = macOSBluetoothConnector;
// try to open channel
if ( [device openRFCOMMChannelAsync:&channel withChannelID:rfcommChannelID delegate:asyncCommDelegate] != kIOReturnSuccess ) {
throw RecoverableException("Error - could not open the rfcomm.\n", true);
}
// store the channel
macOSBluetoothConnector->rfcommchannel = (__bridge void*) channel;

macOSBluetoothConnector->running = true;
// keep thread running
while (macOSBluetoothConnector->running) {
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:.1]];
}
}
void MacOSBluetoothConnector::connect(const std::string& addrStr){
// convert mac adress to nsstring
NSString *addressNSString = [NSString stringWithCString:addrStr.c_str() encoding:[NSString defaultCStringEncoding]];
// get device based on mac adress
IOBluetoothDevice *device = [IOBluetoothDevice deviceWithAddressString:addressNSString];
// if device is not connected
if (![device isConnected]) {
[device openConnection];
}
// store the device in an variable
rfcommDevice = (__bridge void*) device;
uthread = new std::thread(MacOSBluetoothConnector::connectToMac, this);
}

int MacOSBluetoothConnector::recv(char* buf, size_t length)
{
// wait for newly received data
std::unique_lock<std::mutex> g(receiveDataMutex);
receiveDataConditionVariable.wait(g, [this]{ return !receivedBytes.empty(); });

// fill the buf with the new data
std::vector<unsigned char> receivedVector = receivedBytes.front();
receivedBytes.pop_front();

size_t lengthCopied = std::min(length, receivedVector.size());

// copy the first amount of bytes
std::memcpy(buf, receivedVector.data(), lengthCopied);

// too much data, save it for next time
if (receivedVector.size() > length){
receivedVector.erase(receivedVector.begin(), receivedVector.begin() + lengthCopied);
receivedBytes.push_front(receivedVector);
}

return (int)lengthCopied;
}

std::vector<BluetoothDevice> MacOSBluetoothConnector::getConnectedDevices()
{
// create the output vector
std::vector<BluetoothDevice> res;
// loop through the paired devices (also includes non paired devices for some reason)
for (IOBluetoothDevice* device in [IOBluetoothDevice pairedDevices]) {
// check if device is connected
if ([device isConnected]) {
BluetoothDevice dev;
// save the mac address and name
dev.mac = [[device addressString]UTF8String];
dev.name = [[device name] UTF8String];
// add device to the connected devices vector
res.push_back(dev);
}
}

return res;
}

void MacOSBluetoothConnector::disconnect() noexcept
{
running = false;
// wait for the thread to finish
uthread->join();
// close connection
closeConnection();
}
void MacOSBluetoothConnector::closeConnection() {
// get the channel
IOBluetoothRFCOMMChannel *chan = (__bridge IOBluetoothRFCOMMChannel*) rfcommchannel;
[chan setDelegate: nil];
// close the channel
[chan closeChannel];
}


bool MacOSBluetoothConnector::isConnected() noexcept
{
if (!running)
return false;
IOBluetoothRFCOMMChannel *chan = (__bridge IOBluetoothRFCOMMChannel*) rfcommchannel;
return chan.isOpen;
}
Loading

0 comments on commit c03ccd4

Please sign in to comment.