view release on metacpan or search on metacpan
#include "include/chandra/chandra_devtools.h"
#include "include/chandra/chandra_internal.h"
#include "include/chandra/chandra_socket_common.h"
#include "include/chandra/chandra_socket_token.h"
#include "include/chandra/chandra_socket_hub.h"
#include "include/chandra/chandra_socket_client.h"
#include "include/chandra/chandra_notify.h"
#include "include/chandra/chandra_store.h"
#include "include/chandra/chandra_log.h"
#include "include/chandra/chandra_assets.h"
#include "include/chandra/chandra_clipboard.h"
#include "include/chandra/chandra_contextmenu.h"
#include "include/chandra/chandra_window.h"
#include "include/chandra/chandra_splash.h"
#include "include/chandra/chandra_form.h"
#include "include/chandra/chandra_bridge_ext.h"
#include "include/chandra/chandra_canvas.h"
/* Window registry - maps native wid to Perl SV* objects */
static HV *_window_registry = NULL;
static IV _window_id_counter = 0;
INCLUDE: xs/shortcut.xs
INCLUDE: xs/socket_connection.xs
INCLUDE: xs/socket_token.xs
INCLUDE: xs/socket_hub.xs
INCLUDE: xs/socket_client.xs
INCLUDE: xs/app.xs
INCLUDE: xs/assets.xs
INCLUDE: xs/clipboard.xs
INCLUDE: xs/dragdrop.xs
INCLUDE: xs/contextmenu.xs
INCLUDE: xs/store.xs
INCLUDE: xs/log.xs
INCLUDE: xs/window.xs
INCLUDE: xs/splash.xs
INCLUDE: xs/form.xs
INCLUDE: xs/canvas.xs
INCLUDE: xs/toast.xs
INCLUDE: xs/modal.xs
- Page reload recovery via WKUserScript + on_reload callback
- Bridge JS re-injection on reload (window.chandra restored)
- $app->on_reload(sub { ... }) for re-mounting components after reload
- Fix Linux notification shell escaping (single quotes properly escaped)
- Bridge extension circular dependency detection with extension names in error
- Canvas batch rendering wrapped in requestAnimationFrame for vsync
- Windows Pack: Authenticode signing via signtool (sign_cert/sign_password)
- Windows notifications: Shell_NotifyIcon balloon tips (replaces MessageBox)
0.23 2026-04-16
- Fix t/47_clipboard.t test for empty string on FreeBSD/headless systems
- Empty clipboard behavior is now platform-tolerant (undef or empty allowed)
- Fix t/63_pack.t path separator comparison on Windows
- Fix t/26_socket.t plan mismatch on Windows (use done_testing instead of fixed plan)
- Skip Unix socket Hub/Client tests on Windows
- Adds Chandra::Canvas
0.22 2026-04-11
- make manifest to include edge header
0.21 2026-04-11
- Add Edge/WebView2 backend for Windows (Chromium-based rendering)
0.20 2026-04-09
- Update deps - bump min version of File::Raw to 0.08 to get file_join
- Make tests environment agnostic
0.19 2026-04-08
- Add Chandra::Pack - Bundle Chandra apps into distributable packages
0.18 2026-04-08
- Add Chandra::Bridge::Extension - register custom JS modules on window.chandra
- Add App->extend_bridge() convenience method
- Fix clipboard test on FreeBSD: tolerate undef from get_text after set_text('')
0.17 2026-04-07
- Add Chandra::Form - HTML Form helpers with two-way binding for Chandra applications
- Auto-detect any installed webkit2gtk version via pkg-config
- Fix splash test skip on headless Linux (probe with Window not Splash)
0.16 2026-04-07
- Fix Win32 builds: gettimeofday/localtime_r/flock/urandom portability
- Fix headless Linux test failures to satisfy cpantesters
- More test fixes for Strawberry Perl
- Makefile fix for darwin intel arch
0.14 2026-04-06
- Add Chandra::ContextMenu - Context menus for Chandra applications
- A few more fixes to try and get Strawberry to build
0.13 2026-04-05
- Add Chandra::Window - Multi-window management
- Add Chandra::Assets - Asset bundling and resource loading for Chandra apps
- Add Chandra::Clipboard - System clipboard access (text, HTML, images)
- Add Chandra::DragDrop - File drops, text drops, intra-app drag and drop
- Auto-detect headless environments in Makefile.PL for CPAN Testers
- Fix Windows/Strawberry Perl builds (MULTIPLICITY-safe Win32 APIs)
0.12 2026-04-04
- Add Chandra::Store - Persistent key-value storage for Chandra apps
0.10 2026-04-02
- Add Chandra::Shortcut - keyboard shortcuts and global hotkeys
Chandra.xs
Changes
examples/assets_example.pl
examples/assets_mount_example.pl
examples/bind_example.pl
examples/bridge_extension_example.pl
examples/canvas_demo.pl
examples/chat_hub.pl
examples/chat_window.pl
examples/clipboard_example.pl
examples/contextmenu_example.pl
examples/counter.pl
examples/counter_app.pl
examples/devtools_example.pl
examples/dialog_example.pl
examples/dragdrop_example.pl
examples/element_example.pl
examples/form_example.pl
examples/hot_reload_content.html
examples/hot_reload_example.pl
examples/splash_example.pl
examples/splash_simple.pl
examples/store_example.pl
examples/table.pl
examples/tray_example.pl
include/chandra/chandra.h
include/chandra/chandra_assets.h
include/chandra/chandra_bind.h
include/chandra/chandra_bridge_ext.h
include/chandra/chandra_canvas.h
include/chandra/chandra_clipboard.h
include/chandra/chandra_contextmenu.h
include/chandra/chandra_devtools.h
include/chandra/chandra_element.h
include/chandra/chandra_error.h
include/chandra/chandra_form.h
include/chandra/chandra_internal.h
include/chandra/chandra_log.h
include/chandra/chandra_notify.h
include/chandra/chandra_socket_client.h
include/chandra/chandra_socket_common.h
t/37_css_js.t
t/38_notify.t
t/39_shortcut.t
t/40_shortcut_edge.t
t/41_store.t
t/42_store_edge.t
t/43_window.t
t/44_window_edge.t
t/45_assets.t
t/46_assets_edge.t
t/47_clipboard.t
t/48_clipboard_edge.t
t/49_dragdrop.t
t/50_dragdrop_edge.t
t/51_contextmenu.t
t/52_contextmenu_edge.t
t/53_canvas.t
t/54_canvas_paths.t
t/57_log.t
t/58_log_edge.t
t/59_form.t
t/60_form_edge.t
t/79_validation.t
t/80_nav.t
t/lib/Chandra/Test/Display.pm
typemap
xs/app.xs
xs/assets.xs
xs/bind.xs
xs/bridge.xs
xs/bridge_extension.xs
xs/canvas.xs
xs/clipboard.xs
xs/contextmenu.xs
xs/core.xs
xs/devtools.xs
xs/dialog.xs
xs/dragdrop.xs
xs/element.xs
xs/error.xs
xs/event.xs
xs/form.xs
xs/hotreload.xs
examples/clipboard_example.pl view on Meta::CPAN
# Clear
Chandra::Clipboard->clear;
print "Clipboard cleared.\n";
print "Has text after clear: ", Chandra::Clipboard->has_text ? 'yes' : 'no', "\n";
# Integration with Chandra::App (commented - requires running app)
# use Chandra::App;
# my $app = Chandra::App->new(title => 'Clipboard Demo');
# $app->bind('copy', sub {
# my ($text) = @_;
# $app->clipboard->set_text($text);
# });
# $app->bind('paste', sub {
# return $app->clipboard->get_text;
# });
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)(
include/chandra/chandra_clipboard.h view on Meta::CPAN
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"));
include/chandra/chandra_clipboard.h view on Meta::CPAN
"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)(
include/chandra/chandra_clipboard.h view on Meta::CPAN
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)(
include/chandra/chandra_clipboard.h view on Meta::CPAN
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"));
include/chandra/chandra_clipboard.h view on Meta::CPAN
"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)(
include/chandra/chandra_clipboard.h view on Meta::CPAN
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)(
include/chandra/chandra_clipboard.h view on Meta::CPAN
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);
include/chandra/chandra_clipboard.h view on Meta::CPAN
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 */
include/chandra/chandra_clipboard.h view on Meta::CPAN
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 */
lib/Chandra/Clipboard.pm view on Meta::CPAN
use Chandra ();
our $VERSION = '0.30';
1;
__END__
=head1 NAME
Chandra::Clipboard - System clipboard access for Chandra applications
=head1 SYNOPSIS
use Chandra::Clipboard;
# Text
Chandra::Clipboard->set_text('Hello, World!');
my $text = Chandra::Clipboard->get_text;
# HTML
lib/Chandra/Clipboard.pm view on Meta::CPAN
# Query
print "has text\n" if Chandra::Clipboard->has_text;
print "has html\n" if Chandra::Clipboard->has_html;
print "has image\n" if Chandra::Clipboard->has_image;
# Clear
Chandra::Clipboard->clear;
=head1 DESCRIPTION
Chandra::Clipboard provides cross-platform system clipboard access.
All methods are class methods - no object instantiation needed.
=head2 Platform Support
=over 4
=item macOS - NSPasteboard (Cocoa)
=item Linux - GTK clipboard (gtk_clipboard_*)
=item Windows - stub (not yet implemented)
=back
=head1 METHODS
=over 4
=item get_text()
Returns the current clipboard text, or undef if unavailable.
=item set_text($string)
Sets the clipboard to the given text. Returns 1 on success.
=item has_text()
Returns true if the clipboard contains text.
=item get_html()
Returns clipboard HTML content, or undef.
=item set_html($html)
Sets the clipboard to the given HTML. Returns 1 on success.
=item has_html()
Returns true if the clipboard contains HTML.
=item get_image()
Returns raw PNG bytes from the clipboard, or undef.
=item set_image($path)
Loads an image file and places it on the clipboard. Returns 1 on success.
=item has_image()
Returns true if the clipboard contains an image.
=item clear()
Clears all clipboard contents.
=back
=head1 SEE ALSO
L<Chandra>, L<Chandra::App>
=cut
t/47_clipboard.t view on Meta::CPAN
can_ok('Chandra::Clipboard', qw(
get_text set_text has_text
get_html set_html has_html
get_image set_image has_image
clear
));
}
# ---- Text round-trip ----
{
ok(Chandra::Clipboard->set_text('Hello clipboard'), 'set_text returns true');
ok(Chandra::Clipboard->has_text, 'has_text after set');
is(Chandra::Clipboard->get_text, 'Hello clipboard', 'get_text round-trip');
}
# ---- Overwrite text ----
{
Chandra::Clipboard->set_text('first');
is(Chandra::Clipboard->get_text, 'first', 'first text set');
Chandra::Clipboard->set_text('second');
is(Chandra::Clipboard->get_text, 'second', 'text overwritten');
}
t/47_clipboard.t view on Meta::CPAN
ok(defined $got, 'UTF-8 text retrieved');
is($got, $utf8, 'UTF-8 round-trip');
}
# ---- Empty string ----
{
ok(Chandra::Clipboard->set_text(''), 'set empty string');
my $got = Chandra::Clipboard->get_text;
# Empty string handling is platform-dependent:
# - Some platforms return '' (empty string)
# - Some platforms return undef (clipboard clears on empty)
# - Headless systems may not retain clipboard contents at all
ok(!defined $got || $got eq '', 'get_text returns undef or empty for empty string');
}
# ---- HTML round-trip ----
{
my $html = '<b>Bold</b>';
ok(Chandra::Clipboard->set_html($html), 'set_html returns true');
# Note: on some platforms set_html may not produce has_html=true
# (GTK stores as text), so just check set works
}
t/48_clipboard_edge.t view on Meta::CPAN
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use File::Temp qw(tempdir);
use lib 't/lib';
use Chandra::Clipboard;
use Chandra::Test::Display;
Chandra::Test::Display->skip_unless_clipboard;
# ---- Large text ----
{
my $big = 'x' x 100_000;
ok(Chandra::Clipboard->set_text($big), 'set 100k text');
my $got = Chandra::Clipboard->get_text;
is(length($got), 100_000, '100k round-trip length');
is($got, $big, '100k content matches');
}
t/48_clipboard_edge.t view on Meta::CPAN
# Minimal valid 1x1 red PNG (67 bytes)
my $png = pack("H*",
"89504e470d0a1a0a0000000d49484452000000010000000108020000009001" .
"2e00000000c4944415478016360f8cf00000002000160e7274800000000" .
"0049454e44ae426082");
open my $fh, '>:raw', $png_path or die "Cannot write PNG: $!";
print $fh $png;
close $fh;
my $ok = Chandra::Clipboard->set_image($png_path);
skip 'image clipboard not available (headless?)', 4 unless $ok;
ok($ok, 'set_image with valid PNG');
ok(Chandra::Clipboard->has_image, 'has_image after set');
my $data = Chandra::Clipboard->get_image;
ok(defined $data, 'get_image returns data');
ok(length($data) > 0, 'image data is non-empty') if defined $data;
}
# ---- Double clear ----
{
Chandra::Clipboard->clear;
t/lib/Chandra/Test/Display.pm view on Meta::CPAN
my $obj = eval { Chandra::Window->new };
if ($obj && $obj->can('close')) { $obj->close }
exit($obj ? 0 : 1);
}
waitpid($pid, 0);
if ($?) {
plan skip_all => "$label not supported (display unavailable)";
}
}
sub skip_unless_clipboard {
my ($class, %opts) = @_;
my $env_var = $opts{env_skip} || 'CHANDRA_SKIP_CLIPBOARD';
if ($ENV{$env_var}) {
plan skip_all => "$env_var set";
}
require Chandra::Clipboard;
Chandra::Clipboard->set_text('__probe__');
my $got = Chandra::Clipboard->get_text;
unless (defined $got && $got eq '__probe__') {
plan skip_all => 'clipboard not available (no display?)';
}
Chandra::Clipboard->clear;
}
1;
SPAGAIN;
RETVAL = (count > 0) ? newSVsv(POPs) : newSV(0);
PUTBACK; FREETMPS; LEAVE;
(void)hv_stores(hv, "_assets", SvREFCNT_inc(RETVAL));
}
}
OUTPUT:
RETVAL
# ---- clipboard() - returns Chandra::Clipboard class for convenience ----
SV *
clipboard(self)
SV *self
CODE:
{
load_module(PERL_LOADMOD_NOIMPORT,
newSVpvs("Chandra::Clipboard"), NULL);
RETVAL = newSVpvs("Chandra::Clipboard");
}
OUTPUT:
RETVAL
xs/clipboard.xs view on Meta::CPAN
MODULE = Chandra PACKAGE = Chandra::Clipboard
PROTOTYPES: DISABLE
SV *
get_text(class)
const char *class
CODE:
{
char *text = chandra_clipboard_get_text(aTHX);
if (text) {
RETVAL = newSVpv(text, 0);
SvUTF8_on(RETVAL);
safefree(text);
} else {
RETVAL = &PL_sv_undef;
}
}
OUTPUT:
RETVAL
int
set_text(class, text_sv)
const char *class
SV *text_sv
CODE:
{
STRLEN len;
const char *text = SvPV(text_sv, len);
RETVAL = chandra_clipboard_set_text(aTHX_ text, len);
}
OUTPUT:
RETVAL
int
has_text(class)
const char *class
CODE:
{
RETVAL = chandra_clipboard_has_text(aTHX);
}
OUTPUT:
RETVAL
SV *
get_html(class)
const char *class
CODE:
{
char *html = chandra_clipboard_get_html(aTHX);
if (html) {
RETVAL = newSVpv(html, 0);
SvUTF8_on(RETVAL);
safefree(html);
} else {
RETVAL = &PL_sv_undef;
}
}
OUTPUT:
RETVAL
int
set_html(class, html_sv)
const char *class
SV *html_sv
CODE:
{
STRLEN len;
const char *html = SvPV(html_sv, len);
RETVAL = chandra_clipboard_set_html(aTHX_ html, len);
}
OUTPUT:
RETVAL
int
has_html(class)
const char *class
CODE:
{
RETVAL = chandra_clipboard_has_html(aTHX);
}
OUTPUT:
RETVAL
SV *
get_image(class)
const char *class
CODE:
{
RETVAL = chandra_clipboard_get_image(aTHX);
}
OUTPUT:
RETVAL
int
set_image(class, path_sv)
const char *class
SV *path_sv
CODE:
{
const char *path = SvPV_nolen(path_sv);
RETVAL = chandra_clipboard_set_image(aTHX_ path);
}
OUTPUT:
RETVAL
int
has_image(class)
const char *class
CODE:
{
RETVAL = chandra_clipboard_has_image(aTHX);
}
OUTPUT:
RETVAL
void
clear(class)
const char *class
CODE:
{
chandra_clipboard_clear(aTHX);
}