From: Michael Stapelberg Date: Sun, 10 Jun 2012 16:10:37 +0000 (+0200) Subject: Introduce contrib/ X-Git-Tag: 4.3~209^2~6 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=b704238cf6356388805051901016512b958a2583;p=i3%2Fi3 Introduce contrib/ contrib is a place for scripts which live in the i3 git repository because they are closely related. However, they should not be shipped with the distribution packages for example. --- diff --git a/contrib/dump-asy.pl b/contrib/dump-asy.pl new file mode 100755 index 00000000..a8eab04c --- /dev/null +++ b/contrib/dump-asy.pl @@ -0,0 +1,45 @@ +#!/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"); diff --git a/contrib/gtk-tree-watch.pl b/contrib/gtk-tree-watch.pl new file mode 100755 index 00000000..f15d0c18 --- /dev/null +++ b/contrib/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_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 => '', + 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(); + diff --git a/contrib/i3-wsbar b/contrib/i3-wsbar new file mode 100755 index 00000000..a485c150 --- /dev/null +++ b/contrib/i3-wsbar @@ -0,0 +1,290 @@ +#!/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 = ; + 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 [options] + +=head1 OPTIONS + +=over 4 + +=item B<--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> + +Specifies on which outputs the contents of stdin should be appended to the +workspace bar. + +Example: + --input-on "LVDS1" + +=item B<--output-on> + +Specifies for which outputs i3-wsbar should start C. + +=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 diff --git a/contrib/show-download-count.sh b/contrib/show-download-count.sh new file mode 100644 index 00000000..95b955a0 --- /dev/null +++ b/contrib/show-download-count.sh @@ -0,0 +1,23 @@ +#!/bin/sh +# © 2012 Han Boetes (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 diff --git a/dump-asy.pl b/dump-asy.pl deleted file mode 100755 index a8eab04c..00000000 --- a/dump-asy.pl +++ /dev/null @@ -1,45 +0,0 @@ -#!/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"); diff --git a/gtk-tree-watch.pl b/gtk-tree-watch.pl deleted file mode 100755 index f15d0c18..00000000 --- a/gtk-tree-watch.pl +++ /dev/null @@ -1,219 +0,0 @@ -#!/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 => '', - 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(); - diff --git a/i3-wsbar b/i3-wsbar deleted file mode 100755 index a485c150..00000000 --- a/i3-wsbar +++ /dev/null @@ -1,290 +0,0 @@ -#!/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 = ; - 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 [options] - -=head1 OPTIONS - -=over 4 - -=item B<--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> - -Specifies on which outputs the contents of stdin should be appended to the -workspace bar. - -Example: - --input-on "LVDS1" - -=item B<--output-on> - -Specifies for which outputs i3-wsbar should start C. - -=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 diff --git a/show-download-count.sh b/show-download-count.sh deleted file mode 100644 index 95b955a0..00000000 --- a/show-download-count.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# © 2012 Han Boetes (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