Patch to rpws for multi screen support
Matthew King <[email protected]>
| Newsgroups | gmane.comp.window-managers.ratpoison.devel |
|---|---|
| Message-ID | <CAFDK8z7wfmeZGJExZKVB7bkJFHh+g_Kv-UfF86zCgBE0604+TA@mail.gmail.com> |
Also I documented the hell out of it and simplified a few things. Matthew _______________________________________________ Ratpoison-devel mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/ratpoison-devel
rpws.diff
(application/octet-stream, 11.1 KB)
--- /usr/bin/rpws 2012-06-30 15:47:30.000000000 +0100
+++ src/rpws 2012-10-08 22:38:44.306312102 +0100
@@ -32,6 +32,7 @@
print( "for more detailed documentation run \"perldoc $0\"\n" );
}
+## Run the given ratpoison command, returning the result in a string.
sub rp_call
{
my $result = `$ratpoison -c "@_"`;
@@ -40,51 +41,83 @@
return $result;
}
-sub ws_init_ws
+## Return the frame configuration (ratpoison: fdump).
+sub fdump
{
+ return rp_call( "fdump " . shift );
+}
- my $num = shift;
-
- rp_call( "gnew wspl$num" );
- my $fd = fdump();
- rp_call( "setenv fspl$num $fd" )
+## Returns the number of the currently active screen by parsing ratpoison's
+## sdump output.
+sub get_active_screen
+{
+ # Don't ask. A cleaner way to do this would be nice.
+ return (grep { $_->[1] == 1 }
+ map { [(split)[0,5]] }
+ split /,/, rp_call( "sdump" )
+ )[0]->[0];
}
-sub fdump
+## Hook to run when the active screen is changed. Re-activates the new screen's
+## workspace and thus selects its group.
+sub screen_change
{
- return rp_call( "fdump" );
+ my $ws = rp_call( "getenv ws_screen_" . get_active_screen() . "_group" );
+ rp_call( "exec rpws $ws" );
}
+## Initialize a given number of workspaces.
sub ws_init
{
my $num = shift;
+ my $screens = scalar (split /,/, rp_call( "sdump" )); # No. of screens.
- $num >= 2 || die "You must create at least 2 workspaces.";
+ die "You must create at least $screens workspaces."
+ unless $num >= $screens;
- # Backup the frames
- my $fd = fdump();
+ # Create a group (ws_<n>) for each workspace.
+ rp_call( "gnew ws_$_" ) for 1..$num;
- rp_call( "select -" );
- rp_call( "only" );
+ # Initialise each screen s (counting from 0 because X does) to display
+ # workspace s+1, which corresponds to group ws_<n> (counting from 1 because
+ # then 1 or F1 can be bound to the first workspace/group).
+ #
+ # I have no idea if this works unless it's run on startup.
+ for (0..($screens-1)) {
+ # For each screen record its current frame configuration
+ my $fd = fdump($_);
+ # ... store it in ws_<s>_framedef.
+ rp_call( "setenv ws_" . ($_+1) . "_framedef $fd" );
+
+ # I have no idea if this is necessary but it was here before.
+ rp_call( "sselect $_" );
+ rp_call( "select -" ); # Empty the screen of windows
+ rp_call( "only" ); # ... and frames.
+ rp_call( "gselect ws_" . ($_+1) ); # Select this screen's new group.
+
+ # Record which workspace (default: s+1) is displayed on each screen
+ # in ws_screen_<s>_group.
+ rp_call( "setenv ws_screen_${_}_group " . ($_ + 1) );
- my $i;
- for my $i (2..$num)
- {
- ws_init_ws( $i );
+ # Restore the frames recorded previously.
+ rp_call( "frestore $fd" );
}
- # Workspace 1 uses the 'default' group.
- # Start in workspace 1.
- $fd = fdump();
- rp_call( "gselect default" );
- rp_call( "setenv fspl1 $fd" );
- rp_call( "setenv wspl 1" );
-
- # Keep track of workspace count
- rp_call( "setenv wspc $num" );
-
- # restore the frames
- rp_call( "frestore $fd" );
+ # Activate screen 0
+ rp_call( "sselect 0" );
+ rp_call( "gselect ws_1" );
+
+ # Move the 'default' group into ws_1 and delete it.
+ rp_call( "gmerge default" );
+ rp_call( "gdelete default" );
+
+ # Keep track of workspace count.
+ rp_call( "setenv ws_count $num" );
+
+ # Add a hook to follow screen changes if it's not already there.
+ if (rp_call( "listhook switchscreen" ) !~ /rpws screenchange$/m) {
+ rp_call( "addhook switchscreen exec $0 screenchange" );
+ }
if( -e "$lockfile" )
{
@@ -92,81 +125,128 @@
}
}
+## Save the workspace of the screen given (as the only argument)
+## to ws_<n>_framedef.
sub ws_save
{
- my $ws = rp_call( "getenv wspl" );
- my $fd = fdump();
- rp_call( "setenv fspl$ws $fd" );
+ my $screen = shift;
+ my $ws = rp_call( "getenv ws_screen_${screen}_group" );
+ rp_call( "setenv ws_${ws}_framedef " . fdump($screen) );
}
+## Restore the workspace configuration given (as the only argument) to the
+## active screen from ws_<n>_framedef.
+## * If workspace n is active the current screen, reactivate it (useful if
+## the current screen has changed).
+## * If workspace n is active on another screen return an error.
+## * If workspace n has not been used, create a new frame filling the whole screen.
sub ws_restore
{
my $which = shift;
+ my $screen = get_active_screen();
- ws_save();
+ # An array of each screen's id and size.
+ my @sdump = map { [split] } split /,/, rp_call( "sdump" );
+ # Make next/prev increase or decrease by one. Wraps around at each end.
if ( $which =~ /^(?:next|prev)$/ )
{
- my $ws = rp_call( "getenv wspl" );
- my $wspc = rp_call( "getenv wspc" );
+ my $ws = rp_call( "getenv ws_screen_${screen}_group" );
+ my $ws_count = rp_call( "getenv ws_count" );
- if ( $which eq 'next' )
- {
- $ws++;
- }
- else
- {
- $ws--;
- }
- $which = ( ( $ws - 1 ) % $wspc ) + 1;
+ $ws += ($which eq 'next') ? 1 : -1;
+ $which = ( ( $ws - 1 ) % $ws_count ) + 1;
}
- if( $which == 1 )
- {
- rp_call( "gselect default" );
+ # Check that the workspace isn't already active.
+ #
+ # TODO: Integrate with the above so next/prev will skip active workspaces.
+ my %ws_screen = map { rp_call( "getenv ws_screen_$_->[0]_group" )
+ => $_->[0]
+ } @sdump;
+ if (exists $ws_screen{$which} and $ws_screen{$which} != $screen) {
+ rp_call( "echo Not changing to workspace $which which is"
+ . " already on $ws_screen{$which}." );
+ return;
}
- else
- {
- rp_call( "gselect wspl$which");
+
+ # Save the current configuration before destroying it.
+ ws_save($screen);
+
+ # Activate the new workspace's group. This is also called when changing the
+ # active screen.
+ rp_call( "gselect ws_$which");
+
+ # Load the new workspace's configuration
+ my $fd = rp_call( "getenv ws_${which}_framedef" );
+ if ($fd ne '') {
+ # ... activate it if there is one
+ rp_call( "frestore $fd" );
+
+ } else {
+ # ... or create a new fullscreen frame if there isn't.
+
+ # List all frames
+ my @sfdump = sort map { (split)[2] } split /,/, rp_call( "sfdump" );
+ # Next unused frame id.
+ my $fid = $sfdump[$#sfdump] + 1;
+ rp_call( "frestore (frame :number $fid :x 0 :y 0"
+ . " :width $sdump[$screen][3] :height $sdump[$screen][4]"
+ . " :window 0) $screen"
+ );
}
+ # Record the screen's new group.
+ rp_call( "setenv ws_screen_${screen}_group $which" );
+
rp_call( "echo Workspace $which" );
- my $last = rp_call( "getenv fspl$which" );
- rp_call( "frestore $last" );
- rp_call( "setenv wspl $which" );
}
+## Add aliases:
+## * rpws<n>: activate workspace n.
+## * rpwsn; rpwsnext: activate the next workspace (execute rpws next).
+## * rpwsp; rpwsprev: activate the previous workspace (execute rpws prev).
sub add_aliases
{
my $n = shift;
- foreach my $i (1..$n) {
- rp_call ( "alias rpws$i exec $0 $i" );
- }
+ rp_call ( "alias rpws$_ exec $0 $_" ) for 1..$n;
rp_call ( "alias rpwsn exec $0 next" );
+ rp_call ( "alias rpwsnext exec $0 next" );
rp_call ( "alias rpwsp exec $0 prev" );
+ rp_call ( "alias rpwsprev exec $0 prev" );
}
-sub add_keys
+## Bind keys to the above aliases:
+## * M-F<n> (function keys); rpws<n>. (BUG? Is there a maximm no. of F-keys?)
+## * C-M-Right; rpwsnext
+## * C-M-Left; rpwsprev
+sub bind_keys
{
my $n = shift;
- foreach my $i (1..$n) {
- rp_call ( "definekey top M-F$i rpws$i" );
- }
- rp_call ( "definekey top C-M-Right rpwsn" );
- rp_call ( "definekey top C-M-Left rpwsp" );
+ rp_call ( "definekey top M-F$_ rpws$_" ) for 1..$n;
+ rp_call ( "definekey top C-M-Right rpwsnext" );
+ rp_call ( "definekey top C-M-Left rpwsprev" );
}
+
my $arg = shift @ARGV || 'help';
-if( $arg eq "help" ) {
+if( $arg =~ /^-*(h(elp)?|\?)/ ) { # A few different ways of asking for help.
help();
+
+} elsif( $arg eq "screenchange" or $arg eq "screen_change" ) {
+ screen_change();
+
} elsif( $arg eq "init" ) {
- my $num = shift @ARGV;
my %opts;
- ws_init( $num );
getopts('ka', \%opts);
+
+ my $num = shift @ARGV;
+ ws_init( $num );
+
add_aliases( $num ) if $opts{'a'} || $opts{'k'};
- add_keys ( $num ) if $opts{'k'};
+ bind_keys ( $num ) if $opts{'k'};
+
} else {
open LOCK, ">>$lockfile" or die "Cannot open lockfile: $lockfile";
flock(LOCK, LOCK_EX);
@@ -177,38 +257,44 @@
=head1 NAME
-rpws - Implements multiple workspaces in ratpoison
+ rpws - Implements multiple workspaces in ratpoison
=head1 SYNOPSIS
- rpws init n [-k] [-a] - setup rpws with n workspaces.
- -a sets up command aliases;
+ rpws init <n> [-k] [-a] - Setup rpws with n workspaces.
+ -a sets up command aliases.
-k sets up key bindings and aliases.
- rpws help - this documentation
- rpws n - switch to this workspace
-
+ rpws help - This documentation.
+ rpws <n> - Switch to workspace n.
+ rpws next - Switch to the next workspace.
+ rpws prev - Switch to the previous workspace.
=head1 DESCRIPTION
- B<rpws> implements multiple workspaces in ratpoison by making calls
- to fdump, freestore. It was adapted from rpws which comes with
- ratpoison in the contrib directory.
+ B<rpws> implements multiple workspaces in ratpoison by saving and loading
+ frame configuration with fdump and freestore, then 'hiding' inactive
+ workspaces with ratpoison's groups. It was adapted from rpws which comes with
+ ratpoison in the contrib directory. Then adapted again to include support for
+ multiple screens.
=head1 USAGE
-Add the following line in ~/.ratpoisonrc
+ Add the following line in ~/.ratpoisonrc
+
+ exec /path/to/rpws init 6 -a -k
+
+ This creates 6 workspaces. The current configration is stored in workspace 1.
- exec /path/to/rpws init 6 -k
+ The -a flag creates aliases rpws1, rpws2 ... rpws6, rpwsprev and rpwsnext.
-This creates 6 aliases rpws1, rpws2, etc. It also binds the keys M-F1,
-M-F2, etc to each rpwsN alias. Moreover, rpwsn (Next) and rpwsp (Prev) are
-created, and C-M-{Right,Left} are bound to rpws{n,p}.
+ The -k flag binds the keys M-F1, M-F2 ... M-F6 to each rpwsN alias and
+ C-M-{Left,Right} to rpws{prev,next}.
=head1 FILES
- rpws requires use of a lockfile. It defaults to using
-/tmp/rpws.<UID>.lock but this can be changed by setting the
-environment variable RPWS_LOCKFILE to your desired lockfile.
+ rpws requires use of a lockfile. It defaults to using /tmp/rpws.<UID>.lock but
+ this can be changed by setting the environment variable RPWS_LOCKFILE to your
+ desired lockfile.
=head1 AUTHOR