1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
#!/usr/bin/python
import os
import sys
import tempfile
import subprocess
import gobject
import dbus, dbus.service, dbus.mainloop.glib
import ContextProvider as CP
class _property(object):
"""Kind-of a template for property types."""
def __init__(self, datatype, default):
self.datatype = datatype
self.default = default
self.name = None
def __call__(self, name, initial_value=None):
"""Clones self, setting its name and initial value."""
if initial_value is None:
initial_value = self.default
prop = _property(self.datatype, initial_value)
prop.name = name
return prop
# Pre-defined types and their defaults.
INT = _property('INT', 0)
STRING = _property('STRING', '')
DOUBLE = _property('DOUBLE', 0.0)
TRUTH = _property('TRUTH', False)
def xmlfor(busname='ctx.flexiprovider', bus='session', *props):
"""Generates a provider .xml document for the given properties
(which are instances of _property)."""
xml = ['<?xml version="1.0"?>\n'
'<provider bus="%s" service="%s">' % (bus, busname)]
for p in props:
xml.append(' <key name="%s">\n'
' <type>%s</type>\n'
' <doc>A phony but very flexible property.</doc>\n'
' </key>' % (p.name, p.datatype))
xml.append('</provider>\n')
return '\n'.join(xml)
def update_context_providers(xml, dir='.'):
"""Dumps the xml into $dir/flexi-properties.xml."""
tmpfd, tmpfn = tempfile.mkstemp('.xml', 'flexi', dir)
os.write(tmpfd, xml)
os.close(tmpfd)
if "CONTEXT_FLEXI_XML" in os.environ:
os.rename(tmpfn, os.environ["CONTEXT_FLEXI_XML"])
else:
os.rename(tmpfn, dir + '/flexi-properties.xml')
class Flexiprovider(object):
def stdin_ready(self, fd, cond):
if cond & gobject.IO_ERR:
self.loop.quit()
return False
# We assume that stdin is line-buffered (ie. readline() doesn't
# block (too much)). It's true if it's a tty. If piping from
# another program, do an entire line.
# TODO maybe we should rather fix ourselves and accept arbitrary
# amount of input (and accumulate lines ourselves).
l = sys.stdin.readline()
# If l is emtpy, we got EOF.
if l == '':
self.loop.quit()
return False
# Nevertheless, ignore emtpy lines.
l = l.strip()
if l == '':
return True
try:
code = compile(l, '<input from stdin>', 'single')
eval(code, {}, dict(INT=INT,
STRING=STRING,
TRUTH=TRUTH,
DOUBLE=DOUBLE,
set=self.set,
get=self.get,
reset=self.reset,
quit=self.loop.quit,
add=self.add_and_update,
info=self.info))
except:
# We are ignoring errors.
import traceback
traceback.print_exc()
return True
def __init__(self, properties, name='ctx.flexiprovider', bus='session'):
self.busname = name
self.bus = bus
self.loop = gobject.MainLoop()
self.props = {}
self.values = {}
# Hook into libcontextprovider.
self.setters = dict(INT=CP.ContextProvider.set_integer,
STRING=CP.ContextProvider.set_string,
DOUBLE=CP.ContextProvider.set_double,
TRUTH=CP.ContextProvider.set_boolean)
self.subscribed_cb = CP.ContextProvider.SUBSCRIPTION_CHANGED_CALLBACK(lambda x, y: None)
CP.ContextProvider.init(dict(session=0, system=1)[self.bus], self.busname)
# Add properties and set the initial values.
for p in properties:
self.addproperty(p)
self.update_providers()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
def get(self, prop):
return self.values.get(prop, None)
def set(self, prop, val):
if prop not in self.props:
return
self.values[prop] = val
if val is None:
CP.ContextProvider.set_null(prop)
else:
self.setters[self.props[prop].datatype](prop, val)
def reset(self, prop):
if prop not in self.props:
return
self.set(prop, self.props[prop].default)
def addproperty(self, prop):
self.props[prop.name] = prop
self.values[prop.name] = prop.default
CP.ContextProvider.install_key(prop.name, 0, self.subscribed_cb, None)
self.reset(prop.name)
def add_and_update(self, prop):
self.addproperty(prop)
self.update_providers()
def update_providers(self):
# Don't update the registry if we are being run as a commander.
if self.busname == 'org.freedesktop.ContextKit.Commander':
return
update_context_providers(xmlfor(self.busname, self.bus, *self.props.values()))
def info(self):
"""Dumps information about currently provided properties."""
for p in self.props.values():
print p.datatype.ljust(8), p.name.ljust(20), \
self.values[p.name], '(', p.default, ')'
def interactive(self):
"""Runs the provider interactively, accepting the following
commands from the standard input (note that this is Python
syntax, and it gets eval()ed):
set(property, value): sets the given property
get(property): prints the value of the property
reset(property): resets the property
add(propertydesc): adds a new property
info(): prints properties and their values
quit(): stops the whole thing
The provider will stop also if stdin is closed.
"""
# Reopen stdout to be line-buffered.
sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 1)
gobject.io_add_watch(sys.stdin.fileno(),
gobject.IO_IN | gobject.IO_HUP | gobject.IO_ERR,
self.stdin_ready)
self.loop.run()
def run(self):
self.loop.run()
__all__ = ('INT', 'STRING', 'DOUBLE', 'TRUTH', 'Flexiprovider')
|