--- /dev/null
+#!/usr/bin/env perl
+# vim:ts=4:sw=4:expandtab
+# renders the layout tree using asymptote
+
+use strict;
+use warnings;
+use Data::Dumper;
+use AnyEvent::I3;
+use File::Temp;
+use v5.10;
+
+my $i3 = i3("/tmp/nestedcons");
+
+my $tree = $i3->get_tree->recv;
+
+my $tmp = File::Temp->new(UNLINK => 0, SUFFIX => '.asy');
+
+say $tmp "import drawtree;";
+
+say $tmp "treeLevelStep = 2cm;";
+
+sub dump_node {
+ my ($n, $parent) = @_;
+
+ my $o = ($n->{orientation} eq 'none' ? "u" : ($n->{orientation} eq 'horizontal' ? "h" : "v"));
+ my $w = (defined($n->{window}) ? $n->{window} : "N");
+ my $na = $n->{name};
+ $na =~ s/#/\\#/g;
+ my $name = "($na, $o, $w)";
+
+ print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
+
+ print $tmp "n" . $parent->{id} . ", " if defined($parent);
+ print $tmp "\"" . $name . "\");\n";
+
+ dump_node($_, $n) for @{$n->{nodes}};
+}
+
+dump_node($tree);
+say $tmp "draw(n" . $tree->{id} . ", (0, 0));";
+
+close($tmp);
+my $rep = "$tmp";
+$rep =~ s/asy$/eps/;
+system("cd /tmp && asy $tmp && gv $rep && rm $rep");
--- /dev/null
+#!/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_tree->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 => '<Branch>',
+ children => [
+ _Export => {
+ callback => \&menu_export,
+ accelerator => '<ctrl>E',
+ },
+ _Quit => {
+ callback => sub { Gtk2->main_quit; },
+ accelerator => '<ctrl>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();
+
--- /dev/null
+#!/usr/bin/env perl
+# vim:ts=4:sw=4:expandtab:ft=perl
+# © 2010 Michael Stapelberg, see LICENSE for license information
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use IPC::Run qw(start pump);
+use Try::Tiny;
+use AnyEvent::I3;
+use AnyEvent;
+use v5.10;
+
+my $stdin;
+my $socket_path = undef;
+my ($workspaces, $outputs) = ([], {});
+my $last_line = "";
+my $w = AnyEvent->timer(
+ after => 2,
+ cb => sub {
+ say "Connection to i3 timed out. Verify socket path ($socket_path)";
+ exit 1;
+ }
+);
+
+my $command = "";
+my $input_on = "";
+my $output_on = "";
+my $show_all = 0;
+
+my $result = GetOptions(
+ 'command=s' => \$command,
+ 'socket=s' => \$socket_path,
+ 'input-on=s' => \$input_on,
+ 'output-on=s' => \$output_on,
+ 'show-all' => \$show_all,
+ 'help' => sub { pod2usage(1); exit 0 },
+);
+
+if ($command eq '') {
+ say "i3-wsbar is only useful in combination with dzen2.";
+ say "Please specify -c (command)";
+ exit 1;
+}
+
+my $i3 = i3($socket_path);
+
+my @input_on = split(/,/, $input_on);
+my @output_on = split(/,/, $output_on);
+
+# Disable buffering
+$| = 1;
+
+# Wait a short amount of time and try to connect to i3 again
+sub reconnect {
+ my $timer;
+ if (!defined($w)) {
+ $w = AnyEvent->timer(
+ after => 2,
+ cb => sub {
+ say "Connection to i3 timed out. Verify socket path ($socket_path)";
+ exit 1;
+ }
+ );
+ }
+
+ my $c = sub {
+ $timer = AnyEvent->timer(
+ after => 0.01,
+ cb => sub { $i3->connect->cb(\&connected) }
+ );
+ };
+ $c->();
+}
+
+# Connection attempt succeeded or failed
+sub connected {
+ my ($cv) = @_;
+
+ if (!$cv->recv) {
+ reconnect();
+ return;
+ }
+
+ $w = undef;
+
+ $i3->subscribe({
+ workspace => \&ws_change,
+ output => \&output_change,
+ _error => sub { reconnect() }
+ });
+ ws_change();
+ output_change();
+}
+
+# Called when a ws changes
+sub ws_change {
+ # Request the current workspaces and update the output afterwards
+ $i3->get_workspaces->cb(
+ sub {
+ my ($cv) = @_;
+ $workspaces = $cv->recv;
+ update_output();
+ });
+}
+
+# Called when the reply to the GET_OUTPUTS message arrives
+# Compares old outputs with new outputs and starts/kills
+# $command for each output (if specified)
+sub got_outputs {
+ my $reply = shift->recv;
+ my %old = %{$outputs};
+ my %new = map { ($_->{name}, $_) } grep { $_->{active} } @{$reply};
+
+ # If no command was given, we do not need to compare outputs
+ if ($command eq '') {
+ update_output();
+ return;
+ }
+
+ # Handle new outputs
+ for my $name (keys %new) {
+ next if @output_on and !($name ~~ @output_on);
+
+ if (defined($old{$name})) {
+ # Check if the mode changed (by reversing the hashes so
+ # that we can check for equality using the smartmatch op)
+ my %oldrect = reverse %{$old{$name}->{rect}};
+ my %newrect = reverse %{$new{$name}->{rect}};
+ next if (%oldrect ~~ %newrect);
+
+ # On mode changes, we re-start the command
+ $outputs->{$name}->{cmd}->finish;
+ delete $outputs->{$name};
+ }
+
+ my $x = $new{$name}->{rect}->{x};
+ my $w = $new{$name}->{rect}->{width};
+ my $launch = $command;
+ $launch =~ s/([^%])%x/$1$x/g;
+ $launch =~ s/([^%])%w/$1$w/g;
+ $launch =~ s/%%x/%x/g;
+ $launch =~ s/%%w/%w/g;
+
+ $new{$name}->{cmd_input} = '';
+ my @cmd = ('/bin/sh', '-c', $launch);
+ $new{$name}->{cmd} = start \@cmd, \$new{$name}->{cmd_input};
+ $outputs->{$name} = $new{$name};
+ }
+
+ # Handle old outputs
+ for my $name (keys %old) {
+ next if defined($new{$name});
+
+ $outputs->{$name}->{cmd}->finish;
+ delete $outputs->{$name};
+ }
+
+ update_output();
+}
+
+sub output_change {
+ $i3->get_outputs->cb(\&got_outputs)
+}
+
+sub update_output {
+ my $dzen_bg = "#111111";
+ my $out;
+ my $previous_output;
+
+ for my $name (keys %{$outputs}) {
+ my $width = $outputs->{$name}->{rect}->{width};
+
+ $previous_output = undef;
+ $out = qq|^pa(;2)|;
+ for my $ws (@{$workspaces}) {
+ next if $ws->{output} ne $name and !$show_all;
+
+ # Display a separator if we are on a different output now
+ if (defined($previous_output) and
+ ($ws->{output} ne $previous_output)) {
+ $out .= qq|^fg(#900000)^ib(1)\|^ib(0)^p(+4)|;
+ }
+ $previous_output = $ws->{output};
+
+ my ($bg, $fg) = qw(333333 888888);
+ ($bg, $fg) = qw(4c7899 ffffff) if $ws->{visible};
+ ($bg, $fg) = qw(900000 ffffff) if $ws->{urgent};
+
+ my $cmd = q|i3-msg "workspace | . $ws->{name} . q|"|;
+ my $name = $ws->{name};
+
+ # Begin the clickable area
+ $out .= qq|^ca(1,$cmd)|;
+
+ # Draw the rest of the bar in the background color, but
+ # don’t move the "cursor"
+ $out .= qq|^p(_LOCK_X)^fg(#$bg)^r(${width}x17)^p(_UNLOCK_X)|;
+ # Draw the name of the workspace without overwriting the
+ # background color
+ $out .= qq|^p(+3)^fg(#$fg)^ib(1)$name^ib(0)^p(+5)|;
+ # Draw the rest of the bar in the normal background color
+ # without moving the "cursor"
+ $out .= qq|^p(_LOCK_X)^fg($dzen_bg)^r(${width}x17)^p(_UNLOCK_X)|;
+
+ # End the clickable area
+ $out .= qq|^ca()|;
+
+ # Move to the next rect, reset Y coordinate
+ $out .= qq|^p(2)^pa(;2)|;
+ }
+
+ $out .= qq|^p(_LOCK_X)^fg($dzen_bg)^r(${width}x17)^p(_UNLOCK_X)^fg()|;
+ $out .= qq|^p(+5)|;
+ $out .= $last_line if (!@input_on or $name ~~ @input_on);
+ $out .= "\n";
+
+ $outputs->{$name}->{cmd_input} = $out;
+ try {
+ pump $outputs->{$name}->{cmd} while length $outputs->{$name}->{cmd_input};
+ } catch {
+ warn "Could not write to dzen2";
+ exit 1;
+ }
+ }
+}
+
+$i3->connect->cb(\&connected);
+
+$stdin = AnyEvent->io(
+ fh => \*STDIN,
+ poll => 'r',
+ cb => sub {
+ my $line = <STDIN>;
+ if (!defined($line)) {
+ undef $stdin;
+ return;
+ }
+ chomp($line);
+ $last_line = $line;
+ update_output();
+ });
+
+# let AnyEvent do the rest ("endless loop")
+AnyEvent->condvar->recv
+
+__END__
+
+=head1 NAME
+
+i3-wsbar - sample implementation of a standalone workspace bar
+
+=head1 SYNOPSIS
+
+i3-wsbar -c <dzen2-commandline> [options]
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<--command> <command>
+
+This command (at the moment only dzen2 is supported) will be started for each
+output. C<%x> will be replaced with the X coordinate of the output, C<%w> will
+be replaced with the width of the output.
+
+Example:
+ --command "dzen2 -dock -x %x -w %w"
+
+=item B<--input-on> <list-of-RandR-outputs>
+
+Specifies on which outputs the contents of stdin should be appended to the
+workspace bar.
+
+Example:
+ --input-on "LVDS1"
+
+=item B<--output-on> <list-of-RandR-outputs>
+
+Specifies for which outputs i3-wsbar should start C<command>.
+
+=item B<--show-all>
+
+If enabled, all workspaces are shown (not only those of the current output).
+Handy to use with C<--output-on>.
+
+=back
+
+=cut
--- /dev/null
+#!/bin/sh
+# © 2012 Han Boetes <han@mijncomputer.nl> (see also: LICENSE)
+
+YEAR=`date "+%Y"`
+weblog=$(mktemp)
+zcat $(find /var/log/lighttpd/build.i3wm.org -type f -name "access.log.*.gz" | sort | tail -5) > $weblog
+# this will match the latest logfile, which is not yet gzipped
+find /var/log/lighttpd/build.i3wm.org/log$YEAR -type f \! -name "access.log.*.gz" -exec cat '{}' \; >> $weblog
+cat /var/log/lighttpd/build.i3wm.org/access.log >> $weblog
+gitlog=$(mktemp)
+
+# create a git output logfile. Only keep the first 6 chars of the release hash
+git log -150 --pretty=' %h %s' next > $gitlog
+
+awk '/i3-wm_.*\.deb/ {print $7}' $weblog|awk -F'/' '{print $NF}'|awk -F'_' '{print $2 }'|awk -F'-' '{print $NF}' |cut -c 2-8|sort |uniq -c | while read line; do
+ set -- $line
+ # $1 is the number of downloads, $2 is the release md5sum
+ sed -i "/$2/s|^ |$(printf '%3i' $1) d/l |" $gitlog
+done
+
+cat $gitlog
+rm $gitlog
+rm $weblog
+++ /dev/null
-#!/usr/bin/env perl
-# vim:ts=4:sw=4:expandtab
-# renders the layout tree using asymptote
-
-use strict;
-use warnings;
-use Data::Dumper;
-use AnyEvent::I3;
-use File::Temp;
-use v5.10;
-
-my $i3 = i3("/tmp/nestedcons");
-
-my $tree = $i3->get_tree->recv;
-
-my $tmp = File::Temp->new(UNLINK => 0, SUFFIX => '.asy');
-
-say $tmp "import drawtree;";
-
-say $tmp "treeLevelStep = 2cm;";
-
-sub dump_node {
- my ($n, $parent) = @_;
-
- my $o = ($n->{orientation} eq 'none' ? "u" : ($n->{orientation} eq 'horizontal' ? "h" : "v"));
- my $w = (defined($n->{window}) ? $n->{window} : "N");
- my $na = $n->{name};
- $na =~ s/#/\\#/g;
- my $name = "($na, $o, $w)";
-
- print $tmp "TreeNode n" . $n->{id} . " = makeNode(";
-
- print $tmp "n" . $parent->{id} . ", " if defined($parent);
- print $tmp "\"" . $name . "\");\n";
-
- dump_node($_, $n) for @{$n->{nodes}};
-}
-
-dump_node($tree);
-say $tmp "draw(n" . $tree->{id} . ", (0, 0));";
-
-close($tmp);
-my $rep = "$tmp";
-$rep =~ s/asy$/eps/;
-system("cd /tmp && asy $tmp && gv $rep && rm $rep");
+++ /dev/null
-#!/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_tree->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 => '<Branch>',
- children => [
- _Export => {
- callback => \&menu_export,
- accelerator => '<ctrl>E',
- },
- _Quit => {
- callback => sub { Gtk2->main_quit; },
- accelerator => '<ctrl>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();
-
+++ /dev/null
-#!/usr/bin/env perl
-# vim:ts=4:sw=4:expandtab:ft=perl
-# © 2010 Michael Stapelberg, see LICENSE for license information
-
-use strict;
-use warnings;
-use Getopt::Long;
-use Pod::Usage;
-use IPC::Run qw(start pump);
-use Try::Tiny;
-use AnyEvent::I3;
-use AnyEvent;
-use v5.10;
-
-my $stdin;
-my $socket_path = undef;
-my ($workspaces, $outputs) = ([], {});
-my $last_line = "";
-my $w = AnyEvent->timer(
- after => 2,
- cb => sub {
- say "Connection to i3 timed out. Verify socket path ($socket_path)";
- exit 1;
- }
-);
-
-my $command = "";
-my $input_on = "";
-my $output_on = "";
-my $show_all = 0;
-
-my $result = GetOptions(
- 'command=s' => \$command,
- 'socket=s' => \$socket_path,
- 'input-on=s' => \$input_on,
- 'output-on=s' => \$output_on,
- 'show-all' => \$show_all,
- 'help' => sub { pod2usage(1); exit 0 },
-);
-
-if ($command eq '') {
- say "i3-wsbar is only useful in combination with dzen2.";
- say "Please specify -c (command)";
- exit 1;
-}
-
-my $i3 = i3($socket_path);
-
-my @input_on = split(/,/, $input_on);
-my @output_on = split(/,/, $output_on);
-
-# Disable buffering
-$| = 1;
-
-# Wait a short amount of time and try to connect to i3 again
-sub reconnect {
- my $timer;
- if (!defined($w)) {
- $w = AnyEvent->timer(
- after => 2,
- cb => sub {
- say "Connection to i3 timed out. Verify socket path ($socket_path)";
- exit 1;
- }
- );
- }
-
- my $c = sub {
- $timer = AnyEvent->timer(
- after => 0.01,
- cb => sub { $i3->connect->cb(\&connected) }
- );
- };
- $c->();
-}
-
-# Connection attempt succeeded or failed
-sub connected {
- my ($cv) = @_;
-
- if (!$cv->recv) {
- reconnect();
- return;
- }
-
- $w = undef;
-
- $i3->subscribe({
- workspace => \&ws_change,
- output => \&output_change,
- _error => sub { reconnect() }
- });
- ws_change();
- output_change();
-}
-
-# Called when a ws changes
-sub ws_change {
- # Request the current workspaces and update the output afterwards
- $i3->get_workspaces->cb(
- sub {
- my ($cv) = @_;
- $workspaces = $cv->recv;
- update_output();
- });
-}
-
-# Called when the reply to the GET_OUTPUTS message arrives
-# Compares old outputs with new outputs and starts/kills
-# $command for each output (if specified)
-sub got_outputs {
- my $reply = shift->recv;
- my %old = %{$outputs};
- my %new = map { ($_->{name}, $_) } grep { $_->{active} } @{$reply};
-
- # If no command was given, we do not need to compare outputs
- if ($command eq '') {
- update_output();
- return;
- }
-
- # Handle new outputs
- for my $name (keys %new) {
- next if @output_on and !($name ~~ @output_on);
-
- if (defined($old{$name})) {
- # Check if the mode changed (by reversing the hashes so
- # that we can check for equality using the smartmatch op)
- my %oldrect = reverse %{$old{$name}->{rect}};
- my %newrect = reverse %{$new{$name}->{rect}};
- next if (%oldrect ~~ %newrect);
-
- # On mode changes, we re-start the command
- $outputs->{$name}->{cmd}->finish;
- delete $outputs->{$name};
- }
-
- my $x = $new{$name}->{rect}->{x};
- my $w = $new{$name}->{rect}->{width};
- my $launch = $command;
- $launch =~ s/([^%])%x/$1$x/g;
- $launch =~ s/([^%])%w/$1$w/g;
- $launch =~ s/%%x/%x/g;
- $launch =~ s/%%w/%w/g;
-
- $new{$name}->{cmd_input} = '';
- my @cmd = ('/bin/sh', '-c', $launch);
- $new{$name}->{cmd} = start \@cmd, \$new{$name}->{cmd_input};
- $outputs->{$name} = $new{$name};
- }
-
- # Handle old outputs
- for my $name (keys %old) {
- next if defined($new{$name});
-
- $outputs->{$name}->{cmd}->finish;
- delete $outputs->{$name};
- }
-
- update_output();
-}
-
-sub output_change {
- $i3->get_outputs->cb(\&got_outputs)
-}
-
-sub update_output {
- my $dzen_bg = "#111111";
- my $out;
- my $previous_output;
-
- for my $name (keys %{$outputs}) {
- my $width = $outputs->{$name}->{rect}->{width};
-
- $previous_output = undef;
- $out = qq|^pa(;2)|;
- for my $ws (@{$workspaces}) {
- next if $ws->{output} ne $name and !$show_all;
-
- # Display a separator if we are on a different output now
- if (defined($previous_output) and
- ($ws->{output} ne $previous_output)) {
- $out .= qq|^fg(#900000)^ib(1)\|^ib(0)^p(+4)|;
- }
- $previous_output = $ws->{output};
-
- my ($bg, $fg) = qw(333333 888888);
- ($bg, $fg) = qw(4c7899 ffffff) if $ws->{visible};
- ($bg, $fg) = qw(900000 ffffff) if $ws->{urgent};
-
- my $cmd = q|i3-msg "workspace | . $ws->{name} . q|"|;
- my $name = $ws->{name};
-
- # Begin the clickable area
- $out .= qq|^ca(1,$cmd)|;
-
- # Draw the rest of the bar in the background color, but
- # don’t move the "cursor"
- $out .= qq|^p(_LOCK_X)^fg(#$bg)^r(${width}x17)^p(_UNLOCK_X)|;
- # Draw the name of the workspace without overwriting the
- # background color
- $out .= qq|^p(+3)^fg(#$fg)^ib(1)$name^ib(0)^p(+5)|;
- # Draw the rest of the bar in the normal background color
- # without moving the "cursor"
- $out .= qq|^p(_LOCK_X)^fg($dzen_bg)^r(${width}x17)^p(_UNLOCK_X)|;
-
- # End the clickable area
- $out .= qq|^ca()|;
-
- # Move to the next rect, reset Y coordinate
- $out .= qq|^p(2)^pa(;2)|;
- }
-
- $out .= qq|^p(_LOCK_X)^fg($dzen_bg)^r(${width}x17)^p(_UNLOCK_X)^fg()|;
- $out .= qq|^p(+5)|;
- $out .= $last_line if (!@input_on or $name ~~ @input_on);
- $out .= "\n";
-
- $outputs->{$name}->{cmd_input} = $out;
- try {
- pump $outputs->{$name}->{cmd} while length $outputs->{$name}->{cmd_input};
- } catch {
- warn "Could not write to dzen2";
- exit 1;
- }
- }
-}
-
-$i3->connect->cb(\&connected);
-
-$stdin = AnyEvent->io(
- fh => \*STDIN,
- poll => 'r',
- cb => sub {
- my $line = <STDIN>;
- if (!defined($line)) {
- undef $stdin;
- return;
- }
- chomp($line);
- $last_line = $line;
- update_output();
- });
-
-# let AnyEvent do the rest ("endless loop")
-AnyEvent->condvar->recv
-
-__END__
-
-=head1 NAME
-
-i3-wsbar - sample implementation of a standalone workspace bar
-
-=head1 SYNOPSIS
-
-i3-wsbar -c <dzen2-commandline> [options]
-
-=head1 OPTIONS
-
-=over 4
-
-=item B<--command> <command>
-
-This command (at the moment only dzen2 is supported) will be started for each
-output. C<%x> will be replaced with the X coordinate of the output, C<%w> will
-be replaced with the width of the output.
-
-Example:
- --command "dzen2 -dock -x %x -w %w"
-
-=item B<--input-on> <list-of-RandR-outputs>
-
-Specifies on which outputs the contents of stdin should be appended to the
-workspace bar.
-
-Example:
- --input-on "LVDS1"
-
-=item B<--output-on> <list-of-RandR-outputs>
-
-Specifies for which outputs i3-wsbar should start C<command>.
-
-=item B<--show-all>
-
-If enabled, all workspaces are shown (not only those of the current output).
-Handy to use with C<--output-on>.
-
-=back
-
-=cut
+++ /dev/null
-#!/bin/sh
-# © 2012 Han Boetes <han@mijncomputer.nl> (see also: LICENSE)
-
-YEAR=`date "+%Y"`
-weblog=$(mktemp)
-zcat $(find /var/log/lighttpd/build.i3wm.org -type f -name "access.log.*.gz" | sort | tail -5) > $weblog
-# this will match the latest logfile, which is not yet gzipped
-find /var/log/lighttpd/build.i3wm.org/log$YEAR -type f \! -name "access.log.*.gz" -exec cat '{}' \; >> $weblog
-cat /var/log/lighttpd/build.i3wm.org/access.log >> $weblog
-gitlog=$(mktemp)
-
-# create a git output logfile. Only keep the first 6 chars of the release hash
-git log -150 --pretty=' %h %s' next > $gitlog
-
-awk '/i3-wm_.*\.deb/ {print $7}' $weblog|awk -F'/' '{print $NF}'|awk -F'_' '{print $2 }'|awk -F'-' '{print $NF}' |cut -c 2-8|sort |uniq -c | while read line; do
- set -- $line
- # $1 is the number of downloads, $2 is the release md5sum
- sed -i "/$2/s|^ |$(printf '%3i' $1) d/l |" $gitlog
-done
-
-cat $gitlog
-rm $gitlog
-rm $weblog