GNOME session save and restore

Save and restore your GNOME Shell desktop active running application windows and their positions across multiple workspaces using an automated command line script.

Synopsis:

To save your session, press Alt+F2 or on a terminal:

>session save

To restore your session, press Alt+F2 or on a terminal:

>session restore

To restore your session including running applications:

>session restore missing

Installation:

Dependencies:

This script uses Perl, xprop+xwininfo, wmctrl, and optionally xdotool. To ensure that all dependencies exist use:

>sudo yum install -y perl xorg-x11-utils wmctrl xdotool

Note: I only test on my CentOS desktops. Others have reported the script working on Ubuntu, Xubuntu, and other desktops.

Download the script
Place it on your $PATH, and be sure to give it executable permissions:

>wget https://github.com/arnon-weinberg/session/raw/master/session
>chmod +x session
>sudo mv session /usr/local/bin

Use cases:

  • Restore workspace after reboot from kernel upgrade, hardware upgrade, non-recoverable error, battery died, or power outage.
  • Desktop layout messed on laptop after unplugging from an external monitor.
  • Restarting an application with multiple windows across workspaces.
  • Reopen application that crashed or was closed accidentally.
  • Remember and automatically restart open running active apps.
  • Reset application window geometries and arrangements over multiple desktop workspaces using command line or script.

Details:

>session save: Save session in default location (see below)
>session restore existing: Restore geometries of existing windows
>session restore matching: Restore geometries of matching windows [default]
>session restore missing: Restore geometries of missing windows

Available options:
–session: Set session filename [default = ~/.config/gnome-session/session.ini]
–debug=[0-3]: Set debug level

Limitations:
Restoring applications (session restore missing) only runs applications; it cannot restore their previous state – that is up to each application to do.
The script contains a list of exceptions that is currently manually maintained. It lists applications that should be handled specially when restoring them. Because some applications handle their own session save and restore, but they don’t handle restoring geometries, the session restore process can be a 3-step process sometimes: (1) Run session restore missing to restart the application, (2) follow the application’s session restore process, and (3) run session restore to restore its geometries.

Note: The session script never shuts down applications, and simply ignores applications not in the saved session.

Changes:

Relative to the previous version of this script, this upgrade has a much improved window matching algorithm, application running process, updates for recent applications, and fixes for several bugs.
The command line arguments have changed.

This script is now on GitHub.

Note: When submitting bug reports via GitHub, please include full debug output using –debug=3.

9 thoughts on “GNOME session save and restore

  1. From my tests, and from the dependencies of this script, can you confirm that it does not work with the wayland version of gnome?

  2. Hello
    tnx for such a useful little piece of code 🙂
    I think Ive found a bug
    Have a look at the following session.ini:

    ‘0x04001477’ => {
    ‘workspace’ => ‘2’,
    ‘comm’ => ‘eog’,
    ‘pid’ => ‘6776’,
    ‘geometry’ => ‘10,95,53,833,1119’,
    ‘command’ => ‘eog/home/user/Desktop/BtKTaUsUN1g.jpg’,
    ‘exec’ => [
    ‘eog’,
    ‘/home/user/Desktop/BtKTaUsUN1g.jpg’
    ],
    ‘state’ => {
    ‘_NET_WM_STATE_FOCUSED’ => 1
    },
    ‘Name’ => ‘Eog’,
    ‘class’ => ‘eog.Eog’,
    ‘id’ => ‘0x04001477’,
    ‘name’ => ‘fedora mtlW6U9e1yY.jpg’
    },
    ‘0x04000007’ => {
    ‘state’ => {},
    ‘Name’ => ‘Eog’,
    ‘id’ => ‘0x04000007’,
    ‘name’ => ‘fedora BtKTaUsUN1g.jpg’,
    ‘class’ => ‘eog.Eog’,
    ‘command’ => ‘eog/home/user/Desktop/BtKTaUsUN1g.jpg’,
    ‘exec’ => [
    ‘eog’,
    ‘/home/user/Desktop/BtKTaUsUN1g.jpg’
    ],
    ‘comm’ => ‘eog’,
    ‘workspace’ => ‘2’,
    ‘pid’ => ‘6776’,
    ‘geometry’ => ‘10,882,42,1035,1158’
    },

    ‘exec’ and ‘command’ of the first application are wrong (they are the same as in the second application)

    Cheers

    1. Glad you find it useful.
      This behaviour is common for applications that manage their own windows internally, and is not unique to eog. Unfortunately, this also means that multiple windows of such applications cannot be restored by this script.

  3. Hello

    “session restore missing” goes into an almost endless loop (40mins)

    DEBUG: Trying 0x02e00007 + 0x04000007…
    DEBUG: Trying 0x05200007 + 0x03000007…
    DEBUG: Trying 0x03a00007 + 0x03c00007…
    DEBUG: Trying 0x05a00007 + 0x05e00007…
    DEBUG: Trying 0x05400007 + 0x08000007…
    DEBUG: Trying 0x05800007 + 0x06200007…
    DEBUG: Result = 15
    DEBUG: Result = 11 + 15 (best) = 26
    DEBUG: Trying 0x05400007 + 0x06200007…

    1. Interesting. It looks like you have many windows open of a single application (maybe 12), and the script does not scale well. Let me see what I can do about it.

      1. correct. I have usually 25-30 gedit, 40-50 eog, 14 firefox windows, 20-25 terminals. Over 10 virtual desktops. But Im still migrating from my old MacOS setup and will need to double that amount …

  4. GNOME has moved from Xorg to Wayland, and it looks like scripted inspection/control of windows is not possible in Wayland (ie, no wmctrl, xdotool, xwininfo, or xprop equivalents). Such functionality should be implemented by the compositor instead (ie, GNOME rather than Wayland), but has not been to my knowledge. Thus, unless/until then, I do not expect to do any more work on this script.

  5. Hello,
    Thanks a lot for this advanced (at least for me) Perl script for saving/restoring Gnome sessions!
    Please find below a patch adding a “session restore dumb” option which allows me to restore multiple Terminal windows (in different workspaces) in a short time on on multi-CPU laptop with Fedora 39 🙂
    KR,
    Thierry

    diff -u session.orig session
    — session.orig 2024-04-06 13:19:02.183048070 +0200
    +++ session 2024-04-06 17:27:13.880082034 +0200
    @@ -1,7 +1,8 @@
    #!/bin/env perl
    #
    # >session save
    -# >session restore [existing|matching|missing]
    +# *** ADDED: dumb mode ***
    +# >session restore [existing|matching|missing|dumb]
    # >session –session=session-name save
    # >session –session ~/.config/gnome-session/session.ini restore
    #
    @@ -111,7 +112,8 @@
    close ( $SES );

    print “Restoring session.\n”;
    – my $level = { existing => 0, matching => 1, missing => 2 }->{shift()} // 1;
    + # *** ADDED dumb mode ***
    + my $level = { existing => 0, matching => 1, missing => 2, dumb => 3 }->{shift()} // 1;

    eval ( $session );

    @@ -179,7 +181,8 @@
    print “DEBUG: Group $comm (”
    . ( keys ( %{$score{$comm}} ) +0 ) . ” windows)…\n”
    if $opt{debug} > 2;
    – my ( $score, $map ) = depth_first_search ( $score{$comm} );
    + # *** ADDED dumb mode -> skip search ***
    + my ( $score, $map ) = ( $level == 3 ) ? ( @_ ) : depth_first_search ( $score{$comm} );
    print “DEBUG: Best match score = $score\n” if $opt{debug} > 2;

    # This is basically a travelling salesman problem, solved using a
    @@ -271,7 +274,9 @@
    next if ! $session->{id} or $session->{cid} or
    $session->{_cid} or $session->{pid};
    # Only launch once per self_managed application:
    – next if grep { /^\Q$session->{class}\E/ }
    + # *** ADDED dumb mode allows multiple windows of the same kind ***
    + next if ( $level {class}\E/ }
    ( @{$exceptions{self_managed}} ) and
    $stat{self_managed}{$session->{class}}++;
    # Only launch once per pid_group (old pid) per round:
    @@ -326,6 +331,8 @@
    $stat{pid}{$session->{pid_group}} = 0; # Permit another instance
    $stat{wait} = $session->{wait} = 0; # Retry matching process
    $stat{exec}++; # after a waiting period
    + # *** ADDED dumb mode: get current ID of latest window ***
    + $session->{_cid} = wmctrl_latest_window_id() if $level > 2;
    }
    else
    {
    @@ -338,8 +345,11 @@
    if ( $stat{exec} )
    {
    # Calculate a load-adjusted wait time:
    – my $load = `cut -d” ” -f1 /proc/loadavg`+0;
    – my $wait = int(3**$load);
    + # *** ADDED divide by #CPUs ***
    + my $cpus = `lscpu | grep “^CPU(s):” | sed -e ‘s/^.*: *//’`;
    + my $load = (`cut -d” ” -f1 /proc/loadavg`+0)/$cpus;
    + # *** ADDED dumb mode shorter wait ***
    + my $wait = $level 10;

    print “DEBUG: Waiting $wait seconds (load=$load)…\n”
    @@ -363,6 +373,15 @@
    ( values ( %$session ) ) )
    {
    $session->{cid} //= $session->{_cid};
    + # *** ADDED debug of session hash values
    + if ( $opt{debug} > 2 )
    + {
    + print “Session data:”;
    + foreach my $key (keys %$session)
    + {
    + print “key is : $key, value is : $$session{$key}\n”;
    + }
    + }
    next if ! $session->{cid};
    my $current = $windows->{$session->{cid}};

    @@ -459,7 +478,8 @@
    else
    {
    die ( “Usage: session save\n”
    – . ” session restore existing|matching|missing\n” );
    + # *** ADDED dumb mode ***
    + . ” session restore existing|matching|missing|dumb\n” );
    }

    # Get current window list from wmctrl
    @@ -555,6 +575,15 @@
    return ( $windows );
    }

    +# *** ADDED dumb mode latest window ID ***
    +sub wmctrl_latest_window_id
    +{
    + my $window_id = `wmctrl -lpGx | tail -n 1 | sed -e ‘s/ .*//’`;
    + chomp($window_id);
    + return $window_id;
    +}
    +
    +
    # Hack: Check if 2 geometries are roughly equal:
    sub roughly_equal
    {

Leave a Reply

Your email address will not be published. Required fields are marked *