~ubuntu-branches/ubuntu/raring/synaptiks/raring-proposed

« back to all changes in this revision

Viewing changes to .pc/kubuntu_01_fix_crash_no_touchpad.diff/synaptiks/config.py

  • Committer: Bazaar Package Importer
  • Author(s): Felix Geyer
  • Date: 2011-03-11 13:38:31 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20110311133831-s37a3stygdsdnwxr
Tags: 0.5.3-0ubuntu1
* New upstream release.
* Drop kubuntu_01_fix_crash_no_touchpad.diff, applied upstream.
* Enable Kubuntu l18n processing.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
# Copyright (c) 2010, 2011, Sebastian Wiesner <lunaryorn@googlemail.com>
3
 
# All rights reserved.
4
 
 
5
 
# Redistribution and use in source and binary forms, with or without
6
 
# modification, are permitted provided that the following conditions are met:
7
 
 
8
 
# 1. Redistributions of source code must retain the above copyright notice,
9
 
#    this list of conditions and the following disclaimer.
10
 
# 2. Redistributions in binary form must reproduce the above copyright
11
 
#    notice, this list of conditions and the following disclaimer in the
12
 
#    documentation and/or other materials provided with the distribution.
13
 
 
14
 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
 
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
 
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
 
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18
 
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19
 
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20
 
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21
 
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22
 
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23
 
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24
 
# POSSIBILITY OF SUCH DAMAGE.
25
 
 
26
 
 
27
 
"""
28
 
    synaptiks.config
29
 
    ================
30
 
 
31
 
    Configuration classes
32
 
    ---------------------
33
 
 
34
 
    This module provides the configuration classes for the touchpad and the
35
 
    touchpad manager.  These configuration classes are simply mappings, which
36
 
    expose the *current* state of the associated objects (and *not* the state
37
 
    of the configuration on disk).  This allows the user to configure the live
38
 
    state of these objects, which is especially important for touchpad
39
 
    configuration, which can be changed from outside of **synaptiks** using
40
 
    utilities like :program:`xinput`.
41
 
 
42
 
    To save the configuration permanently, these mappings, and consequently the
43
 
    current configuration state of the associated objects, is simply dumped in
44
 
    JSON format to the configuration directory (see
45
 
    :func:`get_configuration_directory()`).
46
 
 
47
 
    To apply a dumped configuration, it is loaded as standard dict from the
48
 
    JSON dump, and then the corresponding configuration mapping is updated with
49
 
    this dict.  All configuration mappings provide a convenient
50
 
    :meth:`~TouchpadConfiguration.load()` method to create a new configuration
51
 
    mapping updated with the dumped configuration.
52
 
 
53
 
    Handling of default values
54
 
    --------------------------
55
 
 
56
 
    The touchpad manager configuration (see :class:`ManagerConfiguration`)
57
 
    provides explicit defaults.  The touchpad configuration (see
58
 
    :class:`TouchpadConfiguration`) however uses defaults provided by the
59
 
    touchpad driver.
60
 
 
61
 
    As the touchpad driver doesn't expose special access to the default values,
62
 
    **synaptiks** simply creates a :class:`TouchpadConfiguration` after session
63
 
    startup, but *before* loading the actual configuration from disk.  At this
64
 
    point, the touchpad still uses the driver default setting, which are now
65
 
    dumped to a special file in JSON format (see
66
 
    :func:`get_touchpad_defaults_file_path()`).  They can later be loaded using
67
 
    :func:`get_touchpad_defaults()`.
68
 
 
69
 
    Script usage
70
 
    ------------
71
 
 
72
 
    .. program:: synaptikscfg
73
 
 
74
 
    This module is usable as script, available also as :program:`synaptikscfg`
75
 
    in the ``$PATH``.  It provides three different, of which ``load`` and
76
 
    ``save`` are really self-explanatory. ``init`` however deserves some
77
 
    detailled explanation.
78
 
 
79
 
    The `init` action is supposed to run automatically as script during session
80
 
    startup.  To do this, the installation script installs an autostart entry
81
 
    to execute ``synaptikscfg init`` at KDE startup.  This action first dumps
82
 
    the default settings from the touchpad driver as described above, and then
83
 
    loads and applies the actual touchpad configuration stored on disk.
84
 
 
85
 
    The command line parsing of the script is implemented with :mod:`argparse`,
86
 
    so you can expected standard semantics, and an extensive ``--help`` option.
87
 
 
88
 
    .. moduleauthor::  Sebastian Wiesner  <lunaryorn@googlemail.com>
89
 
"""
90
 
 
91
 
from __future__ import (print_function, division, unicode_literals,
92
 
                        absolute_import)
93
 
 
94
 
import os
95
 
from collections import MutableMapping
96
 
 
97
 
from synaptiks.util import ensure_directory, save_json, load_json_with_default
98
 
 
99
 
 
100
 
PACKAGE_DIRECTORY = os.path.dirname(__file__)
101
 
 
102
 
 
103
 
def get_configuration_directory():
104
 
    """
105
 
    Get the configuration directory of synaptiks according to the `XDG base
106
 
    directory specification`_ as string.  The directory is guaranteed to exist.
107
 
 
108
 
    The configuration directory is a sub-directory of ``$XDG_CONFIG_HOME``
109
 
    (which defaults to ``$HOME/.config``).
110
 
 
111
 
    .. _`XDG base directory specification`: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
112
 
    """
113
 
    xdg_config_home = os.environ.get('XDG_CONFIG_HOME', os.path.expandvars(
114
 
        os.path.join('$HOME', '.config')))
115
 
    return ensure_directory(os.path.join(xdg_config_home, 'synaptiks'))
116
 
 
117
 
 
118
 
def get_touchpad_config_file_path():
119
 
    """
120
 
    Get the path to the file which stores the touchpad configuration.
121
 
    """
122
 
    return os.path.join(get_configuration_directory(), 'touchpad-config.json')
123
 
 
124
 
 
125
 
def get_touchpad_defaults_file_path():
126
 
    """
127
 
    Get the path to the file which stores the default touchpad configuration as
128
 
    setup by the touchpad driver.
129
 
    """
130
 
    return os.path.join(get_configuration_directory(), 'touchpad-defaults.json')
131
 
 
132
 
 
133
 
def get_management_config_file_path():
134
 
    """
135
 
    Get the path to the file which stores the touchpad management configuration.
136
 
    """
137
 
    return os.path.join(get_configuration_directory(), 'management.json')
138
 
 
139
 
 
140
 
def get_touchpad_defaults(filename=None):
141
 
    """
142
 
    Get the default touchpad settings as :func:`dict` *without* applying it to
143
 
    the touchpad.
144
 
    """
145
 
    if not filename:
146
 
        filename = get_touchpad_defaults_file_path()
147
 
    return load_json_with_default(filename, {})
148
 
 
149
 
 
150
 
class TouchpadConfiguration(MutableMapping):
151
 
    """
152
 
    A mutable mapping class representing the current configuration of the
153
 
    touchpad.
154
 
    """
155
 
 
156
 
    CONFIG_KEYS = frozenset([
157
 
        'minimum_speed', 'maximum_speed', 'acceleration_factor',
158
 
        'edge_motion_always', 'fast_taps',
159
 
        'rt_tap_action', 'rb_tap_action', 'lt_tap_action', 'lb_tap_action',
160
 
        'f1_tap_action', 'f2_tap_action', 'f3_tap_action',
161
 
        'tap_and_drag_gesture', 'locked_drags', 'locked_drags_timeout',
162
 
        'vertical_edge_scrolling', 'horizontal_edge_scrolling',
163
 
        'corner_coasting', 'coasting_speed',
164
 
        'vertical_scrolling_distance', 'horizontal_scrolling_distance',
165
 
        'vertical_two_finger_scrolling', 'horizontal_two_finger_scrolling',
166
 
        'circular_scrolling', 'circular_scrolling_trigger',
167
 
        'circular_scrolling_distance', 'circular_touchpad'])
168
 
 
169
 
    @classmethod
170
 
    def load(cls, touchpad, filename=None):
171
 
        """
172
 
        Load the configuration for the given ``touchpad`` from disc.
173
 
 
174
 
        If no ``filename`` is given, the configuration is loaded from the
175
 
        default configuration file as returned by
176
 
        :func:`get_touchpad_config_file_path`.  Otherwise the configuration is
177
 
        loaded from the given file.  If the file doesn't exist, an empty
178
 
        configuration is loaded.
179
 
 
180
 
        After the configuration is loaded, it is applied to the given
181
 
        ``touchpad``.
182
 
 
183
 
        ``touchpad`` is a :class:`~synaptiks.touchpad.Touchpad` object.
184
 
        ``filename`` is either ``None`` or a string containing the path to a
185
 
        file.
186
 
 
187
 
        Return a :class:`TouchpadConfiguration` object.  Raise
188
 
        :exc:`~exceptions.EnvironmentError`, if the file could not be loaded,
189
 
        but *not* in case of a non-existing file.
190
 
        """
191
 
        if not filename:
192
 
            filename = get_touchpad_config_file_path()
193
 
        config = cls(touchpad)
194
 
        config.update(load_json_with_default(filename, {}))
195
 
        return config
196
 
 
197
 
    def __init__(self, touchpad):
198
 
        """
199
 
        Create a new configuration from the given ``touchpad``.
200
 
 
201
 
        ``touchpad`` is a :class:`~synaptiks.touchpad.Touchpad` object.
202
 
        """
203
 
        self.touchpad = touchpad
204
 
 
205
 
    def __contains__(self, key):
206
 
        return key in self.CONFIG_KEYS
207
 
 
208
 
    def __len__(self):
209
 
        return len(self.CONFIG_KEYS)
210
 
 
211
 
    def __iter__(self):
212
 
        return iter(self.CONFIG_KEYS)
213
 
 
214
 
    def __getitem__(self, key):
215
 
        if key not in self:
216
 
            raise KeyError(key)
217
 
        value = getattr(self.touchpad, key)
218
 
        if isinstance(value, float):
219
 
            # round floats for the sake of comparability and readability
220
 
            value = round(value, 5)
221
 
        return value
222
 
 
223
 
    def __setitem__(self, key, value):
224
 
        if key not in self:
225
 
            raise KeyError(key)
226
 
        setattr(self.touchpad, key, value)
227
 
 
228
 
    def __delitem__(self, key):
229
 
        raise NotImplementedError
230
 
 
231
 
    def save(self, filename=None):
232
 
        """
233
 
        Save the configuration.
234
 
 
235
 
        If no ``filename`` is given, the configuration is saved to the default
236
 
        configuration file as returned by
237
 
        :func:`get_touchpad_config_file_path`.  Otherwise the configuration is
238
 
        saved to the given file.
239
 
 
240
 
        ``filename`` is either ``None`` or a string containing the path to a
241
 
        file.
242
 
 
243
 
        Raise :exc:`~exceptions.EnvironmentError`, if the file could not be
244
 
        written.
245
 
        """
246
 
        if not filename:
247
 
            filename = get_touchpad_config_file_path()
248
 
        save_json(filename, dict(self))
249
 
 
250
 
 
251
 
class ManagerConfiguration(MutableMapping):
252
 
    """
253
 
    A mutable mapping class representing the configuration of a
254
 
    :class:`~synaptiks.management.TouchpadManager`.
255
 
    """
256
 
 
257
 
    #: A mapping with the default values for all configuration keys
258
 
    DEFAULTS = {'monitor_mouses': False, 'ignored_mouses': [],
259
 
                'monitor_keyboard': False, 'idle_time': 2.0,
260
 
                'keys_to_ignore': 2}
261
 
 
262
 
    #: config keys to be applied to the mouse_manager
263
 
    MOUSE_MANAGER_KEYS = frozenset(['ignored_mouses'])
264
 
    #: config keys to be applied to the keyboard monitor
265
 
    KEYBOARD_MONITOR_KEYS = frozenset(['idle_time', 'keys_to_ignore'])
266
 
 
267
 
    @classmethod
268
 
    def load(cls, touchpad_manager, filename=None):
269
 
        """
270
 
        Load the configuration for the given ``touchpad_manager`` from disc.
271
 
 
272
 
        If no ``filename`` is given, the configuration is loaded from the
273
 
        default configuration file as returned by
274
 
        :func:`get_management_config_file_path`.  Otherwise the configuration
275
 
        is loaded from the given file.  If the file doesn't exist, the default
276
 
        config as given by :attr:`DEFAULTS` is loaded.
277
 
 
278
 
        After the configuration is loaded, it is applied to the given
279
 
        ``touchpad_manager``.
280
 
 
281
 
        ``touchpad_manager`` is a
282
 
        :class:`~synaptiks.management.TouchpadManager` object.  ``filename`` is
283
 
        either ``None`` or a string containing the path to a file.
284
 
 
285
 
        Return a :class:`ManagerConfiguration` object.  Raise
286
 
        :exc:`~exceptions.EnvironmentError`, if the file could not be loaded,
287
 
        but *not* in case of a non-existing file.
288
 
        """
289
 
        if not filename:
290
 
            filename = get_management_config_file_path()
291
 
        config = cls(touchpad_manager)
292
 
        # use defaults for all non-existing settings
293
 
        loaded_config = dict(cls.DEFAULTS)
294
 
        loaded_config.update(load_json_with_default(filename, {}))
295
 
        config.update(loaded_config)
296
 
        return config
297
 
 
298
 
    def __init__(self, touchpad_manager):
299
 
        self.touchpad_manager = touchpad_manager
300
 
 
301
 
    @property
302
 
    def mouse_manager(self):
303
 
        return self.touchpad_manager.mouse_manager
304
 
 
305
 
    @property
306
 
    def keyboard_monitor(self):
307
 
        return self.touchpad_manager.keyboard_monitor
308
 
 
309
 
    def __contains__(self, key):
310
 
        return key in self.DEFAULTS
311
 
 
312
 
    def __len__(self):
313
 
        return len(self.DEFAULTS)
314
 
 
315
 
    def __iter__(self):
316
 
        return iter(self.DEFAULTS)
317
 
 
318
 
    def __getitem__(self, key):
319
 
        if key not in self:
320
 
            raise KeyError(key)
321
 
        target = self.touchpad_manager
322
 
        if key in self.MOUSE_MANAGER_KEYS:
323
 
            target = self.mouse_manager
324
 
        elif key in self.KEYBOARD_MONITOR_KEYS:
325
 
            target = self.keyboard_monitor
326
 
        return getattr(target, key)
327
 
 
328
 
    def __setitem__(self, key, value):
329
 
        if key not in self:
330
 
            raise KeyError(key)
331
 
        target = self.touchpad_manager
332
 
        if key in self.MOUSE_MANAGER_KEYS:
333
 
            target = self.mouse_manager
334
 
        elif key in self.KEYBOARD_MONITOR_KEYS:
335
 
            target = self.keyboard_monitor
336
 
        setattr(target, key, value)
337
 
 
338
 
    def __delitem__(self, key):
339
 
        raise NotImplementedError
340
 
 
341
 
    def update(self, other):
342
 
        ignored_mouses = other.pop('ignored_mouses')
343
 
        if other.pop('monitor_mouses'):
344
 
            self.mouse_manager.ignored_mouses = ignored_mouses
345
 
            self.touchpad_manager.monitor_mouses = True
346
 
        else:
347
 
            self.touchpad_manager.monitor_mouses = False
348
 
            self.mouse_manager.ignored_mouses = ignored_mouses
349
 
        super(ManagerConfiguration, self).update(other)
350
 
 
351
 
    def save(self, filename=None):
352
 
        """
353
 
        Save the configuration.
354
 
 
355
 
        If no ``filename`` is given, the configuration is saved to the default
356
 
        configuration file as returned by
357
 
        :func:`get_management_config_file_path`.  Otherwise the configuration
358
 
        is saved to the given file.
359
 
 
360
 
        ``filename`` is either ``None`` or a string containing the path to a
361
 
        file.
362
 
 
363
 
        Raise :exc:`~exceptions.EnvironmentError`, if the file could not be
364
 
        written.
365
 
        """
366
 
        if not filename:
367
 
            filename = get_management_config_file_path()
368
 
        save_json(filename, dict(self))
369
 
 
370
 
 
371
 
def main():
372
 
    from argparse import ArgumentParser
373
 
 
374
 
    from synaptiks import __version__
375
 
    from synaptiks.touchpad import Touchpad
376
 
    from synaptiks._bindings import xlib
377
 
 
378
 
    parser = ArgumentParser(
379
 
        description='synaptiks touchpad configuration utility',
380
 
        epilog="""\
381
 
Copyright (C) 2010 Sebastian Wiesner <lunaryorn@googlemail.com>,
382
 
distributed under the terms of the BSD License""")
383
 
    parser.add_argument('--version', help='Show synaptiks version',
384
 
                        action='version', version=__version__)
385
 
    actions = parser.add_subparsers(title='Actions')
386
 
 
387
 
    init_act = actions.add_parser(
388
 
        'init', help='Initialize touchpad configuration.  Should not be '
389
 
        'called manually, but automatically at session startup.')
390
 
    init_act.set_defaults(action='init')
391
 
 
392
 
    load_act = actions.add_parser(
393
 
        'load', help='Load the touchpad configuration')
394
 
    load_act.add_argument(
395
 
        'filename', nargs='?', help='File to load the configuration from.  If '
396
 
        'empty, the default configuration file is loaded.')
397
 
    load_act.set_defaults(action='load')
398
 
 
399
 
    save_act = actions.add_parser(
400
 
        'save', help='Save the current touchpad configuration')
401
 
    save_act.add_argument(
402
 
        'filename', nargs='?', help='File to save the configuration to.  If '
403
 
        'empty, the default configuration file is used.')
404
 
    save_act.set_defaults(action='save')
405
 
 
406
 
    # default filename to load configuration from
407
 
    parser.set_defaults(filename=None)
408
 
 
409
 
    # we don't have any arguments, but need to make sure, that the builtin
410
 
    # arguments (--help mainly) are handled
411
 
    args = parser.parse_args()
412
 
 
413
 
    with xlib.display() as display:
414
 
        touchpad = Touchpad.find_first(display)
415
 
 
416
 
        if args.action == 'init':
417
 
            driver_defaults = TouchpadConfiguration(touchpad)
418
 
            driver_defaults.save(get_touchpad_defaults_file_path())
419
 
        if args.action in ('init', 'load'):
420
 
            TouchpadConfiguration.load(touchpad, filename=args.filename)
421
 
        if args.action == 'save':
422
 
            current_config = TouchpadConfiguration(touchpad)
423
 
            current_config.save(filename=args.filename)
424
 
 
425
 
 
426
 
if __name__ == '__main__':
427
 
    main()