From 583d646b26c406fb14e9bb1cbb7c5b2801ae9431 Mon Sep 17 00:00:00 2001 From: Thomas Bernard Date: Mon, 18 Mar 2019 10:44:07 +0100 Subject: [PATCH] X11 clipboard paste : support several formats --- src/input.c | 94 ++++++++++++++++++++++++++++++++++++++++++++------ src/input.h | 9 +++++ src/loadsave.c | 64 +++++++++++++++++++++++----------- src/readline.c | 1 + 4 files changed, 137 insertions(+), 31 deletions(-) diff --git a/src/input.c b/src/input.c index b7c794d0..3c0f7f34 100644 --- a/src/input.c +++ b/src/input.c @@ -88,6 +88,7 @@ word * Drop_file_name_unicode = NULL; #if defined(USE_X11) || (defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11)) char * X11_clipboard = NULL; unsigned long X11_clipboard_size = 0; +enum X11_CLIPBOARD_TYPES X11_clipboard_type = X11_CLIPBOARD_NONE; #endif // -- @@ -598,24 +599,86 @@ static int Handle_SelectionNotify(const XSelectionEvent* xselection) XFree(target_name); r = XGetWindowProperty(X11_display, X11_window, xselection->property, 0, LONG_MAX, - False, xselection->target /* type */, &type, &format, + False, AnyPropertyType/*xselection->target*/ /* type */, &type, &format, &count, &bytesAfter, &value); if (r == Success && value != NULL) { +#ifndef __no_pnglib__ + Atom png = XInternAtom(X11_display, "image/png", False); +#endif +#ifndef __no_tifflib__ + Atom tiff = XInternAtom(X11_display, "image/tiff", False); +#endif + Atom urilist = XInternAtom(X11_display, "text/uri-list", False); + Atom utf8string = XInternAtom(X11_display, "UTF8_STRING", False); + // by order of preference + const struct { Atom a; enum X11_CLIPBOARD_TYPES t; } supported[] = { +#ifndef __no_pnglib__ + { png, X11_CLIPBOARD_PNG }, +#endif +#ifndef __no_tifflib__ + { tiff, X11_CLIPBOARD_TIFF }, +#endif + { urilist, X11_CLIPBOARD_URILIST }, + { utf8string, X11_CLIPBOARD_UTF8STRING }, + { None, X11_CLIPBOARD_NONE } + }; char * type_name = XGetAtomName(X11_display, type); GFX2_Log(GFX2_DEBUG, "Clipboard value=%p %lu bytes format=%d type=%s\n", value, count, format, type_name); XFree(type_name); - if (count > 0) + if (xselection->target == XInternAtom(X11_display, "TARGETS", False)) { - X11_clipboard_size = count; - if (xselection->target == XInternAtom(X11_display, "UTF8_STRING", False)) - X11_clipboard = strdup((char *)value); // Text Clipboard - else if (xselection->target == XInternAtom(X11_display, "image/png", False)) - { // Picture clipboard (PNG) - X11_clipboard = malloc(count); - if (X11_clipboard != NULL) - memcpy(X11_clipboard, value, count); + unsigned long i; + Atom * atoms = (Atom *)value; + Atom prefered = None; + for (i = 0; i < count; i++) + { + int j; + char * atom_name = XGetAtomName(X11_display, atoms[i]); + GFX2_Log(GFX2_DEBUG, " %d %s\n", atoms[i], atom_name); + XFree(atom_name); + if (prefered == None) + { + for (j = 0; supported[j].a != None; j++) + { + if (atoms[i] == supported[j].a) + { + prefered = atoms[i]; + break; + } + } + } + } + if (prefered != None) + { + XConvertSelection(X11_display, xselection->selection, prefered, + xselection->property, X11_window, CurrentTime); + } + } + else if (count > 0) + { + X11_clipboard = malloc(count+1); + if (X11_clipboard != NULL) + { + int i; + X11_clipboard_size = count; + X11_clipboard_type = X11_CLIPBOARD_UNKNOWN; + for (i = 0; supported[i].a != None; i++) + { + if (supported[i].a == xselection->target) + { + X11_clipboard_type = supported[i].t; + break; + } + } + memcpy(X11_clipboard, value, count); + X11_clipboard[count] = '\0'; + } + else + { + X11_clipboard_type = X11_CLIPBOARD_NONE; + X11_clipboard_size = 0; } } XFree(value); @@ -632,8 +695,13 @@ static int Handle_SelectionNotify(const XSelectionEvent* xselection) else { char * selection_name = XGetAtomName(X11_display, xselection->selection); - GFX2_Log(GFX2_INFO, "Unhandled SelectNotify selection=%s\n", selection_name); + char * property_name = "None"; + if (xselection->property != None) + XGetAtomName(X11_display, xselection->property); + GFX2_Log(GFX2_INFO, "Unhandled SelectNotify selection=%s property=%s\n", selection_name, property_name); XFree(selection_name); + if (xselection->property != None) + XFree(property_name); } return user_feedback_required; } @@ -1570,7 +1638,9 @@ int Get_input(int sleep_time) if (X11_clipboard) { free(X11_clipboard); + X11_clipboard = NULL; X11_clipboard_size = 0; + X11_clipboard_type = X11_CLIPBOARD_NONE; } SDL_EventState(SDL_SYSWMEVENT, SDL_DISABLE); break; @@ -1965,7 +2035,9 @@ int Get_input(int sleep_time) if (X11_clipboard) { free(X11_clipboard); + X11_clipboard = NULL; X11_clipboard_size = 0; + X11_clipboard_type = X11_CLIPBOARD_NONE; } break; case SelectionRequest: diff --git a/src/input.h b/src/input.h index 898d32cb..45b09993 100644 --- a/src/input.h +++ b/src/input.h @@ -77,10 +77,19 @@ extern char * Drop_file_name; extern word * Drop_file_name_unicode; #if defined(USE_X11) || (defined(SDL_VIDEO_DRIVER_X11) && !defined(NO_X11)) +enum X11_CLIPBOARD_TYPES { + X11_CLIPBOARD_NONE, + X11_CLIPBOARD_UNKNOWN, + X11_CLIPBOARD_PNG, + X11_CLIPBOARD_TIFF, + X11_CLIPBOARD_URILIST, + X11_CLIPBOARD_UTF8STRING +}; /// /// malloc'ed copy of the X11 clipboard extern char * X11_clipboard; extern unsigned long X11_clipboard_size; +extern enum X11_CLIPBOARD_TYPES X11_clipboard_type; #endif diff --git a/src/loadsave.c b/src/loadsave.c index 4cb422db..75d5f33e 100644 --- a/src/loadsave.c +++ b/src/loadsave.c @@ -1485,18 +1485,27 @@ static void Load_ClipBoard_Image(T_IO_Context * context) selection = XInternAtom(X11_display, "CLIPBOARD", False); selection_owner = XGetSelectionOwner(X11_display, selection); if (selection_owner == None) + { + GFX2_Log(GFX2_INFO, "No owner for the X11 \"CLIPBOARD\" selection\n"); return; + } #if defined(USE_SDL) || defined(USE_SDL2) + // Enable processing of X11 events old_wmevent_state = SDL_EventState(SDL_SYSWMEVENT, SDL_QUERY); SDL_EventState(SDL_SYSWMEVENT, SDL_ENABLE); #endif -#ifndef __no_pnglib__ - XConvertSelection(X11_display, selection, XInternAtom(X11_display, "image/png"/*"PIXMAP"*/, False), + // "TARGETS" is a special content type. The selection owner will + // respond with a list of supported type. We will then choose our + // prefered type and ask for it. + // see Handle_SelectionNotify() + // We could ask directly for "image/png" or "image/tiff" but that is + // not sure this is supported by the selection owner. + XConvertSelection(X11_display, selection, XInternAtom(X11_display, "TARGETS", False), XInternAtom(X11_display, "GFX2_CLIP", False), /* Property */ X11_window, CurrentTime); - // wait for the event to be received - for(i = 0; X11_clipboard == NULL && i < 10; i++) + // wait for the event to be received. 500ms maximum + for(i = 0; X11_clipboard == NULL && i < 25; i++) { Get_input(20); } @@ -1504,24 +1513,39 @@ static void Load_ClipBoard_Image(T_IO_Context * context) SDL_EventState(SDL_SYSWMEVENT, old_wmevent_state); #endif - if (X11_clipboard != NULL) + switch(X11_clipboard_type) { - if (png_sig_cmp((byte *)X11_clipboard, 0, 8) == 0) - { - File_error = 0; - Load_PNG_Sub(context, NULL, X11_clipboard, X11_clipboard_size); - } - else - { - GFX2_Log(GFX2_WARNING, "Clipboard content not in PNG format\n"); - } - free(X11_clipboard); - X11_clipboard = NULL; - X11_clipboard_size = 0; - } -#else - GFX2_Log(GFX2_ERROR, "Need PNG support for X11 image copy/paste\n"); + case X11_CLIPBOARD_NONE: + GFX2_Log(GFX2_INFO, "Unable to retrieve X11 \"CLIPBOARD\" selection in a supported format. X11_clipboard=%p\n", X11_clipboard); + break; +#ifndef __no_pnglib__ + case X11_CLIPBOARD_PNG: + if (png_sig_cmp((byte *)X11_clipboard, 0, 8) == 0) + { + File_error = 0; + Load_PNG_Sub(context, NULL, X11_clipboard, X11_clipboard_size); + } + else + GFX2_Log(GFX2_WARNING, "Failed to load PNG Clipboard\n"); + break; #endif +#ifndef __no_tifflib__ + case X11_CLIPBOARD_TIFF: + Load_TIFF_from_memory(context, X11_clipboard, X11_clipboard_size); + if (File_error != 0) + GFX2_Log(GFX2_WARNING, "Failed to load TIFF Clipboard\n"); + break; +#endif + // TODO + case X11_CLIPBOARD_UTF8STRING: + case X11_CLIPBOARD_URILIST: + default: + GFX2_Log(GFX2_WARNING, "Unsupported Clipboard format %d\n", (int)X11_clipboard_type); + } + free(X11_clipboard); + X11_clipboard = NULL; + X11_clipboard_size = 0; + X11_clipboard_type = X11_CLIPBOARD_NONE; #else GFX2_Log(GFX2_ERROR, "Load_ClipBoard_Image() not implemented on this platform yet\n"); diff --git a/src/readline.c b/src/readline.c index 7deff268..b20f0d69 100644 --- a/src/readline.c +++ b/src/readline.c @@ -488,6 +488,7 @@ bye: char * utf8_str = X11_clipboard; X11_clipboard = NULL; X11_clipboard_size = 0; + X11_clipboard_type = X11_CLIPBOARD_NONE; #else { // mac OS without X11