Page MenuHomePhabricator

No OneTemporary

diff --git a/monitorDaemon.py b/monitorDaemon.py
new file mode 100755
--- /dev/null
+++ b/monitorDaemon.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+import subprocess
+import os
+import hashlib
+import binascii
+import re
+import sys
+import configparser
+from Xlib import X, display, Xutil
+from Xlib.ext import randr
+
+class xrandrInterface:
+ def __init__(self):
+ self.display = display.Display()
+ self.screen = self.display.screen()
+ self.window = self.screen.root.create_window(50, 50, 300, 200, 2,
+ self.screen.root_depth,
+ event_mask = (X.ExposureMask |
+ X.StructureNotifyMask |
+ X.ButtonPressMask |
+ X.ButtonReleaseMask |
+ X.Button1MotionMask)
+ )
+ #self.window.xrandr_select_input(
+ # randr.RRScreenChangeNotifyMask
+ # | randr.RRCrtcChangeNotifyMask
+ # | randr.RROutputChangeNotifyMask
+ # | randr.RROutputPropertyNotifyMask
+ # )
+ self.outputStatus = {}
+ self.crtcStatus = {}
+ self.getScreenInformation()
+
+ def getScreenInformation(self):
+ resources = self.window.xrandr_get_screen_resources()._data
+ outputs = resources['outputs']
+ self.outputStatus = {}
+ self.crtcStatus = {}
+ for crtc in resources['crtcs']:
+ crtcinfo = self.display.xrandr_get_crtc_info(crtc, resources['config_timestamp'])._data
+ self.crtcStatus[crtc] = {}
+ #self.crtcStatus[crtc]['width'] = crtcinfo['width']
+ #self.crtcStatus[crtc]['height'] = crtcinfo['height']
+ self.crtcStatus[crtc]['x'] = crtcinfo['x']
+ self.crtcStatus[crtc]['y'] = crtcinfo['y']
+ self.crtcStatus[crtc]['mode'] = crtcinfo['mode']
+ self.crtcStatus[crtc]['outputs'] = crtcinfo['outputs']
+ self.crtcStatus[crtc]['possible_outputs'] = crtcinfo['possible_outputs']
+
+ modedata = self.parseModes(resources['mode_names'], resources['modes'])
+ #print(self.crtcStatus)
+ for output in outputs:
+ info = self.display.xrandr_get_output_info(output, resources['config_timestamp'])._data
+ #print(info)
+ if info['connection'] != 0:
+ continue
+ edid = self.get_edid(output)
+
+ name = info['name']
+ crtc = info['crtc']
+ if crtc == 0:
+ continue
+ else:
+ mode = self.crtcStatus[crtc]['mode']
+ modename = modedata[mode]['name']
+ posx = self.crtcStatus[crtc]['x']
+ posy = self.crtcStatus[crtc]['y']
+
+ self.outputStatus[name] = {}
+ self.outputStatus[name]['edid'] = edid
+ self.outputStatus[name]['crtc'] = crtc
+ self.outputStatus[name]['modename'] = modename
+ self.outputStatus[name]['posx'] = posx
+ self.outputStatus[name]['posy'] = posy
+ #print(self.display.xrandr_list_output_properties(output)._data)
+ """
+ print(self.outputStatus)
+ op = self.getOutputForOutputname('eDP1')
+ print(op)
+ md = self.findModeByName(op, '1920x1080')
+ print(md)
+ cr = self.getUsableCrtcForOutput(op)
+ if not cr:
+ #print('Disabling CRTC')
+ cr = self.getCurrentCrtcForOutput(op)
+ self.disableCrtc(cr)
+ else:
+ #print('Enabling CRTC')
+ print(cr)
+ self.configureCrtcForOutputAndMode(cr, op, md, 0, 0)
+ """
+
+ def applyConfiguration(self, config):
+ # Get list of all outputs
+ # Disable all outputs not in the config
+ # Check if an output already has a CRTC
+ # If yes, reconfigure the current CRTC
+ # If not, find a new CRTC and configure it accordingly
+ print('FIXME')
+ pass
+
+ def getScreenConfiguration(self):
+ return self.outputStatus
+
+ def disableCrtc(self, crtc):
+ resources = self.window.xrandr_get_screen_resources()._data
+ self.display.xrandr_set_crtc_config(65, resources['config_timestamp'], 0, 0, 0, randr.Rotate_0, [])
+
+ def getCurrentCrtcForOutput(self, output):
+ resources = self.window.xrandr_get_screen_resources()._data
+ info = self.display.xrandr_get_output_info(output, resources['config_timestamp'])._data
+ return info['crtc']
+
+ def configureCrtcForOutputAndMode(self, crtc, output, mode, posx, posy):
+ resources = self.window.xrandr_get_screen_resources()._data
+ self.display.xrandr_set_crtc_config(crtc, resources['config_timestamp'], posx, posy, mode, randr.Rotate_0, [output])
+
+ def getOutputForOutputname(self, name):
+ resources = self.window.xrandr_get_screen_resources()._data
+ for output in resources['outputs']:
+ info = self.display.xrandr_get_output_info(output, resources['config_timestamp'])._data
+ if info['name'] == name:
+ return output
+ return None
+
+ def getUsableCrtcForOutput(self, output):
+ resources = self.window.xrandr_get_screen_resources()._data
+ for crtc in resources['crtcs']:
+ crtcinfo = self.display.xrandr_get_crtc_info(crtc, resources['config_timestamp'])._data
+ if len(crtcinfo['outputs']) == 0:
+ if output in crtcinfo['possible_outputs']:
+ return crtc
+ return None
+
+ def findModeByName(self, output, modename):
+ resources = self.window.xrandr_get_screen_resources()._data
+ modedata = self.parseModes(resources['mode_names'], resources['modes'])
+ info = self.display.xrandr_get_output_info(output, resources['config_timestamp'])._data
+ modes = info['modes']
+ for mode in modes:
+ if modedata[mode]['name'] == modename:
+ return mode
+ return None
+
+ def parseModes(self, mode_names, modes):
+ lastIdx = 0
+ modedatas = dict()
+ for mode in modes:
+ modedata = dict(mode._data)
+ modedata['name'] = mode_names[lastIdx:lastIdx + modedata['name_length']]
+ modedatas[modedata['id']] = modedata
+ lastIdx += modedata['name_length']
+ return modedatas
+
+ # query the edid module for output_nr
+ def get_edid(self, output_nr):
+ PROPERTY_EDID = self.display.intern_atom("EDID", 0)
+ INT_TYPE = 19
+
+ props = self.display.xrandr_list_output_properties(output_nr)
+ if PROPERTY_EDID in props.atoms:
+ try:
+ rawedid = self.display.xrandr_get_output_property(output_nr, PROPERTY_EDID, INT_TYPE, 0, 400)
+ except:
+ print("Error loading EDID data of output ", output_nr)
+ return None
+ edidstream = rawedid._data['value']
+ hx = bytearray()
+ hx.extend(edidstream)
+ hs = hashlib.sha1(hx).hexdigest()
+
+ return hs
+ else:
+ return None
+
+class ConfigManager:
+ def __init__(self, cfgfile):
+ self.cfgfile = cfgfile
+ if not os.path.exists(cfgfile):
+ self.recreateCfg()
+ self.dic = {}
+ self.cfg2dic()
+
+ def getDic(self):
+ return self.dic
+
+ def recreateCfg(self):
+ print('Creating new config file: ' + self.cfgfile)
+ config = configparser.ConfigParser()
+ config.add_section('General')
+ config.set('General', 'numEntries', '0')
+ with open(self.cfgfile, 'w') as configfile:
+ config.write(configfile)
+
+ def cfg2dic(self):
+ dic = {}
+ config = configparser.ConfigParser()
+ config.read(self.cfgfile)
+
+ for section in config.sections():
+ dic[section] = {}
+ for key in config[section]:
+ dic[section][key] = config.get(section, key)
+
+ self.dic = dic
+ return dic
+
+ def dic2cfg(self):
+ config = configparser.ConfigParser()
+ for section in self.dic:
+ config.add_section(section)
+ for key in self.dic[section]:
+ config.set(section, key, self.dic[section][key])
+ with open(self.cfgfile, 'w') as configfile:
+ config.write(configfile)
+
+ def getConfig(self, screenConfiguration):
+ ne = int(self.dic['General']['numentries'])
+ for ii in range(0, ne):
+ name = 'Config' + str(ii+1)
+ if int(self.dic[name]['edidcount']) != len(screenConfiguration):
+ continue
+ found = True
+ for outputName in screenConfiguration:
+ lFound = False
+ for jj in range(0, len(screenConfiguration)):
+ if self.dic[name]['edid' + str(jj+1)] == screenConfiguration[outputName]['edid']:
+ lFound = True
+ if lFound and found:
+ found = True
+ else:
+ found = False
+ if found:
+ for jj in range(0, len(screenConfiguration)):
+ return self.dic[name]
+ return None
+
+ def saveConfiguration(self, screenConfiguration):
+ ne = int(self.dic['General']['numentries'])
+ ne += 1
+ self.dic['General']['numentries'] = str(ne)
+ name = 'Config' + str(ne)
+ self.dic[name] = {}
+ self.dic[name]['edidcount'] = str(len(screenConfiguration))
+ cnt = 1
+ for outputName in screenConfiguration.keys():
+ self.dic[name]['edid'+str(cnt)] = screenConfiguration[outputName]['edid']
+ self.dic[name]['output'+str(cnt)] = outputName
+ self.dic[name]['mode'+str(cnt)] = screenConfiguration[outputName]['modename']
+ self.dic[name]['posx'+str(cnt)] = str(screenConfiguration[outputName]['posx'])
+ self.dic[name]['posy'+str(cnt)] = str(screenConfiguration[outputName]['posy'])
+ cnt += 1
+ self.dic2cfg(cfgfile)
+ return True
+
+ def loadAndApplyConfig(self, dic, edids):
+ cfg = getConfig(dic, edids)
+ print(cfg)
+ for edid in cfg:
+ #FIXME: This does not work with negative numbers!
+ if edid[2] == '0':
+ subprocess.call(['xrandr', '--output', edid[1], '--off'])
+ continue
+ modepos = re.findall(r'\d+', edid[2])
+ mode = modepos[0] + 'x' + modepos[1]
+ pos = modepos[2] + 'x' + modepos[3]
+ print(pos)
+ subprocess.call(['xrandr', '--output', edid[1], '--mode', mode, '--pos', pos])
+ return True
+
+
+
+
+settingsdir = os.path.join(os.environ['HOME'], '.config')
+cfgfile = os.path.join(settingsdir, 'monitorDaemon.ini')
+cm = ConfigManager(cfgfile)
+
+cfg = cm.getDic()
+#print(cfg)
+
+xri = xrandrInterface()
+screenConfig = xri.getScreenConfiguration()
+#print(screenConfig)
+
+storedConfig = cm.getConfig(screenConfig)
+
+if storedConfig:
+ print('LoadAndApply')
+ xri.applyConfiguration(screenConfig)
+else:
+ print('SaveConfig')
+ cm.saveConfiguration(screenConfig)
+
+sys.exit(0)
+

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 24, 5:52 PM (1 d, 2 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
531910
Default Alt Text
(11 KB)

Event Timeline