From 042abe20b56e7b599922a04636c90bede11135ea Mon Sep 17 00:00:00 2001 From: Lourens Rozema Date: Fri, 20 Aug 2010 21:33:18 +0200 Subject: [PATCH] Initial commit of gtk-tree-watch.pl which uses GTK/Perl to visualize the i3 tree. It is based on the dump-asy.pl code. --- gtk-tree-watch.pl | 219 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100755 gtk-tree-watch.pl diff --git a/gtk-tree-watch.pl b/gtk-tree-watch.pl new file mode 100755 index 00000000..6d83d1fb --- /dev/null +++ b/gtk-tree-watch.pl @@ -0,0 +1,219 @@ +#!/usr/bin/env perl +# vim:ts=4:sw=4:expandtab +# renders the layout tree using asymptote + +use strict; +use warnings; + +use JSON::XS; +use Data::Dumper; +use AnyEvent::I3; +use v5.10; + +use Gtk2 '-init'; +use Gtk2::SimpleMenu; +use Glib qw/TRUE FALSE/; + +my $window = Gtk2::Window->new('toplevel'); +$window->signal_connect('delete_event' => sub { Gtk2->main_quit; }); + +my $tree_store = Gtk2::TreeStore->new(qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/, qw/Glib::String/); + +my $i3 = i3("/tmp/nestedcons"); + +my $tree_view = Gtk2::TreeView->new($tree_store); + +my $layout_box = undef; + +sub copy_node { + my ($n, $parent, $piter, $pbox) = @_; + + my $o = ($n->{orientation} == 0 ? "u" : ($n->{orientation} == 1 ? "h" : "v")); + my $w = (defined($n->{window}) ? $n->{window} : "N"); + + # convert a rectangle struct to X11 notation (WxH+X+Y) + my $r = $n->{rect}; + my $x = $r->{x}; + my $y = $r->{y}; + my $dim = $r->{width}."x".$r->{height}.($x<0?$x:"+$x").($y<0?$y:"+$y"); + + # add node to the tree with all known properties + my $iter = $tree_store->append($piter); + $tree_store->set($iter, 0 => $n->{name}, 1 => $w, 2 => $o, 3 => sprintf("0x%08x", $n->{id}), 4 => $n->{urgent}, 5 => $n->{focused}, 6 => $n->{layout}, 7 => $dim); + + # also create a box for the node, each node has a vbox + # for combining the title (and properties) with the + # container itself, the container will be empty in case + # of no children, a vbox or hbox + my $box; + if($n->{orientation} == 1) { + $box = Gtk2::HBox->new(1, 5); + } else { + $box = Gtk2::VBox->new(1, 5); + } + + # combine label and container + my $node = Gtk2::Frame->new($n->{name}.",".$o.",".$w); + $node->set_shadow_type('etched-out'); + $node->add($box); + + # the parent is added onto a scrolled window, so add it with a viewport + if(defined($pbox)) { + $pbox->pack_start($node, 1, 1, 0); + } else { + $layout_box = $node; + } + + # recurse into children + copy_node($_, $n, $iter, $box) for @{$n->{nodes}}; + + # if it is a window draw a nice color + if(defined($n->{window})) { + # use a drawing area to fill a colored rectangle + my $area = Gtk2::DrawingArea->new(); + + # the color is stored as hex in the name + $area->{"user-data"} = $n->{name}; + + $area->signal_connect(expose_event => sub { + my ($widget, $event) = @_; + + # fetch a cairo context and it width/height to start drawing nodes + my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window()); + + my $w = $widget->allocation->width; + my $h = $widget->allocation->height; + + my $hc = $widget->{"user-data"}; + my $r = hex(substr($hc, 1, 2)) / 255.0; + my $g = hex(substr($hc, 3, 2)) / 255.0; + my $b = hex(substr($hc, 5, 2)) / 255.0; + + $cr->set_source_rgb($r, $g, $b); + $cr->rectangle(0, 0, $w, $h); + $cr->fill(); + + return FALSE; + }); + + $box->pack_end($area, 1, 1, 0); + } +} + +# Replaced by Gtk2 Boxes: +#sub draw_node { +# my ($n, $cr, $x, $y, $w, $h) = @_; +# +# $cr->set_source_rgb(1.0, 1.0, 1.0); +# $cr->rectangle($x, $y, $w/2, $h/2); +# $cr->fill(); +#} + +my $json_prev = ""; + +my $layout_sw = Gtk2::ScrolledWindow->new(undef, undef); +my $layout_container = Gtk2::HBox->new(0, 0); +$layout_sw->add_with_viewport($layout_container); + +sub copy_tree { + my $tree = $i3->get_workspaces->recv; + + # convert the tree back to json so we only rebuild/redraw when the tree is changed + my $json = encode_json($tree); + if($json ne $json_prev) { + $json_prev = $json; + + # rebuild the tree and the layout + $tree_store->clear(); + if(defined($layout_box)) { + $layout_container->remove($layout_box); + } + copy_node($tree); + $layout_container->add($layout_box); + $layout_container->show_all(); + + # keep things expanded, otherwise the tree collapses every reload which is more annoying then this :-) + $tree_view->expand_all(); + } + + return(TRUE); +} + +sub new_column { + my $tree_column = Gtk2::TreeViewColumn->new(); + $tree_column->set_title(shift); + + my $renderer = Gtk2::CellRendererText->new(); + $tree_column->pack_start($renderer, FALSE); + $tree_column->add_attribute($renderer, text => shift); + + return($tree_column); +} + +my $col = 0; +$tree_view->append_column(new_column("Name", $col++)); +$tree_view->append_column(new_column("Window", $col++)); +$tree_view->append_column(new_column("Orientation", $col++)); +$tree_view->append_column(new_column("ID", $col++)); +$tree_view->append_column(new_column("Urgent", $col++)); +$tree_view->append_column(new_column("Focused", $col++)); +$tree_view->append_column(new_column("Layout", $col++)); +$tree_view->append_column(new_column("Rect", $col++)); + +$tree_view->set_grid_lines("both"); + +my $tree_sw = Gtk2::ScrolledWindow->new(undef, undef); +$tree_sw->add($tree_view); + +# Replaced by Gtk2 Boxes: +#my $area = Gtk2::DrawingArea->new(); +#$area->signal_connect(expose_event => sub { +# my ($widget, $event) = @_; +# +# # fetch a cairo context and it width/height to start drawing nodes +# my $cr = Gtk2::Gdk::Cairo::Context->create($widget->window()); +# +# my $w = $widget->allocation->width; +# my $h = $widget->allocation->height; +# +# draw_node($gtree, $cr, 0, 0, $w, $h); +# +# return FALSE; +#}); + +sub menu_export { + print("TODO: EXPORT\n"); +} + +my $menu_tree = [ + _File => { + item_type => '', + children => [ + _Export => { + callback => \&menu_export, + accelerator => 'E', + }, + _Quit => { + callback => sub { Gtk2->main_quit; }, + accelerator => 'Q', + }, + ], + }, +]; + +my $menu = Gtk2::SimpleMenu->new(menu_tree => $menu_tree); + +my $vbox = Gtk2::VBox->new(0, 0); +$vbox->pack_start($menu->{widget}, 0, 0, 0); +$vbox->pack_end($tree_sw, 1, 1, 0); +$vbox->pack_end($layout_sw, 1, 1, 0); + +$window->add($vbox); +$window->show_all(); +$window->set_size_request(500,500); + +Glib::Timeout->add(1000, "copy_tree", undef, Glib::G_PRIORITY_DEFAULT); +copy_tree(); + +Gtk2->main(); + -- 2.39.5