aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <maandree@operamail.com>2014-04-13 02:34:42 +0200
committerMattias Andrée <maandree@operamail.com>2014-04-13 02:34:42 +0200
commitc63631454c76cc2ece653281d038a0285a28f372 (patch)
treeaea9a27b8c736d0bc49638fffb96d34a3c056ef2
parentupdate deps with auto-auto-complete (diff)
downloadnightshift-c63631454c76cc2ece653281d038a0285a28f372.tar.gz
nightshift-c63631454c76cc2ece653281d038a0285a28f372.tar.bz2
nightshift-c63631454c76cc2ece653281d038a0285a28f372.tar.xz
add support in ui to toggle, kill and revive
Signed-off-by: Mattias Andrée <maandree@operamail.com>
-rw-r--r--DEPENDENCIES1
-rw-r--r--src/interface.py40
-rwxr-xr-xsrc/nightshift.py74
3 files changed, 100 insertions, 15 deletions
diff --git a/DEPENDENCIES b/DEPENDENCIES
index e20c8af..23c729c 100644
--- a/DEPENDENCIES
+++ b/DEPENDENCIES
@@ -2,6 +2,7 @@ RUNTIME DEPENDENCIES:
redshift
python3
+ linux (procfs is used to reexec python)
MAKE DEPENDENCIES:
diff --git a/src/interface.py b/src/interface.py
index 3bb096c..295d3b6 100644
--- a/src/interface.py
+++ b/src/interface.py
@@ -26,6 +26,10 @@ import termios
import threading
+ui_state = { 'focus' : 0
+ }
+
+
def user_interface():
'''
Start user interface
@@ -33,7 +37,7 @@ def user_interface():
global red_condition
red_condition = threading.Condition()
ui_winch()
- daemon_thread(ui_status, args = (ui_status_callback,)).start()
+ daemon_thread(ui_status).start()
daemon_thread(ui_refresh).start()
print('\033[?1049h\033[?25l')
@@ -59,8 +63,13 @@ def ui_print():
print('Brightness: %.0f %% (day: %.0f %%, night: %.0f %%)' % tuple(brightness))
print('Dayness: %.0f %%' % (red_period * 100))
print('Enabled' if red_status else 'Disabled')
+ print()
+ print(('[%s]' if ui_state['focus'] == 0 else '<%s>') % ('Disable' if red_status else 'Enable'), end=' ')
+ print(('[%s]' if ui_state['focus'] == 1 else '<%s>') % 'Kill')
else:
print('Not running')
+ print()
+ print('[%s]' % 'Revive')
def ui_read():
@@ -69,6 +78,29 @@ def ui_read():
c = inbuf.read(1)
if c == b'q':
break
+ elif c == b'\t':
+ red_condition.acquire()
+ try:
+ ui_state['focus'] = 1 - ui_state['focus']
+ red_condition.notify()
+ finally:
+ red_condition.release()
+ elif c in b' \n':
+ red_condition.acquire()
+ try:
+ if red_running:
+ if ui_state['focus'] == 0:
+ sock.sendall('toggle\n'.encode('utf-8'))
+ else:
+ sock.sendall('kill\n'.encode('utf-8'))
+ red_condition.notify()
+ else:
+ respawn_daemon()
+ daemon_thread(ui_status).start()
+ sock.sendall('status\n'.encode('utf-8'))
+ sock.sendall('listen\n'.encode('utf-8'))
+ finally:
+ red_condition.release()
def ui_refresh():
@@ -95,7 +127,7 @@ def ui_winch():
signal.signal(signal.SIGWINCH, winch)
-def ui_status(callback):
+def ui_status():
buf = ''
continue_to_run = True
while continue_to_run:
@@ -109,8 +141,8 @@ def ui_status(callback):
break
if continue_to_run:
msg, buf = buf.split('\n\n')[0], '\n\n'.join(buf.split('\n\n')[1:])
- callback(dict([line.split(': ') for line in msg.split('\n')]))
- callback(None)
+ ui_status_callback(dict([line.split(': ') for line in msg.split('\n')]))
+ ui_status_callback(None)
def ui_status_callback(status):
diff --git a/src/nightshift.py b/src/nightshift.py
index fdaafd6..428cb9a 100755
--- a/src/nightshift.py
+++ b/src/nightshift.py
@@ -83,9 +83,9 @@ red_opts = ['-v']
:list<str> Nightshift parsed options passed to redshift
'''
-daemon = False
+daemon = 0
'''
-:bool Whether or not to run as daemon
+:int Whether or not to run as daemon, 2 if revived
'''
kill = 0
@@ -197,7 +197,8 @@ for arg in sys.argv[1:]:
red_arg += arg[1]
elif isinstance(config_file, list):
config_file.append(arg[1])
- elif arg in ('-d', '--daemon'): daemon = True
+ elif arg in ('-d', '--daemon'): daemon = 1
+ elif arg in ('+d', '++daemon'): daemon = 2
elif arg in ('-x', '--reset', '--kill'): kill += 1
elif arg in ('+x', '--toggle'): toggle = True
elif arg in ('-s', '--status'): status = True
@@ -440,13 +441,16 @@ def run_as_daemon(sock):
thread.join()
-def do_daemon():
+def do_daemon(reexec):
'''
- Run actions for --daemon
+ Run actions for --daemon or ++daemon
+
+ @param reexec:bool Wether to perform actions for ++daemon
'''
- if (kill > 0) or toggle or status:
- print('%s: error: -x, +x and -s can be used when running as the daemon' % sys.argv[0])
- sys.exit(1)
+ if not reexec:
+ if (kill > 0) or toggle or status:
+ print('%s: error: -x, +x and -s can be used when running as the daemon' % sys.argv[0])
+ sys.exit(1)
# Create server socket
try:
@@ -457,6 +461,11 @@ def do_daemon():
sock.bind(socket_path)
sock.listen(backlog)
+ # Signal respawner
+ if reexec:
+ print()
+ sys.stdout.close()
+
# Perform daemon logic
run_as_daemon(sock)
@@ -631,12 +640,55 @@ def do_client():
sock.close()
+def respawn_daemon():
+ '''
+ Restart the nightshift daemon
+ '''
+ global sock
+
+ # Close old socket
+ sock.close()
+
+ ## Server is not running
+ # Create pipe for interprocess signal
+ (r_end, w_end) = os.pipe()
+
+ # Duplicate process
+ pid = os.fork()
+
+ if pid == 0:
+ ## Daemon (child)
+ # Close stdin and stdout
+ os.close(sys.stdin.fileno())
+ os.close(sys.stdout.fileno())
+
+ # Replace stdout with the pipe
+ os.dup2(w_end, sys.stdout.fileno())
+ os.close(w_end)
+
+ # Reexecute image
+ os.execl('/proc/self/exe', '/proc/self/exe', *(sys.argv + ['+d']))
+ else:
+ ## Front-end (parent)
+ # Wait for a signal
+ rc = None
+ with os.fdopen(r_end, 'rb') as file:
+ file.read(1)
+
+ # Close the pipe
+ os.close(w_end)
+
+ # Connect to the server
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.connect(socket_path)
+
+
def run():
'''
- Run as either the daemon (if --daemon) or as a client (otherwise)
+ Run as either the daemon (if --daemon or ++daemon) or as a client (otherwise)
'''
- if daemon:
- do_daemon()
+ if daemon > 0:
+ do_daemon(daemon == 2)
else:
do_client()