#!perl
package xlib;

require "./Xproto2.perl";


sub xlibconvert { # type, data
    my ($typ, $data) = @_;
    my %result;
    my (@fields) = unpack($X::xpackof{$typ},$data);

    for (split(/\:/,$X::xtypeof{$typ})) {
	m/(\w+) (\w+)/;
	$result{$2} = shift @fields;
    }
    # nice trick: roll the sizeof in here too...
    $result{"sizeof"} = $X::xsizeof{$typ};
    $result{"packof"} = $X::xpackof{$typ};
    $result{"typeof"} = $X::xtypeof{$typ};
    $result{"typ"} = $typ;
    return \%result;
}

sub debugxlib { # value
    my ($val) = @_;
    print "$val->{typ}:\n";
    for (split(/:/,$val->{"typeof"})) {
	m/(\w+) (\w+)/;
	print "\t$2: $val->{$2}\n";
    }
    1;
}

sub xpack { my $typ=shift; pack($X::xpackof{$typ},@_); }

use Socket qw(AF_INET PF_INET SOCK_STREAM INADDR_ANY AF_UNIX PF_UNIX);
sub mkport {
  my($saddr,$port,$FD) = @_;
  my $proto = (getprotobyname("tcp"))[2];
  my $sin = Socket::sockaddr_in($port, $saddr);
  socket($FD, PF_INET, SOCK_STREAM, $proto) || return "socket:$!";
  connect($FD, $sin) || return "connect:$!";
  select($FD); $| = 1; select(STDOUT); $| = 1;
  return "OK";
}

sub mkport_unix {
  my($path,$FD) = @_;
  my $proto = 0;		# *not* tcp...
  my $sun = Socket::sockaddr_un($path);
  socket($FD, PF_UNIX, SOCK_STREAM, $proto) || return "socket:$!";
  connect($FD, $sun) || return "connect:$!";
  select($FD); $| = 1; select(STDOUT); $| = 1;
  return "OK";
}

sub split_display { # $disp var
    my ($node, $dispnum, $off, $screen);
    my ($displayname) = @_;
    ($node,$dispnum)=split(':',$displayname);
    ($off,$screen) = split('\.',$dispnum);
    $screen = 0 if !defined($screen);
    return ($node, $off, $screen);
}

require "./xauth.pl";

sub min { my($a,$b)=@_; ($a<$b) ? $a : $b; }
sub serverprint { # $d
    my $d = shift;
    print {$d->{"XFD"}} @_;
    if ($debug || $debugpkt) {
	print "seq: $xseqnum\n"; $xseqnum++;
	printf("x request: ".("%02x " x min(128,length($_[0])))."\n", 
	       unpack("C*",$_[0]));
	print "x request length = ",length($_[0]),"\n";
    }
}

sub x_open_display {
    my($displayname) = @_;
    my($node, $off, $screen, $base);
#  local(XFD);
    my($reply)="";
    my $result = {};
    my (@root, @pixmap_format);

    ($node, $off, $screen) = split_display($displayname);
    $result->{"Screen"} = $screen;
    print "x_open_display: n=$node, o=$off, s=$screen\n" if $debug;
    if ($node eq "") {
	# get a socket... /tmp/.X11-unix/X\d are unix domain sockets
	my $path = "/tmp/.X11-unix/X$off";
	$status = &mkport_unix($path,\*XFD);
    } else {
	($name, $aliases, $type, $len, $thisaddr) = gethostbyname($node);
	$status = &mkport($thisaddr,$X::TCP_PORT+$off,\*XFD);
	if($status ne "OK") { return undef; }
    }
    $result->{"XFD"} = \*XFD;
  
    my $xauth = search_xauth_file($displayname);
# network byte order is MSB first
# byte "B" for MSB first, then CARD16 major, CARD16 minor,
#  STRING8 auth prot name, STRING8 auth prot data
    my $xauthname = defined($xauth)?$xauth->{"authname"}:"";
    my $xauthdata = defined($xauth)?$xauth->{"authdata"}:"";

    my $x= xpack("xConnClientPrefix",
		 unpack("S","lB")&255,
		 $X::PROTOCOL, $X::PROTOCOL_REVISION,
		 length($xauthname),
		 length($xauthdata),
		 0);
    $x.= $xauthname;
    $x .= "\0" x (&pad4(length($x))-length($x));
    $x.= $xauthdata;
    $x .= "\0" x (&pad4(length($x))-length($x));

    $xseqnum = 0;
    serverprint($result, $x);
    print STDOUT "reading...\n" if $debug;
    $result->{"readqueue"} = "";
    my $q = \$result->{"readqueue"};

    extend_queue($result, $X::xsizeof{"xConnSetupPrefix"});

    print STDOUT "done reading...\n" if $debug;

    my($output) = &xlibconvert("xConnSetupPrefix",$$q);
    $success = $output->{"success"};
		 
  if($success == 0) {
      print "Proto Major: ", $output->{"majorVersion"},
            " Proto Minor: ", $output->{"minorVersion"}, "\n";
      $rlen = $output->{"lengthReason"};
      $xlen = $output->{"sizeof"};
      print "reason len: $rlen\n";
      extend_queue($result, $X::xsizeof{"xConnSetupPrefix"}+$rlen);
      $reason = substr($$q,$xlen,$rlen);
      $status = "open failed: $reason";
      return undef;
  } else {
      $$q = substr($$q,$output->{"sizeof"}); # strip xConnSetupPrefix
      extend_queue($result, $X::xsizeof{"xConnSetup"});
      $output = &xlibconvert("xConnSetup", $$q);
      debugxlib($output) if $debug;
      $result->{"setup"} = $output;
      $$q = substr($$q,$output->{"sizeof"}); # strip xConnSetup
      $vlen = $output->{"nbytesVendor"};
      extend_queue($result, $vlen);
      $vendor = substr($$q, 0, $vlen);
      $$q = substr($$q,$vlen);	# strip vendor
      print "Vendor: $vendor\n" if $debug;
      $result->{"vendor"} = $vendor;
      # ignore the rest for now...
      # @output contains most of the fields...
      $resource_id_base = $output->{"ridBase"};
      $resource_id_mask = $output->{"ridMask"};
      $resource_id_ctr = 1;	# store this in $result too?

      $max_request_size = $output->{"maxRequestSize"};

      $nroots = $output->{"numRoots"};
      $nformats = $output->{"numFormats"};
      $$q = substr($$q,&pad4($vlen)-$vlen);	# strip padding
      for $i (0..$nformats-1) {
	  extend_queue($result, $X::xsizeof{"xPixmapFormat"});
	  $pixmap_format[$i] = &xlibconvert("xPixmapFormat", $$q);
	  $$q = substr($$q,$pixmap_format[$i]->{"sizeof"}); # strip xPixmapFormat
	  debugxlib($pixmap_format[$i]) if $debug;
      }
      $result->{"Formats"} = \@pixmap_format;

      for $i (0..$nroots-1) {
	  extend_queue($result, $X::xsizeof{"xWindowRoot"});
	  $root[$i] = &xlibconvert("xWindowRoot", $$q);
	  $$q = substr($$q,$root[$i]->{"sizeof"}); # strip xWindowRoot
	  debugxlib($root[$i]) if $debug;
	  for $j (0..$root[$i]->{"nDepths"}-1) {
	      extend_queue($result, $X::xsizeof{"xDepth"});
	      my $d = &xlibconvert("xDepth", $$q);
	      $$q = substr($$q,$d->{"sizeof"}); # strip xDepth
	      debugxlib($d) if $debug;
	      $root[$i]->{"depth"}[$j] = $d;
	      $base += $d->{"sizeof"};
	      for $k (0..$d->{"nVisuals"}-1) {
		  extend_queue($result, $X::xsizeof{"xVisualType"});
		  my $v = &xlibconvert("xVisualType", $$q);
		  $$q = substr($$q,$v->{"sizeof"}); # strip xVisualType
		  debugxlib($v) if $debug;
		  $d->{"visual"}[$k] = $v;
		  $base += $v->{"sizeof"};
	      }
	  }
      }
      $result->{"Roots"} = \@root;

      $root_window = $root[$screen]->{"windowId"};
      $root_white = $root[$screen]->{"whitePixel"};
      $root_black = $root[$screen]->{"blackPixel"};


      print "Root Window: $root_window\n" if $debug;

      return $result;
  }
}

sub xdebugdisplay {			# xopendisplay result
    my $disp = shift;
    my ($i, $j, $k);
    my ($format, $root, $depth, $visual);
    debugxlib($disp->{"setup"});
    for $i (0..$disp->{"setup"}->{"numFormats"}-1) {
	my $format = $disp->{"Formats"}[$i];
	debugxlib($format);
    }
    for $i (0..$disp->{"setup"}->{"numRoots"}-1) {
	my $root = $disp->{"Roots"}[$i];
	print "Root $i\n";
	debugxlib($root);
	for $j (0..$root->{"nDepths"}-1) {
	    my $depth = $root->{"depth"}[$j];
	    print "Root $i Depth $j\n";
	    debugxlib($depth);
	    for $k (0..$depth->{"nVisuals"}-1) {
		my $visual = $depth->{"visual"}[$k];
		print "Root $i Depth $j Visual $k\n";
		debugxlib($visual);
	    }
	}
    }
}

sub next_resource_id {
    $resource_id_base | ($resource_id_mask & $resource_id_ctr++);
}

sub pad4 { (($_[0]+3)>>2)<<2; }

sub x_beep {
    my ($d,$percentage) = @_;
    serverprint($d,X::newxBellReq($percentage));
    "OK";
}

sub x_washwindow {
    my ($func, $d, $w) = @_;
    serverprint($d, &{$X::xsubs{"x${func}Req"}}($w));
    "OK"
}
sub x_destroywindow { x_washwindow("DestroyWindow", @_); }
sub x_destroysubwindows { x_washwindow("DestroySubwindows", @_); }
sub x_mapwindow { x_washwindow("MapWindow", @_); }
sub x_mapsubwindows { x_washwindow("MapSubwindows", @_); }
sub x_unmapwindow { x_washwindow("UnmapWindow", @_); }
sub x_unmapsubwindows { x_washwindow("UnmapSubwindows", @_); }

sub x_configurewindow {
    my($d,$wind,$mask,@vals)=@_;
    genvec2($d, "xConfigureWindowReq", "XID", [$wind,$mask], \@vals);
}

sub x_reparentwindow {
    my ($d, $w, $p, $x, $y) = @_;
    serverprint($d, X::newxReparentWindowReq($w,$p,$x,$y));
    "OK"
}

sub x_circulatewindow {
    my ($d, $dir, $w) = @_;
    serverprint($d, X::newxCirculateWindowReq($dir,$w));
    "OK"
}

sub x_changesaveset {
    my ($d, $mode, $win) = @_;
    serverprint($d, X::newxChangeSaveSetReq($mode,$win));
    "OK";
}

sub x_clear_area {
    my($d, $wind, $expose, $x,$y,$width,$height)=@_;
    serverprint($d, X::newxClearAreaReq($expose,$wind, $x,$y,$width,$height));
    "OK";
}

sub x_create_pixmap {
    my($d,$draw, $depth, $width, $height)=@_;
    my($ret);
    # 4 is length -- fix it?
    serverprint($d,X::newxCreatePixmapReq($depth, $ret = &next_resource_id,
				       $draw, $width, $height));
    $ret;
}

sub x_create_gc {
    my($d,$draw, $mask, @vals)=@_;
    my($ret);
    genvec2($d,"xCreateGCReq","XID", [$ret = &next_resource_id, $draw, $mask],
	    \@vals);
    $ret;
}

sub x_put_image {
    my($d,$draw,$gc,$format,$image,$destx,$desty,$width,$height,$lpad,$depth,$bpl)
	= @_;
    my($imm,$lns,$off_l,$off);
    my($chunk_lines,$chunk_bytes);
    my $overhead = $X::xsizeof{"xPutImageReq"};
    my $maxreq = (4*$max_request_size)-$overhead;
    $lns = $xh;
    $off_l = 0;
    $off = 0;
    $chunk_lines = int($maxreq/$bpl);
    $chunk_bytes = $bpl * $chunk_lines;
    while(length($image)-$off > $maxreq) {
	$imm = substr($image, $off, $chunk_bytes);
	# print "imm: ",length($imm),"\n";
	&xlib::x_real_put_image($d,$draw,$gc,$format,
				$imm,$destx,$desty+$off_l,
				$width,$chunk_lines,$lpad,$depth);
	$off_l += $chunk_lines;
	$off = $bpl * $off_l;
    }
    $off = $bpl * $off_l;
    if (length($image)-$off > 0) {
	$imm = substr($image, $off);
	# print "imm trail: ",length($imm),"\n";
	&xlib::x_real_put_image($d,$draw,$gc,$format,
				$imm,$destx,$desty+$off_l,
				$width,$height-$off_l,$lpad,$depth);
    }
}

sub x_real_put_image {
    my($d,$draw,$gc,$format,$image,$destx,$desty,$width,$height,$lpad,$depth)
	= @_;

    genvecstr($d, "xPutImageReq", [$format, $draw, $gc, $width, $height,
				   $destx, $desty, $lpad, $depth], $image);
    "OK";
}

sub x_get_window_attributes {
    my ($d,$w) = @_;
    my ($rep,$q) = x_reply($d, "GetWindowAttributes", [$w]);
    $rep;
}

sub x_change_window_attributes {
    my $d = shift;
    my ($win, $mask, @vals) = @_;
    genvec2($d, "xChangeWindowAttributesReq", "XID", [$win,$mask], \@vals);
}

sub x_set_window_background_pixmap {
    my($d,$wind,$pixmap)=@_;
    x_change_window_attributes($d,$wind,$X::defines{"CWBackPixmap"},$pixmap);
}

sub extend_queue {
    my $d = shift;
    my $l = shift;
    while (length ($d->{"readqueue"}) < $l) {
	my $data = "";
	sysread($d->{"XFD"},$data,1024) || die "$! in extend_queue";
	$d->{"readqueue"} .= $data;
    }
}

sub x_getatomname {
    my ($d,$atom) = @_;
    my ($rep,$q) = x_reply($d,"GetAtomName", [$atom]);
    my $sl = $rep->{"nameLength"};
    my $name; my $pl = pad4($sl)-$sl;
    ($name,$$q) = unpack("a$sl x$pl a*", $$q);
    print "atom name: $name\n" if $debug;
    $name;
}

sub x_internatom {
    my ($d,$exists,$name) = @_;
    genvecstr($d, "xInternAtomReq", [$exists,length($name)], $name);
    handle_event($d);
    my ($rep,$q) = xqueueconvert($d,"xInternAtomReply");
    $rep->{"atom"};
}

sub x_getproperty {
    my ($d,$flag,$win,$prop,$type,$loff,$llen) = @_;
    my ($rep,$q) = 
	x_reply($d,"GetProperty", [$flag,$win,$prop,$type,$loff,$llen]);
    my $f = $rep->{"format"};	# 0 8 16 32
    my $sl = $rep->{"nItems"}*$f/8;
    my $val; my $pl = pad4($sl)-$sl;
    ($val,$$q) = unpack("a$sl x$pl a*", $$q);
    print "atom value: $val\n" if $debug && ($f eq 8);
    return ($f, $val, $rep->{"propertyType"});
}

sub x_changeproperty {
    my ($d,$mode,$win,$prop,$type,$form,@vals) = @_;
    # for format=8, use a string, else a list
    my $p = "CARD$form";
    if ($form == 8) {
	@vals = unpack("C*",$vals[0]);
    }
    genvec2($d,"xChangePropertyReq",$p,
	    [$mode,$win,$prop,$type,$form,scalar(@vals)], \@vals);
    "OK";
}

sub x_deleteproperty {
    my ($d, $w, $a) = @_;
    serverprint($d, X::newxDeletePropertyReq($w,$a));
    "OK";
}

sub x_getfontpath {
    my ($d) = @_;
    x_iter($d, "GetFontPath", "nPaths", "STRING8");
}

sub x_listfonts {
    my ($d, $f) = @_;
    x_iter($d,"ListFonts","nFonts","STRING8", [-1,length($f),$f]);
}

sub x_listextensions {
    my ($d) = @_;
    x_iter($d,"ListExtensions","nExtensions","STRING8");
}

sub x_get_geometry {		# very much like GetWindowAttributes...
    my ($d,$w) = @_;
    my ($rep,$q) = x_reply($d, "GetGeometry", [$w]);
    $rep;
}

sub x_get_one { 
    my ($type, $d) = @_;
    my ($rep,$q) = x_reply($d, $type);
    $rep;
}
sub x_get_input_focus { x_get_one("GetInputFocus",@_); }
sub x_get_keyboard_control { x_get_one("GetKeyboardControl",@_); }

require "./xerror.pl";

sub handle_event {
    my $d = shift;
    my $rep;
    my $q = \$d->{"readqueue"};
    extend_queue($d, $X::xsizeof{"xEvent"});
    $rep = xlibconvert("xGenericReply",$$q);
    if ($rep->{"type"} == $X::Reply) {
	print "reply: \n" if $debug;
	debugxlib($rep) if $debug;
	extend_queue($d, $X::xsizeof{"xEvent"}+4*$rep->{"length"});
	# handle reply
	return $rep->{"length"};
    } elsif ($rep->{"type"} == $X::Error) {
	# handle error
	$rep = &xlibconvert("xError",$$q);
	print "xerror: $X::ErrorList[$rep->{errorCode}]\n";
	printf "xerror: resid %x\n", $rep->{"resourceID"};
	print  "major:  $X::requests[$rep->{majorCode}]\n";
	debugxlib($rep);
	$$q = substr($$q,$rep->{"sizeof"});
	return undef;
    } else {
	# process the event off the input queue and on to the event queue...
	$rep = xlibconvert("u",$$q);
	print "xevent: $X::xevents[$rep->{type}]\n" if $debug;
	my $h = $handler{$X::xevents[$rep->{"type"}]};
	&$h if (defined $h);
	debugxlib($rep) if $debug || !defined($h);
	skipevent($d, "xEvent");
	return 0;
    }
    return -1;
}

sub skipevent { # tag 
    my ($d,$tag) = @_;
    my $q = \$d->{"readqueue"};
    $$q = substr($$q, $X::xsizeof{$tag});
    print "event queue length: ",length($$q),"\n" if $debug;
}

sub xqueueconvert {
    my ($d,$tag) = @_;
    my $ret = xlibconvert($tag,$d->{"readqueue"});
    debugxlib($rep) if $debug;
    skipevent($d, $tag);
    ($ret, \$d->{"readqueue"});
}    

sub defaultroot {
    my $d = shift;
    return $d->{"Roots"}[$d->{"Screen"}]->{"windowId"};
}

sub defaultrootdepth {
    my $d = shift;
    return $d->{"Roots"}[$d->{"Screen"}]->{"rootDepth"};
}

sub x_reply {
    my ($d, $func, $args) = @_;
    serverprint($d, &{$X::xsubs{"newx${func}Req"}}(@$args));
    handle_event($d);
    xqueueconvert($d,"x${func}Reply");
}

sub x_iter_raw {
    my ($d, $func, $counter, $vtype, $args) = @_;
    my ($rep,$q) = x_reply($d, $func, $args);
    my ($val,$i,@ret);
    for $i (1..$rep->{$counter}) {
	if ($vtype eq "STRING8") {
	    my ($sl) = unpack("C",$$q);
	    ($sl,$val,$$q) = unpack("C a$sl a*", $$q);
	} else {
	    ($val,$$q) = unpack($X::xpackof{$vtype}."a*", $$q);
	}
	print "$vtype: <$val>\n" if $debug;
	push(@ret,$val);
    }
    return ($rep, @ret);	# some reps have useful info...
}

sub x_iter {
    my ($rep,@ret) = x_iter_raw(@_);
    return @ret;
}

sub x_listproperties {
    my ($d, $w) = @_;
    x_iter($d, "ListProperties", "nProperties", "Atom", [$w]);
}

sub x_querytree {
    my ($d, $root) = @_;
    $root = defaultroot($d) if (!defined($root));
    x_iter_raw($d, "QueryTree", "nChildren", "Window", [$root]);
}

sub x_get_keyboard_mapping {
    my ($d, $first, $count) = @_;
    x_iter_raw($d, "GetKeyboardMapping","length","KeySym", [$first,$count]);
}

sub x_get_modifier_mapping {
    my ($d) = @_;
    x_iter_raw($d, "GetModifierMapping","length","KeyCode");
}

sub x_get_pointer_mapping {
    my ($d) = @_;
    x_iter($d, "GetPointerMapping","nElts","CARD8");
}

sub x_imagetext8 {
    my $d = shift;
    my ($w, $gc, $x, $y, $string) = @_;
    my $l = length($string); 
    genvecstr($d, "xImageText8Req", [$l,$w,$gc,$x,$y], $string);
    "OK";
}

sub x_openfont {
    my $d = shift;
    my ($f) = (@_);		# font name
    my $l = length($f); 
    genvecstr($d, "xOpenFontReq", [$ret = &next_resource_id,$l], $f);
    $ret;
}

sub splitcolor {
    my ($c) = @_;
    return ($$c[0],$$c[1],$$c[2]);
}

sub joincolor {
    my ($r,$g,$b) = @_;
    return [$r,$g,$b];
}

sub x_createfontcursor {
    my ($d,$ch,$fore,$back) = @_;
    if (!defined($d->{"cursorfont"})) {
	$d->{"cursorfont"} = x_openfont($d,"cursor");
    }
    my $c = $d->{"cursorfont"};
    my $cursor;
    
    my ($fr,$fg,$fb) = splitcolor($fore);
    my ($br,$bg,$bb) = splitcolor($back);
    print "Fore($fore): $fr,$fg,$fb\n" if $debug;
    print "Back($back): $br,$bg,$bb\n" if $debug;
    serverprint($d,
		&X::newxCreateGlyphCursorReq($cursor = &next_resource_id,
					     $c, # sourcefont
					     $c, # maskfont
					     $ch, # sourcechar
					     $ch+1, # maskchar
					     $fr, $fg, $fb,
					     $br, $bg, $bb));
    $cursor;
}

sub x_closedisplay {
    my $d = shift;
    close $d->{"XFD"};
}

sub x_create_window {
    my($d,$depth,$parent,$x,$y,$w,$h,$bwid,$class,$vis,$mask, @vals) = @_;
    my($ret);
    genvec2($d,"xCreateWindowReq","XID", 
	    [$depth, $ret = &next_resource_id, $parent, 
	     $x,$y,$w,$h,$bwid, $class, $vis, $mask], \@vals);
    $ret;
}

sub genvec {
    my ($type, @vals) = @_;
    my $p = $X::xpackof{$type};
    my $nvals = length(pack("C*",unpack($p," "x80)));
    my $val = "";
    while(1) {
	my @v = splice(@vals,0,$nvals);
	$val .= pack($p, @v);
	last if scalar(@vals) <= 0;
    }
    return $val;
}

sub genvec2 {
    my ($d, $req, $elem, $args, $vals) = @_;
    my $val = genvec($elem, @$vals);
    serverprint($d,&{$X::xsubs{"new$req"}}(@$args,$val));
    "OK";
}

sub genvecstr {
    my ($d, $req, $args, $val) = @_;
    serverprint($d,&{$X::xsubs{"new$req"}}(@$args,$val));
    "OK";
}

sub xpoly1 {
    my ($req, $type, $d, $draw, $gc, @vals) = @_;
    genvec2($d, "x${req}Req", "x$type", [$draw,$gc], \@vals);
}
sub xpolyarcs { xpoly1("PolyArc", "Arc", @_); }
sub xpolyfillarcs { xpoly1("PolyFillArc", "Arc", @_); }
sub xpolyrects { xpoly1("PolyRectangle", "Rectangle", @_); }
sub xpolyfillrects { xpoly1("PolyFillRectangle", "Rectangle", @_); }
sub xpolysegments { xpoly1("PolySegment", "Segment", @_); }

sub xpoly2 {
    my ($req, $type, $d, $mode, $draw, $gc, @points) = @_;
    genvec2($d,"x${req}Req", "x$type", [$mode,$draw,$gc], \@points);
}
sub xpolypoints { xpoly2("PolyPoint", "Point", @_); }
sub xpolylines { xpoly2("PolyLine", "Point", @_); }

sub xsetselectionowner { 
    my ($d,$owner,$selection,$time) = @_;
    serverprint($d, X::newxSetSelectionOwner($owner,$selection,$time));
    "OK";
}
sub xgetselectionowner {
    my ($d) = @_;
    my ($rep,$q) = x_reply($d, "GetSelectionOwner");
    $rep->{"owner"};
}
sub xconvertselection { 
    my ($d,$who,$selection,$target,$prop,$time) = @_;
    serverprint($d, 
	      X::newxConvertSelection($who,$selection,$target,$prop,$time));
    "OK";
}

# calls this as 
# should be: xsendevent($d,false,PointerWindow,0,X::newxWhatever(...));
# xsendevent($d,false,PointerWindow,0,pack($X::xpackof{"selectionClear"},...));
sub xsendevent {
    my ($d,$prop,$dest,$mask,$event) = @_;
    serverprint($d, X::newxSendEventReq($prop,$dest,$mask,unpack("C32",$event)));
    "OK";
}

sub xgrabpointer {
    my ($d,$owner,$gwind,$mask,$pmode,$kmode,$confw,$cursor,$time) = @_;
    my ($rep,$q) = x_reply($d, "GrabPointer", 
			   [$owner,$gwind,$mask,$pmode,$kmode,$confw,$cursor,$time]);
    return $rep->{"status"};
}
sub xgrabkeyboard {
    my ($d,$owner,$wind,$time,$pmode,$kmode) = @_;
    my ($rep,$q) = x_reply($d, "GrabKeyboard",
			   [$owner,$wind,$time,$pmode,$kmode]);
    return $rep->{"status"};
}
sub xgrabserver {
    my ($d) = @_;
    serverprint($d, X::xGrabServerReq());
    "OK";
}
sub xgrabbutton {
    my ($d,$owner,$grabw,$mask,$pmode,$kmode,$confw,$cursor,$button,$mods)
	= @_;
    serverprint($d, X::newxGrabButtonReq($owner,$grabw,$mask,$pmode,$kmode,$confw,$cursor,$button,$mods));
    "OK";
}
sub xgrabkey {
    my ($d,$owner,$grabw,$mods,$key,$pmode,$kmode) = @_;
    serverprint($d, X::xGrabKeyReq($owner,$grabw,$mods,$key,$pmode,$kmode));
    "OK";
}

sub x_ungrab {
    my ($func, $d, @args) = @_;
    serverprint($d, &{$X::xsubs{"newxUngrab${func}Req"}}(@args));
    "OK";
}
sub xungrabpointer { x_ungrab("Pointer", @_); }	# time
sub xungrabkeyboard { x_ungrab("Keyboard", @_); } # time
sub xungrabbutton { x_ungrab("Button", @_); } # button,window,mods
sub xungrabkey { x_ungrab("Key", @_); }	# keycode,window,mods
sub xungrabserver { x_ungrab("Server", @_); } # nothing

sub xallowevents {
    my ($d,$mode,$time) = @_;
    serverprint($d, X::newxAllowEventsReq($mode,$time));
    "OK";
}
sub xchangeactivepointergrab {
    my ($d, $cursor, $time, $mask) = @_;
    serverprint($d, X::newxChangeActivePointerGrabReq($cursor,$time,$mask));
    "OK";
}
sub xquerypointer {		# just like x_get_geometry
    my ($d, $w) = @_;
    my ($rep,$q) = x_reply($d, "QueryPointer", [$w]);
    $rep;
}

1;
