Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1822382
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Size
11 KB
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
rMD monitorDaemon
Event Timeline
Log In to Comment