# -*- python -*- # This example reads an lisp-esque syntax configuration # file to make it easier for non-programmers to use # Blueshift. It will read a file with the same pathname # just with ‘.conf’ appended (‘lisp-esque.conf’ in this # case.) However, if the filename of this file ends with # with ‘rc’, that part will be removed, for example, if # you rename this script to ‘~/.blueshiftrc’ it will read # ‘~/.blueshift.conf’ rather than ‘~/.blueshiftrc.conf’. # 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 Affero 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # Get the name of .conf file conf = '%s.conf' % (config_file[:-2] if config_file.endswith('rc') else config_file) # TODO it should be possible to change file # Read .conf file with open(conf, 'r') as file: conf = file.read() def abort(text, returncode = 1): ''' Abort the program @param text:str Error message @return returncode:int The programs return code ''' printerr('\033[01;31m%s\033[00m' % text) sys.exit(returncode) def parse(code): ''' Parse the .conf file and return it as a tree @param code:str The .conf file content to parse @return :list<↑|str> The root node in the tree ''' stack, stackptr = [], -1 comment, escape, quote, buf = False, False, None, None col, char, line = 0, 0, 1 for c in code: if comment: if c in '\n\r\f': comment = False elif escape: escape = False if c == 'a': buf += '\a' elif c == 'b': buf += chr(8) elif c == 'e': buf += '\033' elif c == 'f': buf += '\f' elif c == 'n': buf += '\n' elif c == 'r': buf += '\r' elif c == 't': buf += '\t' elif c == 'v': buf += chr(11) elif c == '0': buf += '\0' else: buf += c elif c == quote: quote = None elif (c in ';#') and (quote is None): if buf is not None: stack[stackptr].append(buf) buf = None comment = True elif (c == '(') and (quote is None): if buf is not None: stack[stackptr].append(buf) buf = None stackptr += 1 if stackptr == len(stack): stack.append([]) else: stack[stackptr] = [] elif (c == ')') and (quote is None): if buf is not None: stack[stackptr].append(buf) buf = None if stackptr == 0: return stack[0] stackptr -= 1 stack[stackptr].append(stack[stackptr + 1]) elif (c in ' \t\n\r\f') and (quote is None): if buf is not None: stack[stackptr].append(buf) buf = None else: if buf is None: buf = '' if c == '\\': escape = True elif (c in '\'\"') and (quote is None): quote = c else: buf += c if c == '\t': col |= 7 col += 1 char += 1 if c in '\n\r\f': line += 1 col = 0 char = 0 abort('premature end of file') # Parse .conf file in tree conf = parse(conf) # Parse .conf file tree if isinstance(conf[0], str) and not conf[0].startswith(':'): conf = conf[1:] ## For the following functions, the type of args is the type of args ## after it has been evaluated, they may be functions inside that ## break this until the functions have been evaluated. The type for ## args before evaluation is always list<↑|str>. def _monitors(mods, args): ''' Select monitors to use by index @param mods:[] Not used @param args:list Indices of outputs, : or : or 'nil', empty for all ''' pass def _crtc(mods, args): ''' Find monitors by name @param mods:[]|[str] Optionally the number of monitors to list @param args:list Names of outputs @return :list : encoding of found monitors ''' pass def _size(mods, args): ''' Find monitors by physical size @param mods:[]|[str] Optionally the number of monitors to list @param args:[str, str]|list<[str, str]> Width–height-pairs, in millimetres @return :list : encoding of found monitors ''' pass def _coordinates(mods, args): ''' Specify geographical location by coordinates @param mods:[]|[str] Continuously updates if 'cont' is included @param args:[str, str] The latitude and longitude (northwards and eastwards in degrees) ''' pass def _parse(mods, args): ''' Parse a string into a tree @param mods:[] Not used @param args:[str] The string @return :list<↑|str> The tree ''' pass def _read(mods, args): ''' Read an external file @param mods:[] Not used @param args:[str] The file @return :[str] The content of the file ''' pass def _spawn(mods, args): ''' Run an external command @param mods:[] Not used @param args:list The command @return :[str] The output of the command ''' pass def _include(mods, args): ''' Include external files @param mods:[] Not used @param args:list The files @return :list<↑|str> The content of the file as a tree concatenated ''' pass def _source(mods, args): ''' Load external Python files @param mods:[] Not used @param args:list The files ''' pass def _eval(mods, args): ''' Evaluate strings of Python code @param mods:[] Not used @param args:list<↑|str> The strings @return :list<↑|str> The evaluated valus ''' pass def _timepoints(mods, args): ''' Select time points when different settings are applied, continuous transition betweem them will be used. This are not used by default, be can be enabled in the next section. @param mods:[] Not used @param args:list The time points in 24-hour colour formated as H, H:M or H:M:S, leading zeroes are allowed ''' pass def _points(mods, args): ''' Select method for calculating the time the different settings are (fully) applied @param mods:[] Not used @param args:list Either 'solar' optionally followed by solar elevation in degrees, 'time' or 'constant' ''' pass def _dayness(mods, args): ''' Configure so that adjustments only need day and night settings, time settings application points are reduced to different degrees of these settings @param mods:[] Not used @param args:list Mapping from points (implied by index) to dayness degrees ''' pass def _method(mods, args): ''' Select colour curve applying method @param mods:[] Not used @param args:list The methods to use: 'randr', 'vidmode', 'print' ''' pass def _transfrom(mods, args): ''' Let Blueshift transition from the currently applied settings when it starts @param mods:[] Not used @param args:list Method for (optionally) each monitor: 'randr', 'vidmode' or 'nil' ''' pass def _negative(mods, args): pass def _invert(mods, args): pass def _temperature(mods, args): pass def _compose(mods, args): pass def _current(mods, args): pass def _brightness(mods, args): pass def _contrast(mods, args): pass def _resolution(mods, args): pass def _gamma(mods, args): pass def _pgamma(mods, args): pass def _clip(mods, args): pass def _sigmoid(mods, args): pass def _limits(mods, args): pass def _linearise(mods, args): pass def _manipulate(mods, args): pass def _standardise(mods, args): pass