Page MenuHomePhabricator

No OneTemporary

diff --git a/lt_presentation.py b/lt_presentation.py
--- a/lt_presentation.py
+++ b/lt_presentation.py
@@ -1,346 +1,346 @@
#!/usr/bin/env python
# This is lt_presentation
# A simple system tray tool to send LogiTech Presenter events
# directly to the desired presentation tool.
#
# It wraps around Atril, xdotool and wmctrl and requires python-evdev
# to communicate with the Presenter.
#
# Xfce's Presentation Mode is automatically activated and reset whenever
# a presentation is active.
import dbus
import evdev
import subprocess
import sys
import time
import os
import configparser
from PyQt4.QtCore import QTimer
from PyQt4 import QtGui
class ConfigManager(QtGui.QWidget):
def __init__(self, config, parent = None):
QtGui.QWidget.__init__(self, parent)
self.config = config
class Manager():
def __init__(self, config, parent = None):
self.config = config
self.parent = parent
self.timer = QTimer()
self.timer.timeout.connect(self.check_events)
self.process = None
self.device = None
self.wid = None
self.pm_cookie = None
self.presentation_mode = False
self.presentation_active = False
self.workrave_mode = None
self.viewer = self.which(self.config['programs']['pdfviewer'])
self.wmctrl = self.which('wmctrl')
self.xdotool = self.which('xdotool')
def check_events(self):
if self.process:
if self.process.poll() == None:
try:
for event in self.device.read():
if event.type == evdev.ecodes.EV_KEY:
if event.code == evdev.ecodes.KEY_PAGEUP and event.value == 1:
self.send_key(self.wid, 'Page_Up')
elif event.code == evdev.ecodes.KEY_PAGEDOWN and event.value == 1:
self.send_key(self.wid, 'Page_Down')
elif event.code == evdev.ecodes.KEY_F5 and event.value == 1:
self.send_key(self.wid, 'F5')
elif event.code == evdev.ecodes.KEY_ESC and event.value == 1:
self.send_key(self.wid, 'Escape')
elif event.code == evdev.ecodes.KEY_DOT and event.value == 1:
self.send_key(self.wid, 'b')
except BlockingIOError:
pass
else:
self.stop_presentation()
else:
self.stop_presentation()
def stop_presentation(self):
print('Stopping Presentation')
self.presentation_active = False
self.timer.stop()
try:
self.device.ungrab()
except IOError:
pass
self.device.close()
self.device = None
self.wid = None
if self.config['general']['inhibit_xdg_pm'] == 'yes':
try:
- pm = dbus.SessionBus().get_object("org.freedesktop.PowerManagement", "/org/freedesktop/PowerManagement/Inhibit")
+ pm = dbus.SessionBus().get_object("org.freedesktop.PowerManagement", "/org/freedesktop/PowerManagement/Inhibit", dbus_interface='org.freedesktop.PowerManagement.Inhibit')
pm.UnInhibit(self.pm_cookie)
except:
QtGui.QMessageBox.critical(self.parent, 'Error resetting PM!',
'Failed to reset the Power Manager')
if self.config['general']['xfce_pm_presentation_mode'] == 'yes':
try:
xfc = dbus.SessionBus().get_object('org.xfce.Xfconf', '/org/xfce/Xfconf')
xfc.SetProperty('xfce4-power-manager', '/xfce4-power-manager/presentation-mode', self.presentation_mode)
except:
QtGui.QMessageBox.critical(self.parent, 'Error resetting Xfce PM!',
'Failed to reset the Xfce Power Manager')
if self.config['general']['inhibit_workrave'] == 'yes':
try:
wr = dbus.SessionBus().get_object('org.workrave.Workrave', '/org/workrave/Workrave/Core')
iface = dbus.Interface(wr, 'org.workrave.CoreInterface')
iface.SetOperationMode(self.workrave_mode)
except:
QtGui.QMessageBox.critical(self.parent, 'Error resetting Workrave!',
'Failed to reset Workrave state')
def check_viewer(self):
if self.viewer:
return True
else:
return False
def check_xdotool(self):
if self.xdotool:
ret = subprocess.run([self.xdotool, "--version"], stdout = subprocess.PIPE,
universal_newlines = True)
if ret.returncode == 0:
version = ret.stdout.replace('xdotool version ', '').replace('\n', '')
return version
return False
def check_wmctrl(self):
if self.wmctrl:
ret = subprocess.run([self.wmctrl, "--version"], stdout = subprocess.PIPE,
universal_newlines = True)
if ret.returncode == 0:
version = ret.stdout.replace('\n', '')
return version
return False
def find_window_for_pid(self, pid):
print('Looking for window for PID: ' + str(pid))
ret = subprocess.run([self.wmctrl, "-l", "-p"], stdout = subprocess.PIPE,
universal_newlines = True)
if ret.returncode == 0:
wpid = 0
wmid = 0
for line in ret.stdout.split('\n'):
args = line.split(' ')
pos = 1
for arg in args:
if arg == '':
continue
if pos == 1:
wmid = arg
elif pos == 3:
wpid = arg
pos += 1
if wpid == str(pid):
return wmid
return False
else:
return False
def get_window(self, pattern):
ret = subprocess.run([self.xdotool, "search", "--name", pattern],
stdout = subprocess.PIPE, universal_newlines = True)
if ret.returncode == 0:
return ret.stdout.replace('\n', '')
else:
return False
def get_device(self, devname):
devnames = [a.strip() for a in devname.split(',')]
devices = [evdev.InputDevice(fn) for fn in evdev.list_devices()]
for dev in devices:
if dev.name in devnames:
print('Found possible device: ', dev.fn)
# Check for pointing devices
if 3 in dev.capabilities():
continue
return dev.fn
return False
def send_key(self, wid, key):
ret = subprocess.run([self.xdotool, "key", "--window", wid, key])
return ret.returncode
def startPresentation(self):
print('Start Presentation')
if self.presentation_active:
QtGui.QMessageBox.critical(self.parent, 'Presentation already running!',
'There is already a presentation running.')
return False
device = self.get_device(self.config['devices']['presenter'])
if not device:
QtGui.QMessageBox.critical(self.parent, 'Device not found!',
'Could not find presentation remote control')
return False
self.device = evdev.InputDevice(device)
try:
self.device.grab()
except IOError:
QtGui.QMessageBox.critical(self.parent, 'Error grabbing device',
'Could not grab Input device.')
return False
fname = QtGui.QFileDialog.getOpenFileName(self.parent, 'Open file', '', 'PDF (*.pdf)')
self.process = subprocess.Popen([self.viewer, fname])
# Give the PDF viewer some time to show its window
wid = self.find_window_for_pid(self.process.pid)
count = 0
while not wid and count < 10:
time.sleep(0.5)
wid = self.find_window_for_pid(self.process.pid)
count += 1
if not wid:
QtGui.QMessageBox.critical(self.parent, 'PDF Viewer not found!',
'Could not find the PDF presentation window in time')
return False
self.wid = wid
print('Found PDF Viewer Window: ' + wid)
# Inhibit power management
if self.config['general']['inhibit_xdg_pm'] == 'yes':
try:
pm = dbus.SessionBus().get_object("org.freedesktop.PowerManagement", "/org/freedesktop/PowerManagement/Inhibit")
- self.pm_cookie = pm.Inhibit("lt_presentation", "Presentation Starting")
+ self.pm_cookie = pm.Inhibit("lt_presentation", "Presentation Starting", dbus_interface='org.freedesktop.PowerManagement.Inhibit')
except:
QtGui.QMessageBox.critical(self.parent, 'Power Management!',
'Could not inhibit Power Management. Is the daemon active?')
if self.config['general']['xfce_pm_presentation_mode'] == 'yes':
try:
xfc = dbus.SessionBus().get_object('org.xfce.Xfconf', '/org/xfce/Xfconf')
self.presentation_mode = xfc.GetProperty('xfce4-power-manager', '/xfce4-power-manager/presentation-mode')
xfc.SetProperty('xfce4-power-manager', '/xfce4-power-manager/presentation-mode', dbus.Boolean(True, variant_level=1))
except:
QtGui.QMessageBox.critical(self.parent, 'Xfce Power Manager!',
'Could not set the Xfce Power Manager to presentation mode! Is it active?')
if self.config['general']['inhibit_workrave'] == 'yes':
try:
wr = dbus.SessionBus().get_object('org.workrave.Workrave', '/org/workrave/Workrave/Core')
iface = dbus.Interface(wr, 'org.workrave.CoreInterface')
self.workrave_mode = iface.GetOperationMode()
iface.SetOperationMode('suspended')
except:
QtGui.QMessageBox.critical(self.parent, 'Workrave!',
'Could not suspend Workrave. Is it running?')
self.presentation_active = True
self.timer.start(10)
def which(self, program):
def is_exe(fpath):
return os.path.isfile(fpath) and os.access(fpath, os.X_OK)
fpath, fname = os.path.split(program)
if fpath:
if is_exe(program):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
path = path.strip('"')
exe_file = os.path.join(path, program)
if is_exe(exe_file):
return exe_file
return None
class SystemTrayIcon(QtGui.QSystemTrayIcon):
def __init__(self, icon, m, parent=None):
self.parent = parent
QtGui.QSystemTrayIcon.__init__(self, icon, parent)
menu = QtGui.QMenu(parent)
presentationAction = menu.addAction("Start Presentation...")
presentationAction.triggered.connect(m.startPresentation)
exitAction = menu.addAction("Exit")
exitAction.triggered.connect(QtGui.qApp.quit)
self.setContextMenu(menu)
def load_config(cfgFile):
config = configparser.ConfigParser()
config.read(cfgFile)
return config
def save_config(config, cfgFile):
with open(cfgFile, 'w') as configFile:
config.write(configFile)
def recreate_config(cfgFile):
config = configparser.ConfigParser()
config['general'] = {
'xfce_pm_presentation_mode' : 'yes',
'inhibit_xdg_pm' : 'yes',
'inhibit_workrave' : 'yes'
}
config['programs'] = {
'pdfviewer' : 'atril'
}
config['devices'] = {
'presenter' : 'Logitech USB Receiver, Genius Ring Presenter'
}
save_config(config, cfgFile)
def main():
app = QtGui.QApplication(sys.argv)
app.setQuitOnLastWindowClosed(False);
cfgPath = os.path.expanduser("~") + "/.lt_presentation/"
if not os.path.isdir(cfgPath):
os.makedirs(cfgPath)
cfgFile = cfgPath + "config.ini"
if not os.path.exists(cfgFile):
recreate_config(cfgFile)
cfg = load_config(cfgFile)
w = QtGui.QWidget()
m = Manager(cfg, w)
version = m.check_xdotool()
if version:
print('Running on xdotool version ' + version)
else:
QtGui.QMessageBox.critical(w, 'Tool not found', 'Could not find xdotool, will now quit')
sys.exit(1)
viewer = m.check_viewer()
if viewer:
print('Found configured PDF viewer ' + cfg['programs']['pdfviewer'])
else:
QtGui.QMessageBox.critical(w, 'Tool not found', 'Could not find PDF viewer, will now quit')
sys.exit(1)
wmctrlVersion = m.check_wmctrl()
if wmctrlVersion:
print('Running on wmctrl version ' + wmctrlVersion)
else:
QtGui.QMssageBox.critical(w, 'Tool not found', 'Could not find wmctrl, will now quit')
sys.exit(1)
trayIcon = SystemTrayIcon(QtGui.QIcon("/usr/share/icons/lt_presentation.png"), m, w)
trayIcon.show()
sys.exit(app.exec_())
if __name__ == '__main__':
main()

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 2:26 AM (22 h, 44 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
527082
Default Alt Text
(13 KB)

Event Timeline