forked from AuroraMiddleware/gtk
Merge branch 'macos-native-dnd' into 'main'
macos: DnD Drag support Closes #3423 See merge request GNOME/gtk!5416
This commit is contained in:
commit
5caa66ffb7
@ -27,10 +27,12 @@
|
|||||||
#import "GdkMacosView.h"
|
#import "GdkMacosView.h"
|
||||||
#import "GdkMacosWindow.h"
|
#import "GdkMacosWindow.h"
|
||||||
|
|
||||||
#include "gdkmacosclipboard-private.h"
|
|
||||||
#include "gdkmacosdisplay-private.h"
|
#include "gdkmacosdisplay-private.h"
|
||||||
|
#include "gdkmacosdrag-private.h"
|
||||||
#include "gdkmacosdrop-private.h"
|
#include "gdkmacosdrop-private.h"
|
||||||
|
#include "gdkmacoseventsource-private.h"
|
||||||
#include "gdkmacosmonitor-private.h"
|
#include "gdkmacosmonitor-private.h"
|
||||||
|
#include "gdkmacospasteboard-private.h"
|
||||||
#include "gdkmacossurface-private.h"
|
#include "gdkmacossurface-private.h"
|
||||||
#include "gdkmacospopupsurface-private.h"
|
#include "gdkmacospopupsurface-private.h"
|
||||||
#include "gdkmacostoplevelsurface-private.h"
|
#include "gdkmacostoplevelsurface-private.h"
|
||||||
@ -144,7 +146,7 @@ typedef NSString *CALayerContentsGravity;
|
|||||||
*
|
*
|
||||||
* TODO: Can we improve grab breaking to fix this?
|
* TODO: Can we improve grab breaking to fix this?
|
||||||
*/
|
*/
|
||||||
_gdk_macos_display_send_button_event ([self gdkDisplay], event);
|
_gdk_macos_display_send_event ([self gdkDisplay], event);
|
||||||
|
|
||||||
_gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), time);
|
_gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), time);
|
||||||
|
|
||||||
@ -246,7 +248,7 @@ typedef NSString *CALayerContentsGravity;
|
|||||||
[view release];
|
[view release];
|
||||||
|
|
||||||
/* TODO: We might want to make this more extensible at some point */
|
/* TODO: We might want to make this more extensible at some point */
|
||||||
_gdk_macos_clipboard_register_drag_types (self);
|
_gdk_macos_pasteboard_register_drag_types (self);
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
@ -668,7 +670,61 @@ typedef NSString *CALayerContentsGravity;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NSDraggingSource protocol
|
// NSDraggingSource protocol
|
||||||
// ...
|
|
||||||
|
- (NSDragOperation)draggingSession:(NSDraggingSession *)session sourceOperationMaskForDraggingContext:(NSDraggingContext)context
|
||||||
|
{
|
||||||
|
NSInteger sequence_number = [session draggingSequenceNumber];
|
||||||
|
GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface));
|
||||||
|
GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
|
||||||
|
GdkModifierType state = _gdk_macos_display_get_current_keyboard_modifiers (GDK_MACOS_DISPLAY (display));
|
||||||
|
|
||||||
|
_gdk_macos_drag_set_actions (GDK_MACOS_DRAG (drag), state);
|
||||||
|
|
||||||
|
return _gdk_macos_drag_operation (GDK_MACOS_DRAG (drag));
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)draggingSession:(NSDraggingSession *)session willBeginAtPoint:(NSPoint)screenPoint
|
||||||
|
{
|
||||||
|
NSInteger sequence_number = [session draggingSequenceNumber];
|
||||||
|
GdkDisplay *display = gdk_surface_get_display (GDK_SURFACE (gdk_surface));
|
||||||
|
GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
_gdk_macos_display_from_display_coords (GDK_MACOS_DISPLAY (display), screenPoint.x, screenPoint.y, &x, &y);
|
||||||
|
_gdk_macos_drag_set_start_position (GDK_MACOS_DRAG (drag), x, y);
|
||||||
|
_gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)draggingSession:(NSDraggingSession *)session movedToPoint:(NSPoint)screenPoint
|
||||||
|
{
|
||||||
|
NSInteger sequence_number = [session draggingSequenceNumber];
|
||||||
|
GdkMacosDisplay *display = GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (gdk_surface)));
|
||||||
|
GdkDrag *drag = _gdk_macos_display_find_drag (GDK_MACOS_DISPLAY (display), sequence_number);
|
||||||
|
int x, y;
|
||||||
|
|
||||||
|
_gdk_macos_display_send_event (display, [NSApp currentEvent]);
|
||||||
|
|
||||||
|
_gdk_macos_display_from_display_coords (display, screenPoint.x, screenPoint.y, &x, &y);
|
||||||
|
_gdk_macos_drag_surface_move (GDK_MACOS_DRAG (drag), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)draggingSession:(NSDraggingSession *)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation
|
||||||
|
{
|
||||||
|
NSInteger sequence_number = [session draggingSequenceNumber];
|
||||||
|
GdkMacosDisplay *display = GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (gdk_surface)));
|
||||||
|
GdkDrag *drag = _gdk_macos_display_find_drag (display, sequence_number);
|
||||||
|
|
||||||
|
_gdk_macos_display_send_event (display, [NSApp currentEvent]);
|
||||||
|
gdk_drag_set_selected_action (drag, _gdk_macos_drag_ns_operation_to_action (operation));
|
||||||
|
|
||||||
|
if (gdk_drag_get_selected_action (drag) != 0)
|
||||||
|
g_signal_emit_by_name (drag, "drop-performed");
|
||||||
|
else
|
||||||
|
gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
|
||||||
|
|
||||||
|
_gdk_macos_display_set_drag (display, [session draggingSequenceNumber], NULL);
|
||||||
|
}
|
||||||
|
|
||||||
// end
|
// end
|
||||||
|
|
||||||
-(void)setStyleMask:(NSWindowStyleMask)styleMask
|
-(void)setStyleMask:(NSWindowStyleMask)styleMask
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
|
|
||||||
#define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]])
|
#define GDK_IS_MACOS_WINDOW(obj) ([obj isKindOfClass:[GdkMacosWindow class]])
|
||||||
|
|
||||||
@interface GdkMacosWindow : NSWindow {
|
@interface GdkMacosWindow : NSWindow <NSDraggingSource, NSDraggingDestination> {
|
||||||
GdkMacosSurface *gdk_surface;
|
GdkMacosSurface *gdk_surface;
|
||||||
|
|
||||||
BOOL inMove;
|
BOOL inMove;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "gdkclipboardprivate.h"
|
#include "gdkclipboardprivate.h"
|
||||||
#include "gdkmacosdisplay-private.h"
|
#include "gdkmacosdisplay-private.h"
|
||||||
|
#include "gdkmacospasteboard-private.h"
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@ -41,30 +42,6 @@ NSPasteboardType _gdk_macos_clipboard_to_ns_type (const char
|
|||||||
NSPasteboardType *alternate);
|
NSPasteboardType *alternate);
|
||||||
const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type);
|
const char *_gdk_macos_clipboard_from_ns_type (NSPasteboardType ns_type);
|
||||||
void _gdk_macos_clipboard_register_drag_types (NSWindow *window);
|
void _gdk_macos_clipboard_register_drag_types (NSWindow *window);
|
||||||
GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard);
|
|
||||||
void _gdk_macos_pasteboard_read_async (GObject *object,
|
|
||||||
NSPasteboard *pasteboard,
|
|
||||||
GdkContentFormats *formats,
|
|
||||||
int io_priority,
|
|
||||||
GCancellable *cancellable,
|
|
||||||
GAsyncReadyCallback callback,
|
|
||||||
gpointer user_data);
|
|
||||||
GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object,
|
|
||||||
GAsyncResult *result,
|
|
||||||
const char **out_mime_type,
|
|
||||||
GError **error);
|
|
||||||
|
|
||||||
@interface GdkMacosClipboardDataProvider : NSObject <NSPasteboardItemDataProvider>
|
|
||||||
{
|
|
||||||
GCancellable *cancellable;
|
|
||||||
GdkClipboard *clipboard;
|
|
||||||
char **mimeTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types;
|
|
||||||
-(NSArray<NSPasteboardType> *)types;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <glib/gi18n.h>
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
#include "gdkmacosclipboard-private.h"
|
#include "gdkmacosclipboard-private.h"
|
||||||
|
#include "gdkmacospasteboard-private.h"
|
||||||
#include "gdkmacosutils-private.h"
|
#include "gdkmacosutils-private.h"
|
||||||
#include "gdkprivate.h"
|
#include "gdkprivate.h"
|
||||||
|
|
||||||
@ -32,163 +33,8 @@ struct _GdkMacosClipboard
|
|||||||
NSInteger last_change_count;
|
NSInteger last_change_count;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
GMemoryOutputStream *stream;
|
|
||||||
NSPasteboardItem *item;
|
|
||||||
NSPasteboardType type;
|
|
||||||
GMainContext *main_context;
|
|
||||||
guint done : 1;
|
|
||||||
} WriteRequest;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
TYPE_STRING,
|
|
||||||
TYPE_PBOARD,
|
|
||||||
TYPE_URL,
|
|
||||||
TYPE_FILE_URL,
|
|
||||||
TYPE_COLOR,
|
|
||||||
TYPE_TIFF,
|
|
||||||
TYPE_PNG,
|
|
||||||
TYPE_LAST
|
|
||||||
};
|
|
||||||
|
|
||||||
#define PTYPE(k) (get_pasteboard_type(TYPE_##k))
|
|
||||||
|
|
||||||
static NSPasteboardType pasteboard_types[TYPE_LAST];
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD)
|
G_DEFINE_TYPE (GdkMacosClipboard, _gdk_macos_clipboard, GDK_TYPE_CLIPBOARD)
|
||||||
|
|
||||||
static NSPasteboardType
|
|
||||||
get_pasteboard_type (int type)
|
|
||||||
{
|
|
||||||
static gsize initialized = FALSE;
|
|
||||||
|
|
||||||
g_assert (type >= 0);
|
|
||||||
g_assert (type < TYPE_LAST);
|
|
||||||
|
|
||||||
if (g_once_init_enter (&initialized))
|
|
||||||
{
|
|
||||||
pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG;
|
|
||||||
pasteboard_types[TYPE_STRING] = NSPasteboardTypeString;
|
|
||||||
pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF;
|
|
||||||
pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor;
|
|
||||||
|
|
||||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
||||||
pasteboard_types[TYPE_PBOARD] = NSStringPboardType;
|
|
||||||
G_GNUC_END_IGNORE_DEPRECATIONS
|
|
||||||
|
|
||||||
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
|
|
||||||
pasteboard_types[TYPE_URL] = NSPasteboardTypeURL;
|
|
||||||
pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL;
|
|
||||||
#else
|
|
||||||
pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"];
|
|
||||||
pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_once_init_leave (&initialized, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return pasteboard_types[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
write_request_free (WriteRequest *wr)
|
|
||||||
{
|
|
||||||
g_clear_pointer (&wr->main_context, g_main_context_unref);
|
|
||||||
g_clear_object (&wr->stream);
|
|
||||||
[wr->item release];
|
|
||||||
g_slice_free (WriteRequest, wr);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *
|
|
||||||
_gdk_macos_clipboard_from_ns_type (NSPasteboardType type)
|
|
||||||
{
|
|
||||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
|
||||||
|
|
||||||
if ([type isEqualToString:PTYPE(STRING)] ||
|
|
||||||
[type isEqualToString:PTYPE(PBOARD)])
|
|
||||||
return g_intern_string ("text/plain;charset=utf-8");
|
|
||||||
else if ([type isEqualToString:PTYPE(URL)] ||
|
|
||||||
[type isEqualToString:PTYPE(FILE_URL)])
|
|
||||||
return g_intern_string ("text/uri-list");
|
|
||||||
else if ([type isEqualToString:PTYPE(COLOR)])
|
|
||||||
return g_intern_string ("application/x-color");
|
|
||||||
else if ([type isEqualToString:PTYPE(TIFF)])
|
|
||||||
return g_intern_string ("image/tiff");
|
|
||||||
else if ([type isEqualToString:PTYPE(PNG)])
|
|
||||||
return g_intern_string ("image/png");
|
|
||||||
|
|
||||||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSPasteboardType
|
|
||||||
_gdk_macos_clipboard_to_ns_type (const char *mime_type,
|
|
||||||
NSPasteboardType *alternate)
|
|
||||||
{
|
|
||||||
if (alternate)
|
|
||||||
*alternate = NULL;
|
|
||||||
|
|
||||||
if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0)
|
|
||||||
{
|
|
||||||
return PTYPE(STRING);
|
|
||||||
}
|
|
||||||
else if (g_strcmp0 (mime_type, "text/uri-list") == 0)
|
|
||||||
{
|
|
||||||
if (alternate)
|
|
||||||
*alternate = PTYPE(URL);
|
|
||||||
return PTYPE(FILE_URL);
|
|
||||||
}
|
|
||||||
else if (g_strcmp0 (mime_type, "application/x-color") == 0)
|
|
||||||
{
|
|
||||||
return PTYPE(COLOR);
|
|
||||||
}
|
|
||||||
else if (g_strcmp0 (mime_type, "image/tiff") == 0)
|
|
||||||
{
|
|
||||||
return PTYPE(TIFF);
|
|
||||||
}
|
|
||||||
else if (g_strcmp0 (mime_type, "image/png") == 0)
|
|
||||||
{
|
|
||||||
return PTYPE(PNG);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
populate_content_formats (GdkContentFormatsBuilder *builder,
|
|
||||||
NSPasteboardType type)
|
|
||||||
{
|
|
||||||
const char *mime_type;
|
|
||||||
|
|
||||||
g_return_if_fail (builder != NULL);
|
|
||||||
g_return_if_fail (type != NULL);
|
|
||||||
|
|
||||||
mime_type = _gdk_macos_clipboard_from_ns_type (type);
|
|
||||||
|
|
||||||
if (mime_type != NULL)
|
|
||||||
gdk_content_formats_builder_add_mime_type (builder, mime_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GdkContentFormats *
|
|
||||||
load_offer_formats (NSPasteboard *pasteboard)
|
|
||||||
{
|
|
||||||
GDK_BEGIN_MACOS_ALLOC_POOL;
|
|
||||||
|
|
||||||
GdkContentFormatsBuilder *builder;
|
|
||||||
GdkContentFormats *formats;
|
|
||||||
|
|
||||||
builder = gdk_content_formats_builder_new ();
|
|
||||||
for (NSPasteboardType type in [pasteboard types])
|
|
||||||
populate_content_formats (builder, type);
|
|
||||||
formats = gdk_content_formats_builder_free_to_formats (builder);
|
|
||||||
|
|
||||||
GDK_END_MACOS_ALLOC_POOL;
|
|
||||||
|
|
||||||
return g_steal_pointer (&formats);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_gdk_macos_clipboard_load_contents (GdkMacosClipboard *self)
|
_gdk_macos_clipboard_load_contents (GdkMacosClipboard *self)
|
||||||
{
|
{
|
||||||
@ -199,22 +45,13 @@ _gdk_macos_clipboard_load_contents (GdkMacosClipboard *self)
|
|||||||
|
|
||||||
change_count = [self->pasteboard changeCount];
|
change_count = [self->pasteboard changeCount];
|
||||||
|
|
||||||
formats = load_offer_formats (self->pasteboard);
|
formats = _gdk_macos_pasteboard_load_formats (self->pasteboard);
|
||||||
gdk_clipboard_claim_remote (GDK_CLIPBOARD (self), formats);
|
gdk_clipboard_claim_remote (GDK_CLIPBOARD (self), formats);
|
||||||
gdk_content_formats_unref (formats);
|
gdk_content_formats_unref (formats);
|
||||||
|
|
||||||
self->last_change_count = change_count;
|
self->last_change_count = change_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static GInputStream *
|
|
||||||
create_stream_from_nsdata (NSData *data)
|
|
||||||
{
|
|
||||||
const guint8 *bytes = [data bytes];
|
|
||||||
gsize len = [data length];
|
|
||||||
|
|
||||||
return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
_gdk_macos_clipboard_read_async (GdkClipboard *clipboard,
|
_gdk_macos_clipboard_read_async (GdkClipboard *clipboard,
|
||||||
GdkContentFormats *formats,
|
GdkContentFormats *formats,
|
||||||
@ -245,34 +82,26 @@ static void
|
|||||||
_gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self,
|
_gdk_macos_clipboard_send_to_pasteboard (GdkMacosClipboard *self,
|
||||||
GdkContentProvider *content)
|
GdkContentProvider *content)
|
||||||
{
|
{
|
||||||
|
GdkMacosPasteboardItem *item;
|
||||||
|
NSArray<NSPasteboardItem *> *items;
|
||||||
|
|
||||||
|
g_assert (GDK_IS_MACOS_CLIPBOARD (self));
|
||||||
|
g_assert (GDK_IS_CONTENT_PROVIDER (content));
|
||||||
|
|
||||||
|
if (self->pasteboard == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
GDK_BEGIN_MACOS_ALLOC_POOL;
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
GdkMacosClipboardDataProvider *dataProvider;
|
item = [[GdkMacosPasteboardItem alloc] initForClipboard:GDK_CLIPBOARD (self) withContentProvider:content];
|
||||||
GdkContentFormats *serializable;
|
items = [NSArray arrayWithObject:item];
|
||||||
NSPasteboardItem *item;
|
|
||||||
const char * const *mime_types;
|
|
||||||
gsize n_mime_types;
|
|
||||||
|
|
||||||
g_return_if_fail (GDK_IS_MACOS_CLIPBOARD (self));
|
|
||||||
g_return_if_fail (GDK_IS_CONTENT_PROVIDER (content));
|
|
||||||
|
|
||||||
serializable = gdk_content_provider_ref_storable_formats (content);
|
|
||||||
serializable = gdk_content_formats_union_serialize_mime_types (serializable);
|
|
||||||
mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types);
|
|
||||||
|
|
||||||
dataProvider = [[GdkMacosClipboardDataProvider alloc] initClipboard:GDK_CLIPBOARD (self)
|
|
||||||
mimetypes:mime_types];
|
|
||||||
item = [[NSPasteboardItem alloc] init];
|
|
||||||
[item setDataProvider:dataProvider forTypes:[dataProvider types]];
|
|
||||||
|
|
||||||
[self->pasteboard clearContents];
|
[self->pasteboard clearContents];
|
||||||
if ([self->pasteboard writeObjects:[NSArray arrayWithObject:item]] == NO)
|
if ([self->pasteboard writeObjects:items] == NO)
|
||||||
g_warning ("Failed to write object to pasteboard");
|
g_warning ("Failed to send clipboard to pasteboard");
|
||||||
|
|
||||||
self->last_change_count = [self->pasteboard changeCount];
|
self->last_change_count = [self->pasteboard changeCount];
|
||||||
|
|
||||||
g_clear_pointer (&serializable, gdk_content_formats_unref);
|
|
||||||
|
|
||||||
GDK_END_MACOS_ALLOC_POOL;
|
GDK_END_MACOS_ALLOC_POOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -365,305 +194,3 @@ _gdk_macos_clipboard_check_externally_modified (GdkMacosClipboard *self)
|
|||||||
if ([self->pasteboard changeCount] != self->last_change_count)
|
if ([self->pasteboard changeCount] != self->last_change_count)
|
||||||
_gdk_macos_clipboard_load_contents (self);
|
_gdk_macos_clipboard_load_contents (self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@implementation GdkMacosClipboardDataProvider
|
|
||||||
|
|
||||||
-(id)initClipboard:(GdkClipboard *)gdkClipboard mimetypes:(const char * const *)mime_types;
|
|
||||||
{
|
|
||||||
[super init];
|
|
||||||
|
|
||||||
self->mimeTypes = g_strdupv ((char **)mime_types);
|
|
||||||
self->clipboard = g_object_ref (gdkClipboard);
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)dealloc
|
|
||||||
{
|
|
||||||
g_cancellable_cancel (self->cancellable);
|
|
||||||
|
|
||||||
g_clear_pointer (&self->mimeTypes, g_strfreev);
|
|
||||||
g_clear_object (&self->clipboard);
|
|
||||||
g_clear_object (&self->cancellable);
|
|
||||||
|
|
||||||
[super dealloc];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard
|
|
||||||
{
|
|
||||||
g_clear_object (&self->clipboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSArray<NSPasteboardType> *)types
|
|
||||||
{
|
|
||||||
NSMutableArray *ret = [[NSMutableArray alloc] init];
|
|
||||||
|
|
||||||
for (guint i = 0; self->mimeTypes[i]; i++)
|
|
||||||
{
|
|
||||||
const char *mime_type = self->mimeTypes[i];
|
|
||||||
NSPasteboardType type;
|
|
||||||
NSPasteboardType alternate = nil;
|
|
||||||
|
|
||||||
if ((type = _gdk_macos_clipboard_to_ns_type (mime_type, &alternate)))
|
|
||||||
{
|
|
||||||
[ret addObject:type];
|
|
||||||
if (alternate)
|
|
||||||
[ret addObject:alternate];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return g_steal_pointer (&ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
on_data_ready_cb (GObject *object,
|
|
||||||
GAsyncResult *result,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
GDK_BEGIN_MACOS_ALLOC_POOL;
|
|
||||||
|
|
||||||
GdkClipboard *clipboard = (GdkClipboard *)object;
|
|
||||||
WriteRequest *wr = user_data;
|
|
||||||
GError *error = NULL;
|
|
||||||
NSData *data = nil;
|
|
||||||
|
|
||||||
g_assert (GDK_IS_CLIPBOARD (clipboard));
|
|
||||||
g_assert (G_IS_ASYNC_RESULT (result));
|
|
||||||
g_assert (wr != NULL);
|
|
||||||
g_assert (G_IS_MEMORY_OUTPUT_STREAM (wr->stream));
|
|
||||||
g_assert ([wr->item isKindOfClass:[NSPasteboardItem class]]);
|
|
||||||
|
|
||||||
if (gdk_clipboard_write_finish (clipboard, result, &error))
|
|
||||||
{
|
|
||||||
gsize size;
|
|
||||||
gpointer bytes;
|
|
||||||
|
|
||||||
g_output_stream_close (G_OUTPUT_STREAM (wr->stream), NULL, NULL);
|
|
||||||
|
|
||||||
size = g_memory_output_stream_get_data_size (wr->stream);
|
|
||||||
bytes = g_memory_output_stream_steal_data (wr->stream);
|
|
||||||
data = [[NSData alloc] initWithBytesNoCopy:bytes
|
|
||||||
length:size
|
|
||||||
deallocator:^(void *alloc, NSUInteger length) { g_free (alloc); }];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_warning ("Failed to serialize clipboard contents: %s",
|
|
||||||
error->message);
|
|
||||||
g_clear_error (&error);
|
|
||||||
}
|
|
||||||
|
|
||||||
[wr->item setData:data forType:wr->type];
|
|
||||||
|
|
||||||
wr->done = TRUE;
|
|
||||||
|
|
||||||
GDK_END_MACOS_ALLOC_POOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void) pasteboard:(NSPasteboard *)pasteboard
|
|
||||||
item:(NSPasteboardItem *)item
|
|
||||||
provideDataForType:(NSPasteboardType)type
|
|
||||||
{
|
|
||||||
const char *mime_type = _gdk_macos_clipboard_from_ns_type (type);
|
|
||||||
GMainContext *main_context = g_main_context_default ();
|
|
||||||
WriteRequest *wr;
|
|
||||||
|
|
||||||
if (self->clipboard == NULL || mime_type == NULL)
|
|
||||||
{
|
|
||||||
[item setData:[NSData data] forType:type];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
wr = g_slice_new0 (WriteRequest);
|
|
||||||
wr->item = [item retain];
|
|
||||||
wr->stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new_resizable ());
|
|
||||||
wr->type = type;
|
|
||||||
wr->main_context = g_main_context_ref (main_context);
|
|
||||||
wr->done = FALSE;
|
|
||||||
|
|
||||||
gdk_clipboard_write_async (self->clipboard,
|
|
||||||
mime_type,
|
|
||||||
G_OUTPUT_STREAM (wr->stream),
|
|
||||||
G_PRIORITY_DEFAULT,
|
|
||||||
self->cancellable,
|
|
||||||
on_data_ready_cb,
|
|
||||||
wr);
|
|
||||||
|
|
||||||
/* We're forced to provide data synchronously via this API
|
|
||||||
* so we must block on the main loop. Using another main loop
|
|
||||||
* than the default tends to get us locked up here, so that is
|
|
||||||
* what we'll do for now.
|
|
||||||
*/
|
|
||||||
while (!wr->done)
|
|
||||||
g_main_context_iteration (wr->main_context, TRUE);
|
|
||||||
|
|
||||||
write_request_free (wr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
_gdk_macos_clipboard_register_drag_types (NSWindow *window)
|
|
||||||
{
|
|
||||||
[window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING),
|
|
||||||
PTYPE(PBOARD),
|
|
||||||
PTYPE(URL),
|
|
||||||
PTYPE(FILE_URL),
|
|
||||||
PTYPE(COLOR),
|
|
||||||
PTYPE(TIFF),
|
|
||||||
PTYPE(PNG),
|
|
||||||
nil]];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
GdkContentFormats *
|
|
||||||
_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard)
|
|
||||||
{
|
|
||||||
return load_offer_formats (pasteboard);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
_gdk_macos_pasteboard_read_async (GObject *object,
|
|
||||||
NSPasteboard *pasteboard,
|
|
||||||
GdkContentFormats *formats,
|
|
||||||
int io_priority,
|
|
||||||
GCancellable *cancellable,
|
|
||||||
GAsyncReadyCallback callback,
|
|
||||||
gpointer user_data)
|
|
||||||
{
|
|
||||||
GDK_BEGIN_MACOS_ALLOC_POOL;
|
|
||||||
|
|
||||||
GdkContentFormats *offer_formats = NULL;
|
|
||||||
const char *mime_type;
|
|
||||||
GInputStream *stream = NULL;
|
|
||||||
GTask *task = NULL;
|
|
||||||
|
|
||||||
g_assert (G_IS_OBJECT (object));
|
|
||||||
g_assert (pasteboard != NULL);
|
|
||||||
g_assert (formats != NULL);
|
|
||||||
|
|
||||||
task = g_task_new (object, cancellable, callback, user_data);
|
|
||||||
g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async);
|
|
||||||
g_task_set_priority (task, io_priority);
|
|
||||||
|
|
||||||
offer_formats = load_offer_formats (pasteboard);
|
|
||||||
mime_type = gdk_content_formats_match_mime_type (formats, offer_formats);
|
|
||||||
|
|
||||||
if (mime_type == NULL)
|
|
||||||
{
|
|
||||||
g_task_return_new_error (task,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_NOT_SUPPORTED,
|
|
||||||
"%s",
|
|
||||||
_("No compatible transfer format found"));
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp (mime_type, "text/plain;charset=utf-8") == 0)
|
|
||||||
{
|
|
||||||
NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString];
|
|
||||||
|
|
||||||
if (nsstr != NULL)
|
|
||||||
{
|
|
||||||
const char *str = [nsstr UTF8String];
|
|
||||||
stream = g_memory_input_stream_new_from_data (g_strdup (str),
|
|
||||||
strlen (str) + 1,
|
|
||||||
g_free);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (strcmp (mime_type, "text/uri-list") == 0)
|
|
||||||
{
|
|
||||||
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
|
||||||
|
|
||||||
if ([[pasteboard types] containsObject:PTYPE(FILE_URL)])
|
|
||||||
{
|
|
||||||
GString *str = g_string_new (NULL);
|
|
||||||
NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
|
|
||||||
gsize n_files = [files count];
|
|
||||||
char *data;
|
|
||||||
guint len;
|
|
||||||
|
|
||||||
for (gsize i = 0; i < n_files; ++i)
|
|
||||||
{
|
|
||||||
NSString* uriString = [files objectAtIndex:i];
|
|
||||||
uriString = [@"file://" stringByAppendingString:uriString];
|
|
||||||
uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
g_string_append_printf (str,
|
|
||||||
"%s\r\n",
|
|
||||||
[uriString cStringUsingEncoding:NSUTF8StringEncoding]);
|
|
||||||
}
|
|
||||||
|
|
||||||
len = str->len;
|
|
||||||
data = g_string_free (str, FALSE);
|
|
||||||
stream = g_memory_input_stream_new_from_data (data, len, g_free);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_GNUC_END_IGNORE_DEPRECATIONS;
|
|
||||||
}
|
|
||||||
else if (strcmp (mime_type, "application/x-color") == 0)
|
|
||||||
{
|
|
||||||
NSColorSpace *colorspace;
|
|
||||||
NSColor *nscolor;
|
|
||||||
guint16 color[4];
|
|
||||||
|
|
||||||
colorspace = [NSColorSpace genericRGBColorSpace];
|
|
||||||
nscolor = [[NSColor colorFromPasteboard:pasteboard]
|
|
||||||
colorUsingColorSpace:colorspace];
|
|
||||||
|
|
||||||
color[0] = 0xffff * [nscolor redComponent];
|
|
||||||
color[1] = 0xffff * [nscolor greenComponent];
|
|
||||||
color[2] = 0xffff * [nscolor blueComponent];
|
|
||||||
color[3] = 0xffff * [nscolor alphaComponent];
|
|
||||||
|
|
||||||
stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color),
|
|
||||||
sizeof color,
|
|
||||||
g_free);
|
|
||||||
}
|
|
||||||
else if (strcmp (mime_type, "image/tiff") == 0)
|
|
||||||
{
|
|
||||||
NSData *data = [pasteboard dataForType:PTYPE(TIFF)];
|
|
||||||
stream = create_stream_from_nsdata (data);
|
|
||||||
}
|
|
||||||
else if (strcmp (mime_type, "image/png") == 0)
|
|
||||||
{
|
|
||||||
NSData *data = [pasteboard dataForType:PTYPE(PNG)];
|
|
||||||
stream = create_stream_from_nsdata (data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream != NULL)
|
|
||||||
{
|
|
||||||
g_task_set_task_data (task, g_strdup (mime_type), g_free);
|
|
||||||
g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g_task_return_new_error (task,
|
|
||||||
G_IO_ERROR,
|
|
||||||
G_IO_ERROR_FAILED,
|
|
||||||
_("Failed to decode contents with mime-type of '%s'"),
|
|
||||||
mime_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
g_clear_object (&task);
|
|
||||||
g_clear_pointer (&offer_formats, gdk_content_formats_unref);
|
|
||||||
|
|
||||||
GDK_END_MACOS_ALLOC_POOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GInputStream *
|
|
||||||
_gdk_macos_pasteboard_read_finish (GObject *object,
|
|
||||||
GAsyncResult *result,
|
|
||||||
const char **out_mime_type,
|
|
||||||
GError **error)
|
|
||||||
{
|
|
||||||
GTask *task = (GTask *)result;
|
|
||||||
|
|
||||||
g_assert (G_IS_OBJECT (object));
|
|
||||||
g_assert (G_IS_TASK (task));
|
|
||||||
|
|
||||||
if (out_mime_type != NULL)
|
|
||||||
*out_mime_type = g_strdup (g_task_get_task_data (task));
|
|
||||||
|
|
||||||
return g_task_propagate_pointer (task, error);
|
|
||||||
}
|
|
||||||
|
@ -155,12 +155,13 @@ void _gdk_macos_display_surface_became_key (GdkMacosDisp
|
|||||||
GdkMacosSurface *surface);
|
GdkMacosSurface *surface);
|
||||||
void _gdk_macos_display_clear_sorting (GdkMacosDisplay *self);
|
void _gdk_macos_display_clear_sorting (GdkMacosDisplay *self);
|
||||||
const GList *_gdk_macos_display_get_surfaces (GdkMacosDisplay *self);
|
const GList *_gdk_macos_display_get_surfaces (GdkMacosDisplay *self);
|
||||||
void _gdk_macos_display_send_button_event (GdkMacosDisplay *self,
|
void _gdk_macos_display_send_event (GdkMacosDisplay *self,
|
||||||
NSEvent *nsevent);
|
NSEvent *nsevent);
|
||||||
void _gdk_macos_display_warp_pointer (GdkMacosDisplay *self,
|
void _gdk_macos_display_warp_pointer (GdkMacosDisplay *self,
|
||||||
int x,
|
int x,
|
||||||
int y);
|
int y);
|
||||||
NSEvent *_gdk_macos_display_get_nsevent (GdkEvent *event);
|
NSEvent *_gdk_macos_display_get_nsevent (GdkEvent *event);
|
||||||
|
NSEvent *_gdk_macos_display_get_last_nsevent (void);
|
||||||
GdkDrag *_gdk_macos_display_find_drag (GdkMacosDisplay *self,
|
GdkDrag *_gdk_macos_display_find_drag (GdkMacosDisplay *self,
|
||||||
NSInteger sequence_number);
|
NSInteger sequence_number);
|
||||||
GdkDrop *_gdk_macos_display_find_drop (GdkMacosDisplay *self,
|
GdkDrop *_gdk_macos_display_find_drop (GdkMacosDisplay *self,
|
||||||
|
@ -723,6 +723,85 @@ fill_scroll_event (GdkMacosDisplay *self,
|
|||||||
return g_steal_pointer (&ret);
|
return g_steal_pointer (&ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static GdkEvent *
|
||||||
|
fill_event (GdkMacosDisplay *self,
|
||||||
|
GdkMacosWindow *window,
|
||||||
|
NSEvent *nsevent,
|
||||||
|
int x,
|
||||||
|
int y)
|
||||||
|
{
|
||||||
|
GdkMacosSurface *surface = [window gdkSurface];
|
||||||
|
NSEventType event_type = [nsevent type];
|
||||||
|
GdkEvent *ret = NULL;
|
||||||
|
|
||||||
|
switch ((int)event_type)
|
||||||
|
{
|
||||||
|
case NSEventTypeLeftMouseDown:
|
||||||
|
case NSEventTypeRightMouseDown:
|
||||||
|
case NSEventTypeOtherMouseDown:
|
||||||
|
case NSEventTypeLeftMouseUp:
|
||||||
|
case NSEventTypeRightMouseUp:
|
||||||
|
case NSEventTypeOtherMouseUp:
|
||||||
|
ret = fill_button_event (self, surface, nsevent, x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NSEventTypeLeftMouseDragged:
|
||||||
|
case NSEventTypeRightMouseDragged:
|
||||||
|
case NSEventTypeOtherMouseDragged:
|
||||||
|
case NSEventTypeMouseMoved:
|
||||||
|
ret = fill_motion_event (self, surface, nsevent, x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NSEventTypeMagnify:
|
||||||
|
case NSEventTypeRotate:
|
||||||
|
ret = fill_pinch_event (self, surface, nsevent, x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NSEventTypeMouseExited:
|
||||||
|
case NSEventTypeMouseEntered:
|
||||||
|
{
|
||||||
|
GdkSeat *seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
|
||||||
|
GdkDevice *pointer = gdk_seat_get_pointer (seat);
|
||||||
|
GdkDeviceGrabInfo *grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), pointer);
|
||||||
|
|
||||||
|
if ([(GdkMacosWindow *)window isInManualResizeOrMove])
|
||||||
|
{
|
||||||
|
ret = GDK_MACOS_EVENT_DROP;
|
||||||
|
}
|
||||||
|
else if (grab == NULL)
|
||||||
|
{
|
||||||
|
if (event_type == NSEventTypeMouseExited)
|
||||||
|
[[NSCursor arrowCursor] set];
|
||||||
|
|
||||||
|
ret = synthesize_crossing_event (self, surface, nsevent, x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NSEventTypeKeyDown:
|
||||||
|
case NSEventTypeKeyUp:
|
||||||
|
case NSEventTypeFlagsChanged: {
|
||||||
|
GdkEventType type = _gdk_macos_keymap_get_event_type (nsevent);
|
||||||
|
|
||||||
|
if (type)
|
||||||
|
ret = fill_key_event (self, surface, nsevent, type);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NSEventTypeScrollWheel:
|
||||||
|
ret = fill_scroll_event (self, surface, nsevent, x, y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
is_mouse_button_press_event (NSEventType type)
|
is_mouse_button_press_event (NSEventType type)
|
||||||
{
|
{
|
||||||
@ -1025,16 +1104,12 @@ find_surface_for_ns_event (GdkMacosDisplay *self,
|
|||||||
GdkMacosBaseView *view;
|
GdkMacosBaseView *view;
|
||||||
GdkSurface *surface;
|
GdkSurface *surface;
|
||||||
NSPoint point;
|
NSPoint point;
|
||||||
int x_tmp;
|
|
||||||
int y_tmp;
|
|
||||||
|
|
||||||
g_assert (GDK_IS_MACOS_DISPLAY (self));
|
g_assert (GDK_IS_MACOS_DISPLAY (self));
|
||||||
g_assert (nsevent != NULL);
|
g_assert (nsevent != NULL);
|
||||||
g_assert (x != NULL);
|
g_assert (x != NULL);
|
||||||
g_assert (y != NULL);
|
g_assert (y != NULL);
|
||||||
|
|
||||||
_gdk_macos_display_from_display_coords (self, point.x, point.y, &x_tmp, &y_tmp);
|
|
||||||
|
|
||||||
switch ((int)[nsevent type])
|
switch ((int)[nsevent type])
|
||||||
{
|
{
|
||||||
case NSEventTypeLeftMouseDown:
|
case NSEventTypeLeftMouseDown:
|
||||||
@ -1083,7 +1158,6 @@ _gdk_macos_display_translate (GdkMacosDisplay *self,
|
|||||||
GdkMacosWindow *window;
|
GdkMacosWindow *window;
|
||||||
NSEventType event_type;
|
NSEventType event_type;
|
||||||
NSWindow *event_window;
|
NSWindow *event_window;
|
||||||
GdkEvent *ret = NULL;
|
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
@ -1191,79 +1265,15 @@ _gdk_macos_display_translate (GdkMacosDisplay *self,
|
|||||||
_gdk_macos_display_clear_sorting (self);
|
_gdk_macos_display_clear_sorting (self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fill_event (self, window, nsevent, x, y);
|
||||||
switch ((int)event_type)
|
|
||||||
{
|
|
||||||
case NSEventTypeLeftMouseDown:
|
|
||||||
case NSEventTypeRightMouseDown:
|
|
||||||
case NSEventTypeOtherMouseDown:
|
|
||||||
case NSEventTypeLeftMouseUp:
|
|
||||||
case NSEventTypeRightMouseUp:
|
|
||||||
case NSEventTypeOtherMouseUp:
|
|
||||||
ret = fill_button_event (self, surface, nsevent, x, y);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NSEventTypeLeftMouseDragged:
|
|
||||||
case NSEventTypeRightMouseDragged:
|
|
||||||
case NSEventTypeOtherMouseDragged:
|
|
||||||
case NSEventTypeMouseMoved:
|
|
||||||
ret = fill_motion_event (self, surface, nsevent, x, y);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NSEventTypeMagnify:
|
|
||||||
case NSEventTypeRotate:
|
|
||||||
ret = fill_pinch_event (self, surface, nsevent, x, y);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NSEventTypeMouseExited:
|
|
||||||
case NSEventTypeMouseEntered:
|
|
||||||
{
|
|
||||||
GdkSeat *seat = gdk_display_get_default_seat (GDK_DISPLAY (self));
|
|
||||||
GdkDevice *pointer = gdk_seat_get_pointer (seat);
|
|
||||||
GdkDeviceGrabInfo *grab = _gdk_display_get_last_device_grab (GDK_DISPLAY (self), pointer);
|
|
||||||
|
|
||||||
if ([(GdkMacosWindow *)window isInManualResizeOrMove])
|
|
||||||
{
|
|
||||||
ret = GDK_MACOS_EVENT_DROP;
|
|
||||||
}
|
|
||||||
else if (grab == NULL)
|
|
||||||
{
|
|
||||||
if (event_type == NSEventTypeMouseExited)
|
|
||||||
[[NSCursor arrowCursor] set];
|
|
||||||
|
|
||||||
ret = synthesize_crossing_event (self, surface, nsevent, x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NSEventTypeKeyDown:
|
|
||||||
case NSEventTypeKeyUp:
|
|
||||||
case NSEventTypeFlagsChanged: {
|
|
||||||
GdkEventType type = _gdk_macos_keymap_get_event_type (nsevent);
|
|
||||||
|
|
||||||
if (type)
|
|
||||||
ret = fill_key_event (self, surface, nsevent, type);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case NSEventTypeScrollWheel:
|
|
||||||
ret = fill_scroll_event (self, surface, nsevent, x, y);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
_gdk_macos_display_send_button_event (GdkMacosDisplay *self,
|
_gdk_macos_display_send_event (GdkMacosDisplay *self,
|
||||||
NSEvent *nsevent)
|
NSEvent *nsevent)
|
||||||
{
|
{
|
||||||
GdkMacosSurface *surface;
|
GdkMacosSurface *surface;
|
||||||
|
GdkMacosWindow *window;
|
||||||
GdkEvent *event;
|
GdkEvent *event;
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
@ -1272,7 +1282,8 @@ _gdk_macos_display_send_button_event (GdkMacosDisplay *self,
|
|||||||
g_return_if_fail (nsevent != NULL);
|
g_return_if_fail (nsevent != NULL);
|
||||||
|
|
||||||
if ((surface = find_surface_for_ns_event (self, nsevent, &x, &y)) &&
|
if ((surface = find_surface_for_ns_event (self, nsevent, &x, &y)) &&
|
||||||
(event = fill_button_event (self, surface, nsevent, x, y)))
|
(window = (GdkMacosWindow *)_gdk_macos_surface_get_native (surface)) &&
|
||||||
|
(event = fill_event (self, window, nsevent, x, y)))
|
||||||
_gdk_windowing_got_event (GDK_DISPLAY (self),
|
_gdk_windowing_got_event (GDK_DISPLAY (self),
|
||||||
_gdk_event_queue_append (GDK_DISPLAY (self), event),
|
_gdk_event_queue_append (GDK_DISPLAY (self), event),
|
||||||
event,
|
event,
|
||||||
|
@ -1024,6 +1024,16 @@ _gdk_macos_display_get_nsevent (GdkEvent *event)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSEvent *
|
||||||
|
_gdk_macos_display_get_last_nsevent ()
|
||||||
|
{
|
||||||
|
const GdkToNSEventMap *map = g_queue_peek_tail (&event_map);
|
||||||
|
if (map)
|
||||||
|
return map->nsevent;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
GdkDrag *
|
GdkDrag *
|
||||||
_gdk_macos_display_find_drag (GdkMacosDisplay *self,
|
_gdk_macos_display_find_drag (GdkMacosDisplay *self,
|
||||||
NSInteger sequence_number)
|
NSInteger sequence_number)
|
||||||
|
@ -41,7 +41,6 @@ struct _GdkMacosDrag
|
|||||||
GdkDrag parent_instance;
|
GdkDrag parent_instance;
|
||||||
|
|
||||||
GdkMacosDragSurface *drag_surface;
|
GdkMacosDragSurface *drag_surface;
|
||||||
GdkSeat *drag_seat;
|
|
||||||
GdkCursor *cursor;
|
GdkCursor *cursor;
|
||||||
|
|
||||||
int hot_x;
|
int hot_x;
|
||||||
@ -62,8 +61,22 @@ struct _GdkMacosDragClass
|
|||||||
GdkDragClass parent_class;
|
GdkDragClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gdk_macos_drag_get_type (void) G_GNUC_CONST;
|
GType gdk_macos_drag_get_type (void) G_GNUC_CONST;
|
||||||
gboolean _gdk_macos_drag_begin (GdkMacosDrag *self);
|
gboolean _gdk_macos_drag_begin (GdkMacosDrag *self,
|
||||||
|
GdkContentProvider *content,
|
||||||
|
GdkMacosWindow *window);
|
||||||
|
NSDragOperation _gdk_macos_drag_operation (GdkMacosDrag *self);
|
||||||
|
GdkDragAction _gdk_macos_drag_ns_operation_to_action
|
||||||
|
(NSDragOperation operation);
|
||||||
|
void _gdk_macos_drag_surface_move (GdkMacosDrag *self,
|
||||||
|
int x_root,
|
||||||
|
int y_root);
|
||||||
|
void _gdk_macos_drag_set_start_position (GdkMacosDrag *self,
|
||||||
|
int start_x,
|
||||||
|
int start_y);
|
||||||
|
void _gdk_macos_drag_set_actions (GdkMacosDrag *self,
|
||||||
|
GdkModifierType mods);
|
||||||
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "gdkmacoscursor-private.h"
|
#include "gdkmacoscursor-private.h"
|
||||||
#include "gdkmacosdisplay-private.h"
|
#include "gdkmacosdisplay-private.h"
|
||||||
#include "gdkmacosdragsurface-private.h"
|
#include "gdkmacosdragsurface-private.h"
|
||||||
|
#include "gdkmacospasteboard-private.h"
|
||||||
|
|
||||||
#include "gdk/gdkdeviceprivate.h"
|
#include "gdk/gdkdeviceprivate.h"
|
||||||
#include "gdk/gdkeventsprivate.h"
|
#include "gdk/gdkeventsprivate.h"
|
||||||
@ -187,47 +188,6 @@ gdk_macos_drag_set_cursor (GdkDrag *drag,
|
|||||||
[nscursor set];
|
[nscursor set];
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
|
||||||
drag_grab (GdkMacosDrag *self)
|
|
||||||
{
|
|
||||||
GdkSeat *seat;
|
|
||||||
|
|
||||||
g_assert (GDK_IS_MACOS_DRAG (self));
|
|
||||||
|
|
||||||
seat = gdk_device_get_seat (gdk_drag_get_device (GDK_DRAG (self)));
|
|
||||||
|
|
||||||
if (gdk_seat_grab (seat,
|
|
||||||
GDK_SURFACE (self->drag_surface),
|
|
||||||
GDK_SEAT_CAPABILITY_ALL_POINTING,
|
|
||||||
FALSE,
|
|
||||||
self->cursor,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL) != GDK_GRAB_SUCCESS)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
g_set_object (&self->drag_seat, seat);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
drag_ungrab (GdkMacosDrag *self)
|
|
||||||
{
|
|
||||||
GdkDisplay *display;
|
|
||||||
|
|
||||||
g_assert (GDK_IS_MACOS_DRAG (self));
|
|
||||||
|
|
||||||
if (self->drag_seat)
|
|
||||||
{
|
|
||||||
gdk_seat_ungrab (self->drag_seat);
|
|
||||||
g_clear_object (&self->drag_seat);
|
|
||||||
}
|
|
||||||
|
|
||||||
display = gdk_drag_get_display (GDK_DRAG (self));
|
|
||||||
_gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), GDK_CURRENT_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gdk_macos_drag_cancel (GdkDrag *drag,
|
gdk_macos_drag_cancel (GdkDrag *drag,
|
||||||
GdkDragCancelReason reason)
|
GdkDragCancelReason reason)
|
||||||
@ -240,7 +200,6 @@ gdk_macos_drag_cancel (GdkDrag *drag,
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
self->cancelled = TRUE;
|
self->cancelled = TRUE;
|
||||||
drag_ungrab (self);
|
|
||||||
gdk_drag_drop_done (drag, FALSE);
|
gdk_drag_drop_done (drag, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,7 +212,6 @@ gdk_macos_drag_drop_performed (GdkDrag *drag,
|
|||||||
g_assert (GDK_IS_MACOS_DRAG (self));
|
g_assert (GDK_IS_MACOS_DRAG (self));
|
||||||
|
|
||||||
g_object_ref (self);
|
g_object_ref (self);
|
||||||
drag_ungrab (self);
|
|
||||||
g_signal_emit_by_name (drag, "dnd-finished");
|
g_signal_emit_by_name (drag, "dnd-finished");
|
||||||
gdk_drag_drop_done (drag, TRUE);
|
gdk_drag_drop_done (drag, TRUE);
|
||||||
g_object_unref (self);
|
g_object_unref (self);
|
||||||
@ -316,225 +274,6 @@ gdk_drag_get_current_actions (GdkModifierType state,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
|
||||||
gdk_drag_update (GdkDrag *drag,
|
|
||||||
double x_root,
|
|
||||||
double y_root,
|
|
||||||
GdkModifierType mods,
|
|
||||||
guint32 evtime)
|
|
||||||
{
|
|
||||||
GdkMacosDrag *self = (GdkMacosDrag *)drag;
|
|
||||||
GdkDragAction suggested_action;
|
|
||||||
GdkDragAction possible_actions;
|
|
||||||
|
|
||||||
g_assert (GDK_IS_MACOS_DRAG (self));
|
|
||||||
|
|
||||||
self->last_x = x_root;
|
|
||||||
self->last_y = y_root;
|
|
||||||
|
|
||||||
gdk_drag_get_current_actions (mods,
|
|
||||||
GDK_BUTTON_PRIMARY,
|
|
||||||
gdk_drag_get_actions (drag),
|
|
||||||
&suggested_action,
|
|
||||||
&possible_actions);
|
|
||||||
|
|
||||||
if (GDK_IS_MACOS_SURFACE (self->drag_surface))
|
|
||||||
_gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface),
|
|
||||||
x_root - self->hot_x,
|
|
||||||
y_root - self->hot_y);
|
|
||||||
|
|
||||||
if (!self->did_update)
|
|
||||||
{
|
|
||||||
self->start_x = self->last_x;
|
|
||||||
self->start_y = self->last_y;
|
|
||||||
self->did_update = TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
gdk_drag_set_actions (drag, possible_actions);
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gdk_dnd_handle_motion_event (GdkDrag *drag,
|
|
||||||
GdkEvent *event)
|
|
||||||
{
|
|
||||||
double x, y;
|
|
||||||
int x_root, y_root;
|
|
||||||
|
|
||||||
g_assert (GDK_IS_MACOS_DRAG (drag));
|
|
||||||
g_assert (event != NULL);
|
|
||||||
|
|
||||||
/* Ignore motion while doing zoomback */
|
|
||||||
if (GDK_MACOS_DRAG (drag)->cancelled)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gdk_event_get_position (event, &x, &y);
|
|
||||||
x_root = event->surface->x + x;
|
|
||||||
y_root = event->surface->y + y;
|
|
||||||
gdk_drag_update (drag, x_root, y_root,
|
|
||||||
gdk_event_get_modifier_state (event),
|
|
||||||
gdk_event_get_time (event));
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gdk_dnd_handle_grab_broken_event (GdkDrag *drag,
|
|
||||||
GdkEvent *event)
|
|
||||||
{
|
|
||||||
GdkMacosDrag *self = GDK_MACOS_DRAG (drag);
|
|
||||||
gboolean is_implicit = gdk_grab_broken_event_get_implicit (event);
|
|
||||||
GdkSurface *grab_surface = gdk_grab_broken_event_get_grab_surface (event);
|
|
||||||
|
|
||||||
/* Don't cancel if we break the implicit grab from the initial button_press. */
|
|
||||||
if (is_implicit || grab_surface == (GdkSurface *)self->drag_surface)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
if (gdk_event_get_device (event) != gdk_drag_get_device (drag))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
gdk_drag_cancel (drag, GDK_DRAG_CANCEL_ERROR);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gdk_dnd_handle_button_event (GdkDrag *drag,
|
|
||||||
GdkEvent *event)
|
|
||||||
{
|
|
||||||
GdkMacosDrag *self = GDK_MACOS_DRAG (drag);
|
|
||||||
|
|
||||||
g_assert (GDK_IS_MACOS_DRAG (self));
|
|
||||||
g_assert (event != NULL);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* FIXME: Check the button matches */
|
|
||||||
if (event->button != self->button)
|
|
||||||
return FALSE;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (gdk_drag_get_selected_action (drag) != 0)
|
|
||||||
g_signal_emit_by_name (drag, "drop-performed");
|
|
||||||
else
|
|
||||||
gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gdk_dnd_handle_key_event (GdkDrag *drag,
|
|
||||||
GdkEvent *event)
|
|
||||||
{
|
|
||||||
GdkMacosDrag *self = GDK_MACOS_DRAG (drag);
|
|
||||||
GdkModifierType state;
|
|
||||||
GdkDevice *pointer;
|
|
||||||
GdkSeat *seat;
|
|
||||||
int dx, dy;
|
|
||||||
|
|
||||||
dx = dy = 0;
|
|
||||||
state = gdk_event_get_modifier_state (event);
|
|
||||||
seat = gdk_event_get_seat (event);
|
|
||||||
pointer = gdk_seat_get_pointer (seat);
|
|
||||||
|
|
||||||
if (event->event_type == GDK_KEY_PRESS)
|
|
||||||
{
|
|
||||||
guint keyval = gdk_key_event_get_keyval (event);
|
|
||||||
|
|
||||||
switch (keyval)
|
|
||||||
{
|
|
||||||
case GDK_KEY_Escape:
|
|
||||||
gdk_drag_cancel (drag, GDK_DRAG_CANCEL_USER_CANCELLED);
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
case GDK_KEY_space:
|
|
||||||
case GDK_KEY_Return:
|
|
||||||
case GDK_KEY_ISO_Enter:
|
|
||||||
case GDK_KEY_KP_Enter:
|
|
||||||
case GDK_KEY_KP_Space:
|
|
||||||
if (gdk_drag_get_selected_action (drag) != 0)
|
|
||||||
g_signal_emit_by_name (drag, "drop-performed");
|
|
||||||
else
|
|
||||||
gdk_drag_cancel (drag, GDK_DRAG_CANCEL_NO_TARGET);
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
case GDK_KEY_Up:
|
|
||||||
case GDK_KEY_KP_Up:
|
|
||||||
dy = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GDK_KEY_Down:
|
|
||||||
case GDK_KEY_KP_Down:
|
|
||||||
dy = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GDK_KEY_Left:
|
|
||||||
case GDK_KEY_KP_Left:
|
|
||||||
dx = (state & GDK_ALT_MASK) ? -BIG_STEP : -SMALL_STEP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GDK_KEY_Right:
|
|
||||||
case GDK_KEY_KP_Right:
|
|
||||||
dx = (state & GDK_ALT_MASK) ? BIG_STEP : SMALL_STEP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The state is not yet updated in the event, so we need
|
|
||||||
* to query it here. We could use XGetModifierMapping, but
|
|
||||||
* that would be overkill.
|
|
||||||
*/
|
|
||||||
gdk_macos_device_query_state (pointer, NULL, NULL, NULL, NULL, &state);
|
|
||||||
|
|
||||||
if (dx != 0 || dy != 0)
|
|
||||||
{
|
|
||||||
GdkDisplay *display = gdk_event_get_display ((GdkEvent *)event);
|
|
||||||
|
|
||||||
self->last_x += dx;
|
|
||||||
self->last_y += dy;
|
|
||||||
|
|
||||||
_gdk_macos_display_warp_pointer (GDK_MACOS_DISPLAY (display),
|
|
||||||
self->last_x,
|
|
||||||
self->last_y);
|
|
||||||
}
|
|
||||||
|
|
||||||
gdk_drag_update (drag,
|
|
||||||
self->last_x, self->last_y,
|
|
||||||
state,
|
|
||||||
gdk_event_get_time (event));
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static gboolean
|
|
||||||
gdk_macos_drag_handle_event (GdkDrag *drag,
|
|
||||||
GdkEvent *event)
|
|
||||||
{
|
|
||||||
g_assert (GDK_IS_MACOS_DRAG (drag));
|
|
||||||
g_assert (event != NULL);
|
|
||||||
|
|
||||||
switch ((guint) event->event_type)
|
|
||||||
{
|
|
||||||
case GDK_MOTION_NOTIFY:
|
|
||||||
return gdk_dnd_handle_motion_event (drag, event);
|
|
||||||
|
|
||||||
case GDK_BUTTON_RELEASE:
|
|
||||||
return gdk_dnd_handle_button_event (drag, event);
|
|
||||||
|
|
||||||
case GDK_KEY_PRESS:
|
|
||||||
case GDK_KEY_RELEASE:
|
|
||||||
return gdk_dnd_handle_key_event (drag, event);
|
|
||||||
|
|
||||||
case GDK_GRAB_BROKEN:
|
|
||||||
return gdk_dnd_handle_grab_broken_event (drag, event);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gdk_macos_drag_finalize (GObject *object)
|
gdk_macos_drag_finalize (GObject *object)
|
||||||
{
|
{
|
||||||
@ -542,11 +281,6 @@ gdk_macos_drag_finalize (GObject *object)
|
|||||||
GdkMacosDragSurface *drag_surface = g_steal_pointer (&self->drag_surface);
|
GdkMacosDragSurface *drag_surface = g_steal_pointer (&self->drag_surface);
|
||||||
|
|
||||||
g_clear_object (&self->cursor);
|
g_clear_object (&self->cursor);
|
||||||
if (self->drag_seat)
|
|
||||||
{
|
|
||||||
gdk_seat_ungrab (self->drag_seat);
|
|
||||||
g_clear_object (&self->drag_seat);
|
|
||||||
}
|
|
||||||
|
|
||||||
G_OBJECT_CLASS (gdk_macos_drag_parent_class)->finalize (object);
|
G_OBJECT_CLASS (gdk_macos_drag_parent_class)->finalize (object);
|
||||||
|
|
||||||
@ -608,7 +342,6 @@ gdk_macos_drag_class_init (GdkMacosDragClass *klass)
|
|||||||
drag_class->set_cursor = gdk_macos_drag_set_cursor;
|
drag_class->set_cursor = gdk_macos_drag_set_cursor;
|
||||||
drag_class->cancel = gdk_macos_drag_cancel;
|
drag_class->cancel = gdk_macos_drag_cancel;
|
||||||
drag_class->drop_performed = gdk_macos_drag_drop_performed;
|
drag_class->drop_performed = gdk_macos_drag_drop_performed;
|
||||||
drag_class->handle_event = gdk_macos_drag_handle_event;
|
|
||||||
|
|
||||||
properties [PROP_DRAG_SURFACE] =
|
properties [PROP_DRAG_SURFACE] =
|
||||||
g_param_spec_object ("drag-surface", NULL, NULL,
|
g_param_spec_object ("drag-surface", NULL, NULL,
|
||||||
@ -624,11 +357,113 @@ gdk_macos_drag_init (GdkMacosDrag *self)
|
|||||||
}
|
}
|
||||||
|
|
||||||
gboolean
|
gboolean
|
||||||
_gdk_macos_drag_begin (GdkMacosDrag *self)
|
_gdk_macos_drag_begin (GdkMacosDrag *self,
|
||||||
|
GdkContentProvider *content,
|
||||||
|
GdkMacosWindow *window)
|
||||||
{
|
{
|
||||||
|
NSArray<NSDraggingItem *> *items;
|
||||||
|
NSDraggingSession *session;
|
||||||
|
NSPasteboardItem *item;
|
||||||
|
NSEvent *nsevent;
|
||||||
|
|
||||||
g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), FALSE);
|
g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), FALSE);
|
||||||
|
g_return_val_if_fail (GDK_IS_MACOS_WINDOW (window), FALSE);
|
||||||
|
|
||||||
_gdk_macos_surface_show (GDK_MACOS_SURFACE (self->drag_surface));
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
return drag_grab (self);
|
item = [[GdkMacosPasteboardItem alloc] initForDrag:GDK_DRAG (self) withContentProvider:content];
|
||||||
|
items = [NSArray arrayWithObject:item];
|
||||||
|
nsevent = _gdk_macos_display_get_last_nsevent ();
|
||||||
|
|
||||||
|
session = [[window contentView] beginDraggingSessionWithItems:items
|
||||||
|
event:nsevent
|
||||||
|
source:window];
|
||||||
|
|
||||||
|
GDK_END_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
|
_gdk_macos_display_set_drag (GDK_MACOS_DISPLAY (gdk_drag_get_display (GDK_DRAG (self))),
|
||||||
|
[session draggingSequenceNumber],
|
||||||
|
GDK_DRAG (self));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDragOperation
|
||||||
|
_gdk_macos_drag_operation (GdkMacosDrag *self)
|
||||||
|
{
|
||||||
|
NSDragOperation operation = NSDragOperationNone;
|
||||||
|
GdkDragAction actions;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GDK_IS_MACOS_DRAG (self), NSDragOperationNone);
|
||||||
|
|
||||||
|
actions = gdk_drag_get_actions (GDK_DRAG (self));
|
||||||
|
|
||||||
|
if (actions & GDK_ACTION_LINK)
|
||||||
|
operation |= NSDragOperationLink;
|
||||||
|
|
||||||
|
if (actions & GDK_ACTION_MOVE)
|
||||||
|
operation |= NSDragOperationMove;
|
||||||
|
|
||||||
|
if (actions & GDK_ACTION_COPY)
|
||||||
|
operation |= NSDragOperationCopy;
|
||||||
|
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
|
||||||
|
GdkDragAction
|
||||||
|
_gdk_macos_drag_ns_operation_to_action (NSDragOperation operation)
|
||||||
|
{
|
||||||
|
if (operation & NSDragOperationCopy)
|
||||||
|
return GDK_ACTION_COPY;
|
||||||
|
if (operation & NSDragOperationMove)
|
||||||
|
return GDK_ACTION_MOVE;
|
||||||
|
if (operation & NSDragOperationLink)
|
||||||
|
return GDK_ACTION_LINK;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_gdk_macos_drag_surface_move (GdkMacosDrag *self,
|
||||||
|
int x_root,
|
||||||
|
int y_root)
|
||||||
|
{
|
||||||
|
g_return_if_fail (GDK_IS_MACOS_DRAG (self));
|
||||||
|
|
||||||
|
self->last_x = x_root;
|
||||||
|
self->last_y = y_root;
|
||||||
|
|
||||||
|
if (GDK_IS_MACOS_SURFACE (self->drag_surface))
|
||||||
|
_gdk_macos_surface_move (GDK_MACOS_SURFACE (self->drag_surface),
|
||||||
|
x_root - self->hot_x,
|
||||||
|
y_root - self->hot_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_gdk_macos_drag_set_start_position (GdkMacosDrag *self,
|
||||||
|
int start_x,
|
||||||
|
int start_y)
|
||||||
|
{
|
||||||
|
g_return_if_fail (GDK_IS_MACOS_DRAG (self));
|
||||||
|
|
||||||
|
self->start_x = start_x;
|
||||||
|
self->start_y = start_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_gdk_macos_drag_set_actions (GdkMacosDrag *self,
|
||||||
|
GdkModifierType mods)
|
||||||
|
{
|
||||||
|
GdkDragAction suggested_action;
|
||||||
|
GdkDragAction possible_actions;
|
||||||
|
|
||||||
|
g_assert (GDK_IS_MACOS_DRAG (self));
|
||||||
|
|
||||||
|
gdk_drag_get_current_actions (mods,
|
||||||
|
GDK_BUTTON_PRIMARY,
|
||||||
|
gdk_drag_get_actions (GDK_DRAG (self)),
|
||||||
|
&suggested_action,
|
||||||
|
&possible_actions);
|
||||||
|
|
||||||
|
gdk_drag_set_selected_action (GDK_DRAG (self), suggested_action);
|
||||||
|
gdk_drag_set_actions (GDK_DRAG (self), possible_actions);
|
||||||
}
|
}
|
||||||
|
@ -37,11 +37,15 @@
|
|||||||
/*
|
/*
|
||||||
* This file implementations integration between the GLib main loop and
|
* This file implementations integration between the GLib main loop and
|
||||||
* the native system of the Core Foundation run loop and Cocoa event
|
* the native system of the Core Foundation run loop and Cocoa event
|
||||||
* handling. There are basically two different cases that we need to
|
* handling. There are basically three different cases that we need to
|
||||||
* handle: either the GLib main loop is in control (the application
|
* handle:
|
||||||
* has called gtk_main(), or is otherwise iterating the main loop), or
|
*
|
||||||
* CFRunLoop is in control (we are in a modal operation such as window
|
* - the GLib main loop is in control. The application has called
|
||||||
* resizing or drag-and-drop.)
|
* gtk_main(), or is otherwise iterating the main loop.
|
||||||
|
* - CFRunLoop is in control. We are in a modal operation such as window
|
||||||
|
* resizing.
|
||||||
|
* - CFRunLoop is running a nested loop. This happens when a drag-and-drop
|
||||||
|
* operation has been initiated.
|
||||||
*
|
*
|
||||||
* When the GLib main loop is in control we integrate in native event
|
* When the GLib main loop is in control we integrate in native event
|
||||||
* handling in two ways: first we add a GSource that handles checking
|
* handling in two ways: first we add a GSource that handles checking
|
||||||
@ -57,14 +61,23 @@
|
|||||||
* stages of the GLib main loop (prepare, check, dispatch), and make the
|
* stages of the GLib main loop (prepare, check, dispatch), and make the
|
||||||
* appropriate calls into GLib.
|
* appropriate calls into GLib.
|
||||||
*
|
*
|
||||||
* Both cases share a single problem: the OS X API’s don’t allow us to
|
* When initiating a drag operation, a nested CFRunLoop is executed.
|
||||||
|
* The nested run loop is started when fetching a native event in our GLib
|
||||||
|
* main loop. The application does not receive any events until the nested loop
|
||||||
|
* is finished. We work around this by forwarding the
|
||||||
|
* events that trigger the callbacks of the NSDraggingSource protocol.
|
||||||
|
* The "run loop observer" is executing the GLib main loop stages as long as we're
|
||||||
|
* in the nested run loop, as if CFRunLoop were in control.
|
||||||
|
* See also GdkMacosWindow.
|
||||||
|
*
|
||||||
|
* All cases share a single problem: the macOS API’s don’t allow us to
|
||||||
* wait simultaneously for file descriptors and for events. So when we
|
* wait simultaneously for file descriptors and for events. So when we
|
||||||
* need to do a blocking wait that includes file descriptor activity, we
|
* need to do a blocking wait that includes file descriptor activity, we
|
||||||
* push the actual work of calling select() to a helper thread (the
|
* push the actual work of calling select() to a helper thread (the
|
||||||
* "select thread") and wait for native events in the main thread.
|
* "select thread") and wait for native events in the main thread.
|
||||||
*
|
*
|
||||||
* The main known limitation of this code is that if a callback is triggered
|
* The main known limitation of this code is that if a callback is triggered
|
||||||
* via the OS X run loop while we are "polling" (in either case described
|
* via the macOS run loop while we are "polling" (in either case described
|
||||||
* above), iteration of the GLib main loop is not possible from within
|
* above), iteration of the GLib main loop is not possible from within
|
||||||
* that callback. If the programmer tries to do so explicitly, then they
|
* that callback. If the programmer tries to do so explicitly, then they
|
||||||
* will get a warning from GLib "main loop already active in another thread".
|
* will get a warning from GLib "main loop already active in another thread".
|
||||||
@ -640,6 +653,23 @@ _gdk_macos_event_source_get_pending (void)
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_gdk_macos_event_source_queue_event (NSEvent *event)
|
||||||
|
{
|
||||||
|
/* Just used to wake us up; if an event and a FD arrived at the same
|
||||||
|
* time; could have come from a previous iteration in some cases,
|
||||||
|
* but the spurious wake up is harmless if a little inefficient.
|
||||||
|
*/
|
||||||
|
if (!event ||
|
||||||
|
([event type] == NSEventTypeApplicationDefined &&
|
||||||
|
[event subtype] == GDK_MACOS_EVENT_SUBTYPE_EVENTLOOP))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!current_events)
|
||||||
|
current_events = g_queue_new ();
|
||||||
|
g_queue_push_head (current_events, [event retain]);
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gdk_macos_event_source_prepare (GSource *source,
|
gdk_macos_event_source_prepare (GSource *source,
|
||||||
int *timeout)
|
int *timeout)
|
||||||
@ -782,23 +812,7 @@ poll_func (GPollFD *ufds,
|
|||||||
if (last_ufds == ufds && n_ready < 0)
|
if (last_ufds == ufds && n_ready < 0)
|
||||||
n_ready = select_thread_collect_poll (ufds, nfds);
|
n_ready = select_thread_collect_poll (ufds, nfds);
|
||||||
|
|
||||||
if (event &&
|
_gdk_macos_event_source_queue_event (event);
|
||||||
[event type] == NSEventTypeApplicationDefined &&
|
|
||||||
[event subtype] == GDK_MACOS_EVENT_SUBTYPE_EVENTLOOP)
|
|
||||||
{
|
|
||||||
/* Just used to wake us up; if an event and a FD arrived at the same
|
|
||||||
* time; could have come from a previous iteration in some cases,
|
|
||||||
* but the spurious wake up is harmless if a little inefficient.
|
|
||||||
*/
|
|
||||||
event = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event)
|
|
||||||
{
|
|
||||||
if (!current_events)
|
|
||||||
current_events = g_queue_new ();
|
|
||||||
g_queue_push_head (current_events, [event retain]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return n_ready;
|
return n_ready;
|
||||||
}
|
}
|
||||||
@ -1018,7 +1032,10 @@ run_loop_observer_callback (CFRunLoopObserverRef observer,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getting_events > 0) /* Activity we triggered */
|
/* DnD starts a nested runloop, or so it seems.
|
||||||
|
If we have such a loop, we still want to run
|
||||||
|
our idle handlers. */
|
||||||
|
if (getting_events > 0 && current_loop_level < 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (activity)
|
switch (activity)
|
||||||
@ -1042,7 +1059,6 @@ run_loop_observer_callback (CFRunLoopObserverRef observer,
|
|||||||
run_loop_exit ();
|
run_loop_exit ();
|
||||||
break;
|
break;
|
||||||
case kCFRunLoopAllActivities:
|
case kCFRunLoopAllActivities:
|
||||||
/* TODO: Do most of the above? */
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
74
gdk/macos/gdkmacospasteboard-private.h
Normal file
74
gdk/macos/gdkmacospasteboard-private.h
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2021 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __GDK_MACOS_PASTEBOARD_PRIVATE_H__
|
||||||
|
#define __GDK_MACOS_PASTEBOARD_PRIVATE_H__
|
||||||
|
|
||||||
|
#include <AppKit/AppKit.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#include "gdkclipboardprivate.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
@interface GdkMacosPasteboardItemDataProvider : NSObject <NSPasteboardItemDataProvider>
|
||||||
|
{
|
||||||
|
GdkContentProvider *_contentProvider;
|
||||||
|
GdkClipboard *_clipboard;
|
||||||
|
GdkDrag *_drag;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initForClipboard:(GdkClipboard *)clipboard withContentProvider:(GdkContentProvider *)contentProvider;
|
||||||
|
-(id)initForDrag:(GdkDrag *)drag withContentProvider:(GdkContentProvider *)contentProvider;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GdkMacosPasteboardItem : NSPasteboardItem
|
||||||
|
{
|
||||||
|
GdkContentProvider *_contentProvider;
|
||||||
|
GdkClipboard *_clipboard;
|
||||||
|
GdkDrag *_drag;
|
||||||
|
NSRect _draggingFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initForClipboard:(GdkClipboard *)clipboard withContentProvider:(GdkContentProvider *)contentProvider;
|
||||||
|
-(id)initForDrag:(GdkDrag *)drag withContentProvider:(GdkContentProvider *)contentProvider;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
NSPasteboardType _gdk_macos_pasteboard_to_ns_type (const char *mime_type,
|
||||||
|
NSPasteboardType *alternate);
|
||||||
|
const char *_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type);
|
||||||
|
GdkContentFormats *_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard);
|
||||||
|
void _gdk_macos_pasteboard_register_drag_types (NSWindow *window);
|
||||||
|
void _gdk_macos_pasteboard_read_async (GObject *object,
|
||||||
|
NSPasteboard *pasteboard,
|
||||||
|
GdkContentFormats *formats,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
GInputStream *_gdk_macos_pasteboard_read_finish (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
const char **out_mime_type,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif /* __GDK_MACOS_PASTEBOARD_PRIVATE_H__ */
|
602
gdk/macos/gdkmacospasteboard.c
Normal file
602
gdk/macos/gdkmacospasteboard.c
Normal file
@ -0,0 +1,602 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2021 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This library is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU Lesser General Public
|
||||||
|
* License as published by the Free Software Foundation; either
|
||||||
|
* version 2.1 of the License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This library is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public
|
||||||
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include <glib/gi18n.h>
|
||||||
|
|
||||||
|
#include "gdkdragprivate.h"
|
||||||
|
#include "gdkmacospasteboard-private.h"
|
||||||
|
#include "gdkmacosutils-private.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
TYPE_STRING,
|
||||||
|
TYPE_PBOARD,
|
||||||
|
TYPE_URL,
|
||||||
|
TYPE_FILE_URL,
|
||||||
|
TYPE_COLOR,
|
||||||
|
TYPE_TIFF,
|
||||||
|
TYPE_PNG,
|
||||||
|
TYPE_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PTYPE(k) (get_pasteboard_type(TYPE_##k))
|
||||||
|
|
||||||
|
static NSPasteboardType pasteboard_types[TYPE_LAST];
|
||||||
|
|
||||||
|
static NSPasteboardType
|
||||||
|
get_pasteboard_type (int type)
|
||||||
|
{
|
||||||
|
static gsize initialized = FALSE;
|
||||||
|
|
||||||
|
g_assert (type >= 0);
|
||||||
|
g_assert (type < TYPE_LAST);
|
||||||
|
|
||||||
|
if (g_once_init_enter (&initialized))
|
||||||
|
{
|
||||||
|
pasteboard_types[TYPE_PNG] = NSPasteboardTypePNG;
|
||||||
|
pasteboard_types[TYPE_STRING] = NSPasteboardTypeString;
|
||||||
|
pasteboard_types[TYPE_TIFF] = NSPasteboardTypeTIFF;
|
||||||
|
pasteboard_types[TYPE_COLOR] = NSPasteboardTypeColor;
|
||||||
|
|
||||||
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
||||||
|
pasteboard_types[TYPE_PBOARD] = NSStringPboardType;
|
||||||
|
G_GNUC_END_IGNORE_DEPRECATIONS
|
||||||
|
|
||||||
|
#ifdef AVAILABLE_MAC_OS_X_VERSION_10_13_AND_LATER
|
||||||
|
pasteboard_types[TYPE_URL] = NSPasteboardTypeURL;
|
||||||
|
pasteboard_types[TYPE_FILE_URL] = NSPasteboardTypeFileURL;
|
||||||
|
#else
|
||||||
|
pasteboard_types[TYPE_URL] = [[NSString alloc] initWithUTF8String:"public.url"];
|
||||||
|
pasteboard_types[TYPE_FILE_URL] = [[NSString alloc] initWithUTF8String:"public.file-url"];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_once_init_leave (&initialized, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pasteboard_types[type];
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *
|
||||||
|
_gdk_macos_pasteboard_from_ns_type (NSPasteboardType type)
|
||||||
|
{
|
||||||
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
||||||
|
|
||||||
|
if ([type isEqualToString:PTYPE(STRING)] ||
|
||||||
|
[type isEqualToString:PTYPE(PBOARD)])
|
||||||
|
return g_intern_string ("text/plain;charset=utf-8");
|
||||||
|
else if ([type isEqualToString:PTYPE(URL)] ||
|
||||||
|
[type isEqualToString:PTYPE(FILE_URL)])
|
||||||
|
return g_intern_string ("text/uri-list");
|
||||||
|
else if ([type isEqualToString:PTYPE(COLOR)])
|
||||||
|
return g_intern_string ("application/x-color");
|
||||||
|
else if ([type isEqualToString:PTYPE(TIFF)])
|
||||||
|
return g_intern_string ("image/tiff");
|
||||||
|
else if ([type isEqualToString:PTYPE(PNG)])
|
||||||
|
return g_intern_string ("image/png");
|
||||||
|
|
||||||
|
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
NSPasteboardType
|
||||||
|
_gdk_macos_pasteboard_to_ns_type (const char *mime_type,
|
||||||
|
NSPasteboardType *alternate)
|
||||||
|
{
|
||||||
|
if (alternate)
|
||||||
|
*alternate = NULL;
|
||||||
|
|
||||||
|
if (g_strcmp0 (mime_type, "text/plain;charset=utf-8") == 0)
|
||||||
|
{
|
||||||
|
return PTYPE(STRING);
|
||||||
|
}
|
||||||
|
else if (g_strcmp0 (mime_type, "text/uri-list") == 0)
|
||||||
|
{
|
||||||
|
if (alternate)
|
||||||
|
*alternate = PTYPE(URL);
|
||||||
|
return PTYPE(FILE_URL);
|
||||||
|
}
|
||||||
|
else if (g_strcmp0 (mime_type, "application/x-color") == 0)
|
||||||
|
{
|
||||||
|
return PTYPE(COLOR);
|
||||||
|
}
|
||||||
|
else if (g_strcmp0 (mime_type, "image/tiff") == 0)
|
||||||
|
{
|
||||||
|
return PTYPE(TIFF);
|
||||||
|
}
|
||||||
|
else if (g_strcmp0 (mime_type, "image/png") == 0)
|
||||||
|
{
|
||||||
|
return PTYPE(PNG);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
populate_content_formats (GdkContentFormatsBuilder *builder,
|
||||||
|
NSPasteboardType type)
|
||||||
|
{
|
||||||
|
const char *mime_type;
|
||||||
|
|
||||||
|
g_assert (builder != NULL);
|
||||||
|
g_assert (type != NULL);
|
||||||
|
|
||||||
|
if ((mime_type = _gdk_macos_pasteboard_from_ns_type (type)))
|
||||||
|
gdk_content_formats_builder_add_mime_type (builder, mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GdkContentFormats *
|
||||||
|
load_offer_formats (NSPasteboard *pasteboard)
|
||||||
|
{
|
||||||
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
|
GdkContentFormatsBuilder *builder;
|
||||||
|
GdkContentFormats *formats;
|
||||||
|
|
||||||
|
builder = gdk_content_formats_builder_new ();
|
||||||
|
for (NSPasteboardType type in [pasteboard types])
|
||||||
|
populate_content_formats (builder, type);
|
||||||
|
formats = gdk_content_formats_builder_free_to_formats (builder);
|
||||||
|
|
||||||
|
GDK_END_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
|
return g_steal_pointer (&formats);
|
||||||
|
}
|
||||||
|
|
||||||
|
GdkContentFormats *
|
||||||
|
_gdk_macos_pasteboard_load_formats (NSPasteboard *pasteboard)
|
||||||
|
{
|
||||||
|
return load_offer_formats (pasteboard);
|
||||||
|
}
|
||||||
|
|
||||||
|
static GInputStream *
|
||||||
|
create_stream_from_nsdata (NSData *data)
|
||||||
|
{
|
||||||
|
const guint8 *bytes = [data bytes];
|
||||||
|
gsize len = [data length];
|
||||||
|
|
||||||
|
return g_memory_input_stream_new_from_data (g_memdup2 (bytes, len), len, g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_gdk_macos_pasteboard_read_async (GObject *object,
|
||||||
|
NSPasteboard *pasteboard,
|
||||||
|
GdkContentFormats *formats,
|
||||||
|
int io_priority,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
|
GdkContentFormats *offer_formats = NULL;
|
||||||
|
const char *mime_type;
|
||||||
|
GInputStream *stream = NULL;
|
||||||
|
GTask *task = NULL;
|
||||||
|
|
||||||
|
g_assert (G_IS_OBJECT (object));
|
||||||
|
g_assert (pasteboard != NULL);
|
||||||
|
g_assert (formats != NULL);
|
||||||
|
|
||||||
|
task = g_task_new (object, cancellable, callback, user_data);
|
||||||
|
g_task_set_source_tag (task, _gdk_macos_pasteboard_read_async);
|
||||||
|
g_task_set_priority (task, io_priority);
|
||||||
|
|
||||||
|
offer_formats = load_offer_formats (pasteboard);
|
||||||
|
mime_type = gdk_content_formats_match_mime_type (formats, offer_formats);
|
||||||
|
|
||||||
|
if (mime_type == NULL)
|
||||||
|
{
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_NOT_SUPPORTED,
|
||||||
|
"%s",
|
||||||
|
_("No compatible transfer format found"));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp (mime_type, "text/plain;charset=utf-8") == 0)
|
||||||
|
{
|
||||||
|
NSString *nsstr = [pasteboard stringForType:NSPasteboardTypeString];
|
||||||
|
|
||||||
|
if (nsstr != NULL)
|
||||||
|
{
|
||||||
|
const char *str = [nsstr UTF8String];
|
||||||
|
stream = g_memory_input_stream_new_from_data (g_strdup (str),
|
||||||
|
strlen (str) + 1,
|
||||||
|
g_free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strcmp (mime_type, "text/uri-list") == 0)
|
||||||
|
{
|
||||||
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
|
||||||
|
|
||||||
|
if ([[pasteboard types] containsObject:PTYPE(FILE_URL)])
|
||||||
|
{
|
||||||
|
GString *str = g_string_new (NULL);
|
||||||
|
NSArray *files = [pasteboard propertyListForType:NSFilenamesPboardType];
|
||||||
|
gsize n_files = [files count];
|
||||||
|
char *data;
|
||||||
|
guint len;
|
||||||
|
|
||||||
|
for (gsize i = 0; i < n_files; ++i)
|
||||||
|
{
|
||||||
|
NSString* uriString = [files objectAtIndex:i];
|
||||||
|
uriString = [@"file://" stringByAppendingString:uriString];
|
||||||
|
uriString = [uriString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
|
||||||
|
|
||||||
|
g_string_append_printf (str,
|
||||||
|
"%s\r\n",
|
||||||
|
[uriString cStringUsingEncoding:NSUTF8StringEncoding]);
|
||||||
|
}
|
||||||
|
|
||||||
|
len = str->len;
|
||||||
|
data = g_string_free (str, FALSE);
|
||||||
|
stream = g_memory_input_stream_new_from_data (data, len, g_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_GNUC_END_IGNORE_DEPRECATIONS;
|
||||||
|
}
|
||||||
|
else if (strcmp (mime_type, "application/x-color") == 0)
|
||||||
|
{
|
||||||
|
NSColorSpace *colorspace;
|
||||||
|
NSColor *nscolor;
|
||||||
|
guint16 color[4];
|
||||||
|
|
||||||
|
colorspace = [NSColorSpace genericRGBColorSpace];
|
||||||
|
nscolor = [[NSColor colorFromPasteboard:pasteboard]
|
||||||
|
colorUsingColorSpace:colorspace];
|
||||||
|
|
||||||
|
color[0] = 0xffff * [nscolor redComponent];
|
||||||
|
color[1] = 0xffff * [nscolor greenComponent];
|
||||||
|
color[2] = 0xffff * [nscolor blueComponent];
|
||||||
|
color[3] = 0xffff * [nscolor alphaComponent];
|
||||||
|
|
||||||
|
stream = g_memory_input_stream_new_from_data (g_memdup2 (&color, sizeof color),
|
||||||
|
sizeof color,
|
||||||
|
g_free);
|
||||||
|
}
|
||||||
|
else if (strcmp (mime_type, "image/tiff") == 0)
|
||||||
|
{
|
||||||
|
NSData *data = [pasteboard dataForType:PTYPE(TIFF)];
|
||||||
|
stream = create_stream_from_nsdata (data);
|
||||||
|
}
|
||||||
|
else if (strcmp (mime_type, "image/png") == 0)
|
||||||
|
{
|
||||||
|
NSData *data = [pasteboard dataForType:PTYPE(PNG)];
|
||||||
|
stream = create_stream_from_nsdata (data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stream != NULL)
|
||||||
|
{
|
||||||
|
g_task_set_task_data (task, g_strdup (mime_type), g_free);
|
||||||
|
g_task_return_pointer (task, g_steal_pointer (&stream), g_object_unref);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
G_IO_ERROR,
|
||||||
|
G_IO_ERROR_FAILED,
|
||||||
|
_("Failed to decode contents with mime-type of '%s'"),
|
||||||
|
mime_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
g_clear_object (&task);
|
||||||
|
g_clear_pointer (&offer_formats, gdk_content_formats_unref);
|
||||||
|
|
||||||
|
GDK_END_MACOS_ALLOC_POOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
GInputStream *
|
||||||
|
_gdk_macos_pasteboard_read_finish (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
const char **out_mime_type,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GTask *task = (GTask *)result;
|
||||||
|
|
||||||
|
g_assert (G_IS_OBJECT (object));
|
||||||
|
g_assert (G_IS_TASK (task));
|
||||||
|
|
||||||
|
if (out_mime_type != NULL)
|
||||||
|
*out_mime_type = g_strdup (g_task_get_task_data (task));
|
||||||
|
|
||||||
|
return g_task_propagate_pointer (task, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
_gdk_macos_pasteboard_register_drag_types (NSWindow *window)
|
||||||
|
{
|
||||||
|
[window registerForDraggedTypes:[NSArray arrayWithObjects:PTYPE(STRING),
|
||||||
|
PTYPE(PBOARD),
|
||||||
|
PTYPE(URL),
|
||||||
|
PTYPE(FILE_URL),
|
||||||
|
PTYPE(COLOR),
|
||||||
|
PTYPE(TIFF),
|
||||||
|
PTYPE(PNG),
|
||||||
|
nil]];
|
||||||
|
}
|
||||||
|
|
||||||
|
@implementation GdkMacosPasteboardItemDataProvider
|
||||||
|
|
||||||
|
-(id)initForClipboard:(GdkClipboard*)clipboard withContentProvider:(GdkContentProvider*)contentProvider
|
||||||
|
{
|
||||||
|
[super init];
|
||||||
|
g_set_object (&self->_clipboard, clipboard);
|
||||||
|
g_set_object (&self->_contentProvider, contentProvider);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initForDrag:(GdkDrag*)drag withContentProvider:(GdkContentProvider*)contentProvider
|
||||||
|
{
|
||||||
|
[super init];
|
||||||
|
g_set_object (&self->_drag, drag);
|
||||||
|
g_set_object (&self->_contentProvider, contentProvider);
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)dealloc
|
||||||
|
{
|
||||||
|
g_clear_object (&self->_contentProvider);
|
||||||
|
g_clear_object (&self->_clipboard);
|
||||||
|
g_clear_object (&self->_drag);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSArray<NSPasteboardType> *)types
|
||||||
|
{
|
||||||
|
NSMutableArray *ret = [[NSMutableArray alloc] init];
|
||||||
|
GdkContentFormats *serializable;
|
||||||
|
const char * const *mime_types;
|
||||||
|
gsize n_mime_types;
|
||||||
|
|
||||||
|
serializable = gdk_content_provider_ref_storable_formats (self->_contentProvider);
|
||||||
|
serializable = gdk_content_formats_union_serialize_mime_types (serializable);
|
||||||
|
mime_types = gdk_content_formats_get_mime_types (serializable, &n_mime_types);
|
||||||
|
|
||||||
|
for (gsize i = 0; i < n_mime_types; i++)
|
||||||
|
{
|
||||||
|
const char *mime_type = mime_types[i];
|
||||||
|
NSPasteboardType type;
|
||||||
|
NSPasteboardType alternate = nil;
|
||||||
|
|
||||||
|
if ((type = _gdk_macos_pasteboard_to_ns_type (mime_type, &alternate)))
|
||||||
|
{
|
||||||
|
[ret addObject:type];
|
||||||
|
if (alternate)
|
||||||
|
[ret addObject:alternate];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gdk_content_formats_unref (serializable);
|
||||||
|
|
||||||
|
/* Default to an url type (think gobject://internal)
|
||||||
|
* to support internal, GType-based DnD.
|
||||||
|
*/
|
||||||
|
if (n_mime_types == 0)
|
||||||
|
{
|
||||||
|
GdkContentFormats *formats;
|
||||||
|
gsize n_gtypes;
|
||||||
|
|
||||||
|
formats = gdk_content_provider_ref_formats (self->_contentProvider);
|
||||||
|
gdk_content_formats_get_gtypes (formats, &n_gtypes);
|
||||||
|
|
||||||
|
if (n_gtypes)
|
||||||
|
[ret addObject:NSPasteboardTypeURL];
|
||||||
|
|
||||||
|
gdk_content_formats_unref (formats);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g_steal_pointer (&ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GMemoryOutputStream *stream;
|
||||||
|
NSPasteboardItem *item;
|
||||||
|
NSPasteboardType type;
|
||||||
|
GMainContext *main_context;
|
||||||
|
guint done : 1;
|
||||||
|
} WriteRequest;
|
||||||
|
|
||||||
|
static void
|
||||||
|
write_request_free (WriteRequest *wr)
|
||||||
|
{
|
||||||
|
g_clear_pointer (&wr->main_context, g_main_context_unref);
|
||||||
|
g_clear_object (&wr->stream);
|
||||||
|
[wr->item release];
|
||||||
|
g_slice_free (WriteRequest, wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
on_data_ready_cb (GObject *object,
|
||||||
|
GAsyncResult *result,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
GDK_BEGIN_MACOS_ALLOC_POOL;
|
||||||
|
|
||||||
|
WriteRequest *wr = user_data;
|
||||||
|
GError *error = NULL;
|
||||||
|
NSData *data = nil;
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
g_assert (G_IS_OBJECT (object));
|
||||||
|
g_assert (GDK_IS_CLIPBOARD (object) || GDK_IS_DRAG (object));
|
||||||
|
g_assert (G_IS_ASYNC_RESULT (result));
|
||||||
|
g_assert (wr != NULL);
|
||||||
|
g_assert (G_IS_MEMORY_OUTPUT_STREAM (wr->stream));
|
||||||
|
g_assert ([wr->item isKindOfClass:[NSPasteboardItem class]]);
|
||||||
|
|
||||||
|
if (GDK_IS_CLIPBOARD (object))
|
||||||
|
ret = gdk_clipboard_write_finish (GDK_CLIPBOARD (object), result, &error);
|
||||||
|
else if (GDK_IS_DRAG (object))
|
||||||
|
ret = gdk_drag_write_finish (GDK_DRAG (object), result, &error);
|
||||||
|
else
|
||||||
|
g_return_if_reached ();
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
{
|
||||||
|
gsize size;
|
||||||
|
gpointer bytes;
|
||||||
|
|
||||||
|
g_output_stream_close (G_OUTPUT_STREAM (wr->stream), NULL, NULL);
|
||||||
|
|
||||||
|
size = g_memory_output_stream_get_data_size (wr->stream);
|
||||||
|
bytes = g_memory_output_stream_steal_data (wr->stream);
|
||||||
|
data = [[NSData alloc] initWithBytesNoCopy:bytes
|
||||||
|
length:size
|
||||||
|
deallocator:^(void *alloc, NSUInteger length) { g_free (alloc); }];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
g_warning ("Failed to serialize pasteboard contents: %s",
|
||||||
|
error->message);
|
||||||
|
g_clear_error (&error);
|
||||||
|
}
|
||||||
|
|
||||||
|
[wr->item setData:data forType:wr->type];
|
||||||
|
|
||||||
|
wr->done = TRUE;
|
||||||
|
|
||||||
|
GDK_END_MACOS_ALLOC_POOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)pasteboard:(NSPasteboard *)pasteboard item:(NSPasteboardItem *)item provideDataForType:(NSPasteboardType)type
|
||||||
|
{
|
||||||
|
const char *mime_type = _gdk_macos_pasteboard_from_ns_type (type);
|
||||||
|
GMainContext *main_context = g_main_context_default ();
|
||||||
|
WriteRequest *wr;
|
||||||
|
|
||||||
|
if (self->_contentProvider == NULL || mime_type == NULL)
|
||||||
|
{
|
||||||
|
[item setData:[NSData data] forType:type];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wr = g_slice_new0 (WriteRequest);
|
||||||
|
wr->item = [item retain];
|
||||||
|
wr->stream = G_MEMORY_OUTPUT_STREAM (g_memory_output_stream_new_resizable ());
|
||||||
|
wr->type = type;
|
||||||
|
wr->main_context = g_main_context_ref (main_context);
|
||||||
|
wr->done = FALSE;
|
||||||
|
|
||||||
|
if (GDK_IS_CLIPBOARD (self->_clipboard))
|
||||||
|
gdk_clipboard_write_async (self->_clipboard,
|
||||||
|
mime_type,
|
||||||
|
G_OUTPUT_STREAM (wr->stream),
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
NULL,
|
||||||
|
on_data_ready_cb,
|
||||||
|
wr);
|
||||||
|
else if (GDK_IS_DRAG (self->_drag))
|
||||||
|
gdk_drag_write_async (self->_drag,
|
||||||
|
mime_type,
|
||||||
|
G_OUTPUT_STREAM (wr->stream),
|
||||||
|
G_PRIORITY_DEFAULT,
|
||||||
|
NULL,
|
||||||
|
on_data_ready_cb,
|
||||||
|
wr);
|
||||||
|
else
|
||||||
|
g_return_if_reached ();
|
||||||
|
|
||||||
|
/* We're forced to provide data synchronously via this API
|
||||||
|
* so we must block on the main loop. Using another main loop
|
||||||
|
* than the default tends to get us locked up here, so that is
|
||||||
|
* what we'll do for now.
|
||||||
|
*/
|
||||||
|
while (!wr->done)
|
||||||
|
g_main_context_iteration (wr->main_context, TRUE);
|
||||||
|
|
||||||
|
write_request_free (wr);
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)pasteboardFinishedWithDataProvider:(NSPasteboard *)pasteboard
|
||||||
|
{
|
||||||
|
g_clear_object (&self->_clipboard);
|
||||||
|
g_clear_object (&self->_drag);
|
||||||
|
g_clear_object (&self->_contentProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GdkMacosPasteboardItem
|
||||||
|
|
||||||
|
-(id)initForClipboard:(GdkClipboard*)clipboard withContentProvider:(GdkContentProvider*)contentProvider
|
||||||
|
{
|
||||||
|
GdkMacosPasteboardItemDataProvider *dataProvider;
|
||||||
|
|
||||||
|
dataProvider = [[GdkMacosPasteboardItemDataProvider alloc] initForClipboard:clipboard withContentProvider:contentProvider];
|
||||||
|
|
||||||
|
[super init];
|
||||||
|
g_set_object (&self->_clipboard, clipboard);
|
||||||
|
g_set_object (&self->_contentProvider, contentProvider);
|
||||||
|
[self setDataProvider:dataProvider forTypes:[dataProvider types]];
|
||||||
|
|
||||||
|
[dataProvider release];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)initForDrag:(GdkDrag*)drag withContentProvider:(GdkContentProvider*)contentProvider
|
||||||
|
{
|
||||||
|
GdkMacosPasteboardItemDataProvider *dataProvider;
|
||||||
|
|
||||||
|
dataProvider = [[GdkMacosPasteboardItemDataProvider alloc] initForDrag:drag withContentProvider:contentProvider];
|
||||||
|
|
||||||
|
[super init];
|
||||||
|
g_set_object (&self->_drag, drag);
|
||||||
|
g_set_object (&self->_contentProvider, contentProvider);
|
||||||
|
[self setDataProvider:dataProvider forTypes:[dataProvider types]];
|
||||||
|
|
||||||
|
[dataProvider release];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)dealloc
|
||||||
|
{
|
||||||
|
g_clear_object (&self->_contentProvider);
|
||||||
|
g_clear_object (&self->_clipboard);
|
||||||
|
g_clear_object (&self->_drag);
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSRect)draggingFrame
|
||||||
|
{
|
||||||
|
return self->_draggingFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(void)setDraggingFrame:(NSRect)draggingFrame;
|
||||||
|
{
|
||||||
|
self->_draggingFrame = draggingFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(id)item
|
||||||
|
{
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
-(NSArray* (^) (void))imageComponentsProvider
|
||||||
|
{
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -446,7 +446,7 @@ gdk_macos_surface_drag_begin (GdkSurface *surface,
|
|||||||
gdk_drag_get_selected_action (GDK_DRAG (drag)));
|
gdk_drag_get_selected_action (GDK_DRAG (drag)));
|
||||||
gdk_drag_set_cursor (GDK_DRAG (drag), cursor);
|
gdk_drag_set_cursor (GDK_DRAG (drag), cursor);
|
||||||
|
|
||||||
if (!_gdk_macos_drag_begin (drag))
|
if (!_gdk_macos_drag_begin (drag, content, self->window))
|
||||||
{
|
{
|
||||||
g_object_unref (drag);
|
g_object_unref (drag);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -19,6 +19,7 @@ gdk_macos_sources = files([
|
|||||||
'gdkmacoseventsource.c',
|
'gdkmacoseventsource.c',
|
||||||
'gdkmacoskeymap.c',
|
'gdkmacoskeymap.c',
|
||||||
'gdkmacosmonitor.c',
|
'gdkmacosmonitor.c',
|
||||||
|
'gdkmacospasteboard.c',
|
||||||
'gdkmacospopupsurface.c',
|
'gdkmacospopupsurface.c',
|
||||||
'gdkmacosseat.c',
|
'gdkmacosseat.c',
|
||||||
'gdkmacossurface.c',
|
'gdkmacossurface.c',
|
||||||
|
Loading…
Reference in New Issue
Block a user