The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.
#!/usr/bin/perl -w

# Copyright 2011, 2012 Kevin Ryde

# This file is part of X11-Protocol-Other.
#
# X11-Protocol-Other is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation; either version 3, or (at your option) any
# later version.
#
# X11-Protocol-Other is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with X11-Protocol-Other.  If not, see <http://www.gnu.org/licenses/>.

use strict;
use X11::Protocol;

use lib 'devel', '.';

# uncomment this to run the ### lines
use Smart::Comments;

my $X = X11::Protocol->new (':0');
$X->{'event_handler'} = \&event_handler;

$X->init_extension('XFIXES') or die $@;
$X->init_extension('DAMAGE') or die $@;

$|=1;
my $root_x = 0;
my $root_y = 0;
my $width = 100;
my $height = 100;

my $origin = $X->root;
# my $origin = $X->new_rsrc;
# $X->CreateWindow ($origin,
#                   $X->root,         # parent
#                   'InputOutput',
#                   0,                # depth, from parent
#                   'CopyFromParent', # visual
#                   0,0,              # x,y
#                   20,20,
#                   0,                # border
#                  );
# $X->MapWindow ($origin);

my $origin_x = 1000;
my $origin_y = 0;
my $origin_region = $X->new_rsrc;
$X->XFixesCreateRegion ($origin_region, [$origin_x,$origin_y,10,10]);


my $parts = $X->new_rsrc;
$X->XFixesCreateRegion ($parts);

my $damage = $X->new_rsrc;
$X->DamageCreate ($damage, $origin, 'NonEmpty');
$X->QueryPointer ($X->{'root'});

my $window = $X->new_rsrc;
$X->CreateWindow ($window,
                  $X->root,         # parent
                  'InputOutput',
                  0,                # depth, from parent
                  'CopyFromParent', # visual
                  0,0,              # x,y
                  $width,$height,
                  0,                # border
                  background_pixel => $X->black_pixel,
                  event_mask => $X->pack_event_mask('Exposure','KeyPress'),
                 );
$X->MapWindow ($window);

my $gc;
my $want_freshen;

sub event_handler {
  my (%h) = @_;
  ### event_handler: \%h
  if ($h{'name'} eq 'ConfigureNotify') {
    $width = $h{'width'};
    $width = $h{'height'};
    $want_freshen = 1;
  } elsif ($h{'name'} eq 'DamageNotify') {
    $want_freshen = 1;
  } elsif ($h{'name'} eq 'Expose') {
    expose();
  }
};

sub freshen {
  ### freshen(): $damage, $parts
  $X->DamageSubtract ($damage, 'None', $parts);
  dump_region ($X,$parts,'damaged');
  return if ! $window;

  $X->XFixesIntersectRegion ($parts, $origin_region, $parts);
  dump_region ($X,$parts,'intersected with origin');

  my $window_region = $X->new_rsrc;
  $X->XFixesCreateRegionFromWindow ($window_region, $window, 'Bounding');
  dump_region ($X,$window_region,'own window');

  my ($same_screen, $child, $x, $y)
    = $X->TranslateCoordinates ($window, $X->root, 0,0);
  ### $x
  ### $y
  $X->XFixesTranslateRegion ($window_region, $x, $y);
  dump_region ($X,$window_region,'own window translated');
  $X->XFixesSubtractRegion ($parts, $window_region, $parts);
  $X->XFixesDestroyRegion ($window_region);

  dump_region ($X,$parts,'subtracted own window');

  #    $X->XFixesSetGCClipRegion ($gc, $parts, 0,0);

  my ($bounding, @rects) = $X->XFixesFetchRegion ($parts);
  if ($bounding->[2] && $bounding->[3]) {
    expose();
  } else {
    ### no draw...
  }
}

sub expose {
  ### expose...
  if (! $gc) {
    ### make gc
    $gc = $X->new_rsrc;
    $X->CreateGC ($gc, $window, subwindow_mode => 'IncludeInferiors');
  }
  # $X->SetGCClipRegion ($gc, $parts, 0,0);
  $X->CopyArea ($origin, $window, $gc,
                $origin_x,$origin_y,
                $width,$height,
                0,0); # dst x,y
}

sub dump_region {
  my ($X, $region, $name) = @_;
  my @rects = $X->XFixesFetchRegion ($region);
  if (! defined $name) { $name = ''; }
  printf "region %X  $name\n", $region;
  if (@rects) {
    foreach my $rect (@rects) {
      print "  ",join(',',@$rect),"\n";
    }
  } else {
    print "  empty";
  }
}

sub fh_readable {
  my ($fh) = @_;
  require IO::Select;
  my $s = IO::Select->new;
  $s->add($fh);
  my @ready = $s->can_read(1);
  return scalar(@ready);
}

for (;;) {
  if (fh_readable ($X->{'connection'}->fh)) {
    ### handle_input
    $X->handle_input;
  }
  if ($want_freshen) {
    $want_freshen = 0;
    freshen();
  }
  # else {
  #    $X->handle_input;
  #  }
}

### exit
exit 0;