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.

  • Hi Scott, please let me kn ow if it works for you. – Jacob Vlijm May 06 '16 at 05:44
  • That might be a partial solution, but having a script that will be running 90% of the time send automated keypresses without regard to whatever currently has the keyboard focus seems like something that would cause really confusing problems. – Scott Severance May 06 '16 at 06:05
  • Note that it will only press the key if the computer is idle, and: The keypress itself is meaningless and has no effect on playing video in full screen, which goes for whatever you run, and: is only pressed once per two minutes These things make ik impossible that it clashes with anything. Furthermore, it is toggled with a shortcut. – Jacob Vlijm May 06 '16 at 06:07
  • Actually, it's not possible to send a keypress which is guaranteed to have no effect on any arbitrary application. Your script sends ^L. If whatever is listening for keypresses ignores that combination, it does nothing. But if some app recognizes ^L, then whatever action is specified will be taken. However, changing the command to xdotool key Control will probably solve that; I don't know of anything that does anything significance in response to a bare Ctrl keypress. – Scott Severance May 06 '16 at 06:17
  • Control_L = left Ctrl key, not L capital (please check with xev), and is meaningless when pressed alone. Since it only presses when idle, it is pressed alone. – Jacob Vlijm May 06 '16 at 06:19
  • Left control will work, then (though I do wonder how one would specify a control sequence such as ^L. Anyway, while your answer is definitely helpful, this question isn't a duplicate, as my edit hopefully clarified. A toggle is pretty much useless unless there's a way to determine its state (beyond issuing a ps command. I can, of course, write an indicator into the script, and I may do so, but an answer to this question would include something that could be just dropped in. – Scott Severance May 06 '16 at 06:24
  • Well, the answer might not be exactly what you had in mind (after your edit), the question still is, which defines the dupe. – Jacob Vlijm May 06 '16 at 06:36
  • I wrote a script that works for resetting the timer with the screensaver / sleep / suspend when you have an application / video in full screen mode: http://askubuntu.com/a/778817/231142 It might help. – Terrance Jun 11 '16 at 13:42
  • 1
    Nominating to reopen: This is not a duplicate of the listed question, because a solution which only prevents hibernation when watching videos is not a solution. An acceptable solution must prevent sleep at any arbitrary time selected by the user. – Scott Severance Jun 19 '16 at 05:47

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.