Chandra
view release on metacpan or search on metacpan
include/chandra/chandra_clipboard.h view on Meta::CPAN
/*
* chandra_clipboard.h - System clipboard access
*
* Platform support:
* - macOS: NSPasteboard (Cocoa)
* - Linux: GTK clipboard (gtk_clipboard_*)
* - Windows: Win32 clipboard API (stub for now)
*
* Include with CHANDRA_XS_IMPLEMENTATION defined to get implementations.
*/
#ifndef CHANDRA_CLIPBOARD_H
#define CHANDRA_CLIPBOARD_H
#include "chandra.h"
/* ---- API declarations ---- */
/* Text clipboard */
static char *chandra_clipboard_get_text(pTHX);
static int chandra_clipboard_set_text(pTHX_ const char *text, STRLEN len);
static int chandra_clipboard_has_text(pTHX);
/* HTML clipboard */
static char *chandra_clipboard_get_html(pTHX);
static int chandra_clipboard_set_html(pTHX_ const char *html, STRLEN len);
static int chandra_clipboard_has_html(pTHX);
/* Image clipboard */
static SV *chandra_clipboard_get_image(pTHX);
static int chandra_clipboard_set_image(pTHX_ const char *path);
static int chandra_clipboard_has_image(pTHX);
/* Clear clipboard */
static void chandra_clipboard_clear(pTHX);
/* ================================================================
* Implementation - compiled only in Chandra.xs
* ================================================================ */
#ifdef CHANDRA_XS_IMPLEMENTATION
/* ============================================================================
* macOS Implementation - NSPasteboard
* ============================================================================ */
#if defined(WEBVIEW_COCOA)
static char *chandra_clipboard_get_text(pTHX) {
char *result = NULL;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.utf8-plain-text");
id str = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("stringForType:"), typeStr);
if (str) {
const char *utf8 = ((const char *(*)(id, SEL))objc_msgSend)(
str, sel_registerName("UTF8String"));
if (utf8) {
size_t slen = strlen(utf8);
result = (char *)safemalloc(slen + 1);
memcpy(result, utf8, slen + 1);
}
}
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return result;
}
static int chandra_clipboard_set_text(pTHX_ const char *text, STRLEN len) {
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
((void(*)(id, SEL))objc_msgSend)(
pb, sel_registerName("clearContents"));
id nsStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
text);
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.utf8-plain-text");
BOOL ok = ((BOOL(*)(id, SEL, id, id))objc_msgSend)(
pb, sel_registerName("setString:forType:"),
nsStr, typeStr);
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return ok ? 1 : 0;
}
static int chandra_clipboard_has_text(pTHX) {
int result = 0;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.utf8-plain-text");
id str = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("stringForType:"), typeStr);
result = (str != nil) ? 1 : 0;
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return result;
}
static char *chandra_clipboard_get_html(pTHX) {
char *result = NULL;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.html");
id str = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("stringForType:"), typeStr);
if (str) {
const char *utf8 = ((const char *(*)(id, SEL))objc_msgSend)(
str, sel_registerName("UTF8String"));
if (utf8) {
size_t slen = strlen(utf8);
result = (char *)safemalloc(slen + 1);
memcpy(result, utf8, slen + 1);
}
}
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return result;
}
static int chandra_clipboard_set_html(pTHX_ const char *html, STRLEN len) {
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
((void(*)(id, SEL))objc_msgSend)(
pb, sel_registerName("clearContents"));
id nsStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
html);
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.html");
BOOL ok = ((BOOL(*)(id, SEL, id, id))objc_msgSend)(
pb, sel_registerName("setString:forType:"),
nsStr, typeStr);
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return ok ? 1 : 0;
}
static int chandra_clipboard_has_html(pTHX) {
int result = 0;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.html");
id str = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("stringForType:"), typeStr);
result = (str != nil) ? 1 : 0;
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return result;
}
static SV *chandra_clipboard_get_image(pTHX) {
SV *result = &PL_sv_undef;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.png");
id data = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("dataForType:"), typeStr);
if (!data) {
/* Try TIFF as fallback */
id tiffType = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.tiff");
data = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("dataForType:"), tiffType);
if (data) {
/* Convert TIFF to PNG via NSBitmapImageRep */
id rep = ((id(*)(id, SEL, id))objc_msgSend)(
(id)objc_getClass("NSBitmapImageRep"),
sel_registerName("imageRepWithData:"), data);
if (rep) {
/* NSBitmapImageFileTypePNG = 4 */
id props = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSDictionary"),
sel_registerName("dictionary"));
data = ((id(*)(id, SEL, unsigned long, id))objc_msgSend)(
rep, sel_registerName("representationUsingType:properties:"),
(unsigned long)4, props);
}
}
}
if (data) {
const void *bytes = ((const void *(*)(id, SEL))objc_msgSend)(
data, sel_registerName("bytes"));
NSUInteger length = ((NSUInteger(*)(id, SEL))objc_msgSend)(
data, sel_registerName("length"));
if (bytes && length > 0) {
result = newSVpvn((const char *)bytes, (STRLEN)length);
}
}
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return result;
}
static int chandra_clipboard_set_image(pTHX_ const char *path) {
int ok = 0;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pathStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
path);
id data = ((id(*)(id, SEL, id))objc_msgSend)(
(id)objc_getClass("NSData"),
sel_registerName("dataWithContentsOfFile:"),
pathStr);
if (data) {
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
((void(*)(id, SEL))objc_msgSend)(
pb, sel_registerName("clearContents"));
id typeStr = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.png");
ok = ((BOOL(*)(id, SEL, id, id))objc_msgSend)(
pb, sel_registerName("setData:forType:"),
data, typeStr) ? 1 : 0;
}
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return ok;
}
static int chandra_clipboard_has_image(pTHX) {
int result = 0;
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
/* Check for PNG */
id pngType = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.png");
id data = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("dataForType:"), pngType);
if (!data) {
/* Check for TIFF */
id tiffType = ((id(*)(id, SEL, const char *))objc_msgSend)(
(id)objc_getClass("NSString"),
sel_registerName("stringWithUTF8String:"),
"public.tiff");
data = ((id(*)(id, SEL, id))objc_msgSend)(
pb, sel_registerName("dataForType:"), tiffType);
}
result = (data != nil) ? 1 : 0;
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
return result;
}
static void chandra_clipboard_clear(pTHX) {
id pool = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSAutoreleasePool"),
sel_registerName("new"));
id pb = ((id(*)(id, SEL))objc_msgSend)(
(id)objc_getClass("NSPasteboard"),
sel_registerName("generalPasteboard"));
((void(*)(id, SEL))objc_msgSend)(
pb, sel_registerName("clearContents"));
((void(*)(id, SEL))objc_msgSend)(pool, sel_registerName("drain"));
}
/* ============================================================================
* Linux Implementation - GTK clipboard
* ============================================================================ */
#elif defined(WEBVIEW_GTK)
#include <gtk/gtk.h>
#include <string.h>
/* Ensure GTK is initialized for clipboard access */
static void chandra_clipboard_ensure_gtk(void) {
static int inited = 0;
if (!inited) {
if (!gtk_init_check(NULL, NULL)) {
/* GTK init failed - clipboard won't work */
}
inited = 1;
}
}
static char *chandra_clipboard_get_text(pTHX) {
char *result = NULL;
GtkClipboard *cb;
gchar *text;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
text = gtk_clipboard_wait_for_text(cb);
if (text) {
size_t slen = strlen(text);
result = (char *)safemalloc(slen + 1);
memcpy(result, text, slen + 1);
g_free(text);
}
return result;
}
static int chandra_clipboard_set_text(pTHX_ const char *text, STRLEN len) {
GtkClipboard *cb;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_text(cb, text, (gint)len);
gtk_clipboard_store(cb);
return 1;
}
static int chandra_clipboard_has_text(pTHX) {
GtkClipboard *cb;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
return gtk_clipboard_wait_is_text_available(cb) ? 1 : 0;
}
static char *chandra_clipboard_get_html(pTHX) {
char *result = NULL;
GtkClipboard *cb;
GtkSelectionData *sel;
GdkAtom html_atom;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
html_atom = gdk_atom_intern("text/html", FALSE);
sel = gtk_clipboard_wait_for_contents(cb, html_atom);
if (sel) {
const guchar *data = gtk_selection_data_get_data(sel);
gint dlen = gtk_selection_data_get_length(sel);
if (data && dlen > 0) {
result = (char *)safemalloc(dlen + 1);
memcpy(result, data, dlen);
result[dlen] = '\0';
}
gtk_selection_data_free(sel);
}
return result;
}
static int chandra_clipboard_set_html(pTHX_ const char *html, STRLEN len) {
/* GTK doesn't have a simple set_html - use target list approach */
/* For simplicity, store as text with text/html target */
GtkClipboard *cb;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
/* Set as plain text; HTML-aware apps can request text/html target */
gtk_clipboard_set_text(cb, html, (gint)len);
gtk_clipboard_store(cb);
return 1;
}
static int chandra_clipboard_has_html(pTHX) {
GtkClipboard *cb;
GdkAtom html_atom;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
html_atom = gdk_atom_intern("text/html", FALSE);
return gtk_clipboard_wait_is_target_available(cb, html_atom) ? 1 : 0;
}
static SV *chandra_clipboard_get_image(pTHX) {
SV *result = &PL_sv_undef;
GtkClipboard *cb;
GdkPixbuf *pixbuf;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
pixbuf = gtk_clipboard_wait_for_image(cb);
if (pixbuf) {
gchar *buf = NULL;
gsize buf_size = 0;
GError *error = NULL;
if (gdk_pixbuf_save_to_buffer(pixbuf, &buf, &buf_size, "png", &error, NULL)) {
result = newSVpvn(buf, (STRLEN)buf_size);
g_free(buf);
}
if (error) g_error_free(error);
g_object_unref(pixbuf);
}
return result;
}
static int chandra_clipboard_set_image(pTHX_ const char *path) {
GtkClipboard *cb;
GdkPixbuf *pixbuf;
GError *error = NULL;
chandra_clipboard_ensure_gtk();
pixbuf = gdk_pixbuf_new_from_file(path, &error);
if (!pixbuf) {
if (error) g_error_free(error);
return 0;
}
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_set_image(cb, pixbuf);
gtk_clipboard_store(cb);
g_object_unref(pixbuf);
return 1;
}
static int chandra_clipboard_has_image(pTHX) {
GtkClipboard *cb;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
return gtk_clipboard_wait_is_image_available(cb) ? 1 : 0;
}
static void chandra_clipboard_clear(pTHX) {
GtkClipboard *cb;
chandra_clipboard_ensure_gtk();
cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
gtk_clipboard_clear(cb);
gtk_clipboard_store(cb);
}
/* ============================================================================
* Windows Implementation - Win32 clipboard API (stub)
* ============================================================================ */
#elif defined(WEBVIEW_EDGE)
static char *chandra_clipboard_get_text(pTHX) { return NULL; }
static int chandra_clipboard_set_text(pTHX_ const char *text, STRLEN len) { return 0; }
static int chandra_clipboard_has_text(pTHX) { return 0; }
static char *chandra_clipboard_get_html(pTHX) { return NULL; }
static int chandra_clipboard_set_html(pTHX_ const char *html, STRLEN len) { return 0; }
static int chandra_clipboard_has_html(pTHX) { return 0; }
static SV *chandra_clipboard_get_image(pTHX) { return &PL_sv_undef; }
static int chandra_clipboard_set_image(pTHX_ const char *path) { return 0; }
static int chandra_clipboard_has_image(pTHX) { return 0; }
static void chandra_clipboard_clear(pTHX) {}
#else
/* Unknown platform */
static char *chandra_clipboard_get_text(pTHX) { return NULL; }
static int chandra_clipboard_set_text(pTHX_ const char *text, STRLEN len) { return 0; }
static int chandra_clipboard_has_text(pTHX) { return 0; }
static char *chandra_clipboard_get_html(pTHX) { return NULL; }
static int chandra_clipboard_set_html(pTHX_ const char *html, STRLEN len) { return 0; }
static int chandra_clipboard_has_html(pTHX) { return 0; }
static SV *chandra_clipboard_get_image(pTHX) { return &PL_sv_undef; }
static int chandra_clipboard_set_image(pTHX_ const char *path) { return 0; }
static int chandra_clipboard_has_image(pTHX) { return 0; }
static void chandra_clipboard_clear(pTHX) {}
#endif /* platform selection */
#endif /* CHANDRA_XS_IMPLEMENTATION */
#endif /* CHANDRA_CLIPBOARD_H */
( run in 1.110 second using v1.01-cache-2.11-cpan-84e82930d8c )