2

I recently upgraded from 14.04 Trusty to 16.04 Xenial. Prior to the upgrade, I used the caffeine-plus indicator icon to tell the my laptop when it could sleep. The mode I normally used was to have Caffeine enabled so the computer would only sleep/suspend when the lid was closed. However, there were occasions when I wanted to be able to let the idle timer have its intended effect.

Since the upgrade, Caffeine doesn't appear to do anything anymore. I can leave my computer with a long-running process, deliberately keeping the lid open, only to come back to find it sleeping and the process unfinished.

How can I get the previous behavior back? Note that I'm looking for a toggle, not a permanent change. As a toggle, it should have a visual indication as to whether it's enabled or not. An indicator icon would be great.

Note

The searching I did prior to asking this question turned up either: a) old (outdated) posts about how to use Caffeine, or b) permanently disabling sleep to work around various hardware bugs. My question is simply about restoring the functionality I had in 14.04, a topic I didn't find addressed.

1 Answers1

2

Edit

After a bit of work, I wrote up a more complete and easier-to-use solution than is found below. You can download the program on GitHub. You'll also need to install the dependencies:

sudo apt install xdotool xprintidle

Original Answer

After Jacob Vlijm pointed me toward a partial solution, I combined a modified version of his script with bits of Caffeine and some of my own code and came up with a replacement for Caffeine.

Instructions

  1. Make sure the necessary packages are installed. Note: caffeine-plus is used only for icons. If you don't care about proper icons, you don't need it.

    sudo apt install caffeine-plus xprintidle xdotool
    
  2. Save the below script somewhere and make it executable.

    #!/usr/bin/python3
    # coding=utf-8
    #
    # Copyright © 2016 Scott Severance
    # Code mixed in from Caffeine Plus and Jacob Vlijm
    #
    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    import argparse
    import os
    import signal
    import time
    from subprocess import Popen, check_output, call
    import gi
    gi.require_version('Gtk', '3.0')
    gi.require_version('AppIndicator3', '0.1')
    from gi.repository import GObject, Gtk, AppIndicator3
    
    class SleepInhibit(GObject.GObject):
    
        def __init__(self):
            GObject.GObject.__init__(self)
            self.inhibited = False
            self._add_indicator()
            self.inhibit_proc = None
    
        def _add_indicator(self):
            self.AppInd = AppIndicator3.Indicator.new("sleep-inhibit-off",
                                                      "sleep-inhibit",
                                                      AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
            self.AppInd.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
            self.AppInd.set_icon ("caffeine-cup-empty")
    
            self._build_indicator_menu(self.AppInd)
    
        def _build_indicator_menu(self, indicator):
            menu = Gtk.Menu()
    
            menu_item = Gtk.MenuItem("Toggle Sleep Inhibition")
            menu.append(menu_item)
            menu_item.connect("activate", self.on_toggle)
            menu_item.show()
    
            menu_item = Gtk.MenuItem("Quit")
            menu.append(menu_item)
            menu_item.connect("activate", self.on_quit)
            menu_item.show()
    
            indicator.set_menu(menu)
    
        def on_toggle(self, menuitem):
            self.inhibited = not self.inhibited
            self.flip_switch()
    
        def on_quit(self, menuitem):
            if self.inhibit_proc:
                self.kill_inhibit_proc()
            exit(0)
    
        def _set_icon_disabled(self):
            self.AppInd.set_icon('caffeine-cup-empty')
    
        def _set_icon_enabled(self):
            self.AppInd.set_icon('caffeine-cup-full')
    
        def flip_switch(self):
            if self.inhibited:
                self._set_icon_enabled()
                self.inhibit_proc = Popen([__file__, "--mode=inhibit-process"])
            else:
                self.kill_inhibit_proc()
                self._set_icon_disabled()
    
        def kill_inhibit_proc(self):
            self.inhibit_proc.terminate()
            self.inhibit_proc.wait()
            self.inhibit_proc = None
    
    def inhibit():
        seconds = 120 # number of seconds to start preventing blank screen / suspend
        while True:
            try:
                curr_idle = check_output(["xprintidle"]).decode("utf-8").strip()
                if int(curr_idle) > seconds*1000:
                    call(["xdotool", "key", "Control_L"])
                time.sleep(10)
            except FileNotFoundError:
                exit('ERROR: This program depends on xprintidle and xdotool being installed.')
            except KeyboardInterrupt:
                exit(0)
    
    def parse_args():
        parser = argparse.ArgumentParser(description='''Indicator to prevent
            computer from sleeping. It depends on the commands xprintidle and
            xdotool being properly installed on your system. If they aren't
            installed already, please install them. Also, the icons are taken from
            caffeine-plus, so if it isn't installed, you will probably see a broken
            icon.''')
        mode = '''The mode can be either indicator (default) or inhibit-process. If
            mode is indicator, then an indicator icon is created. inhibit-process is
            to be called by the indicator. When sleep is inhibited, it runs,
            preventing sleep.'''
        parser.add_argument('--mode', type=str, default='indicator', help=mode)
        return parser.parse_args()
    
    def main():
        args = parse_args()
        if args.mode == 'indicator':
            signal.signal(signal.SIGINT, signal.SIG_DFL)
            GObject.threads_init()
            SleepInhibit()
            Gtk.main()
        elif args.mode == 'inhibit-process':
            inhibit()
        else:
            exit('ERROR: Invalid value for --mode!')
    
    if __name__ == '__main__':
        main()
    
  3. Run the script.