#!/usr/bin/perl -w my $RCS_Id = '$Id: openbox.pl,v 1.5 2008-11-29 11:20:46+01 jv Exp $ '; # Author : Johan Vromans # Created On : Thu Jun 26 16:32:16 2008 # Last Modified By: Johan Vromans # Last Modified On: Mon Dec 1 10:31:42 2008 # Update Count : 388 # Status : Unknown, Use with caution! ################ Common stuff ################ use strict; # Package or program libraries, if appropriate. # $LIBDIR = $ENV{'LIBDIR'} || '/usr/local/lib/sample'; # use lib qw($LIBDIR); # require 'common.pl'; # Package name. my $my_package = 'Sciurix'; # Program name and version. my ($my_name, $my_version) = $RCS_Id =~ /: (.+).pl,v ([\d.]+)/; # Tack '*' if it is not checked in into RCS. $my_version .= '*' if length('$Locker: $ ') > 12; ################ Command line parameters ################ use Getopt::Long 2.13; Getopt::Long::Configure("pass_through"); # Command line options. my $ob_reconfig = 0; # reconfigure (reload) my $ob_restart = 0; # restart my $verbose = 0; # verbose processing # Development options (not shown with -help). my $debug = 0; # debugging my $trace = 0; # trace (show process) my $test = 0; # test mode. # Process command line options. app_options(); # Post-processing. $trace |= ($debug || $test); ################ Presets ################ my $TMPDIR = $ENV{TMPDIR} || $ENV{TEMP} || '/usr/tmp'; ################ The Process ################ use Text::FakeXML; use Encode; my ($system, $fqdn) = get_system_identity(); my %sysmap = ( 'phoenix:0.0' => { workspaces => [ qw(Local Aux1 Aux2 CM Aux3 Aux4) ], }, fiona => { workspaces => [ qw(Local Aux1 Aux2 CM) ], hibernate => 1, }, 'phoenix:0.1' => { workspaces => [ qw(Local) ], }, 'phoenix:20.0' => { workspaces => [ qw(Local) ], }, ); my $map = $sysmap{$system.$ENV{DISPLAY}} || $sysmap{$system}; die("Unknown system: $system\n") unless $map; die("Display not local\n") unless $ENV{DISPLAY} =~ /^:\d+(?:\.\d+)?$/; load_symbols(); my $libdir = join("/", $ENV{HOME}, "XStart", $fqdn, $ENV{DISPLAY}); system("mkdir", "-p", $libdir) unless -d $libdir; die("Missing dir $libdir\n") unless -d $libdir; my $obrc = "$libdir/obrc.xml"; my $obmenu = "$libdir/obmenu.xml"; my $rcfd; my $rc; # To save typing. my $open = sub { $rc->xml_elt_open(@_) }; my $close = sub { $rc->xml_elt_close(@_) }; my $elt = sub { $rc->xml_elt(@_) }; my $sep = sub { $elt->("separator", undef, $_[0] ? (label => $_[0]) : ()) }; my $add = sub { $rc->append(@_) }; my $comment = sub { $rc->xml_comment(@_) }; my @tm = localtime; my $ts = sprintf("%04d-%02d-%02d %02d:%02d:%02d", 1900+$tm[5], 1+$tm[4], @tm[3,2,1,0]); create_openbox_rc(); create_openbox_menu(); create_devilspie_configs(); my @obcmd = ( "/usr/bin/openbox", "--config-file", $obrc ); push(@obcmd, "--reconfigure") if $ob_reconfig; push(@obcmd, "--restart") if $ob_restart; push(@obcmd, @ARGV); system("echo", "+", @obcmd); exec @obcmd unless $test; exit; ################ sub create_openbox_rc { undef $rcfd; open($rcfd, ">:encoding(utf8)", $obrc) or die("$obrc: $!\n"); $rc = Text::FakeXML->new( fh => $rcfd, version => '1.0', encoding => 'UTF-8' ); $comment->("Generated by $my_name $my_version on $ts - DO NOT EDIT"); #### Preamble. $open->("openbox_config", xmlns => "http://openbox.org/3.4/rc"); $add->("\n"); #### Contents. rc_resistance(); rc_focus_policy(); rc_window_placement(); rc_theme(); rc_desktops(); rc_resize_policy(); rc_reserved_margins(); rc_dock(); rc_keyboard(); rc_mouse(); rc_menus(); rc_applications(); #### Postamble. $close->("openbox_config"); close($rcfd) or die("$obmenu: $!\n"); } sub create_openbox_menu { undef $rcfd; open($rcfd, ">:encoding(utf8)", $obmenu) or die("$obmenu: $!\n"); $rc = Text::FakeXML->new( fh => $rcfd, version => '1.0', encoding => 'UTF-8' ); $comment->("Generated by $my_name $my_version on $ts - DO NOT EDIT"); #### Preamble. $open->("openbox_menu", xmlns => "http://openbox.org/3.4/menu"); $add->("\n"); #### Contents. # We have three main menus: the root menu, the terms menu and the # execs (apps) menu. Most of them include submenus. # Also note that root refers to terms and execs. menu_terms_menu(); menu_execs_menu(); menu_root_menu(); #### Postamble. $close->("openbox_menu"); close($rcfd) or die("$obmenu: $!\n"); } sub create_devilspie_configs { # Since 'modern' GTK based apps no longer honour the -geometry # specs, we use devilspie to control placement of some of these # windows. my ($f, $ds); my ($x, $y, $h, $w); my $border = 1; # border my $deco = 25; # top + bottom decoration my $voff = 6; # panel #### Firefox undef $f; $ds = $libdir . "/firefox.ds"; open($f, ">", $ds) or die("$ds: $!\n"); $h = $ENV{X_HEIGHT} - 120; print { $f } <{workspaces}}; $i++ ) { $ws_cm = $i+1, last if $map->{workspaces}->[$i] eq "CM"; } if ( $ws_cm ) { undef $f; $ds = $libdir . "/jpilot.ds"; open($f, ">", $ds) or die("$ds: $!\n"); $x = int($ENV{X_WIDTH} / 2); $w = int($ENV{X_WIDTH} / 2) - 2 * $border; $h = $ENV{X_HEIGHT} - $deco - $voff; print { $f } <("resistance"); $elt->("strength", 10); $elt->("screen_edge_strength", 20); $close->("resistance"); $add->("\n"); } #### Focus policy. sub rc_focus_policy { $open->("focus"); $comment->("Always try to focus new windows when they appear.". " Other rules do apply."); $elt->("focusNew", "yes"); $comment->("Move focus to a window when you move the mouse into it."); $elt->("followMouse", "yes"); $comment->("Focus the last used window when changing desktops,". " instead of the one under the mouse pointer. When". " followMouse is enabled."); $elt->("focusLast", "no"); $comment->("Move focus under the mouse, even when the mouse". " is not moving."); $elt->("underMouse", "no"); $comment->("When followMouse is enabled, the mouse must be". " inside the window for this many milliseconds". " (1000 = 1 sec) before moving focus to it."); $elt->("focusDelay", 400); $comment->("When followMouse is enabled, and a window is". " given focus by moving the mouse into it, also". " raise the window."); $elt->("raiseOnFocus", "yes"); $close->("focus"); $add->("\n"); } #### Window placement. sub rc_window_placement { $add->(<<'EOD'); UnderMouse
yes
Mouse
EOD } #### Theme sub rc_theme { $add->(<<'EOD'); Clearlooks NLIMC no yes sans 8 bold normal sans 8 bold normal sans 9 normal normal sans 9 normal normal sans 9 bold normal EOD } #### Desktops (workspaces) sub rc_desktops { $open->("desktops"); $add->(<<'EOD'); EOD $elt->("number", scalar(@{$map->{workspaces}})); $elt->("firstdesk", 1); $open->("names"); $elt->("name", $_) foreach @{$map->{workspaces}}; $close->("names"); $comment->("The number of milliseconds to show the popup when". " switching desktops. Set this to 0 to disable the popup."); $elt->("popupTime", 875); $close->("desktops"); $add->("\n"); } #### Resize. sub rc_resize_policy { $add->(<<'EOD'); yes Always Center 0 0 EOD } #### Margins. sub rc_reserved_margins { $add->(<<'EOD'); 0 0 0 0 EOD } #### Dock sub rc_dock { $add->(<<'EOD'); Left 0 0 no Above Vertical no 300 300 Middle EOD } #### Keyboard sub rc_keyboard { $open->("keyboard"); $elt->("chainQuitKey", "C-g"); # W is the Windows key (Super_L and Super_R). # OpenBox can handle it (no need to remap) but it must be # a modifier, so the following lines are needed in .Xmodmap: # clear mod5 # add mod5 Super_L Super_R $comment->("Keybindings for desktop switching."); $add->(< no EOD $comment->("Keybindings for sending windows to desktops."); $add->(< no EOD $comment->("Keybindings to switch desktops directly."); $add->(<{workspaces}}; $_ EOD $add->(<<'EOD'); yes yes client-menu terms-menu root-menu exec-menu client-list-combined-menu EOD $close->("keyboard"); $add->("\n"); } #### Mouse. sub rc_mouse { $add->(<<'EOD'); 8 200 0 client-menu top left right bottom bottom client-menu client-menu root-menu terms-menu exec-menu client-menu EOD } #### Menu. sub rc_menus { $add->(< $obmenu 200 no 100 yes EOD } #### Applications. sub rc_applications { $open->("applications"); my %gadgets = ( "XClock" => "xload", "XBiff" => "xbiff", "XPostit" => "xpostit", "XBclock" => "xledclock", "XLoad" => "xload", "Xmessage" => "xmessage", ); my %undecorated = ( %gadgets, "XVroot" => "xv", "XEyes" => "xeyes", "Vncviewer" => "", "XTerm" => "Console", ); my %occupyall = ( %gadgets, # "Battery*" => "". # "CPU*" => "", ); my %allapps = ( %gadgets, %undecorated, %occupyall ); foreach my $app ( keys %allapps ) { $open->("application", class => $app, $allapps{$app} ? (name => $allapps{$app}) : ()); $elt->("decor", "no") if exists($undecorated{$app}); $elt->("desktop", "all") if exists($occupyall{$app}); $elt->("skip_taskbar", "yes") if exists($occupyall{$app}); $close->("application"); } $close->("applications"); $add->("\n"); } #### Root Menu. sub menu_root_menu { $open->("menu", id => "root-menu", label => "$system$ENV{DISPLAY}"); $sep->("$system$ENV{DISPLAY}"); $elt->("menu", undef, id => "terms-menu"); $elt->("menu", undef, id => "exec-menu"); $elt->("menu", undef, id => "client-list-combined-menu"); $sep->(); $open->("menu", id => "gnome-menu", label => "GNOME Menu"); $elt->("menu", undef, id => "applications-menu", label => "Applications", execute => "/usr/share/openbox/xdg-menu applications"); $elt->("menu", undef, id => "preferences-menu", label => "Preferences", execute => "/usr/share/openbox/xdg-menu preferences"); $elt->("menu", undef, id => "administration-menu", label => "Administration", execute => "/usr/share/openbox/xdg-menu system-settings"); $close->("menu"); $sep->(); $open->("item", label => "Background"); $open->("action", name => "Execute"); $elt->("command", "xsetroot -solid $ENV{ROOT_BGCOLOR}"); $close->("action"); $close->("item"); foreach ( qw(Reconfigure Restart Exit) ) { $open->("item", label => $_); if ( /^Re/ ) { $elt->("action", undef, name => $_); } else { $open->("action", name => $_); $elt->("prompt", "no"); $close->("action"); } $close->("item"); } $sep->("Session"); if ( $map->{hibernate} && -x "/usr/sbin/hibernate" ) { $open->("item", label => "Hibernate"); $open->("action", name => "Execute"); $elt->("prompt", "yes"); $elt->("command", "sudo /usr/sbin/hibernate"); $close->("action"); $close->("item"); } foreach ( qw(SessionLogout) ) { $open->("item", label => $_ eq "SessionLogout" ? "Log Out" : $_); $open->("action", name => $_); $elt->("prompt", "yes"); $close->("action"); $close->("item"); } $close->("menu"); } #### Terms menu. sub menu_terms_menu { ## Consoles Menu. $open->("menu", id => "consoles-menu", label => "Consoles"); $sep->("Consoles"); for ( 1 .. 6 ) { $open->("item", label => "Console $_"); $open->("action", name => "Execute"); $elt->("command", "sh -c 'sudo chvt $_ >/dev/null 2>&1'"); $close->("action"); $close->("item"); } $close->("menu"); ## Terms menu. $open->("menu", id => "terms-menu", label => "New Window"); $sep->("New Window"); my $action = sub { my ($label, $command, $msg) = @_; $open->("item", label => $label); if ( $msg ) { $open->("action", name => "Execute"); $elt->("command", "echo \"$msg\""); $close->("action"); } $open->("action", name => "Execute"); $elt->("command", $command); $close->("action"); $close->("item"); }; my $xterm = sub { my ($label, $host, $user) = @_; my ($msg, $command); if ( $host ) { if ( $user ) { $msg = "Starting Xterm for $host:$user"; $command = "uxterm -T $host:$user -e ssh -X $user@$host"; } else { $msg = "Starting Xterm for $host"; $command = "uxterm -T $host:$ENV{USER} -e ssh -X $host"; } } else { if ( $user ) { $msg = "Starting local Xterm for $user"; $command = "uxterm -T $system:$user -e login $user"; } else { $msg = "Starting local Xterm"; $command = "uxterm -T $system:$ENV{USER} -ls"; } } $action->($label, $command, $msg); }; $xterm->("Local", undef); foreach my $host ( qw(phoenix fiona deirdre joni) ) { $xterm->(ucfirst($host), $host) unless $system eq $host; } foreach my $host ( qw(joni) ) { $action->(ucfirst($host)." (VNC)", "vncviewer -Autoselect=0 -FullColour -passwd /home/jv/.vnc/passwd joni:0") unless $system eq $host; } $sep->(); $action->("VMware", "vmware -l"); $action->("GNUtella", "vncviewer -geometry 1024x768+0+0 -Autoselect=0 -FullColour -passwd /home/jv/.vnc/passwd phoenix:20"); $sep->(); $xterm->("XS4All", "xs4all"); $xterm->("WarpNet", "warpnet"); $xterm->("Booking", "blackhawk"); $sep->(); $elt->("menu", undef, id => "consoles-menu"); $close->("menu"); } sub findbin { my ($bin) = @_; foreach ( split(/:/, $ENV{PATH}) ) { return "$_/$bin" if -x "$_/$bin"; } return; } sub menu_execs_menu { my $app; my $action = sub { my ($label, $command) = @_; $open->("item", label => $label); $open->("action", name => "Execute"); $elt->("command", $command); $close->("action"); $close->("item"); }; ## Execute menu. $open->("menu", id => "exec-menu", label => "Execute"); $sep->("Execute"); $add->("\n"); ## Emacs submenu. $open->("menu", id => "emacs-menu", label => "Emacs"); #$sep->("Emacs"); $action->("Default", "emacs"); $action->("VM", "emacs -f jv-start-vm"); $action->("Plain", "emacs -q"); $close->("menu"); $add->("\n"); $action->("Calendar", "jpilot"); $add->("\n"); ## Browser submenu. if ( 0 ) { $open->("menu", id => "browser-menu", label => "Browser"); #$sep->("Browser"); foreach my $app ( qw(browser firefox mozilla konqueror) ) { my $bin = findbin($app); next unless $bin; $action->(ucfirst($app eq "browser" ? "default" : $app), $bin); } $close->("menu"); } ## Browser else { foreach my $app ( qw(browser firefox mozilla konqueror) ) { my $bin = findbin($app); next unless $bin; $action->(ucfirst($app), $bin); last; } } $add->("\n"); $open->("menu", id => "eekboek-menu", label => "EekBoek"); my $dir = $ENV{HOME}."/Squirrel/Administratie/Boekhouding/2008"; $action->("Mutaties", "gnudoit '(find-file \"$dir/mutaties.eb\")'"); $action->("Shell", "sh -c 'cd $dir; xterm ebshell'"); $action->("GUI", "sh -c 'cd $dir; ebgui'"); $close->("menu"); $add->("\n"); $action->("Zim", "zim"); $elt->("menu", undef, id => "openoffice-menu"); if ( ( $app = findbin("thunderbird")) ) { $action->("Mail", $app); if ( ( $app = findbin("pgpkeyman")) ) { $open->("menu", id => "pgpkeyman-menu", label => "PGP Keys"); #$sep->("PGP Keys"); $action->("Key management", "pgpkeyman"); $action->("Key management (Virgo)", "pgpkeyman -virgo"); $close->("menu"); } } if ( ( $app = findbin("wxkeyring")) ) { my $w = $ENV{X_WIDTH} - 450; $w = 10 if $w < 0; $action->("GNU Keyring", "$app -geometry 400x400+$w+62 --clip ". $ENV{HOME}."/.jpilot/Keys-Gtkr.pdb"); } $elt->("menu", undef, id => "musicplay-menu"); if ( 0 && ( $app = findbin("k3b")) ) { $action->("CD/DVD Burner", $app); } if ( -e "/dev/scanner" && ( $app = findbin("scan_copier") ) ) { $open->("menu", id => "scanner-menu", label => "Scanner"); #$sep->("Scanner"); $action->("Copy", "$app -once"); $action->("Copier", "$app"); $action->("XSane", "$app -xsane"); $action->("Stop", "killall scanimage"); $action->("Lamp Off", "scanimage -n --lamp-off"); $close->("menu"); } my $irssi = findbin("irssi"); my $gajim = findbin("gajim"); my $gaim = $gajim ? undef : findbin("gaim"); if ( $irssi || $gaim || $gajim ) { $open->("menu", id => "irc-menu", label => "IRC / IM"); #$sep->("IRC / IM"); $action->("Irssi", "uxterm -T IRC -geometry 80x60 -e screen -d -RR -S irssi irssi") if $irssi; $action->("Gajim", $gajim) if $gajim; $action->("Gaim", $gaim) if $gaim; $close->("menu"); } $action->("MIDI Server", "$app -iA -Os") if $app = findbin("timidity"); for my $g ( qw(Eclipse JEdit XV) ) { next unless $app = findbin(lc $g); $action->($g, "sh -c 'cd $ENV{HOME}/tmp && $app'" . ( $g eq 'JEdit' ? ' -norestore' : '')); } unless ( $system eq "phoenix" ) { $sep->("Phoenix"); $action->("Emacs", "sh -c 'xhost +phoenix; ssh -n -X phoenix emacs -f jv-start-vm'"); $action->("Browser", "sh -c 'xhost +phoenix; ssh -n -X phoenix mozilla'"); $action->("Mail", "sh -c 'xhost +phoenix; ssh -n -X phoenix thunderbird'"); } $elt->("separator", undef); # The Gadgets. my $needmenu = 1; $open->("menu", id => "gadgets-menu", label => "Gadgets"); my @gadgets; my $d; opendir($d, $libdir) and @gadgets = sort(readdir($d)) and closedir($d); for my $g ( @gadgets ) { next unless $g =~ /^X/; $action->($g, "sh $libdir/$g"); $open->("menu", id => "gadgets-menu", label => "Gadgets") unless $needmenu++; } for my $g ( qw(XMag) ) { next unless $app = findbin(lc $g); $open->("menu", id => "gadgets-menu", label => "Gadgets") unless $needmenu++; $action->($g, $app); } $close->("menu") if $needmenu; $elt->("menu", undef, id => "proc-menu", label => "Processes", execute => "~/wrk/openbox/procinfo.pl") if -x $ENV{HOME}."/wrk/openbox/procinfo.pl"; $elt->("menu", undef, id => "mpd-menu", label => "MPD", execute => "~/wrk/openbox/ob-mpd.py") if -x $ENV{HOME}."/wrk/openbox/ob-mpd.py"; $close->("menu"); } ################ Subroutines ################ sub get_system_identity { # Who am I? if ( open(my $f, "<", "/etc/sysconfig/network") ) { while ( <$f> ) { if ( /HOSTNAME=(([^.]+)\S+)/ ) { return ($2, $1); } } close($f); } use Sys::Hostname; my $fqdn = hostname(); die("Cannot establish system name\n") unless $fqdn && $fqdn =~ /^([^.]+)\S+$/; return ($1, $fqdn); } sub load_symbols { open(my $f, "-|", "xrdb -symbols -screen") or die("Gannot get Xrdb symbols: $! [$?]\n"); while ( <$f> ) { next unless /^-D(WIDTH|HEIGHT|NUM_SCREENS)=(.*)/; $ENV{"X_$1"} = $2; } close($f); } ################ Subroutines ################ sub Text::FakeXML::append { my ($self, $text) = @_; $self->print($text); } ################ Subroutines ################ sub app_options { my $help = 0; # handled locally my $ident = 0; # handled locally # Process options, if any. # Make sure defaults are set before returning! return unless @ARGV > 0; if ( !GetOptions( 'reconfig' => \$ob_reconfig, 'restart' => \$ob_restart, 'ident' => \$ident, 'verbose' => \$verbose, 'test' => \$test, 'trace' => \$trace, 'help|?' => \$help, 'debug' => \$debug, ) or $help ) { app_usage(2); } app_ident() if $ident; } sub app_ident { print STDERR ("This is $my_package [$my_name $my_version]\n"); } sub app_usage { my ($exit) = @_; app_ident(); print STDERR <