view release on metacpan or search on metacpan
0.007 2014-03-08 T. R. Wyant
No changes since 0.006_02.
0.006_02 2014-03-02 T. R. Wyant
Notify users of intent to remove the configuration prompt (about
installing pbtool) and simply install it by default. The -y and -n
options will remain, with their present function.
0.006_01 2014-03-01 T. R. Wyant
Serialize access of tests to clipboard. This involves an increase in
the required version of Test::More, to 0.96.
0.006 2013-11-18 T. R. Wyant
No changes since 0.005_01.
0.005_01 2013-11-16 T. R. Wyant
Fix compile errors under Xcode 5.0.2.
0.005 2013-05-11 T. R. Wyant
No changes since 0.004_01.
Mac-Pasteboard is Copyright (C) 2008, 2011-2026 by Thomas R. Wyant, III
DESCRIPTION
This XS module accesses Mac OS X pasteboards, which can be thought of as
clipboards with bells and whistles. System-defined pasteboards can be
accessed, and user-defined pasteboards can be created and accessed. Each
pasteboard can contain multiple items of data, and each item can contain
multiple 'flavors.'
NOTE that Mac OS X appears to restrict pasteboard access to processes
that are logged in interactively. Ssh sessions and cron jobs can not
create the requisite pasteboard handles, giving coreFoundationUnknownErr
(-4960).
Flavors (Apple's technical term) correspond more or less to MIME types,
as the case may be. For instance, 'public.utf16-plain-text' conforms to
'public.plain-text', which in turn conforms to 'public.text'. But none
of them conforms to 'public.image'. Conformance is transitive, so
'public.utf16-plain-text' also conforms to 'public.plain-text', and so
on.
This module makes all these features available to the user, but is (I
hope!) organized in such a way that the user who does not wish to deal
with them need not do so, since defaults are provided to cover what the
author suspects to be the most common case: manipulating plain text on
the system clipboard.
The programming interface is object-oriented. Each object represents a
pasteboard, with the default being the system clipboard. Item ID is an
attribute of the object rather than a method argument, with the default
(undef) being special-cased to write to item ID 1, but read from the
most recent ID. Flavor _is_ an argument, but defaults to
'com.apple.traditional-mac-plain-text', which is the flavor used by the
'pbcopy' and 'pbpaste' executables provided with Mac OS X. A couple
convenience subroutines (pbcopy() and pbpaste()) are provided to make
things even simpler, and are exported by default. Also exported on
demand are various manifest constants: pasteboard names, flavor and
status flags, and pasteboard-related error codes.
This is the file you are currently looking at.
droplet
This is the source for an Apple Script droplet which passes the full
path names of any files dropped on it to a Perl script. To make this
into an Apple Script application bundle, open the Script Editor (in
/Applications/AppleScript until OS 10.10 Yosemite, where it became
/Applications/Utilities/Script Editor). Then run eg/droplet onto the
clipboard by (e.g.)
$ pbcopy <eg/droplet
Then paste the source into the Script Editor and save it as an
application bundle. The Mac::Pasteboard::Droplet documentation gives
the details on how to inject your Perl script into the application
bundle.
save_image
This script saves an image on the system clipboard to a file. The
file name may be given on the command line; it defaults to
'clipboard'. If there is a file name extension associated with the
image's flavor, that extension is tacked onto the end of the file
name. For example, if the clipboard contains flavor 'com.apple.pict'
and this script is run, the output file will be 'clipboard.pict'
unless the user specifies otherwise. If there is more than one image
on the pasteboard, all will be dumped. If there is no image on the
pasteboard, the script dies with a semi-appropriate message.
eg/save_image view on Meta::CPAN
#!/usr/local/bin/perl
use strict;
use warnings;
use Mac::Pasteboard;
my $file = shift @ARGV || 'clipboard';
my $pb = Mac::Pasteboard->new ();
my @img = $pb->paste_all ('public.image')
or die "No data conforming to 'public.image' found.\n";
foreach my $item (@img) {
my $tags = $pb->flavor_tags ($item->{flavor});
my $fn = $tags->{extension} ? "$file.$tags->{extension}" : $file;
warn "Creating $fn\n";
lib/Mac/Pasteboard.pm view on Meta::CPAN
return;
}
# Autoload methods go after =cut, and are processed by the autosplit program.
1;
__END__
=head1 NAME
Mac::Pasteboard - Manipulate Mac OS X clipboards/pasteboards.
=head1 SYNOPSIS
To acquire text from the system clipboard, replacing it with your own:
use Mac::Pasteboard;
my $old_text = pbpaste();
pbcopy ("Hello, sailor!\n");
or equivalently, using the object-oriented interface,
use Mac::Pasteboard;
my $pb = Mac::Pasteboard->new ();
my $old_text = $pb->paste ();
lib/Mac/Pasteboard.pm view on Meta::CPAN
B<Some> taint support was added in version C<0.015_01>. Specifically, if
you are running with taint support turned on, data off the pasteboard
will be tainted, and an attempt to create a pasteboard with a tainted
name will result in an exception. More such will be added if it seems
warranted.
=head1 DESCRIPTION
This XS module accesses Mac OS X pasteboards, which can be thought of as
clipboards with bells and whistles. Under Mac OS X, the system clipboard
is simply a special case of a pasteboard. In the following
documentation, 'clipboard' refers to the system clipboard, and
'pasteboard' refers to pasteboards in general.
This module uses the Pasteboard interface, which was introduced in Mac
OS 10.3 (a.k.a. 'Panther'), so it requires Mac OS 10.3 or better to run.
The simple case of placing plain text onto and reading it from the
system clipboard is accomplished by subroutines pbcopy() and pbpaste()
respectively. These correspond roughly to the command-line executables
of the same name, and are exported by default. If this is all you are
interested in, you can stop reading here. The rest of this section
describes the bells and whistles associated with a Mac OS X pasteboard.
A Mac OS X pasteboard contains zero or more data items, each of which is
capable of holding one or more flavors of data. The system defines a
couple pasteboards, including the system clipboard, named
'com.apple.pasteboard.clipboard'. The system clipboard is the default
taken if new() is called without arguments.
Data items are identified by an item id which is provided by the creator
of the item, and which (the documentation says) should only be
interpreted by the creator. Item flavors may be duplicated between items
but not within items. The item L<id|/id> is an attribute of
the Mac::Pasteboard object, with the default chosen so that you should
not need to worry about it unless you explicitly want more than one item
on a pasteboard.
lib/Mac/Pasteboard.pm view on Meta::CPAN
and true for failure.
The following methods are provided:
=head2 new
$pb = Mac::Pasteboard->new( $name )
This method creates a new pasteboard object, connected to the pasteboard
of the given name, creating the pasteboard if necessary. If called with
no argument, you get the system clipboard, a.k.a.
L</kPasteboardClipboard>, a.k.a. C<'com.apple.pasteboard.clipboard'>.
Passing undef to new() is B<not> equivalent to calling it with no
arguments at all, since undef is the encoding for
L</kPasteboardUniqueName>.
If running with taint checking enabled, a tainted pasteboard name will
cause an exception.
Note that an error in creating a new pasteboard B<will> cause an
exception, since the L<fatal|/fatal> attribute defaults to 1.
If you want to get a status back, you will need to call
lib/Mac/Pasteboard.pm view on Meta::CPAN
If running with taint checking enabled, the C<{data}> value will be
tainted.
The L</SEE ALSO> section has a link to the I<Uniform Type Identifiers
Overview>, which deals with the notion of type conformance.
=head2 pbcopy
pbcopy( $data, $flavor, $flags )
This convenience subroutine (B<not> method) clears the system clipboard
and then copies the given data to it. All three arguments are optional
(the prototype being C<(;$$$)>. If C<$data> is undef, the value of C<$_>
is used. If C<$flavor> is C<undef>, the L<default
flavor|/defaultFlavor> is used. If C<$flags> is C<undef>,
L<kPasteboardFlavorNoFlags|/kPasteboardFlavorNoFlags> is used.
In other words, this subroutine is more-or-less equivalent to the
C<pbcopy> executable.
=head2 pbcopy_find
lib/Mac/Pasteboard.pm view on Meta::CPAN
This convenience subroutine (B<not> method) returns the default data
flavor for the 'find' pasteboard. if the argument is defined and not
C<''>, the argument becomes the new default flavor and the old default
flavor is returned.
=head2 pbpaste
( $data, $flags ) = pbpaste( $flavor )
This convenience subroutine (B<not> method) retrieves the given flavor
of data from the system clipboard, and its associated flavor flags. The
flavor is optional, the default being the
L<default flavor|/defaultFlavor>. If the given flavor is not found
C<undef> is returned for C<$data>.
The functionality is equivalent to calling C<paste()> on an object whose
L<id|/id> attribute is C<undef>.
If called in scalar context, you get C<$data>.
In other words, this subroutine is more-or-less equivalent to the
lib/Mac/Pasteboard.pm view on Meta::CPAN
This name may not be the name you used to create the
pasteboard, even if you used one of the built-in names. But unless you
created the pasteboard using name kPasteboardUniqueName, the name will
be equivalent. That is,
my $pb1 = Mac::Pasteboard->new();
my $pb2 = Mac::Pasteboard->new(
$pb1->get('name'));
gives two handles to the same clipboard.
=head2 requested_name
This read-only string attribute reports the name passed to C<new()>.
=head2 status
This dualvar attribute contains the status of the last operation. You
can set this with an integer; the dualvar will be generated.
lib/Mac/Pasteboard.pm view on Meta::CPAN
available to the process that placed it on the pasteboard.
Oddly enough, the 'pbpaste' executable seems to be able to find such
data. But the Pasteboard Peeker demo application can not, so I am pretty
sure this module is working OK. Unfortunately I was unable to find the
source for pbpaste online, so I am unable to verify what's going on.
=head3 kPasteboardFlavorSenderTranslated
This pasteboard flavor flag indicates that the flavor's data has been
translated in some way by the process that placed it on the clipboard,
and it will not be saved by the Finder in clipping files.
=head3 kPasteboardFlavorSystemTranslated
This pasteboard flavor flag indicates that the flavor's data must be
translated by the Translation Manager. This flag cannot be set
programmatically, and the Finder will not save this data in clipping
files.
=head2 Pasteboard and flavor names
=head3 defaultFlavor
This constant represents the name of the default flavor, either
C<'com.apple.traditional-mac-plain-text'> or
C<'public.utf8-plain-text'>, depending on what version of macOS you are
running and how this module was installed.
=head3 kPasteboardClipboard
This constant represents the name of the system clipboard,
C<'com.apple.pasteboard.clipboard'>.
=head3 kPasteboardFind
This constant represents the name of the find pasteboard,
C<'com.apple.pasteboard.find'>.
=head3 kPasteboardUniqueName
This constant specifies that a unique name be generated for the
pasteboard. Under Mac OS 10.4 (Tiger) or above, the generated name will
lib/Mac/Pasteboard.pm view on Meta::CPAN
earlier versions.
=head1 SUPPORT
Support is by the author. Please file bug reports at
L<https://github.com/trwyant/perl-Mac-Pasteboard/issues/> or in
electronic mail to the author.
=head1 SEE ALSO
The B<Clipboard> module by Ryan King will access text on the clipboard
under most operating systems. Under macOS. recent versions use this
module; older ones shell out to the I<pbpaste> and I<pbcopy> executables.
The I<pbpaste> and I<pbcopy> executables themselves are available, and
described by their respective man pages.
The I<Pasteboard Manager Reference> is available online at
L<https://web.archive.org/web/20090718011220/http://developer.apple.com/documentation/Carbon/Reference/Pasteboard_Reference/Reference/reference.html>.
See also the I<Pasteboard Manager Programming Guide> at
L<https://web.archive.org/web/20090718063745/http://developer.apple.com/documentation/Carbon/Conceptual/Pasteboard_Prog_Guide/paste_intro/paste_intro.html>.
if (!stat && pbref != NULL) {
stat = pbl_clear (pbref);
CFRelease (pbref);
}
} else if (!strcmp (argv[1], "copy")) {
if (ARGUMENT(2) == NULL) {
fprintf (stderr, "You must supply an argument to 'copy'\n");
} else {
PasteboardRef pbref;
stat = pbl_create(
ARGUMENT_D( 4, "com.apple.pasteboard.clipboard" ),
(void **) &pbref, NULL );
if (!stat && pbref != NULL) {
stat = pbl_clear (pbref);
if (!stat)
stat = pbl_copy (
pbref,
(const unsigned char *) ARGUMENT(2),
strlen (ARGUMENT(2)),
1, ARGUMENT(3), 0);
CFRelease (pbref);
}
}
} else if (!strcmp (argv[1], "create")) {
PasteboardRef pbref = NULL;
char *pbname = NULL;
stat = pbl_create (
ARGUMENT_D( 2, "com.apple.pasteboard.clipboard" ),
(void **) &pbref, &pbname);
if (pbname != NULL) {
fprintf (stderr, "Created pasteboard \"%s\"\n", pbname);
FREE ("main pbname", pbname);
}
if (pbref != NULL)
CFRelease (pbref);
} else if (!strcmp (argv[1], "paste")) {
PasteboardRef pbref;
stat = pbl_create(
ARGUMENT_D( 2, "com.apple.pasteboard.clipboard" ),
(void **) &pbref, NULL );
if (!stat && pbref != NULL) {
unsigned char* data;
size_t size;
PB_FLAVOR_FLAGS flags;
stat = pbl_paste( pbref, 1, 0UL, ARGUMENT( 3 ),
&data, &size, &flags );
if ( data != NULL ) {
data[size] = '\0';
printf( "data: '%s'\n", data );
printf( "size: %lu\n", size );
printf( "flags: %#lx\n", ( unsigned long ) flags );
}
CFRelease (pbref);
}
} else if (!strcmp (argv[1], "pbl_all")) {
PasteboardRef pbref;
stat = pbl_create(
ARGUMENT_D( 2, "com.apple.pasteboard.clipboard" ),
(void **) &pbref, NULL );
if (!stat && pbref != NULL) {
pbl_rqst_t rqst = {
1,
0,
NULL,
1,
};
pbl_resp_t *resp;
size_t num_resp;
/*
* pbl_uti_tags returns the preferred tags associated with the given
* UTI in the given structure. On return, the elements of the structure
* will contain either strings (which must be freed), or NULL.
*/
void pbl_uti_tags (char * c_uti, pbl_uti_tags_t * tags);
/*
* pbl_all returns everything on the clipboard, subject to the settings
* in the rqst argument, to wit:
* if rqst.all is false, only data matching the given id is
* returned.
* if rqst.want_data is false, the actual flavor data is not
* returned.
*/
OSStatus pbl_all (
void * pbref,
pbl_rqst_t *rqst,
script/pbtool view on Meta::CPAN
#!/usr/local/bin/perl
=head1 NAME
pbtool - Manipulate Mac OS X pasteboards/clipboards.
=head1 SYNOPSIS
$ pbtool
pbtool> paste
Flags = 0 (kPasteboardFlavorNoFlags)
Able was I ere I saw Elba.
pbtool> clear
pbtool> copy "Madam, I'm Adam."
pbtool> exit
script/pbtool view on Meta::CPAN
causes I<copy> to use item ID 1, and I<paste> to use the last item that
contains the desired flavor), specify -noid.
The default is -noid.
=head1 DETAILS
I<Pbtool> is a Perl script that acts as a wrapper for Mac::Pasteboard.
Most functions of the package are available through the script, and it
adds a couple on its own account. The commands in general operate on the
current pasteboard, which initially is the system clipboard. Commands
also exist for changing the script's notion of the current pasteboard.
Input is from standard in, using Term::ReadLine if that is available and
the input is a terminal.
Blank input lines and input lines whose first non-blank character is '#'
are ignored. Any lines left are broken into tokens on spaces, though
quoted text is kept together. Text::ParseWords does the heavy lifting
here.
script/pbtool view on Meta::CPAN
}
-t and print "\n";
my $pb;
=head2 clear [name]
This command clears the current pasteboard. If a name is given, the
named pasteboard becomes the current pasteboard, and it is cleared. If
no name is given and there is no current pasteboard, the system
clipboard becomes the current pasteboard and is cleared.
=cut
sub clear {
$pb or create (@_);
$pb->clear ();
return;
}
=head2 copy
pbtool> copy data [flavor [flags]]
This command copies the given data to the current pasteboard as the
given flavor and the given flavor flags. The flavor flags default to 0,
and the flavor to 'com.apple.traditional-mac-plain-text'. If there is no
current pasteboard the system clipboard becomes the current pasteboard,
but you get an error anyway because you do not own it at this point, not
having cleared it.
If the I<id> setting is set to a number, your data is copied to the item
with that ID. If it is set to undef, it is copied to item id 1.
=cut
sub copy {
$pb or create ();
script/pbtool view on Meta::CPAN
return;
}
=head2 copy_file
pbtool> copy_file file_name [flavor [flags]]
This command copies the contents of the given file to the current
pasteboard as the given flavor and the given flavor flags. The flavor
flags default to 0, and the flavor to C<defaultFlavor>. If there is no
current pasteboard the system clipboard becomes the current pasteboard,
but you get an error anyway because you do not own it at this point, not
having cleared it.
The file's encoding is assumed to be that implied by the flavor, if any.
If the I<id> setting is set to a number, your data is copied to the item
with that ID. If it is set to undef, it is copied to item id 1.
=cut
script/pbtool view on Meta::CPAN
close $fh;
$pb->copy( $data, $flavor, $flags );
return;
}
=head2 create
pbtool> create [name]
The named pasteboard is created if necessary, and becomes the current
pasteboard. If no name is specified, you get the system clipboard, named
'com.apple.pasteboard.clipboard'.
=cut
{
my %cache;
sub create { ## no critic (RequireArgUnpacking)
my $name = defined $_[0] ? $_[0] : kPasteboardClipboard;
$pb = $cache{$name} ||= Mac::Pasteboard->new( $name );
$pb->set(
encode => ! $opt{binary},
script/pbtool view on Meta::CPAN
print $pb->get( 'default_flavor' ), "\n";
defined $flavor
and $pb->set( default_flavor => $flavor );
return;
}
=head2 flavors
pbtool> flavors [conforms_to]
This command dumps the flavors of data present on the clipboard which
conform to the given flavor, If no conforming flavor is given, all
flavors are dumped. If the I<id> is defined, only data from that
pasteboard item are dumped. The output is in YAML if module B<YAML>
can be loaded, or in B<Data::Dumper> format if that module can be
loaded. Either way, what you actually get is an array of anonymous
hashes. Each hash has the following keys:
flag_names: a reference to a list of the names of the flags set;
flags: the flavor flags;
flavor: the name of the flavor;
script/pbtool view on Meta::CPAN
sub help {
pod2usage( { -verbose => 2, -exitval => 'NOEXIT' } );
return;
}
=head2 name
pbtool> name
This command displays the name of the current pasteboard. If there is no
current pasteboard, the system clipboard is made the current pasteboard,
and its name is displayed.
=cut
sub name {
$pb or create ();
print $pb->get ('name'), "\n";
return;
}
script/pbtool view on Meta::CPAN
=head2 paste_all
pbtool> paste_all [conforms_to]
This command displays all data on the current pasteboard conforming to
the given flavor. If no flavor is given, all flavors are displayed. If
the I<id> is set, only data from the corresponding item are displayed.
The output is the same as for L<flavors|/flavors>, but in addition the
'data' key holds the actual data. If there is no current clipboard, the
system pasteboard is made the current clipboard.
See
L<https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/understanding_utis/understand_utis_intro/understand_utis_intro.html>
for the concept of conformance.
=cut
sub paste_all {
my @args = @_;
$pb or create ();
script/pbtool view on Meta::CPAN
_expand_flavors( $pb, @flavors );
print Dump( \@flavors );
return;
}
=head2 pbpaste
pbtool> pbpaste
This command is equivalent to I<paste>, but always uses the system
clipboard.
=cut
sub pbpaste {
my @args = @_;
my ($data, $flags) = Mac::Pasteboard::pbpaste (@args);
defined $data or die "No data found\n";
print STDERR "Flags = $flags (",
scalar Mac::Pasteboard::flavor_flag_names ($flags), ")\n";
print $data;
script/pbtool view on Meta::CPAN
@_ = ();
goto &opt;
}
=head2 status
pbtool> status [new_value]
This command displays the current status setting of the current
pasteboard, optionally setting it first to the given value. If there is
no current pasteboard, the system clipboard is made the current
pasteboard, and its status is displayed.
=cut
sub status {
my @args = @_;
$pb or create ();
@args and $pb->set (status => $args[0]);
print $pb->get ('status'), "\n";
return;
}
=head2 synch
pbtool> synch [pasteboard_name]
This command synchronizes with the current pasteboard. If a name is
given, that pasteboard is made the current pasteboard, and it is
synchronized. If there is no current pasteboard and no name is given,
the system clipboard is made the current pasteboard.
The synchronization flags returned by the operation are written to
standard out.
=cut
sub synch {
my @args = @_;
(!$pb || @args) and create (@args);
my $flags = $pb->synch ();
script/pbtool view on Meta::CPAN
} else {
push @rslt, $_;
}
}
return @rslt;
}
# @args = _options (@args);
#
# This subroutine feeds its input to GetOptions. Anything left
# over is assumed to be the name of a clipboard to make current.
# An error results in the display of a brief error message.
sub _options {
local @ARGV = @_;
GetOptions( \%opt,
qw{ binary! default_flavor|default-flavor=s echo! id=i },
noid => sub { $opt{id} = undef },
help => sub { pod2usage( { -verbose => 2 } ) },
) or pod2usage( { -verbose => 0 } );
$pb and $pb->set(
t/pasteboard.t view on Meta::CPAN
use strict;
use warnings;
use Test::More 0.96; # Because of subtest();
note <<'EOD';
The tests were originally segregated into their own files because this
was the handiest way to segregate the tests of different functions. But
this causes fights over the clipboard when the tests are run in
parallel, resulting in test failures. Renaming the test files and then
just doing them seemed like the simplest way to serialize the whole
mess.
EOD
diag '';
foreach my $name ( qw{ LANG LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY
LC_NUMERIC LC_TIME LC_MESSAGES } ) {
my_diag_value( $name, $ENV{$name} );
t/pasteboard.t view on Meta::CPAN
} or diag 'I18N::Langinfo unavailable';
}
if ( eval { require Mac::Pasteboard; 1 } ) {
foreach my $sub ( qw{ defaultEncode defaultFlavor __variant } ) {
my $code = Mac::Pasteboard->can( $sub );
my_diag_value( $sub, $code->() );
}
}
subtest 'Copy to clipboard' => sub {
do './t/copy.tx';
};
subtest 'Error handling' => sub {
do './t/error.tx';
};
subtest 'Miscellaneous' => sub {
do './t/misc.tx';
};
subtest 'Paste from clipboard' => sub {
do './t/paste.tx';
};
subtest 'Synch with clipboard' => sub {
do './t/synch.tx';
};
done_testing;
sub my_diag_value {
my ( $name, $value ) = @_;
if ( defined $value ) {
diag "$name='$value'";
} else {