summaryrefslogtreecommitdiff
path: root/CompizPlugin.py
blob: ca36d14c96f5bfd33316827eb7edf153f8f7649d (plain)
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
#
# Copyright Eyemagnet Limited
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
# 02111-1307, USA.
# 
# Author: Kristian F. Erikson <kristian@erikson.dk>
#

import dbus
import os

from CompizError import *
from CompizTools import *
from CompizConnection import _screen0
from CompizConnection import _allscreens

_pluginActivateUpdateDelay = 5 # seconds

class CompizPlugin(object):
    """
    Instances of the CompizPlugin are intended to represent an active or inactive Compiz plugin.
    
    Each instance of a plugin loads it's options (variables we can change) from accessing what is
    available over the Dbus for the particular plugin.
    
    Actions (or methods) are mainly the visual effects we can call (they're normally associated
    with a button or keypress, but not limited to these). The dict of available actions
    is built by accessing what is available over the Dbus. However as actions often require 
    additional arguments these are spefified in "plugins.xml". This is because the arguments are
    not exposed over the Dbus. If an action does not work, determining if arguments are missing 
    is a goodplace to start.
    """
    
    def __init__(self, name, manager):
        """ Builds the CompizPlugin """
        self.name = name
        self.objectLocation = '/' + name
        self.manager = manager
        self.options = {}
        self.actions = {}
        self.active = False
        self.update()
        
    def listOptions(self):
        """ Returns a list of available options for the plugin """
        if self.active:         # Only return options if we're active
            return self.options
        else:
            return {}
    
    def listActions(self):
        """ Returns a list of available actions for the plugin """
        if self.active:         # Only return actions if we're active
            return self.actions
        else:
            return {}

    def update(self):
        """ Can be used after a plugin has been activated to change it's status and
        reload it's options and actions from the Dbus. """
        check = 0
        try:
            self.screen0 = self.manager.connection.getList(self.objectLocation + _screen0)
            check += 1
        except dbus.exceptions.DBusException:
            self.screen0 = {}
        try:
            self.allscreens = self.manager.connection.getList(self.objectLocation + _allscreens)
            check += 1
        except dbus.exceptions.DBusException:
            self.allscreens = {}
        if check >= 1:  # If we fail to connect to either allscreens or screen0 the plugin must be active
            self.active = True
        else:
            self.active = False
            
        if self.active: # Figure out all the option types and build self.options

            def addAction( itemKey ):   # Only add actions if they don't exist already (from XML)
                if '_button' in itemKey:     # Strip string of _key & _button to aviod duplicates
                    index = itemKey.rindex("_button")
                    itemKey = itemKey[:index]
                    
                if '_key' in itemKey:
                    index = itemKey.rindex("_key")
                    itemKey = itemKey[:index]
                    
                if '_edge' in itemKey:
                    index = itemKey.rindex("_edge")
                    itemKey = itemKey[:index]
                    
                try:    # Non intrusive way of check if the action already exists
                    self.actions[str(itemKey)]
                except KeyError:
                    self.actions[str(itemKey)] = None
                    
            def checkForActions( item , response ):
                if '_key' in item:
                    addAction(item)    # Must be an action so add to actions
                elif '_button' in item:
                    addAction(item)
                elif '_edge' in item:
                    addAction(item)
                elif 'initiate' in item:
                    addAction(item)
                elif response == None:
                    addAction(item)
                else:
                    self.options[str(item)] = convertFromDbusToTypes(response)    # If not an action must be an option
                
            try:
                if type(self.screen0) == type(dbus.String("string")):    # If we got back a string instead of a list.
                    try:
                        dbusResponse = self.manager.connection.get(self.objectLocation + _screen0 + '/' + str(self.screen0))
                    except dbus.exceptions.DBusException:
                        dbusResponse = None
                        
                    checkForActions( self.screen0 , dbusResponse )
                else:
                   for screenitem in self.screen0:
                        try:
                            dbusResponse = self.manager.connection.get(self.objectLocation + _screen0 + '/' + str(screenitem))
                        except dbus.exceptions.DBusException:
                            dbusResponse = None
                            
                        checkForActions( screenitem , dbusResponse )
            except TypeError:
                pass
                
            try:
                if type(self.allscreens) == type(dbus.String("string")):    # If we got back a string instead of a list.
                    try:
                        dbusResponse = self.manager.connection.get(self.objectLocation + _allscreens + '/' + str(self.allscreens))
                    except dbus.exceptions.DBusException:
                        dbusResponse = None
                        
                    checkForActions( self.allscreens , dbusResponse )
                else:
                    for screenitem in self.allscreens:      # If we got back a list
                        try:
                            dbusResponse = self.manager.connection.get(self.objectLocation + _allscreens + '/' + str(screenitem))
                        except dbus.exceptions.DBusException:
                            dbusResponse = None
                            
                        checkForActions( screenitem , dbusResponse )
            except TypeError:
                pass

    def setupActions(self, actionsXML):
        """ Used  by class CompizManager to help parse the action arguments from the XML file. """
        if actionsXML == None:
            pass
        else:
            # Parse the input from the XML file
            inActions = actionsXML.getchildren()
            
            for action in inActions:
                if action.getchildren():
                    self.actions[action.tag] = {}
                    for subaction in action.getchildren():
                        self.actions[action.tag][subaction.tag] = convertFromStringToTypes(subaction.attrib)
                else:
                    self.actions[action.tag] = convertFromStringToTypes(action.attrib)
                
    def setOption(self, optionName, newOptionValue):
        """ Sets the value of an option. Refer to listOptions() to determine what the type of value is for an option. """
        if self.active:
            if optionName in self.options:
                locationString = ''
                try:
                    if optionName in self.screen0:
                        locationString = '/' + self.name + _screen0 + '/'+ optionName
                except TypeError:   # Catching TypeError here as the tuples may be None
                    pass
                try:
                    if optionName in self.allscreens:
                        locationString = '/' + self.name + _allscreens + '/'+ optionName
                except TypeError:   # Catching TypeError here as the tuples may be None
                    pass
                    
                if self.options[optionName] == type(newOptionValue):   # Check parameter is the right type (single value)
                    self.manager.connection.setOption(locationString, newOptionValue)
                    
                elif type(self.options[optionName]) == type([]):       # If the option value happens to be a list
                    if type(self.options[optionName]) == type(newOptionValue):   # Check what we got is also a list
                        
                        array = convertFromPythonToDbus(newOptionValue)
                        
                        self.manager.connection.setOption(locationString, array) # Send off built array
                        
                    else: raise CompizPluginTypeArgError
                else: raise CompizPluginTypeArgError
            else: raise CompizPluginOptionError   # Raise exception if we could not find option
        else: raise CompizPluginNotActiveError   # Raise exception if plugin is not active

    def getOption(self, optionName):
        """ Returns value associated with the option """
        if self.active:
            if optionName in self.options:
                locationString = ''
                try:
                    if optionName in self.screen0:
                        locationString = '/' + self.name + _screen0 + '/'+ optionName
                except TypeError:   # Catching TypeError here as the tuples may be None
                    pass
                try:
                    if optionName in self.allscreens:
                        locationString = '/' + self.name + _allscreens + '/'+ optionName
                except TypeError:   # Catching TypeError here as the tuples may be None
                    pass
                
                dbusType = self.manager.connection.get(locationString)
                
                return convertFromDbusToPython(dbusType)
            else:
                raise CompizPluginOptionError   # Raise exception if we could not find option
        else:
            raise CompizPluginNotActiveError   # Raise exception if plugin is not active
    
    def doAction(self, action, arguments = ''):      # Takes the name of the action and the arguments in a dict
        """ Calls an action. Some actions require arguments which then must be specified. 
        If arguments are required can be determined by examining the output of listActions(). """
        if self.active:
            if action in self.actions:
                paramsString = []
                
                # Attach root window whenever making a method call
                paramsString.append(unicode('root'))
                xwininfo = os.popen("xwininfo -root | grep id: | awk '{ print $4 }'")
                xwininfoOutput = xwininfo.read()
                paramsString.append(int(xwininfoOutput, 16))
                
                advancedMethod = False
                
                try:
                    for param in self.actions[action]:
                        if type(self.actions[action][param]) == type({}):
                            advancedMethod = True
                        else:
                            # First attach name of parameter
                            paramsString.append(unicode(param))
                            if param == 'window':       # If param is a window title find id for us
                                if self.actions[action][param] == type(arguments[param]):   # Check parameters are correct type
                                    xwininfo = os.popen("xwininfo -name '" + arguments[param] + "' | grep id: | awk '{ print $4 }'")
                                    xwininfoOutput = xwininfo.read()
                                    try:
                                        paramsString.append(int(xwininfoOutput, 16))
                                    except ValueError:
                                        raise CompizPluginWindowNotFoundError   # If window not found raise exception
                                else:
                                    raise CompizPluginTypeArgError
                            else:                       # Otherwise go ahead and attach the parameter
                                if self.actions[action][param] == type(arguments[param]):   # Check parameters are correct type
                                    paramsString.append(arguments[param])
                                else:
                                    raise CompizPluginTypeArgError
                except TypeError:
                    pass    # If there are no parameters don't attach any

                # TODO: This code piece needs refactoring or deletion. Not a priority atm though
                if advancedMethod:      # Attempt to use annotate's advanced features.
                    paramsString.append(unicode('tool'))
                    for arg in arguments:
                        paramsString.append(unicode(arg))
                        for param in arguments[arg]:
                            paramsString.append(unicode(param))
                            paramsString.append(unicode(arguments[arg][param]))
                    
                # Now the string is built we should send it
                # Find out if it's in screen 0 or allscreens first
                foundAction = False # Bool to check if found in allscreens or screen0
                locationString = '' # Build string of where action is located on dbus
                
                if self.screen0 == None:
                    pass
                else:                       # First check screen0 for keys and buttons and edges
                    if (action + "_button") in self.screen0:
                        locationString = '/' + self.name + _screen0 + '/' + action + "_button"
                        foundAction = True
                    elif (action + "_key") in self.screen0:
                        locationString = '/' + self.name + _screen0 + '/' + action + "_key"
                        foundAction = True
                    elif (action + "_edge") in self.screen0:
                        locationString = '/' + self.name + _screen0 + '/' + action + "_edge"
                        foundAction = True
                
                if self.allscreens == None:
                    pass
                else:                        # Then check allscreens for keys and buttons and edges
                    if (action + "_button") in self.allscreens:
                        locationString = '/' + self.name + _allscreens + '/' + action + "_button"
                        foundAction = True
                    elif (action + "_key") in self.allscreens:
                        locationString = '/' + self.name + _allscreens + '/' + action + "_key"
                        foundAction = True
                    elif (action + "_edge") in self.allscreens:
                        locationString = '/' + self.name + _allscreens + '/' + action + "_edge"
                        foundAction = True
                        
                if foundAction == False:    # If not found yet must not be key or button
                    if self.screen0 == None:
                        pass
                    else:
                        if action in self.screen0:
                            locationString = '/' + self.name + _screen0 + '/' + action
                    if self.allscreens == None:
                        pass
                    else:
                        if action in self.allscreens:
                            locationString = '/' + self.name + _allscreens + '/' + action
                
                self.manager.connection.doAction(locationString,*paramsString)
            else:
                raise CompizPluginActionError   # Raise exception if action does not exist
        else:
            raise CompizPluginNotActiveError # Raise error is plugin is not active