#!/opt/exp/perl/bin/perl -w

use Tk;
use strict;
use X11::Protocol;
use IO::Select;

my $x11 = X11::Protocol->new();

my $mainwin;
my $mainwin2;
my $button;
my $toplevel;
my $height = 0;
my $old_height = 0;
my $width = 0;
my $old_width = 0;
my $counter = 0;
my $preserved = 0;
my $Xevent = 0;
my %events;

if (defined $Plugin::brinfo{xwindow_id})
{
        my $mw_bdw = 10;
        my $mw_wd = 200;
        my $mw_ht = 200;
        my $fm_bdw = 5 ;
        my $fm_wd = 150 ;
        my $fm_ht = 150 ;
        my $grid_minsz = 144;
        my $grid_pad = 5;

        $mainwin = new MainWindow(
                '-use' => $Plugin::brinfo{'xwindow_id'},
		-title => "Test MainWindow",
                -borderwidth=>$mw_bdw,
                -relief=>'sunken',
                -width=> $mw_wd,
                -height=> $mw_ht);

	$mainwin2 = new MainWindow(
		-title => "Test MainWindow Two",
                -borderwidth=>$mw_bdw,
                -relief=>'sunken',
                -width=> $mw_wd,
                -height=> $mw_ht);
	
	$button = $mainwin->Button( -text => "Quit",
					-command => sub { exit; })->pack;

} else {
	exit;
}

#
# set up a handler for X-window events using X11::Protocol
#
$x11->{'event_handler'} = \&handle_events;

#$button->OnDestroy(sub { $x11->handle_input; &handle_events;});

#
# Give MainLoop some time to get things established, then
# go try to trap resize and other events.
#
$mainwin->after(5, \&set_destroy);
#
# If you don't withdraw mainwin2, you can watch the button preserve
# itself for the duration of the resize.
#
#$mainwin2->withdraw;

MainLoop;

my %id_hash;

sub get_tree {
#
# Create a hash of the xid's of the window heirarchy, at least as far up
# as the Netscape provided window.  This is currently incomplete.  The
# rest of the tree is determined in set_destroy.
# 
#	print "Entering get_tree\n";
	$id_hash{'mainwin_id'} = $mainwin->id;
#	printf("Mainwin id = 0x%x\n", oct($id_hash{'mainwin_id'}));

	$id_hash{'mainwin2_id'} = $mainwin2->id;

	#
	# main_parent is the invisible 'wrapper' window provided by Tk
	#
	($id_hash{'main_root'}, $id_hash{'main_parent'}, $id_hash{'main_kids'})
		= $x11->QueryTree(oct($id_hash{'mainwin_id'}));
#	printf("Mainwin Parent xid = 0x%x\n", $id_hash{'main_parent'});

	$id_hash{'button_id'} = $button->id;

	#
	# gparent is the browser provided window that gets destroyed and
	# recreated when the browser is resized.
	#
	($id_hash{'gparent_root'}, $id_hash{'gparent'},
		$id_hash{'main_parent_kids'}) =
		$x11->QueryTree($id_hash{'main_parent'});
#	printf("Mainwin gparent id = 0x%x\n", $id_hash{'gparent'});

	#
	# ggparent is the browser provided window that remains constant
	# throughout.
	#
	($id_hash{'ggparent_root'}, $id_hash{'ggparent'},
		$id_hash{'gparent_kids'}) =
		$x11->QueryTree($id_hash{'gparent'});
#	printf("Mainwin ggparent id = 0x%x\n", $id_hash{'ggparent'});

	#
	# One more level up, for setting notify attributes.  Experience
        # seems to indicate the further up we set the Notify attributes,
        # the sooner the application sees the resize event and the less
        # likely the application windows will be Destroyed before they
	# they can be preserved.
	#
	($id_hash{'gggparent_root'}, $id_hash{'gggparent'},
		$id_hash{'ggparent_kids'}) =
		$x11->QueryTree($id_hash{'ggparent'});
#	printf("Mainwin gggparent id = 0x%x\n", $id_hash{'gggparent'});

	#
	# One more level up, for setting notify attributes
	#
	($id_hash{'ggggparent_root'}, $id_hash{'ggggparent'},
		$id_hash{'gggparent_kids'}) =
		$x11->QueryTree($id_hash{'gggparent'});
#	printf("Mainwin ggggparent id = 0x%x\n", $id_hash{'ggggparent'});

	#
	# One more level up, for setting notify attributes
	#
	($id_hash{'gggggparent_root'}, $id_hash{'gggggparent'},
		$id_hash{'ggggparent_kids'}) =
		$x11->QueryTree($id_hash{'ggggparent'});
#	printf("Mainwin gggggparent id = 0x%x\n", $id_hash{'gggggparent'});


	#
	# One more level up, for setting notify attributes
	#
	($id_hash{'4ggparent_root'}, $id_hash{'4ggparent'},
		$id_hash{'ggggparent_kids'}) =
		$x11->QueryTree($id_hash{'gggggparent'});
#	printf("Mainwin 4ggparent id = 0x%x\n", $id_hash{'4ggparent'});

	#
	# One more level up, for setting notify attributes
	#
	($id_hash{'5ggparent_root'}, $id_hash{'5ggparent'},
		$id_hash{'4ggparent_kids'}) =
		$x11->QueryTree($id_hash{'4ggparent'});
#	printf("Mainwin 5ggparent id = 0x%x\n", $id_hash{'5ggparent'});

	#
	# One more level up, for setting notify attributes
	#
	($id_hash{'6ggparent_root'}, $id_hash{'6ggparent'},
		$id_hash{'5ggparent_kids'}) =
		$x11->QueryTree($id_hash{'5ggparent'});
#	printf("Mainwin 6ggparent id = 0x%x\n", $id_hash{'6ggparent'});

	#
	# This ought to be the netscape main window. But it won't let
	# our application set attributes on it.
	#
	($id_hash{'7ggparent_root'}, $id_hash{'7ggparent'},
		$id_hash{'6ggparent_kids'}) =
		$x11->QueryTree($id_hash{'6ggparent'});
#	printf("Mainwin 7ggparent id = 0x%x\n", $id_hash{'7ggparent'});
}

sub preserve_win
{
#
# First we check whether the window has already been preserved because
# X generates multiple ResizeRequest events for a single mouse drag and
# we only want to preserve it for the first one. If it's not been preserved,
# we preserve it and set the preserved flag.  The flag will get cleared by
# restore_win.
#

#        print "Entering reparent_win\n";

	$height = $events{'height'};
	$width = $events{'width'};
	
	if ($preserved == 0)
	{
		$preserved = 1;
		$x11->UnmapSubwindows($id_hash{'main_parent'});
		$x11->UnmapWindow($id_hash{'main_parent'});

		$x11->ReparentWindow( $id_hash{'main_parent'}, oct($id_hash{'mainwin2_id'}), (0,0));

		$x11->MapSubwindows($id_hash{'main_parent'});
		$x11->MapWindow($id_hash{'main_parent'});
		$mainwin->update;
		$mainwin2->update;
		$button->update;
	}
}

sub restore_win
{
#	print "Entering restore_win.\n";
	if ($preserved == 1)
	{
		#
		# get the id of the new browser supplied embedded window
		#
		my ($ggparent_root, $gggparent, @ggparent_kids) =
			$x11->QueryTree($id_hash{'ggparent'});


#		printf ("gggparent = 0x%x\n", $gggparent);
	#	printf ("ggparent = 0x%x\n", $ggparent_kids[0]);

		$x11->UnmapSubwindows($id_hash{'main_parent'});
		$x11->UnmapWindow($id_hash{'main_parent'});
		$x11->ReparentWindow($id_hash{'main_parent'}, $ggparent_kids[0], (0,0));
		$x11->MapSubwindows($id_hash{'main_parent'});
		$x11->MapWindow($id_hash{'main_parent'});

		$mainwin->update;
		$mainwin2->update;
		$button->update;
		$preserved = 0;
	}
	
	
}

sub ck_event_complete {

#	print "Entering ck_event_complete.\n";

#
# In order to determine whether the user is done resizing the browser,
# we have to check that the size hasn't changed "recently".  Unfortunately,
# it's possible to have more than 50 iterations of the event loop between
# each of the several ResizeRequest events that make up the complete 
# resizing.  So, here we take 'recently' to mean that we've checked at
# least 75 times and the size hasn't changed.  The value was established
# by trial and error.  It's possible it could be influenced by such things
# as how fast or slow the user moves the pointer, cpu speed, etc.
#

	if ( $old_height == $height && $old_width == $width )
	{
		if ($counter > 75 )
		{
			&restore_win;
			$Xevent = 0;
			$counter = 0;
		} else {

			$counter++;
		}

	} else {

		$old_height = $height;
		$old_width = $width;
	}


}

sub handle_events {
	%events = @_;
	my $key;

	$Xevent = 1;
	$counter = 0;

#	print "Entering handle_events\n";
#	foreach $key (keys %events)
#	{
#              printf "%s %s\n", $key, $events{$key};
#	}

#	print "Event was:  ", $events{'name'}, "\n";
	&preserve_win if (($events{'name'} eq 'ResizeRequest' ||
			   $events{'name'} eq 'ConfigureNotify' ||
			   $events{'name'} eq 'ConfigureRequest' ||
			   $events{'name'} eq 'DestroyNotify') &&
			   $preserved == 0);

}

sub set_destroy {

	&get_tree;


	my $sock_sel = IO::Select->new();
	$sock_sel->add(${$x11->{'connection'}});

#
# set up an event mask that will cause handle_events to get entered
# whenever a ResizeRequest event occurs. We do this by 'redirecting'
# the event from the Netscape X-client to ourself.
#
	my $event_mask = $x11->pack_event_mask('ResizeRedirect', 'SubstructureNotify');
#
# set the event mask on the Netscape window
#
	$x11->ChangeWindowAttributes( $id_hash{'6ggparent'}, 'event_mask' => $event_mask);
#
# pretend to be a MainLoop, sorta.  Wait for X-events propagated as
# a result of setting the mask.
#
	while ( 1 == 1 )
	{
#
# need at least one of DoOneEvent or the widget->update's to get any
# sort of response from the widgets (obviously). Even so, the response
# doesn't occur until after an X-event has been received and handled
# and this can be quite a long time.  This could probably be improved
# somewhat by setting an event mask on the button and/or mainwin and
# responding to it in &show_events.  But, carry this far enough and
# you've recreated the whole event loop.  If handle-events could run
# in a one-shot mode, or better yet if Tk had an OnXEvent(window, event,
# handler) method none of this would be necessary.
#
		$mainwin->update;
		$mainwin2->update;
		$button->update;

#		print "Doing the MainLoop, sorta!\n";
		if(my @ready = $sock_sel->can_read(0)){
			$x11->handle_input; 
		}
		&ck_event_complete if ( $Xevent == 1 );
		
	
#
# TCL_DONT_WAIT = 2, causes DoOneEvent to return immediately if there are
# no events outstanding
#
#		DoOneEvent(2);
	}

}

