Skip to content

Commit

Permalink
UTM: initial display works
Browse files Browse the repository at this point in the history
  • Loading branch information
osy86 committed Apr 18, 2019
1 parent 520fa5a commit ad5a688
Show file tree
Hide file tree
Showing 23 changed files with 482 additions and 122 deletions.
7 changes: 3 additions & 4 deletions CocoaSpice/CSConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,15 @@ NS_ASSUME_NONNULL_BEGIN
@interface CSConnection : NSObject

@property (nonatomic, readonly) NSArray<NSArray<CSDisplayMetal *> *> *monitors;
@property (nonatomic, readonly) CSDisplayMetal *firstDisplay;
@property (nonatomic, weak) id<CSConnectionDelegate> delegate;
@property (nonatomic, weak, nullable) id<CSConnectionDelegate> delegate;
@property (nonatomic, copy) NSString *host;
@property (nonatomic, copy) NSString *port;
@property (nonatomic, assign) BOOL audioEnabled;

+ (void)spiceSetDebug:(BOOL)enabled;
- (id)initWithHost:(NSString *)host port:(NSString *)port;
- (void)connectWithCompletion:(void (^)(BOOL))completion;
- (void)disconnectWithCompletion:(void (^)(BOOL))completion;
- (BOOL)connect;
- (void)disconnect;

@end

Expand Down
98 changes: 61 additions & 37 deletions CocoaSpice/CSConnection.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,46 @@
#import <glib.h>
#import <spice-client.h>

#define GLIB_OBJC_RETAIN(x) (__bridge_retained void *)(x)
#define GLIB_OBJC_RELEASE(x) (__bridge void *)(__bridge_transfer NSObject *)(__bridge void *)(x)

@implementation CSConnection {
SpiceSession *_session;
SpiceMainChannel *_main;
SpiceAudio *_audio;
NSMutableArray<NSMutableArray<CSDisplayMetal *> *> *_monitors;
void (^_connectionCallback)(BOOL);
}

static void logHandler(const gchar *log_domain, GLogLevelFlags log_level,
const gchar *message, gpointer user_data)
{
GDateTime *now;
gchar *dateTimeStr;

char* levelStr = "UNKNOWN";
if (log_level & G_LOG_LEVEL_ERROR) {
levelStr = "ERROR";
} else if (log_level & G_LOG_LEVEL_CRITICAL) {
levelStr = "CRITICAL";
} else if (log_level & G_LOG_LEVEL_WARNING) {
levelStr = "WARNING";
} else if (log_level & G_LOG_LEVEL_MESSAGE) {
levelStr = "MESSAGE";
} else if (log_level & G_LOG_LEVEL_INFO) {
levelStr = "INFO";
} else if (log_level & G_LOG_LEVEL_DEBUG) {
levelStr = "DEBUG";
}

now = g_date_time_new_now_local();
dateTimeStr = g_date_time_format(now, "%Y-%m-%d %T");

fprintf(stderr, "%s,%03d %s %s-%s\n", dateTimeStr,
g_date_time_get_microsecond(now) / 1000, levelStr,
log_domain, message);

g_date_time_unref(now);
g_free(dateTimeStr);
}

static void cs_main_channel_event(SpiceChannel *channel, SpiceChannelEvent event,
Expand All @@ -36,8 +70,6 @@ static void cs_main_channel_event(SpiceChannel *channel, SpiceChannelEvent event
switch (event) {
case SPICE_CHANNEL_OPENED:
g_message("main channel: opened");
self->_connectionCallback(YES);
self->_connectionCallback = nil;
[self.delegate spiceConnected:self];
break;
case SPICE_CHANNEL_SWITCHING:
Expand All @@ -47,8 +79,6 @@ static void cs_main_channel_event(SpiceChannel *channel, SpiceChannelEvent event
/* this event is only sent if the channel was succesfully opened before */
g_message("main channel: closed");
spice_session_disconnect(self->_session);
self->_connectionCallback(YES);
self->_connectionCallback = nil;
break;
case SPICE_CHANNEL_ERROR_IO:
case SPICE_CHANNEL_ERROR_TLS:
Expand All @@ -59,8 +89,7 @@ static void cs_main_channel_event(SpiceChannel *channel, SpiceChannelEvent event
if (error) {
g_message("channel error: %s", error->message);
}
self->_connectionCallback(NO);
self->_connectionCallback = nil;
[self.delegate spiceError:self err:(error ? [NSString stringWithUTF8String:error->message] : nil)];
break;
default:
/* TODO: more sophisticated error handling */
Expand All @@ -84,7 +113,7 @@ static void cs_display_monitors(SpiceChannel *display, GParamSpec *pspec,
NULL);
g_return_if_fail(monitors != NULL);

if (self->_monitors) {
if (!self->_monitors) {
self->_monitors = [NSMutableArray<NSMutableArray<CSDisplayMetal *> *> array];
}

Expand All @@ -94,8 +123,10 @@ static void cs_display_monitors(SpiceChannel *display, GParamSpec *pspec,
}

// create new monitors for this display
for (i = self->_monitors.count; i < monitors->len; i++) {
[self->_monitors[chid] addObject:[[CSDisplayMetal alloc] initWithSession:self->_session channelID:chid monitorID:i]];
for (i = self->_monitors[chid].count; i < monitors->len; i++) {
CSDisplayMetal *monitor = [[CSDisplayMetal alloc] initWithSession:self->_session channelID:chid monitorID:i];
[self->_monitors[chid] addObject:monitor];
[self.delegate spiceDisplayCreated:self display:monitor];
}

// clear any extra displays
Expand All @@ -119,14 +150,16 @@ static void cs_channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data
SPICE_DEBUG("new main channel");
g_assert(!self->_main); // should only be 1 main channel
self->_main = SPICE_MAIN_CHANNEL(channel);
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_connect(channel, "channel-event",
G_CALLBACK(cs_main_channel_event), (__bridge void *)self);
G_CALLBACK(cs_main_channel_event), GLIB_OBJC_RETAIN(self));
}

if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
SPICE_DEBUG("new display channel (#%d)", chid);
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_connect(channel, "notify::monitors",
G_CALLBACK(cs_display_monitors), (__bridge void *)self);
G_CALLBACK(cs_display_monitors), GLIB_OBJC_RETAIN(self));
spice_channel_connect(channel);
}

Expand All @@ -147,14 +180,16 @@ static void cs_channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer

g_object_get(channel, "channel-id", &chid, NULL);
if (SPICE_IS_MAIN_CHANNEL(channel)) {
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
SPICE_DEBUG("zap main channel");
self->_main = NULL;
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_main_channel_event), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_main_channel_event), GLIB_OBJC_RELEASE(self));
}

if (SPICE_IS_DISPLAY_CHANNEL(channel)) {
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
SPICE_DEBUG("zap display channel (#%d)", chid);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_display_monitors), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_display_monitors), GLIB_OBJC_RELEASE(self));
[self->_monitors[chid] removeAllObjects];
}

Expand All @@ -175,15 +210,6 @@ static void cs_connection_destroy(SpiceSession *session,
return _monitors;
}

- (CSDisplayMetal *)firstDisplay {
if (_monitors.count > 0) {
if (_monitors[0].count > 0) {
return _monitors[0][0];
}
}
return nil;
}

- (void)setHost:(NSString *)host {
g_object_set(_session, "host", [host UTF8String], NULL);
}
Expand All @@ -210,26 +236,29 @@ - (NSString *)port {

+ (void)spiceSetDebug:(BOOL)enabled {
spice_util_set_debug(enabled);
g_log_set_default_handler(logHandler, NULL);
}

- (id)init {
self = [super init];
if (self) {
_session = spice_session_new();
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_connect(_session, "channel-new",
G_CALLBACK(cs_channel_new), (__bridge void *)self);
G_CALLBACK(cs_channel_new), GLIB_OBJC_RETAIN(self));
g_signal_connect(_session, "channel-destroy",
G_CALLBACK(cs_channel_destroy), (__bridge void *)self);
G_CALLBACK(cs_channel_destroy), GLIB_OBJC_RETAIN(self));
g_signal_connect(_session, "disconnected",
G_CALLBACK(cs_connection_destroy), (__bridge void *)self);
G_CALLBACK(cs_connection_destroy), GLIB_OBJC_RETAIN(self));
}
return self;
}

- (void)dealloc {
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_new), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_destroy), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_connection_destroy), (__bridge void *)self);
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_new), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_destroy), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_connection_destroy), GLIB_OBJC_RELEASE(self));
g_object_unref(_session);
_session = NULL;
}
Expand All @@ -243,16 +272,11 @@ - (id)initWithHost:(NSString *)host port:(NSString *)port {
return self;
}

- (void)connectWithCompletion:(void (^)(BOOL))completion {
_connectionCallback = completion;
if (!spice_session_connect(_session)) {
completion(NO);
_connectionCallback = nil;
}
- (BOOL)connect {
return spice_session_connect(_session);
}

- (void)disconnectWithCompletion:(void (^)(BOOL))completion {
_connectionCallback = completion;
- (void)disconnect {
spice_session_disconnect(_session);
}

Expand Down
3 changes: 3 additions & 0 deletions CocoaSpice/CSConnectionDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@
#import <Foundation/Foundation.h>

@class CSConnection;
@class CSDisplayMetal;

NS_ASSUME_NONNULL_BEGIN

@protocol CSConnectionDelegate <NSObject>

- (void)spiceConnected:(CSConnection *)connection;
- (void)spiceDisconnected:(CSConnection *)connection;
- (void)spiceError:(CSConnection *)connection err:(nullable NSString *)msg;
- (void)spiceDisplayCreated:(CSConnection *)connection display:(CSDisplayMetal *)display;

@end

Expand Down
6 changes: 3 additions & 3 deletions CocoaSpice/CSDisplayMetal.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@
//

#import <Foundation/Foundation.h>
#import "UTMRendererDelegate.h"
#import "UTMRenderSource.h"
@import CoreGraphics;

typedef struct _SpiceSession SpiceSession;

NS_ASSUME_NONNULL_BEGIN

@interface CSDisplayMetal : NSObject <UTMRendererDelegate>
@interface CSDisplayMetal : NSObject <UTMRenderSource>

@property (nonatomic, assign) BOOL ready;
@property (nonatomic, readonly, assign) SpiceSession *session;
@property (nonatomic, readonly, nullable) SpiceSession *session;
@property (nonatomic, readonly, assign) NSInteger channelID;
@property (nonatomic, readonly, assign) NSInteger monitorID;

Expand Down
46 changes: 26 additions & 20 deletions CocoaSpice/CSDisplayMetal.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#import <spice-client.h>
#import <spice/protocol.h>

#define GLIB_OBJC_RETAIN(x) (__bridge_retained void *)(x)
#define GLIB_OBJC_RELEASE(x) (__bridge void *)(__bridge_transfer NSObject *)(__bridge void *)(x)

#define DISPLAY_DEBUG(display, fmt, ...) \
SPICE_DEBUG("%d:%d " fmt, \
(int)display.channelID, \
Expand Down Expand Up @@ -69,7 +72,6 @@ static void cs_primary_destroy(SpiceDisplayChannel *channel, gpointer data) {
self->_canvasFormat = 0;
self->_canvasStride = 0;
self->_canvasData = NULL;
self->_texture = nil;
}
}

Expand Down Expand Up @@ -162,18 +164,19 @@ static void cs_channel_new(SpiceSession *s, SpiceChannel *channel, gpointer data
}
self->_display = SPICE_DISPLAY_CHANNEL(channel);
NSCAssert(!self->_sigsconnected, @"Signals already connected!");
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_connect(channel, "display-primary-create",
G_CALLBACK(cs_primary_create), (__bridge void *)self);
G_CALLBACK(cs_primary_create), GLIB_OBJC_RETAIN(self));
g_signal_connect(channel, "display-primary-destroy",
G_CALLBACK(cs_primary_destroy), (__bridge void *)self);
G_CALLBACK(cs_primary_destroy), GLIB_OBJC_RETAIN(self));
g_signal_connect(channel, "display-invalidate",
G_CALLBACK(cs_invalidate), (__bridge void *)self);
G_CALLBACK(cs_invalidate), GLIB_OBJC_RETAIN(self));
g_signal_connect_after(channel, "display-mark",
G_CALLBACK(cs_mark), (__bridge void *)self);
G_CALLBACK(cs_mark), GLIB_OBJC_RETAIN(self));
g_signal_connect_after(channel, "notify::monitors",
G_CALLBACK(cs_update_monitor_area), (__bridge void *)self);
G_CALLBACK(cs_update_monitor_area), GLIB_OBJC_RETAIN(self));
g_signal_connect_after(channel, "gst-video-overlay",
G_CALLBACK(cs_set_overlay), (__bridge void *)self);
G_CALLBACK(cs_set_overlay), GLIB_OBJC_RETAIN(self));
self->_sigsconnected = YES;
if (spice_display_channel_get_primary(channel, 0, &primary)) {
cs_primary_create(channel, primary.format, primary.width, primary.height,
Expand All @@ -200,12 +203,13 @@ static void cs_channel_destroy(SpiceSession *s, SpiceChannel *channel, gpointer
cs_primary_destroy(self->_display, (__bridge void *)self);
self->_display = NULL;
NSCAssert(self->_sigsconnected, @"Signals not connected!");
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_primary_create), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_primary_destroy), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_invalidate), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_mark), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_update_monitor_area), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_set_overlay), (__bridge void *)self);
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_primary_create), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_primary_destroy), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_invalidate), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_mark), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_update_monitor_area), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(channel, G_CALLBACK(cs_set_overlay), GLIB_OBJC_RELEASE(self));
self->_sigsconnected = NO;
return;
}
Expand All @@ -225,9 +229,9 @@ - (void)setDevice:(id<MTLDevice>)device {
return _device;
}

@synthesize texture;
@synthesize numVertices;
@synthesize vertices;
@synthesize texture = _texture;
@synthesize numVertices = _numVertices;
@synthesize vertices = _vertices;

- (id)initWithSession:(nonnull SpiceSession *)session channelID:(NSInteger)channelID monitorID:(NSInteger)monitorID {
self = [self init];
Expand All @@ -241,10 +245,11 @@ - (id)initWithSession:(nonnull SpiceSession *)session channelID:(NSInteger)chann
_sigsconnected = NO;
g_object_ref(session);

NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_connect(session, "channel-new",
G_CALLBACK(cs_channel_new), (__bridge void *)self);
G_CALLBACK(cs_channel_new), GLIB_OBJC_RETAIN(self));
g_signal_connect(session, "channel-destroy",
G_CALLBACK(cs_channel_destroy), (__bridge void *)self);
G_CALLBACK(cs_channel_destroy), GLIB_OBJC_RETAIN(self));
list = spice_session_get_channels(session);
for (it = g_list_first(list); it != NULL; it = g_list_next(it)) {
if (SPICE_IS_DISPLAY_CHANNEL(it->data)) {
Expand All @@ -264,8 +269,9 @@ - (void)dealloc {
if (_display) {
cs_channel_destroy(self.session, SPICE_CHANNEL(_display), (__bridge void *)self);
}
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_new), (__bridge void *)self);
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_destroy), (__bridge void *)self);
NSLog(@"%s:%d", __FUNCTION__, __LINE__);
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_new), GLIB_OBJC_RELEASE(self));
g_signal_handlers_disconnect_by_func(_session, G_CALLBACK(cs_channel_destroy), GLIB_OBJC_RELEASE(self));
g_object_unref(_session);
_session = NULL;
}
Expand Down
1 change: 1 addition & 0 deletions ConfigurationViews/VMConfigDrivesViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ - (void)viewDidLoad {
}

- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self refreshViewFromConfiguration];
}

Expand Down
2 changes: 1 addition & 1 deletion Managers/UTMQemu.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ - (void)startDylib:(nonnull NSString *)dylib main:(nonnull NSString *)main compl
void *dlctx;
pthread_t qemu_thread;

dlctx = dlopen([main UTF8String], RTLD_LOCAL);
dlctx = dlopen([dylib UTF8String], RTLD_LOCAL);
if (dlctx == NULL) {
NSString *err = [NSString stringWithUTF8String:dlerror()];
completion(NO, err);
Expand Down
Loading

0 comments on commit ad5a688

Please sign in to comment.