EDIT
In the original answer, further below, the countdown window appeared after an arbitrary idle time. Re- reading your question, you might want it permanently. The permanent version is below (which is simpler), the original answer further down.
1a. Version, permanently showing countdown time
The solution is a background script, showing a semi- transparent countdown window. The window behaves like a notification: it is always visible on top, but (of course) you can work in your other windows as usual:
![enter image description here](https://i.stack.imgur.com/aRUEx.png)
The initial time is the idle- time before suspend should be activated. The time is reset on mouse- keyboard events.
As the image shows, the script comes with different pre- set color options (see further below).
How to setup
The script needs xprintidle
:
sudo apt-get install xprintidle
- Copy the script below into an empty file, save it as
countdown.py
Run it with the idle time as argument:
python3 /path/to/countdown.py <idle_time>
e.g.
python3 /path/to/countdown.py 300
to enter suspend after 5 minutes.
If all works fine, add it to startup applications: Dash > Startup Applications > Add. Add the command:
/path/to/runner.py <idle_time>
The script
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, Pango
from threading import Thread
import subprocess
import time
import signal
import sys
import os
# --- set the color (index) below (1 is the first)
color = 1
# ---
textcolors = ["grey", "orange", "green", "blue", "white"]
# --- don't change anything below
txtcolor = textcolors[color-1]
countdown = int(sys.argv[1])
susp = os.path.dirname(os.path.realpath(__file__))+"/susp.sh"
class CountDown(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
maingrid = Gtk.Grid()
self.add(maingrid)
maingrid.set_border_width(40)
# set initial text for the spash window
self.label = Gtk.Label(convert_seconds(countdown))
self.label.modify_font(Pango.FontDescription('Ubuntu 22'))
self.label.set_width_chars(10)
maingrid.attach(self.label, 0, 0, 1, 1)
self.update = Thread(target=self.start_countdown, args=[countdown])
# daemonize the thread
self.update.setDaemon(True)
self.update.start()
def start_countdown(self, countdown):
idle1 = idletime()
t = countdown
while True:
time.sleep(1)
idle2 = idletime()
if idle2 < idle1:
t = countdown
else:
t -= 1
if t <= 0:
subprocess.Popen(["systemctl", "suspend"])
GObject.idle_add(self.label.set_text, convert_seconds(t),
priority=GObject.PRIORITY_DEFAULT)
idle1 = idle2
def stop(self):
Gtk.main_quit()
def get_screen():
scr = [s.split("x") for s in subprocess.check_output([
"xrandr"]).decode("utf-8").split() if "+0+0" in s][0]
return int(scr[0]) - 300
def convert_seconds(sec):
timedisplay = [
str(int(sec/3600)),
str(int((sec % 3600)/60)),
str(int(sec % 60)),
]
for i, n in enumerate(timedisplay):
if len(n) == 1:
timedisplay[i] = "0"+n
return ":".join(timedisplay)
def idletime():
return int(subprocess.check_output(
"xprintidle"
).decode("utf-8").strip())/1000
def splashwindow():
window = CountDown()
window.set_decorated(False)
window.set_resizable(False)
window.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0,0,0,1))
window.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse(txtcolor))
window.set_opacity(0.6)
window.move(get_screen(), 80)
window.set_keep_above(True)
window.show_all()
Gtk.main()
GObject.threads_init()
splashwindow()
Note
The text color can be changed, as explained at the very bottom of the second version of the answer.
1b. As requested in a comment: luxury version of the same script: text color changes to yellow if half the time passed, to red 30 seconds before suspend.
>
>
Use it exactly as 1a
.
The script
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, Pango
from threading import Thread
import subprocess
import time
import signal
import sys
import os
# --- set the color (index) below (1 is the first)
color = 1
# ---
textcolors = ["grey", "orange", "green", "blue", "white", "yellow", "red"]
# --- don't change anything below
txtcolor = textcolors[color-1]
al_cl1 = textcolors[5]; al_cl2 = textcolors[6]
countdown = int(sys.argv[1])
alarm1 = int(countdown/2)
alarm2 = 30
class CountDown(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
maingrid = Gtk.Grid()
self.add(maingrid)
maingrid.set_border_width(40)
# set initial text for the spash window
self.label = Gtk.Label(convert_seconds(countdown))
self.label.modify_font(Pango.FontDescription('Ubuntu 22'))
self.label.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse(txtcolor))
self.label.set_width_chars(10)
maingrid.attach(self.label, 0, 0, 1, 1)
self.update = Thread(target=self.start_countdown, args=[countdown])
# daemonize the thread
self.update.setDaemon(True)
self.update.start()
def mod_color(self, color):
self.label.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse(color))
def start_countdown(self, countdown):
idle1 = idletime()
t1 = countdown
t2 = countdown
while True:
time.sleep(1)
idle2 = idletime()
if idle2 < idle1:
t2 = countdown
if t1 <= alarm1:
# print("change textcolor default")
GObject.idle_add(self.mod_color, txtcolor,
priority=GObject.PRIORITY_DEFAULT)
else:
t2 -= 1
if all([t2 <= alarm2, t1 > alarm2]):
# print("change textcolor red")
GObject.idle_add(self.mod_color, al_cl2,
priority=GObject.PRIORITY_DEFAULT)
elif all([t2 <= alarm1, t1 > alarm1]):
# print("change textcolor yellow")
GObject.idle_add(self.mod_color, al_cl1,
priority=GObject.PRIORITY_DEFAULT)
if t2 <= 0:
subprocess.Popen(["systemctl", "suspend"])
GObject.idle_add(self.label.set_text, convert_seconds(t2),
priority=GObject.PRIORITY_DEFAULT)
idle1 = idle2
t1 = t2
def stop(self):
Gtk.main_quit()
def get_screen():
scr = [s.split("x") for s in subprocess.check_output([
"xrandr"]).decode("utf-8").split() if "+0+0" in s][0]
return int(scr[0]) - 300
def convert_seconds(sec):
timedisplay = [
str(int(sec/3600)),
str(int((sec % 3600)/60)),
str(int(sec % 60)),
]
for i, n in enumerate(timedisplay):
if len(n) == 1:
timedisplay[i] = "0"+n
return ":".join(timedisplay)
def idletime():
return int(subprocess.check_output(
"xprintidle"
).decode("utf-8").strip())/1000
def splashwindow():
window = CountDown()
window.set_decorated(False)
window.set_resizable(False)
window.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0,0,0,1))
window.set_opacity(0.6)
window.move(get_screen(), 80)
window.set_keep_above(True)
window.show_all()
Gtk.main()
GObject.threads_init()
splashwindow()
2. Original answer: version, showing countdown time after x idle time
The setup below will show a countdown (during an arbitrary time length) to the next suspend:
![enter image description here](https://i.stack.imgur.com/wbRmu.png)
The window is always on top of all other windows, exactly like the notification bubbles.
The setup replaces the "normal" suspend settings, which means you need to disable suspend from system settings.
About the solution
The command to suspend in the script is:
systemctl suspend
which doesn't require sudo
. The consequence is that you will need at least 15.04
to use this solution.
The script was written and tested on Ubuntu
(Unity) 15.10
, but there is no specific code in it that should be Unity specific. I assume it works fine on all default Ubuntu versions > 15.04
How it works
To setup (detailed version further below), simply copy the three scripts involved into one and the same directory, exactly named as indicated. To run, simply run the main script (running the time- check).
- If the idle time exceeds a certain limit, the countdown window is called.
- If during countdown the computer becomes un- idle (mouse- or keyboard event) the window is closed (its pid is killed).
- If the timer has ended its count down, it runs a simple script to suspend
How to setup
The script needs xprintidle
:
sudo apt-get install xprintidle
Copy the three scripts below into separate empty files, save the in one and the same directory, exactly named as indicated:
A. save (exactly) as win.py
:
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GObject, Pango
from threading import Thread
import subprocess
import time
import signal
import sys
import os
# --- set the color (index) below (1 is the first)
color = 1
# ---
textcolors = ["grey", "orange", "green", "blue", "white"]
# --- don't change anything below
txtcolor = textcolors[color-1]
countdown = int(sys.argv[1])
susp = os.path.dirname(os.path.realpath(__file__))+"/susp.sh"
class CountDown(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
maingrid = Gtk.Grid()
self.add(maingrid)
maingrid.set_border_width(40)
# set initial text for the spash window
self.label = Gtk.Label(convert_seconds(countdown))
self.label.modify_font(Pango.FontDescription('Ubuntu 22'))
self.label.set_width_chars(10)
maingrid.attach(self.label, 0, 0, 1, 1)
self.update = Thread(target=self.start_countdown, args=[countdown])
# daemonize the thread
self.update.setDaemon(True)
self.update.start()
def start_countdown(self, countdown):
t = countdown
while t > 0:
time.sleep(1)
t -= 1
GObject.idle_add(self.label.set_text, convert_seconds(t),
priority=GObject.PRIORITY_DEFAULT)
print(t)
subprocess.Popen(["/bin/bash", susp])
self.stop()
def stop(self):
Gtk.main_quit()
def get_screen():
scr = [s.split("x") for s in subprocess.check_output([
"xrandr"]).decode("utf-8").split() if "+0+0" in s][0]
return int(scr[0]) - 300
def convert_seconds(sec):
timedisplay = [
str(int(sec/3600)),
str(int((sec % 3600)/60)),
str(int(sec % 60)),
]
for i, n in enumerate(timedisplay):
if len(n) == 1:
timedisplay[i] = "0"+n
return ":".join(timedisplay)
def splashwindow():
window = CountDown()
window.set_decorated(False)
window.set_resizable(False)
window.override_background_color(Gtk.StateType.NORMAL, Gdk.RGBA(0,0,0,1))
window.modify_fg(Gtk.StateFlags.NORMAL, Gdk.color_parse(txtcolor))
window.set_opacity(0.6)
window.move(get_screen(), 80)
window.set_keep_above(True)
window.show_all()
Gtk.main()
GObject.threads_init()
splashwindow()
B. Save exactly as runner.py
:
#!/usr/bin/env python3
import subprocess
import time
import os
import sys
window_mod = os.path.dirname(os.path.realpath(__file__))+"/win.py"
suspend = int(sys.argv[1])
countdown = int(sys.argv[2])
w = False
while True:
time.sleep(1)
idletime = int(subprocess.check_output(
"xprintidle"
).decode("utf-8").strip())/1000
if all([idletime > suspend-countdown, w == False]):
subprocess.Popen(["python3", window_mod, str(countdown)])
w = True
elif all([idletime < suspend-countdown, w == True]):
try:
procdata = subprocess.check_output([
"pgrep", "-f", window_mod
]).decode("utf-8").strip()
procs = procdata.splitlines()
except subprocess.CalledProcessError:
pass
else:
for p in procs:
subprocess.Popen(["kill", p])
w = False
C. Save (exactly) as susp.sh
:
#!/bin/bash
sleep 3
systemctl suspend
Make all three scripts executable, and again, make sure they are in one and the same directory.
You're practically done.
- Disable your "usual" suspend settings
Test- run the script the suspend time (idle time before suspend should be applied), and the count down time (in seconds) as arguments, e.g.:
/path/to/runner.py 600 300
to set idle time to 10 minutes, counter starts 5 minutes before suspend.
If all works fine, add it to startup applications: Dash > Startup Applications > Add. Add the command:
/path/to/runner.py <idle_time> <countdown_time>
Notes
In the head section of the win.py
, you can set different colors for the displayed text:
# --- set the color (index) below (1 is the first)
color = 1
# ---
textcolors = ["grey", "orange", "green", "blue", "white"]
Playing with the values in the lines:
maingrid.set_border_width(10)
and
return int(scr[0]) - 200
(from the function get_screen
, where 200 is the distance of the left side of the window to the right side of the screen), and
window.move(get_screen(), 35)
(where 35 is the distance between the window and the top of the screen), you can easily change the geometry of the window, e.g.:
![enter image description here](https://i.stack.imgur.com/f6uQn.png)
Have fun :)
Best Answer
Moving the mouse to a defined (absolute) position
..is simply done by the command (e.g.):
To move the mouse to the centre of the screen however is a relative command, for which we need to read the screen's information and do some calculations. This is done in the two small scripts below.
Straightforward version (move cursor to the center of the left screen)
To move the mouse to the center of the (leftmost) screen, use the script below:
install xdotool
Copy the script into an empty file, save it as
center_screen.py
Run it:
Extended version (optional arguments x, y)
If arbitrary coordinates are optional, use:
This version will move the cursor to the center of the screen, when run without arguments, or to an arbitrary position, when run with arguments, e.g.:
Explanation
In the output of the command:
xrandr
, all we need to find is the string like:...which contains the data on the leftmost screen (
+0+
). both figures in1680x1050
are then to be divided by two, to be used in:The line:
is then to decide wether the given arguments should be used or the calculated ones.