Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F1720891
lt_presentation.py
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Size
12 KB
Subscribers
None
lt_presentation.py
View Options
#!/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
.
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"
)
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
Details
Attached
Mime Type
text/x-python
Expires
Sat, Nov 23, 9:12 AM (3 h, 44 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
911081
Default Alt Text
lt_presentation.py (12 KB)
Attached To
rLTPRES Logitech Presentation Tool
Event Timeline
Log In to Comment