ui/cocoa: Add clipboard support
Signed-off-by: Akihiko Odaki <akihiko.odaki@gmail.com> Message-Id: <20210616141954.54291-1-akihiko.odaki@gmail.com> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
15280e85d7
commit
7e3e20d891
@ -187,7 +187,7 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer,
|
|||||||
QemuClipboardInfo *info,
|
QemuClipboardInfo *info,
|
||||||
QemuClipboardType type,
|
QemuClipboardType type,
|
||||||
uint32_t size,
|
uint32_t size,
|
||||||
void *data,
|
const void *data,
|
||||||
bool update);
|
bool update);
|
||||||
|
|
||||||
#endif /* QEMU_CLIPBOARD_H */
|
#endif /* QEMU_CLIPBOARD_H */
|
||||||
|
@ -73,7 +73,7 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer,
|
|||||||
QemuClipboardInfo *info,
|
QemuClipboardInfo *info,
|
||||||
QemuClipboardType type,
|
QemuClipboardType type,
|
||||||
uint32_t size,
|
uint32_t size,
|
||||||
void *data,
|
const void *data,
|
||||||
bool update)
|
bool update)
|
||||||
{
|
{
|
||||||
if (!info ||
|
if (!info ||
|
||||||
|
109
ui/cocoa.m
109
ui/cocoa.m
@ -28,6 +28,7 @@
|
|||||||
#include <crt_externs.h>
|
#include <crt_externs.h>
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
|
#include "ui/clipboard.h"
|
||||||
#include "ui/console.h"
|
#include "ui/console.h"
|
||||||
#include "ui/input.h"
|
#include "ui/input.h"
|
||||||
#include "ui/kbd-state.h"
|
#include "ui/kbd-state.h"
|
||||||
@ -105,6 +106,10 @@ static QemuSemaphore display_init_sem;
|
|||||||
static QemuSemaphore app_started_sem;
|
static QemuSemaphore app_started_sem;
|
||||||
static bool allow_events;
|
static bool allow_events;
|
||||||
|
|
||||||
|
static NSInteger cbchangecount = -1;
|
||||||
|
static QemuClipboardInfo *cbinfo;
|
||||||
|
static QemuEvent cbevent;
|
||||||
|
|
||||||
// Utility functions to run specified code block with iothread lock held
|
// Utility functions to run specified code block with iothread lock held
|
||||||
typedef void (^CodeBlock)(void);
|
typedef void (^CodeBlock)(void);
|
||||||
typedef bool (^BoolCodeBlock)(void);
|
typedef bool (^BoolCodeBlock)(void);
|
||||||
@ -1758,6 +1763,93 @@ static void addRemovableDevicesMenuItems(void)
|
|||||||
qapi_free_BlockInfoList(pointerToFree);
|
qapi_free_BlockInfoList(pointerToFree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@interface QemuCocoaPasteboardTypeOwner : NSObject<NSPasteboardTypeOwner>
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation QemuCocoaPasteboardTypeOwner
|
||||||
|
|
||||||
|
- (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)type
|
||||||
|
{
|
||||||
|
if (type != NSPasteboardTypeString) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
with_iothread_lock(^{
|
||||||
|
QemuClipboardInfo *info = qemu_clipboard_info_ref(cbinfo);
|
||||||
|
qemu_event_reset(&cbevent);
|
||||||
|
qemu_clipboard_request(info, QEMU_CLIPBOARD_TYPE_TEXT);
|
||||||
|
|
||||||
|
while (info == cbinfo &&
|
||||||
|
info->types[QEMU_CLIPBOARD_TYPE_TEXT].available &&
|
||||||
|
info->types[QEMU_CLIPBOARD_TYPE_TEXT].data == NULL) {
|
||||||
|
qemu_mutex_unlock_iothread();
|
||||||
|
qemu_event_wait(&cbevent);
|
||||||
|
qemu_mutex_lock_iothread();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info == cbinfo) {
|
||||||
|
NSData *data = [[NSData alloc] initWithBytes:info->types[QEMU_CLIPBOARD_TYPE_TEXT].data
|
||||||
|
length:info->types[QEMU_CLIPBOARD_TYPE_TEXT].size];
|
||||||
|
[sender setData:data forType:NSPasteboardTypeString];
|
||||||
|
[data release];
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_clipboard_info_unref(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
static QemuCocoaPasteboardTypeOwner *cbowner;
|
||||||
|
|
||||||
|
static void cocoa_clipboard_notify(Notifier *notifier, void *data);
|
||||||
|
static void cocoa_clipboard_request(QemuClipboardInfo *info,
|
||||||
|
QemuClipboardType type);
|
||||||
|
|
||||||
|
static QemuClipboardPeer cbpeer = {
|
||||||
|
.name = "cocoa",
|
||||||
|
.update = { .notify = cocoa_clipboard_notify },
|
||||||
|
.request = cocoa_clipboard_request
|
||||||
|
};
|
||||||
|
|
||||||
|
static void cocoa_clipboard_notify(Notifier *notifier, void *data)
|
||||||
|
{
|
||||||
|
QemuClipboardInfo *info = data;
|
||||||
|
|
||||||
|
if (info->owner == &cbpeer || info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info != cbinfo) {
|
||||||
|
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
|
||||||
|
qemu_clipboard_info_unref(cbinfo);
|
||||||
|
cbinfo = qemu_clipboard_info_ref(info);
|
||||||
|
cbchangecount = [[NSPasteboard generalPasteboard] declareTypes:@[NSPasteboardTypeString] owner:cbowner];
|
||||||
|
[pool release];
|
||||||
|
}
|
||||||
|
|
||||||
|
qemu_event_set(&cbevent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cocoa_clipboard_request(QemuClipboardInfo *info,
|
||||||
|
QemuClipboardType type)
|
||||||
|
{
|
||||||
|
NSData *text;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case QEMU_CLIPBOARD_TYPE_TEXT:
|
||||||
|
text = [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeString];
|
||||||
|
if (text) {
|
||||||
|
qemu_clipboard_set_data(&cbpeer, info, type,
|
||||||
|
[text length], [text bytes], true);
|
||||||
|
[text release];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The startup process for the OSX/Cocoa UI is complicated, because
|
* The startup process for the OSX/Cocoa UI is complicated, because
|
||||||
* OSX insists that the UI runs on the initial main thread, and so we
|
* OSX insists that the UI runs on the initial main thread, and so we
|
||||||
@ -1792,6 +1884,7 @@ static void *call_qemu_main(void *opaque)
|
|||||||
COCOA_DEBUG("Second thread: calling qemu_main()\n");
|
COCOA_DEBUG("Second thread: calling qemu_main()\n");
|
||||||
status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
|
status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
|
||||||
COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
|
COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
|
||||||
|
[cbowner release];
|
||||||
exit(status);
|
exit(status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1914,6 +2007,18 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
|
|||||||
[cocoaView setAbsoluteEnabled:YES];
|
[cocoaView setAbsoluteEnabled:YES];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cbchangecount != [[NSPasteboard generalPasteboard] changeCount]) {
|
||||||
|
qemu_clipboard_info_unref(cbinfo);
|
||||||
|
cbinfo = qemu_clipboard_info_new(&cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD);
|
||||||
|
if ([[NSPasteboard generalPasteboard] availableTypeFromArray:@[NSPasteboardTypeString]]) {
|
||||||
|
cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
|
||||||
|
}
|
||||||
|
qemu_clipboard_update(cbinfo);
|
||||||
|
cbchangecount = [[NSPasteboard generalPasteboard] changeCount];
|
||||||
|
qemu_event_set(&cbevent);
|
||||||
|
}
|
||||||
|
|
||||||
[pool release];
|
[pool release];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1939,6 +2044,10 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
|
|||||||
|
|
||||||
// register vga output callbacks
|
// register vga output callbacks
|
||||||
register_displaychangelistener(&dcl);
|
register_displaychangelistener(&dcl);
|
||||||
|
|
||||||
|
qemu_event_init(&cbevent, false);
|
||||||
|
cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
|
||||||
|
qemu_clipboard_peer_register(&cbpeer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static QemuDisplay qemu_display_cocoa = {
|
static QemuDisplay qemu_display_cocoa = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user