29
30
# --== COMMANDS ==--
32
MSG_CMD_PLAY, # Play a file Parameters: 'filename'
33
MSG_CMD_STOP, # Stop playing Parameters:
34
MSG_CMD_SEEK, # Jump to a position Parameters: 'seconds'
35
MSG_CMD_BUFFER, # Buffer a file Parameters: 'filename'
36
MSG_CMD_TOGGLE_PAUSE, # Toggle play/pause Parameters:
33
MSG_CMD_PLAY, # Play a file Parameters: 'filename'
34
MSG_CMD_STOP, # Stop playing Parameters:
35
MSG_CMD_SEEK, # Jump to a position Parameters: 'seconds'
36
MSG_CMD_BUFFER, # Buffer a file Parameters: 'filename'
37
MSG_CMD_TOGGLE_PAUSE, # Toggle play/pause Parameters:
39
MSG_CMD_NEXT, # Play the next track Parameters:
40
MSG_CMD_PREVIOUS, # Play the previous track Parameters:
41
MSG_CMD_TRACKLIST_SET, # Replace the tracklist Parameters: 'files', 'playNow'
42
MSG_CMD_TRACKLIST_ADD, # Extend the tracklist Parameters: 'files'
43
MSG_CMD_TRACKLIST_CLR, # Clear the tracklist Parameters:
40
MSG_CMD_NEXT, # Play the next track Parameters:
41
MSG_CMD_PREVIOUS, # Play the previous track Parameters:
42
MSG_CMD_TRACKLIST_SET, # Replace the tracklist Parameters: 'files', 'playNow'
43
MSG_CMD_TRACKLIST_ADD, # Extend the tracklist Parameters: 'files'
44
MSG_CMD_TRACKLIST_CLR, # Clear the tracklist Parameters:
46
MSG_CMD_EXPLORER_ADD, # Add a new explorer Parameters: 'module', 'name', 'pixbuf', 'widget'
47
MSG_CMD_EXPLORER_ADD, # Add a new explorer Parameters: 'modName', 'expName', 'icon', 'widget', 'usrParam'
48
MSG_CMD_EXPLORER_REMOVE, # Remove an explorer Parameters: 'modName', 'expName'
65
66
MSG_EVT_APP_QUIT, # The application is quitting Parameters:
66
67
MSG_EVT_APP_STARTED, # The application has just started Parameters:
70
MSG_EVT_MOD_LOADED, # The module has been loaded by request of the user Parameters:
71
MSG_EVT_MOD_UNLOADED, # The module has been unloaded by request of the user Parameters:
69
MSG_EVT_EXPLORER_CHANGED, # A new explorer has been selected Parameters: 'module', 'name'
74
MSG_EVT_EXPLORER_CHANGED, # A new explorer has been selected Parameters: 'modName', 'expName', 'usrParam'
76
mMutex = Lock() # This mutex protects all the functions from concurrent access
77
mModules = {} # All known modules, together with an 'active' boolean
78
mHandlers = dict([(msg, []) for msg in xrange(MSG_END_VALUE)]) # For each message, store the list of registered modules
82
""" Dynamically load all modules """
83
# Make sure the path is known to the loader
84
modDir = os.path.dirname(__file__)
85
if not modDir in sys.path:
86
sys.path.append(modDir)
88
for filename in os.listdir(modDir):
89
if filename.endswith('.py') and filename != '__init__.py':
91
modName = filename.rsplit('.', 1)[0]
92
mod = __import__(modName)
93
mModules[getattr(mod, modName)(wTree)] = True
95
print _('Unable to load module "%s"!') % (os.path.join(modDir, filename))
81
# Values associated with a module
83
MOD_PMODULE, # The actual Python module object
84
MOD_CLASSNAME, # The classname of the module
85
MOD_DESC, # Description
86
MOD_INSTANCE, # Instance, None if not currently enabled
87
MOD_MANDATORY, # If True, the module cannot be unloaded
88
MOD_CONFIGURABLE, # If True, the module may be configured
89
MOD_DEPS # Special dependencies of the module (Python modules that may not be installed on the system (e.g., pynotify, pyosd))
94
mModules = {} # All known modules, together with an 'active' boolean
95
mHandlers = dict([(msg, []) for msg in xrange(MSG_END_VALUE)]) # For each message, store the set of registered modules
96
mModulesLock = Lock() # This lock protects the modules list from concurrent access
97
mHandlersLock = Lock() # This lock protects the handlers list from concurrent access
98
mEnabledModules = None # List of modules currently enabled
101
# Make sure the path to the modules is known to the Python loader
102
mModDir = os.path.dirname(__file__)
103
if not mModDir in sys.path:
104
sys.path.append(mModDir)
107
def __checkDeps(deps):
108
""" Given a list of Python modules, return a new list containing the modules that are not currently installed or None """
112
except: unmetDeps.append(dep)
114
if len(unmetDeps) == 0: return None
115
else: return unmetDeps
118
def initialLoad(wTree):
120
Find all modules, instantiate those that are mandatory or that have been previously enabled by the user
121
This should be the first called function of this module
123
global mWTree, mEnabledModules
126
mEnabledModules = prefs.get(__name__, 'enabled_modules', [])
128
for filename in [os.path.splitext(f)[0] for f in os.listdir(mModDir) if f.endswith('.py') and f != '__init__.py']:
130
pModule = __import__(filename)
132
if hasattr(pModule, 'MOD_NAME'): name = getattr(pModule, 'MOD_NAME')
133
else: name = '%s %d' % (_('Unnamed module'), len(mModules))
134
# The module description
135
if hasattr(pModule, 'MOD_DESC'): desc = getattr(pModule, 'MOD_DESC')
137
# Is it a mandatory module?
138
if hasattr(pModule, 'MOD_IS_MANDATORY'): isMandatory = getattr(pModule, 'MOD_IS_MANDATORY')
139
else: isMandatory = True
140
# Can it be configured?
141
if hasattr(pModule, 'MOD_IS_CONFIGURABLE'): isConfigurable = getattr(pModule, 'MOD_IS_CONFIGURABLE')
142
else: isConfigurable = False
143
# Does it require special Python modules?
144
if hasattr(pModule, 'MOD_DEPS'): deps = getattr(pModule, 'MOD_DEPS')
146
# Should it be instanciated immediately?
147
if isMandatory or name in mEnabledModules:
148
unmetDeps = __checkDeps(deps)
149
if unmetDeps is None: instance = getattr(pModule, filename)(wTree)
150
else: instance = None
153
# Ok, add it to the dictionary
154
mModules[name] = [pModule, filename, desc, instance, isMandatory, isConfigurable, deps]
156
print _('ERROR: Unable to load module "%s"!') % os.path.join(mModDir, filename)
157
traceback.print_exc()
158
# Remove modules that are no longer there from the list of enabled modules
159
mEnabledModules[:] = [module for module in mEnabledModules if module in mModules]
160
prefs.set(__name__, 'enabled_modules', mEnabledModules)
97
161
# Start the threaded modules, this has no effect on non-threaded ones
98
for module in mModules:
162
for module in [m for m in mModules.itervalues() if m[MOD_INSTANCE] is not None]:
163
module[MOD_INSTANCE].start()
167
""" Unload the given module """
168
mModulesLock.acquire()
169
data = mModules[name]
170
instance = data[MOD_INSTANCE]
171
data[MOD_INSTANCE] = None
172
mModulesLock.release()
177
mHandlersLock.acquire()
178
# Warn the module that it is going to be unloaded
179
if instance in mHandlers[MSG_EVT_MOD_UNLOADED]:
180
instance.postMsg(MSG_EVT_MOD_UNLOADED, {})
181
# Remove it from all registered handlers
182
for handlers in [h for h in mHandlers.itervalues() if instance in h]:
183
handlers.remove(instance)
184
mHandlersLock.release()
186
mEnabledModules.remove(name)
187
prefs.set(__name__, 'enabled_modules', mEnabledModules)
191
""" Load the given module, return an error message if not possible or None """
192
mModulesLock.acquire()
193
modData = mModules[name]
196
unmetDeps = __checkDeps(modData[MOD_DEPS])
197
if unmetDeps is not None:
198
mModulesLock.release()
199
errMsg = _('The following Python modules are not available:')
201
errMsg += '\n * '.join(unmetDeps)
203
errMsg += _('You must install them if you want to enable this module.')
207
instance = getattr(modData[MOD_PMODULE], modData[MOD_CLASSNAME])(mWTree)
208
modData[MOD_INSTANCE] = instance
209
mEnabledModules.append(name)
210
prefs.set(__name__, 'enabled_modules', mEnabledModules)
212
return traceback.format_exc()
214
mModulesLock.release()
218
mHandlersLock.acquire()
219
if instance in mHandlers[MSG_EVT_MOD_LOADED]:
220
instance.postMsg(MSG_EVT_MOD_LOADED, {})
221
mHandlersLock.release()
227
""" Return a copy of all known modules """
228
mModulesLock.acquire()
229
copy = mModules.items()
230
mModulesLock.release()
102
235
def register(module, msgList):
103
236
""" Register the given module for all messages in the given list """
237
mHandlersLock.acquire()
105
238
for msg in msgList:
106
if module not in mHandlers[msg]:
107
mHandlers[msg].append(module)
112
""" Unload the given module if it is active """
115
for handlers in mHandlers.values():
116
if module in handlers:
117
handlers.remove(module)
118
mModules[module] = False
239
mHandlers[msg].append(module)
240
mHandlersLock.release()
243
def configure(modName, parentDlg):
244
""" Ask the given module to display its configuration dialog """
245
mModulesLock.acquire()
246
mModules[modName][MOD_INSTANCE].configure(parentDlg)
247
mModulesLock.release()
122
250
def __postMsg(msg, params={}):
199
330
Thread.__init__(self)
200
331
ModuleBase.__init__(self)
202
self.queue = [] # List of queued messages
203
self.mutex = Lock() # Protect access to the queue
204
self.semaphore = Semaphore(0) # Block the thread until some messages are available
333
self.queue = [] # List of queued messages
334
self.mutex = Lock() # Protect access to the queue
335
self.semaphore = Semaphore(0) # Block the thread until some messages are available
336
self.gtkSemaphore = Semaphore(0) # Used to execute some code in the GTK main loop
206
338
# Some messages required by this class
207
register(self, [MSG_EVT_APP_QUIT])
339
register(self, [MSG_EVT_APP_QUIT, MSG_EVT_MOD_UNLOADED])
342
def __gtkExecute(self, func):
343
""" Private function, must be executed in the GTK main loop """
345
self.gtkSemaphore.release()
348
def gtkExecute(self, func):
349
""" Execute func in the GTK main loop, and block the execution until done """
350
gobject.idle_add(self.__gtkExecute, func)
351
self.gtkSemaphore.acquire()
210
354
def postMsg(self, msg, params):