Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1720820
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Size
29 KB
Subscribers
None
View Options
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,35 @@
+pkgname=monitorDaemon-hg
+pkgver=r5.4735eea7ea00
+pkgrel=1
+pkgdesc="A monitor daemon to remember screen positions"
+arch=(any)
+depends=('python2' 'python2-pyqt4')
+makedepends=('mercurial')
+source=('monitorDaemon-hg::hg+https://code.rnb-consulting.at/source/md/')
+url="http://www.aboehler.at/"
+provides=('monitorDaemon')
+conflicts=('monitorDaemon')
+license=("GPL")
+sha256sums=(SKIP)
+
+pkgver() {
+ cd "$pkgname"
+ printf "r%s.%s" "$(hg identify -n)" "$(hg identify -i)"
+}
+
+build() {
+ cd ${pkgname}
+ msg "Nothing to be done"
+}
+
+package() {
+ mkdir -p "${pkgdir}"/usr/bin
+ mkdir -p "${pkgdir}"/usr/share/applications
+ mkdir -p "${pkgdir}"/usr/share/icons
+ cd ${pkgname}
+ install -m755 monitorDaemon.py "${pkgdir}"/usr/bin/monitorDaemon
+ install -m644 monitorDaemon.png "${pkgdir}"/usr/share/icons
+ install -m644 monitorDaemon.desktop "${pkgdir}"/usr/share/applications
+}
+
+# vim:set ts=2 sw=2 et:
diff --git a/monitorDaemon.desktop b/monitorDaemon.desktop
new file mode 100644
--- /dev/null
+++ b/monitorDaemon.desktop
@@ -0,0 +1,20 @@
+[Desktop Entry]
+Comment[en_US]=
+Comment=
+Exec=/usr/bin/monitorDaemon
+GenericName[en_US]=Monitor Daemon
+GenericName=Monitor Daemon
+Icon=monitorDaemon.png
+MimeType=
+Name[en_US]=monitorDaemon
+Name=monitorDaemon
+NoDisplay=false
+Path=
+StartupNotify=true
+Terminal=false
+Type=Application
+X-DBUS-ServiceName=
+X-DBUS-StartupType=
+X-KDE-SubstituteUID=false
+X-KDE-Username=
+Categories=Utility;Python;
diff --git a/monitorDaemon.png b/monitorDaemon.png
new file mode 100644
index 0000000000000000000000000000000000000000..c25bf825a5222af3e3321d8e10c9b311b1c56269
GIT binary patch
literal 8128
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
a0QdiN3yh3^LMI~t0000<MNUMnLSTY83mZrP
diff --git a/monitorDaemon.py b/monitorDaemon.py
--- a/monitorDaemon.py
+++ b/monitorDaemon.py
@@ -1,439 +1,445 @@
#!/usr/bin/env python2
import os
import hashlib
import sys
import ConfigParser
from Xlib import display
from Xlib.ext import randr
from PyQt4.QtCore import QThread, QTimer, pyqtSignal, QObject, QMetaObject, Qt
from PyQt4 import QtGui
DEFAULT_TIMEOUT = 250
class xrandrInterface(QThread):
screenConfigChanged = pyqtSignal()
def __init__(self):
QThread.__init__(self)
self.display = display.Display()
self.screen = self.display.screen()
self.running = True
self.window = self.screen.root.create_window(0, 0, 1, 1, 1,
self.screen.root_depth,
#event_mask = (X.ExposureMask |
#X.StructureNotifyMask |
#X.ButtonPressMask |
#X.ButtonReleaseMask |
#X.Button1MotionMask)
)
self.outputStatus = {}
self.oldOutputStatus = {}
self.crtcStatus = {}
self.getScreenInformation()
self.enableEvents()
self.timer = QTimer()
self.timer.setSingleShot(True)
self.timer.timeout.connect(self.screenInformationChanged)
def screenInformationChanged(self):
self.getScreenInformation()
self.screenConfigChanged.emit()
def disableEvents(self):
self.window.xrandr_select_input(0)
def enableEvents(self):
self.window.xrandr_select_input(
randr.RRScreenChangeNotifyMask
#| randr.RRCrtcChangeNotifyMask
#| randr.RROutputChangeNotifyMask
#| randr.RROutputPropertyNotifyMask
)
def getScreenInformation(self):
self.oldOutputStatus = self.outputStatus
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]['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'])
for output in outputs:
info = self.display.xrandr_get_output_info(output, resources['config_timestamp'])._data
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
def applyConfiguration(self, config):
resources = self.window.xrandr_get_screen_resources()._data
outputs = resources['outputs']
# Find all available outputs and disable outputs
# not present in the config
for output in outputs:
found = False
for ii in range(0, int(config['edidcount'])):
edid = self.get_edid(output)
if edid:
if config['edid' + str(ii+1)] == edid and config['mode' + str(ii+1)] != '0':
found = True
if not found:
crtc = self.getCurrentCrtcForOutput(output)
if crtc:
self.disableCrtc(crtc)
# Apply stored screen configuration, based on EDID
for ii in range(0, int(config['edidcount'])):
if config['mode' + str(ii+1)] != '0':
output = self.getOutputForEdid(config['edid' + str(ii+1)])
crtc = self.getCurrentCrtcForOutput(output)
if not crtc:
crtc = self.getUsableCrtcForOutput(output)
mode = self.findModeByName(output, config['mode' + str(ii+1)])
self.configureCrtcForOutputAndMode(crtc, output, mode, int(config['posx' + str(ii+1)]), int(config['posy' + str(ii+1)]))
self.updateScreenSize()
self.getScreenInformation()
def updateScreenSize(self):
# get all CRTC configs and calculate the required screen size
# set screen size afterwards
resources = self.window.xrandr_get_screen_resources()._data
outputs = resources['outputs']
width = 0
height = 0
for output in outputs:
oi = self.display.xrandr_get_output_info(output, resources['config_timestamp'])._data
crtc = oi['crtc']
if crtc:
info = self.display.xrandr_get_crtc_info(crtc, resources['config_timestamp'])._data
w = info['x'] + info['width']
h = info['y'] + info['height']
if w > width:
width = w
if h > height:
height = h
# Do the same as the other RandR implementations: set width/height in mm
# so that we match 96 dpi.
mm_width = (width / 96.0) * 25.4 + 0.5;
mm_height = (height / 96.0) * 25.4 + 0.5;
self.window.xrandr_set_screen_size(width, height, mm_width, mm_height)
def getScreenConfiguration(self):
return self.outputStatus
def getOldScreenConfiguration(self):
return self.oldOutputStatus
def disableCrtc(self, crtc):
resources = self.window.xrandr_get_screen_resources()._data
self.display.xrandr_set_crtc_config(crtc, 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
def getOutputForEdid(self, edid):
resources = self.window.xrandr_get_screen_resources()._data
for output in resources['outputs']:
medid = self.get_edid(output)
if edid == medid:
return output
return None
# 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
def run(self):
while self.running:
print('running')
e = self.display.next_event()
# Window has been destroyed, quit
#if e.type == X.DestroyNotify:
# sys.exit(0)
# Screen information has changed
if e.type == self.display.extension_event.ScreenChangeNotify:
print('Screen change')
print(e._data)
# Trigger a QTimer here for a few seconds that
# then updates the screen information and
# runs the update code
# If a timer is running, reset it to the default
self.timer.stop()
self.timer.start(DEFAULT_TIMEOUT)
# CRTC information has changed
#elif e.type == self.display.extension_event.CrtcChangeNotify:
# print('CRTC change')
# print(e._data)
# Output information has changed
#elif e.type == self.display.extension_event.OutputChangeNotify:
# print('Output change')
# print(e._data)
# Output property information has changed
#elif e.type == self.display.extension_event.OutputPropertyNotify:
# print('Output property change')
# print(e._data)
# Somebody wants to tell us something
#elif e.type == X.ClientMessage:
# if e.client_type == self.WM_PROTOCOLS:
# fmt, data = e.data
# if fmt == 32 and data[0] == self.WM_DELETE_WINDOW:
# sys.exit(0)
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, value in config.items(section):
dic[section][key] = value
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:
print(self.dic[name])
#for jj in range(0, len(screenConfiguration)):
return (self.dic[name], name)
return (None, None)
def saveConfiguration(self, screenConfiguration):
oldConfig, oldname = self.getConfig(screenConfiguration)
if oldConfig:
name = oldname
else:
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()
return True
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, m, parent=None):
self.parent = parent
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu(parent)
saveAction = menu.addAction("Save Current Configuration")
saveAction.triggered.connect(m.saveConfig)
loadAction = menu.addAction("Load and Apply Configuration")
loadAction.triggered.connect(m.loadAndApply)
exitAction = menu.addAction("Exit")
exitAction.triggered.connect(m.quit)
self.setContextMenu(menu)
class Manager(QObject):
applyConfig = pyqtSignal([dict])
def __init__(self, config, xri, parent = None):
QObject.__init__(self)
self.config = config
self.parent = parent
self.xri = xri
#self.xri.start()
def saveConfig(self):
screenConfig = self.xri.getScreenConfiguration()
self.config.saveConfiguration(screenConfig)
def loadAndApply(self):
print('loadAndApply')
screenConfig = self.xri.getScreenConfiguration()
storedConfig, name = self.config.getConfig(screenConfig)
print(storedConfig)
if storedConfig:
self.applyConfig.emit(storedConfig)
print('done')
def screenConfigurationChanged(self):
print('Slot Received')
oldConfig = self.xri.getOldScreenConfiguration()
newConfig = self.xri.getScreenConfiguration()
if len(oldConfig.keys()) != len(newConfig.keys()):
self.loadAndApply()
else:
for output in oldConfig:
if newConfig.has_key(output):
if oldConfig[output]['edid'] == newConfig[output]['edid']:
continue
self.loadAndApply()
break
print('done.')
def quit(self):
QtGui.qApp.quit()
def main():
app = QtGui.QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False);
settingsdir = os.path.join(os.environ['HOME'], '.config')
cfgfile = os.path.join(settingsdir, 'monitorDaemon.ini')
cm = ConfigManager(cfgfile)
w = QtGui.QWidget()
x = xrandrInterface()
m = Manager(cm, x, w)
x.screenConfigChanged.connect(m.screenConfigurationChanged)
m.applyConfig.connect(x.applyConfiguration)
QMetaObject.invokeMethod(x, "start", Qt.QueuedConnection)
-
+
+ # Wait for up to 30 seconds for the systemTray to become available (required for, e.g., Xfce)
+ count = 0
+ while not QtGui.QSystemTrayIcon.isSystemTrayAvailable() and count < 30:
+ time.sleep(1)
+ count += 1
+
trayIcon = SystemTrayIcon(QtGui.QIcon("/usr/share/icons/monitorDaemon.png"), m, w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Nov 22, 8:06 PM (14 h, 23 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
533942
Default Alt Text
(29 KB)
Attached To
rMD monitorDaemon
Event Timeline
Log In to Comment