#!/usr/bin/perl
our $APP     = 'clipbored';
our $VERSION = '1.2.9';
use strict;
use Getopt::Long;
use Pod::Usage;
use File::Path 'mkpath';


my $xsel_log = clip_location();
my $pidfile  = '/tmp/clipbored.pid';

my($opt_no_daemon, $opt_x_buffer) = (undef, lc($ENV{CLIPBORED_X_SELECTION}));
if(!defined($opt_x_buffer)) {
  $opt_x_buffer = 'primary';
}

my($opt_no_daemon) = undef;
GetOptions(
 'no-daemon' => \$opt_no_daemon,
 'kill'      => \&killkid,
 'clear'     => sub {
   if(-e $xsel_log) {
     open (my $fh, '>', $xsel_log) or die("Could not open $xsel_log: $!");
     close($fh);
   }
   print "$xsel_log cleared\n";
   exit(0);
   },
 'last:i'    => \&lastlog,
 'help'      => sub {pod2usage(-verbose => 1); exit },
 'man'       => sub {pod2usage(-verbose => 3); exit },
 'version'   => sub { printf("%s v%s\n", $APP, $VERSION); exit },
);

if(-f $pidfile) {
  print "clipbored is already running\n";
  exit 1;
}

sync_cb();

sub lastlog {
  shift;
  my $wayback = shift;
  $wayback = 25 unless(defined($wayback));

  if($wayback < 1) {
    $wayback = 25;
  }
  open(my $fh, '<', $xsel_log) or die("Could not open $xsel_log: $!");
  my @records = <$fh>;
  close($fh);

  if(scalar(@records) < $wayback) {
    $wayback = scalar(@records);
  }

  my $i = 0;
  my @colors = -t STDOUT ? ("\e[1m", "\e[1m", "\e[0m") : ("", "", "");
  for(@records[scalar(@records) - $wayback .. scalar(@records) - 1]) {
    printf("%s%2d%s %s", $colors[0], $i, $colors[2], $_);
    $i++;
  }
  #FIXME collected since DATE
  printf("%s%s%s selections collected\n", $colors[1], scalar(@records), $colors[2]);
  exit(0);
}


sub sync_cb {
  daemonize() unless(defined($opt_no_daemon));
  while(1) {
    my %selections = ();
    chomp(my $current_selection = `/usr/bin/xclip -o -selection $opt_x_buffer`);
    if(defined($current_selection)) {
      open(my $r_xsel, '<', $xsel_log) or die("Cant open $xsel_log: $!");
      chomp(my @selections = <$r_xsel>);
      close($r_xsel);

      $current_selection =~ s/\n/ /g; # newline hassle

      map { $selections{$_} = undef } @selections;

      if(exists($selections{$current_selection})) {
        # DUPE
      }
      else {
        open(my $a_xsel, '>>', $xsel_log) or die("Cant open $xsel_log: $!");
        print $a_xsel $current_selection, "\n";
        close($a_xsel);
        print $current_selection, "\n" if(defined($opt_no_daemon));
      }
    }
    sleep 2;
  }
}

sub killkid {
  open(my $fh, '<', $pidfile) or print "clipbored is not running\n" and exit(1);
  my $target = <$fh>;
  close($fh);

  if(kill(9, $target)) {
    print "clipbored with PID $target terminated\n";
  }
  else {
    print "Could not kill $target: $!";
  }
  exit(0);
}

sub daemonize {
  use POSIX 'setsid';
  my $PID = fork();
  exit(0) if($PID); #parent
  exit(1) if(!defined($PID)); # out of resources

  setsid();
  $PID = fork();
  exit(1) if(!defined($PID));

  if($PID) { # parent
    waitpid($PID, 0);
    unlink($pidfile); # remove the lock when child have died
    exit(0);
  }
  elsif($PID == 0) { # child
    open(my $fh, '>', $pidfile) or die("Cant open $pidfile: $!");
    print $fh $$;
    close($fh);
    open(STDOUT, '>', '/dev/null');
    open(STDERR, '>', '/dev/null');
    open(STDIN,  '<', '/dev/null');
  }
}

sub clip_location {
  # File::Path exports a make_path function that would allow us to emulate
  # mkdir -p behaviour. People running Perl < 5.10 didnt like that though.
  my $dir = undef;
  if(!defined($ENV{XDG_DATA_HOME}) or(!-d $ENV{XDG_DATA_HOME})) {
    $dir = "$ENV{HOME}/.local";
  }
  else {
    $dir = $ENV{XDG_DATA_HOME};
  }
  if(!-d $dir) {
    mkpath($dir);
  }
  $dir .= "/clipbored";
  if(!-d $dir) {
    mkpath($dir);
  }
  $dir .= '/clips';
  open(my $fh, '>>', $dir) or die($!);
  close($fh);
  return($dir);
}

__END__

=pod

=head1 NAME

clipbored - continuously collects all selections in Xorg's clipboard buffers

=head1 SYNOPSIS

  clipbored [OPTIONS]

=head1 DESCRIPTION

B<clipbored> is a daemon that continuously grabs all non-duplicate selections
in the X.org clipboard buffers and writes them to a plaintext history file for
later use.

There are several scripts distributed with clipbored that'll use the history
file for different purposes.

=head2 Scripts

  dmenurl   - launch dmenu with all previously yanked URLs for you to select
              from.

  dmenuclip - launch dmenu listing all previously clipboarded content

=head1 OPTIONS

  -l,   --last        show the n latest additions
  -c,   --clear       clear all history
  -n,   --no-daemon   do not detach from the shell
  -k,   --kill        kill a running clipbored session
  -h,   --help        show this help
  -m,   --man         display the manual
  -v,   --version     show version info

=head1 ENVIRONMENT

The history file location is $XDG_DATA_HOME/clipbored/clips

The X selection to use can be specified by setting the B<CLIPBORED_X_SELECTION>
environment variable.

If unset, or set to I<primary> , text is grabbed from the B<XA_PRIMARY> buffer.
When text is selected with the mouse, or piped through xclip/xsel with zero
arguments, it ends up here. This is most likely what you want.

If set to I<clipboard>, text is grabbed from the B<XA_CLIPBOARD> buffer. Data
ends up in this buffer when an explicit action is taken to cut/copy text; used
in many GUI environments.

To the best of my knowledge, the I<secondary> buffer is rarely used at all.

The helper scripts can read properties from environment variables.
These are recognized:

  CLIPBORED_DMENU_LISTMODE    regular/vertical
  CLIPBORED_DMENU_NORMAL_FG   foreground color in HEX
  CLIPBORED_DMENU_NORMAL_BG   background color in HEX
  CLIPBORED_DMENU_SELECT_FG   selected item background color in HEX
  CLIPBORED_DMENU_SELECT_BG   selected item foreground color in HEX
  CLIPBORED_DMENU_FONT        font that will be used
  CLIPBORED_DMENU_LINES       how many lines that will be shown in vertical mode
  CLIPBORED_X_SELECTION       X buffer to use: primary, secondary, clipboard

=head1 AUTHOR

    \ \ | / /
     \ \ - /
      \ | /
      (O O)
      ( < )
      (-=-)

  Magnus Woldrich
  CPAN ID: WOLDRICH
  m@japh.se
  http://japh.se

=head1 CONTRIBUTORS

Markus Weimar suggested we should be able to pick the clipboard buffer to use.
Since I very rarely use any GUI applications, I wasn't aware of the fact that
when CTRL+C/CTRL+V etc is used, it goes into the XA_CLIPBOARD buffer instead of 
the XA_PRIMARY, probably rendering clipbored somewhat useless to the users using
these types of applications. :)

=head1 REPORTING BUGS

Report bugs to L<m@japh.se> or L<use the issue tracker|http://github.com/trapd00r/clipbored/issues>.

clipbored home page: L<http://github.com/trapd00r/clipbored/>

=head1 COPYRIGHT

Copyright 2010, 2011, 2018- the B<clipbored>s L</AUTHOR> and L</CONTRIBUTORS> as listed
above.

=head1 LICENSE

This program is free software; you can redistribute it and/or modify it under
the same terms as Perl itself.

=cut
