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 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()