Skip to content

Commit 7e3e20d

Browse files
akihikodakikraxel
authored andcommitted
ui/cocoa: Add clipboard support
Signed-off-by: Akihiko Odaki <[email protected]> Message-Id: <[email protected]> Signed-off-by: Gerd Hoffmann <[email protected]>
1 parent 15280e8 commit 7e3e20d

File tree

3 files changed

+111
-2
lines changed

3 files changed

+111
-2
lines changed

include/ui/clipboard.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer,
187187
QemuClipboardInfo *info,
188188
QemuClipboardType type,
189189
uint32_t size,
190-
void *data,
190+
const void *data,
191191
bool update);
192192

193193
#endif /* QEMU_CLIPBOARD_H */

ui/clipboard.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void qemu_clipboard_set_data(QemuClipboardPeer *peer,
7373
QemuClipboardInfo *info,
7474
QemuClipboardType type,
7575
uint32_t size,
76-
void *data,
76+
const void *data,
7777
bool update)
7878
{
7979
if (!info ||

ui/cocoa.m

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <crt_externs.h>
2929

3030
#include "qemu-common.h"
31+
#include "ui/clipboard.h"
3132
#include "ui/console.h"
3233
#include "ui/input.h"
3334
#include "ui/kbd-state.h"
@@ -105,6 +106,10 @@ static void cocoa_switch(DisplayChangeListener *dcl,
105106
static QemuSemaphore app_started_sem;
106107
static bool allow_events;
107108

109+
static NSInteger cbchangecount = -1;
110+
static QemuClipboardInfo *cbinfo;
111+
static QemuEvent cbevent;
112+
108113
// Utility functions to run specified code block with iothread lock held
109114
typedef void (^CodeBlock)(void);
110115
typedef bool (^BoolCodeBlock)(void);
@@ -1758,6 +1763,93 @@ static void addRemovableDevicesMenuItems(void)
17581763
qapi_free_BlockInfoList(pointerToFree);
17591764
}
17601765

1766+
@interface QemuCocoaPasteboardTypeOwner : NSObject<NSPasteboardTypeOwner>
1767+
@end
1768+
1769+
@implementation QemuCocoaPasteboardTypeOwner
1770+
1771+
- (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSPasteboardType)type
1772+
{
1773+
if (type != NSPasteboardTypeString) {
1774+
return;
1775+
}
1776+
1777+
with_iothread_lock(^{
1778+
QemuClipboardInfo *info = qemu_clipboard_info_ref(cbinfo);
1779+
qemu_event_reset(&cbevent);
1780+
qemu_clipboard_request(info, QEMU_CLIPBOARD_TYPE_TEXT);
1781+
1782+
while (info == cbinfo &&
1783+
info->types[QEMU_CLIPBOARD_TYPE_TEXT].available &&
1784+
info->types[QEMU_CLIPBOARD_TYPE_TEXT].data == NULL) {
1785+
qemu_mutex_unlock_iothread();
1786+
qemu_event_wait(&cbevent);
1787+
qemu_mutex_lock_iothread();
1788+
}
1789+
1790+
if (info == cbinfo) {
1791+
NSData *data = [[NSData alloc] initWithBytes:info->types[QEMU_CLIPBOARD_TYPE_TEXT].data
1792+
length:info->types[QEMU_CLIPBOARD_TYPE_TEXT].size];
1793+
[sender setData:data forType:NSPasteboardTypeString];
1794+
[data release];
1795+
}
1796+
1797+
qemu_clipboard_info_unref(info);
1798+
});
1799+
}
1800+
1801+
@end
1802+
1803+
static QemuCocoaPasteboardTypeOwner *cbowner;
1804+
1805+
static void cocoa_clipboard_notify(Notifier *notifier, void *data);
1806+
static void cocoa_clipboard_request(QemuClipboardInfo *info,
1807+
QemuClipboardType type);
1808+
1809+
static QemuClipboardPeer cbpeer = {
1810+
.name = "cocoa",
1811+
.update = { .notify = cocoa_clipboard_notify },
1812+
.request = cocoa_clipboard_request
1813+
};
1814+
1815+
static void cocoa_clipboard_notify(Notifier *notifier, void *data)
1816+
{
1817+
QemuClipboardInfo *info = data;
1818+
1819+
if (info->owner == &cbpeer || info->selection != QEMU_CLIPBOARD_SELECTION_CLIPBOARD) {
1820+
return;
1821+
}
1822+
1823+
if (info != cbinfo) {
1824+
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
1825+
qemu_clipboard_info_unref(cbinfo);
1826+
cbinfo = qemu_clipboard_info_ref(info);
1827+
cbchangecount = [[NSPasteboard generalPasteboard] declareTypes:@[NSPasteboardTypeString] owner:cbowner];
1828+
[pool release];
1829+
}
1830+
1831+
qemu_event_set(&cbevent);
1832+
}
1833+
1834+
static void cocoa_clipboard_request(QemuClipboardInfo *info,
1835+
QemuClipboardType type)
1836+
{
1837+
NSData *text;
1838+
1839+
switch (type) {
1840+
case QEMU_CLIPBOARD_TYPE_TEXT:
1841+
text = [[NSPasteboard generalPasteboard] dataForType:NSPasteboardTypeString];
1842+
if (text) {
1843+
qemu_clipboard_set_data(&cbpeer, info, type,
1844+
[text length], [text bytes], true);
1845+
[text release];
1846+
}
1847+
break;
1848+
default:
1849+
break;
1850+
}
1851+
}
1852+
17611853
/*
17621854
* The startup process for the OSX/Cocoa UI is complicated, because
17631855
* OSX insists that the UI runs on the initial main thread, and so we
@@ -1792,6 +1884,7 @@ static void addRemovableDevicesMenuItems(void)
17921884
COCOA_DEBUG("Second thread: calling qemu_main()\n");
17931885
status = qemu_main(gArgc, gArgv, *_NSGetEnviron());
17941886
COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n");
1887+
[cbowner release];
17951888
exit(status);
17961889
}
17971890

@@ -1914,6 +2007,18 @@ static void cocoa_refresh(DisplayChangeListener *dcl)
19142007
[cocoaView setAbsoluteEnabled:YES];
19152008
});
19162009
}
2010+
2011+
if (cbchangecount != [[NSPasteboard generalPasteboard] changeCount]) {
2012+
qemu_clipboard_info_unref(cbinfo);
2013+
cbinfo = qemu_clipboard_info_new(&cbpeer, QEMU_CLIPBOARD_SELECTION_CLIPBOARD);
2014+
if ([[NSPasteboard generalPasteboard] availableTypeFromArray:@[NSPasteboardTypeString]]) {
2015+
cbinfo->types[QEMU_CLIPBOARD_TYPE_TEXT].available = true;
2016+
}
2017+
qemu_clipboard_update(cbinfo);
2018+
cbchangecount = [[NSPasteboard generalPasteboard] changeCount];
2019+
qemu_event_set(&cbevent);
2020+
}
2021+
19172022
[pool release];
19182023
}
19192024

@@ -1939,6 +2044,10 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts)
19392044

19402045
// register vga output callbacks
19412046
register_displaychangelistener(&dcl);
2047+
2048+
qemu_event_init(&cbevent, false);
2049+
cbowner = [[QemuCocoaPasteboardTypeOwner alloc] init];
2050+
qemu_clipboard_peer_register(&cbpeer);
19422051
}
19432052

19442053
static QemuDisplay qemu_display_cocoa = {

0 commit comments

Comments
 (0)