#!/usr/bin/env python3
# -*- python -*-
copyright='''
Copyright © 2014 Mattias Andrée (maandree@member.fsf.org)
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 .
'''
import os
import sys
import socket
import threading
from subprocess import Popen, PIPE
PROGRAM_NAME = 'nightshift'
'''
:str The name of the program
'''
PROGRAM_VERSION = '1.0'
'''
:str The version of the program
'''
## Set process title
def setproctitle(title):
'''
Set process title
@param title:str The title of the process
'''
import ctypes
try:
# Remove path, keep only the file,
# otherwise we get really bad effects, namely
# the name title is truncates by the number
# of slashes in the title. At least that is
# the observed behaviour when using procps-ng.
title = title.split('/')[-1]
# Create strng buffer with title
title = title.encode(sys.getdefaultencoding(), 'replace')
title = ctypes.create_string_buffer(title)
if 'linux' in sys.platform:
# Set process title on Linux
libc = ctypes.cdll.LoadLibrary('libc.so.6')
libc.prctl(15, ctypes.byref(title), 0, 0, 0)
elif 'bsd' in sys.platform:
# Set process title on at least FreeBSD
libc = ctypes.cdll.LoadLibrary('libc.so.7')
libc.setproctitle(ctypes.create_string_buffer(b'-%s'), title)
except:
pass
setproctitle(sys.argv[0])
## Parse options
for arg in sys.argv[1:]:
if arg in ('-V', '--version'):
## Print the version of nightshift and of redshift
print('%s %s' % (PROGRAM_NAME, PROGRAM_VERSION))
Popen(['redshift', '-V'], stdout = sys.stdout).wait()
sys.exit(0)
elif arg in ('-C', '--copyright'):
## Print copyright information
print(copyright[1 : -1])
sys.exit(0)
elif arg in ('-W', '--warranty'):
## Print warranty disclaimer
print(copyright.split('\n\n')[-2])
sys.exit(0)
elif arg in ('-h', '--help'):
## Display help message
text = '''USAGE: nightshift [OPTIONS...] [-- REDSHIFT-OPTIONS...]
Terminal user interface for redshift, a program for setting the colour
temperature of the display according to the time of day.
-h --help Display this help message
-V --version Show program version
-C --copyright Show program copyright information
-W --warranty Show program warrantly disclaimer
-d --daemon Start as daemon
-x --reset Reset mode (remove adjustment from screen)
+x --toggle Temporarily disable or enable adjustments
-s --status Print status information
-c --config FILE Load settings from specified configuration file
-b --brightness DAY:NIGHT Screen brightness to set at daytime/night
-b --brightness BRIGHTNESS Screen brightness to apply
-t --temperature DAY:NIGHT Colour temperature to set at daytime/night
-t --temperature TEMP Colour temperature to apply
-l --location LAT:LON Your current location
-l --location PROVIDER Select provider for automatic location updates
(Type `list' to see available providers)
-m --method METHOD Method to use to set colour temperature
(Type `list' to see available methods)
-r --no-transition Disable temperature transitions
Please report bugs to
'''
text = text.split('\n')[:-1]
indent = min([len(line) - len(line.lstrip()) for line in text if line.rstrip().startswith(' ')])
print('\n'.join([line[indent:] if line.startswith(' ') else line for line in text]))
sys.exit(0)
else:
## Unrecognised option
sys.stderr.write('%s: error: unrecognised option: %s\n' % (sys.argv[0], arg))
sys.exit(1)
socket_path = '%s.%s-%s' % ('/dev/shm/', PROGRAM_NAME, os.environ['USER'])
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
# Connect to the server
sock.connect(socket_path)
except:
# The process need separate sockets, lets close it
# and let both process recreate it
sock.close()
sock = None
if sock is None:
## 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
os.close(sys.stdin.fileno())
# Create server socket
os.unlink(socket_path)
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.bind(socket_path)
sock.listen(5)
# Send signal
with os.fdopen(w_end, 'wb') as file:
file.write(b'\n')
file.flush()
# Close the pipe
os.close(r_end)
## TODO (for testing)
import signal
while True:
signal.pause()
# Close socket
sock.close()
# Close process
sys.exit(0)
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)
print('connecting')
sock.connect(socket_path)
print('connected')
try:
proc = Popen(['redshift', '-v'], stdout = PIPE, stderr = open(os.devnull))
red_brightness, red_period, red_temperature, red_running = 1, 1, 6500, True
red_condition = threading.Condition()
def read_status():
global red_brightness, red_period, red_temperature, red_running
while True:
got = proc.stdout.readline()
if (got is None) or (len(got) == 0):
break
got = got.decode('utf-8', 'replace')[:-1]
(key, value) = got.split(': ')
red_condition.acquire()
try:
if key == 'Brightness':
red_brightness = float(value)
elif key == 'Period':
if value == 'Night':
red_period = 0
elif value == 'Daytime':
red_period = 1
else:
red_period = float(value.split(' ')[1][1 : -1]) / 100
elif key == 'Color temperature':
red_temperature = float(value[:-1])
except:
pass
red_condition.notify()
red_condition.release()
red_running = False
thread = threading.Thread(target = read_status)
thread.setDaemon(True)
thread.start()
while red_running:
red_condition.acquire()
red_condition.wait()
print('%f: %f, %f' % (red_period, red_temperature, red_brightness))
red_condition.release()
finally:
# Close socket
sock.close()