Chandra

 view release on metacpan or  search on metacpan

Chandra.xs  view on Meta::CPAN

#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;

Chandra.xs  view on Meta::CPAN

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

Changes  view on Meta::CPAN

  - 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)

Changes  view on Meta::CPAN

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

Changes  view on Meta::CPAN

   - 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

MANIFEST  view on Meta::CPAN

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

MANIFEST  view on Meta::CPAN

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

MANIFEST  view on Meta::CPAN

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

MANIFEST  view on Meta::CPAN

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;

xs/app.xs  view on Meta::CPAN

        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);
}



( run in 2.049 seconds using v1.01-cache-2.11-cpan-84e82930d8c )