Merge lp:~sylvain-pineau/checkbox/plainbox-support into lp:checkbox
- plainbox-support
- Merge into trunk
Proposed by
Sylvain Pineau
Status: | Merged |
---|---|
Approved by: | Zygmunt Krynicki |
Approved revision: | 2594 |
Merged at revision: | 2588 |
Proposed branch: | lp:~sylvain-pineau/checkbox/plainbox-support |
Merge into: | lp:checkbox |
Diff against target: |
127501 lines (+126960/-1) 98 files modified
checkbox-support/README.rst (+6/-0) checkbox-support/checkbox_support/contrib/xrandr.py (+1064/-0) checkbox-support/checkbox_support/dbus/__init__.py (+89/-0) checkbox-support/checkbox_support/dbus/udisks2.py (+479/-0) checkbox-support/checkbox_support/heuristics/__init__.py (+56/-0) checkbox-support/checkbox_support/heuristics/tests/test_udisks2.py (+40/-0) checkbox-support/checkbox_support/heuristics/udev.py (+44/-0) checkbox-support/checkbox_support/heuristics/udisks2.py (+62/-0) checkbox-support/checkbox_support/lib/bit.py (+46/-0) checkbox-support/checkbox_support/lib/conversion.py (+172/-0) checkbox-support/checkbox_support/lib/dmi.py (+241/-0) checkbox-support/checkbox_support/lib/input.py (+585/-0) checkbox-support/checkbox_support/lib/path.py (+62/-0) checkbox-support/checkbox_support/lib/pci.py (+89/-0) checkbox-support/checkbox_support/lib/template.py (+143/-0) checkbox-support/checkbox_support/lib/tz.py (+55/-0) checkbox-support/checkbox_support/lib/usb.py (+59/-0) checkbox-support/checkbox_support/parsers/cpuinfo.py (+180/-0) checkbox-support/checkbox_support/parsers/dmidecode.py (+126/-0) checkbox-support/checkbox_support/parsers/efi.py (+52/-0) checkbox-support/checkbox_support/parsers/lshwjson.py (+23/-0) checkbox-support/checkbox_support/parsers/meminfo.py (+46/-0) checkbox-support/checkbox_support/parsers/modinfo.py (+89/-0) checkbox-support/checkbox_support/parsers/pactl.py (+543/-0) checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_quantal.txt (+143/-0) checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_toshiba.txt (+166/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-0.txt (+33/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-1.txt (+51/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-2.txt (+30/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise.txt (+41/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise-radeon-hdmi-available.txt (+608/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise-radeon.txt (+608/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise-xps1340.txt (+466/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise.txt (+696/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-raring-t430s-dp-available.txt (+889/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-raring-t430s.txt (+889/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/modules-desktop-precise-0.txt (+8/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/modules-desktop-precise.txt (+213/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/samples-desktop-precise.txt (+60/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/sinks-desktop-precise-0.txt (+53/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/sinks-desktop-precise-1.txt (+56/-0) checkbox-support/checkbox_support/parsers/tests/pactl_data/sinks-desktop-precise.txt (+56/-0) checkbox-support/checkbox_support/parsers/tests/test_dmi.py (+80/-0) checkbox-support/checkbox_support/parsers/tests/test_dmidecode.py (+60/-0) checkbox-support/checkbox_support/parsers/tests/test_efi.py (+63/-0) checkbox-support/checkbox_support/parsers/tests/test_pactl.py (+561/-0) checkbox-support/checkbox_support/parsers/tests/test_udevadm.py (+628/-0) checkbox-support/checkbox_support/parsers/tests/test_xinput.py (+130/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/CALXEDA_HIGHBANK.txt (+6826/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_INSPIRON3521_TOUCHSCREEN.txt (+5642/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_LATITUDEE4310.txt (+5158/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_LATITUDEE6430.txt (+5516/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_OPTIPLEX9020AIO.txt (+5518/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_VOSTRO3460_FINGERPRINT.txt (+5373/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_VOSTROV131.txt (+5357/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/DELL_XPS1340.txt (+5621/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/HOME_MADE.txt (+5383/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/HP_ENVY_15_MEDIATEK_BT.txt (+5163/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/HP_PAVILION14_NOTEBOOK_MEDIATEK_BT.txt (+4947/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/HP_PAVILIONSLEEKBOOK14_ACCELEROMETER.txt (+4447/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/HP_PRO2110.txt (+4642/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/HP_PROBOOK6550B_ACCELEROMETER.txt (+5636/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/LENOVO_E431.txt (+5716/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/LENOVO_E445.txt (+5546/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/LENOVO_T420.txt (+5034/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/LENOVO_T430S.txt (+7630/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/PANDABOARD.txt (+4237/-0) checkbox-support/checkbox_support/parsers/tests/udevadm_data/SAMSUNG_N310.txt (+4092/-0) checkbox-support/checkbox_support/parsers/udevadm.py (+781/-0) checkbox-support/checkbox_support/parsers/xinput.py (+198/-0) checkbox-support/checkbox_support/scripts/audio_settings.py (+375/-0) checkbox-support/checkbox_support/scripts/gputest_benchmark.py (+106/-0) checkbox-support/checkbox_support/scripts/tests/test_audio_settings.py (+151/-0) checkbox-support/checkbox_support/scripts/tests/test_gputest_benchmark.py (+78/-0) checkbox-support/checkbox_support/tests/__init__.py (+50/-0) checkbox-support/checkbox_support/udev.py (+93/-0) checkbox-support/checkbox_support/vendor/__init__.py (+28/-0) checkbox-support/checkbox_support/vendor/mock.py (+2367/-0) checkbox-support/setup.py (+40/-0) plainbox-provider-checkbox/provider_bin/accelerometer_test (+347/-0) plainbox-provider-checkbox/provider_bin/audio_driver_info (+109/-0) plainbox-provider-checkbox/provider_bin/audio_settings (+9/-0) plainbox-provider-checkbox/provider_bin/filter_templates (+134/-0) plainbox-provider-checkbox/provider_bin/gputest_benchmark (+27/-0) plainbox-provider-checkbox/provider_bin/graphics_modes_info (+74/-0) plainbox-provider-checkbox/provider_bin/graphics_stress_test (+467/-0) plainbox-provider-checkbox/provider_bin/lock_screen_watcher (+101/-0) plainbox-provider-checkbox/provider_bin/lsmod_info (+40/-0) plainbox-provider-checkbox/provider_bin/memory_compare (+91/-0) plainbox-provider-checkbox/provider_bin/network_bandwidth_test (+671/-0) plainbox-provider-checkbox/provider_bin/network_device_info (+262/-0) plainbox-provider-checkbox/provider_bin/removable_storage_test (+594/-0) plainbox-provider-checkbox/provider_bin/removable_storage_watcher (+890/-0) plainbox-provider-checkbox/provider_bin/rotation_test (+73/-0) plainbox-provider-checkbox/provider_bin/run_templates (+144/-0) plainbox-provider-checkbox/provider_bin/touchpad_driver_info (+82/-0) plainbox-provider-checkbox/provider_bin/udisks2_monitor (+154/-0) setup.py (+1/-1) |
To merge this branch: | bzr merge lp:~sylvain-pineau/checkbox/plainbox-support |
Related bugs: | |
Related blueprints: |
python3-checkbox split
(Undefined)
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Sylvain Pineau (community) | Needs Resubmitting | ||
Review via email: mp+198573@code.launchpad.net |
Commit message
Description of the change
Implementation proposal of the new support library for plainbox provider scripts
Notes:
- Proposed package name: checkbox-support
- Proposed python namespace: checkbox_support
- Remove the original checkbox.
- Tests run ok
To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote : | # |
Revision history for this message
Sylvain Pineau (sylvain-pineau) wrote : | # |
It's now ready for review/testing
review:
Needs Resubmitting
Revision history for this message
Zygmunt Krynicki (zyga) wrote : | # |
This looks good.
I need to look at it in the tree and think about any potential issues but you have my tentative +1
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'checkbox-support' |
2 | === added symlink 'checkbox-support/COPYING' |
3 | === target is u'../plainbox/COPYING' |
4 | === added file 'checkbox-support/README.rst' |
5 | --- checkbox-support/README.rst 1970-01-01 00:00:00 +0000 |
6 | +++ checkbox-support/README.rst 2014-01-07 13:44:32 +0000 |
7 | @@ -0,0 +1,6 @@ |
8 | +CheckBox Support |
9 | +================ |
10 | + |
11 | +CheckBox Support library is a collection of python modules used by PlainBox |
12 | +providers scripts. |
13 | +They were originally provided by the checkbox python package. |
14 | |
15 | === added directory 'checkbox-support/checkbox_support' |
16 | === added file 'checkbox-support/checkbox_support/__init__.py' |
17 | === added directory 'checkbox-support/checkbox_support/contrib' |
18 | === added file 'checkbox-support/checkbox_support/contrib/__init__.py' |
19 | === added file 'checkbox-support/checkbox_support/contrib/xrandr.py' |
20 | --- checkbox-support/checkbox_support/contrib/xrandr.py 1970-01-01 00:00:00 +0000 |
21 | +++ checkbox-support/checkbox_support/contrib/xrandr.py 2014-01-07 13:44:32 +0000 |
22 | @@ -0,0 +1,1064 @@ |
23 | +#!/usr/bin/python3 |
24 | +# -*- coding: utf-8 -*- |
25 | +# |
26 | +# Python-XRandR provides a high level API for the XRandR extension of the |
27 | +# X.org server. XRandR allows to configure resolution, refresh rate, rotation |
28 | +# of the screen and multiple outputs of graphics cards. |
29 | +# |
30 | +# Copyright 2007 © Sebastian Heinlein <sebastian.heinlein@web.de> |
31 | +# Copyright 2007 © Michael Vogt <mvo@ubuntu.com> |
32 | +# Copyright 2007 © Canonical Ltd. |
33 | +# |
34 | +# In many aspects it follows the design of the xrand tool of the X.org, which |
35 | +# comes with the following copyright: |
36 | +# |
37 | +# Copyright © 2001 Keith Packard, member of The XFree86 Project, Inc. |
38 | +# Copyright © 2002 Hewlett Packard Company, Inc. |
39 | +# Copyright © 2006 Intel Corporation |
40 | +# |
41 | +# And can be downloaded here: |
42 | +# |
43 | +# git://anongit.freedesktop.org/git/xorg/app/xrandr |
44 | +# |
45 | +# This library is free software; you can redistribute it and/or |
46 | +# modify it under the terms of the GNU Lesser General Public |
47 | +# License as published by the Free Software Foundation; either |
48 | +# version 2.1 of the License, or any later version. |
49 | +# |
50 | +# This library is distributed in the hope that it will be useful, |
51 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
52 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
53 | +# Lesser General Public License for more details. |
54 | +# |
55 | +# You should have received a copy of the GNU Lesser General Public |
56 | +# License along with this library; if not, write to the Free Software |
57 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
58 | +# MA 02110-1301 USA |
59 | + |
60 | +from ctypes import ( |
61 | + POINTER, |
62 | + Structure, |
63 | + byref, |
64 | + c_char_p, |
65 | + c_void_p, |
66 | + c_int, |
67 | + c_long, |
68 | + c_ulong, |
69 | + c_ushort, |
70 | + cdll, |
71 | +) |
72 | +import os |
73 | + |
74 | +RR_ROTATE_0 = 1 |
75 | +RR_ROTATE_90 = 2 |
76 | +RR_ROTATE_180 = 4 |
77 | +RR_ROTATE_270 = 8 |
78 | +RR_REFLECT_X = 16 |
79 | +RR_REFLECT_Y = 32 |
80 | + |
81 | +RR_CONNECTED = 0 |
82 | +RR_DISCONNECTED = 1 |
83 | +RR_UNKOWN_CONNECTION = 2 |
84 | + |
85 | +RR_BAD_OUTPUT = 0 |
86 | +RR_BAD_CRTC = 1 |
87 | +RR_BAD_MODE = 2 |
88 | + |
89 | +RR_SET_CONFIG_SUCCESS = 0 |
90 | +RR_SET_CONFIG_INVALID_CONFIG_TIME = 1 |
91 | +RR_SET_CONFIG_INVALID_TIME = 2 |
92 | +RR_SET_CONFIG_FAILED = 3 |
93 | + |
94 | +# Flags to keep track of changes |
95 | +CHANGES_NONE = 0 |
96 | +CHANGES_CRTC = 1 |
97 | +CHANGES_MODE = 2 |
98 | +CHANGES_RELATION = 4 |
99 | +CHANGES_POSITION = 8 |
100 | +CHANGES_ROTATION = 16 |
101 | +CHANGES_REFLECTION = 32 |
102 | +CHANGES_AUTOMATIC = 64 |
103 | +CHANGES_REFRESH = 128 |
104 | +CHANGES_PROPERTY = 256 |
105 | + |
106 | +# Relation information |
107 | +RELATION_ABOVE = 0 |
108 | +RELATION_BELOW = 1 |
109 | +RELATION_RIGHT_OF = 2 |
110 | +RELATION_LEFT_OF = 3 |
111 | +RELATION_SAME_AS = 4 |
112 | + |
113 | +# some fundamental datatypes |
114 | +RRCrtc = c_long |
115 | +RROutput = c_long |
116 | +RRMode = c_long |
117 | +Connection = c_ushort |
118 | +SubpixelOrder = c_ushort |
119 | +Time = c_ulong |
120 | +Rotation = c_ushort |
121 | +Status = c_int |
122 | + |
123 | +# load the libs |
124 | +xlib = cdll.LoadLibrary('libX11.so.6') |
125 | +rr = cdll.LoadLibrary('libXrandr.so.2') |
126 | + |
127 | + |
128 | +# query resources |
129 | +class _XRRModeInfo(Structure): |
130 | + _fields_ = [ |
131 | + ("id", RRMode), # XID is c_long |
132 | + ("width", c_int), |
133 | + ("height", c_int), |
134 | + ("dotClock", c_long), |
135 | + ("hSyncStart", c_int), |
136 | + ("hSyncEnd", c_int), |
137 | + ("hTotal", c_int), |
138 | + ("hSkew", c_int), |
139 | + ("vSyncStart", c_int), |
140 | + ("vSyncEnd", c_int), |
141 | + ("vTotal", c_int), |
142 | + ("name", c_char_p), |
143 | + ("nameLength", c_int), |
144 | + ("modeFlags", c_long), |
145 | + ] |
146 | + |
147 | + |
148 | +class _XRRScreenSize(Structure): |
149 | + _fields_ = [ |
150 | + ("width", c_int), |
151 | + ("height", c_int), |
152 | + ("mwidth", c_int), |
153 | + ("mheight", c_int) |
154 | + ] |
155 | + |
156 | + |
157 | +class _XRRCrtcInfo(Structure): |
158 | + _fields_ = [ |
159 | + ("timestamp", Time), |
160 | + ("x", c_int), |
161 | + ("y", c_int), |
162 | + ("width", c_int), |
163 | + ("height", c_int), |
164 | + ("mode", RRMode), |
165 | + ("rotation", c_int), |
166 | + ("noutput", c_int), |
167 | + ("outputs", POINTER(RROutput)), |
168 | + ("rotations", Rotation), |
169 | + ("npossible", c_int), |
170 | + ("possible", POINTER(RROutput)), |
171 | + ] |
172 | + |
173 | + |
174 | +class _XRRScreenResources(Structure): |
175 | + _fields_ = [ |
176 | + ("timestamp", Time), |
177 | + ("configTimestamp", Time), |
178 | + ("ncrtc", c_int), |
179 | + ("crtcs", POINTER(RRCrtc)), |
180 | + ("noutput", c_int), |
181 | + ("outputs", POINTER(RROutput)), |
182 | + ("nmode", c_int), |
183 | + ("modes", POINTER(_XRRModeInfo)), |
184 | + ] |
185 | + |
186 | + |
187 | +class RRError(Exception): |
188 | + """Base exception class of the module""" |
189 | + pass |
190 | + |
191 | + |
192 | +class UnsupportedRRError(RRError): |
193 | + """Raised if the required XRandR extension version is not available""" |
194 | + def __init__(self, required, current): |
195 | + self.required = required |
196 | + self.current = current |
197 | + |
198 | + |
199 | +# XRRGetOutputInfo |
200 | +class _XRROutputInfo(Structure): |
201 | + _fields_ = [ |
202 | + ("timestamp", Time), |
203 | + ("crtc", c_int), |
204 | + ("name", c_char_p), |
205 | + ("nameLen", c_int), |
206 | + ("mm_width", c_ulong), |
207 | + ("mm_height", c_ulong), |
208 | + ("connection", Connection), |
209 | + ("subpixel_order", SubpixelOrder), |
210 | + ("ncrtc", c_int), |
211 | + ("crtcs", POINTER(RRCrtc)), |
212 | + ("nclone", c_int), |
213 | + ("clones", POINTER(RROutput)), |
214 | + ("nmode", c_int), |
215 | + ("npreferred", c_int), |
216 | + ("modes", POINTER(RRMode)) |
217 | + ] |
218 | + |
219 | + |
220 | +class _XRRCrtcGamma(Structure): |
221 | + _fields_ = [ |
222 | + ('size', c_int), |
223 | + ('red', POINTER(c_ushort)), |
224 | + ('green', POINTER(c_ushort)), |
225 | + ('blue', POINTER(c_ushort)), |
226 | + ] |
227 | + |
228 | + |
229 | +def _array_conv(array, type, conv=lambda x: x): |
230 | + length = len(array) |
231 | + res = (type * length)() |
232 | + for i in range(length): |
233 | + res[i] = conv(array[i]) |
234 | + return res |
235 | + |
236 | + |
237 | +class Output: |
238 | + """The output is a reference to a supported output jacket of the graphics |
239 | + card. Outputs are attached to a hardware pipe to be used. Furthermore |
240 | + they can be a clone of another output or show a subset of the screen""" |
241 | + def __init__(self, info, id, screen): |
242 | + """Initializes an output instance""" |
243 | + self._info = info |
244 | + self.id = id |
245 | + self._screen = screen |
246 | + # Store changes later here |
247 | + self._mode = None |
248 | + self._crtc = None |
249 | + self._rotation = RR_ROTATE_0 |
250 | + self._relation = None |
251 | + self._relation_offset = 0 |
252 | + self._relative_to = None |
253 | + self._position = None |
254 | + self._reflection = None |
255 | + self._automatic = None |
256 | + self._rate = None |
257 | + self._changes = CHANGES_NONE |
258 | + self._x = 0 |
259 | + self._y = 0 |
260 | + |
261 | + self.name = self._info.contents.name |
262 | + |
263 | + def __del__(self): |
264 | + """Frees the internal reference to the output info if the output gets |
265 | + removed""" |
266 | + rr.XRRFreeOutputInfo(self._info) |
267 | + |
268 | + def get_physical_width(self): |
269 | + """Returns the display width reported by the connected output device""" |
270 | + return self._info.contents.mm_width |
271 | + |
272 | + def get_physical_height(self): |
273 | + """ |
274 | + Returns the display height reported by the connected output device |
275 | + """ |
276 | + return self._info.contents.mm_height |
277 | + |
278 | + def get_crtc(self): |
279 | + """Returns the xid of the hardware pipe to which the output is |
280 | + attached. If the output is disabled it will return 0""" |
281 | + return self._info.contents.crtc |
282 | + |
283 | + def get_crtcs(self): |
284 | + """Returns the xids of the hardware pipes to which the output could |
285 | + be attached""" |
286 | + crtcs = [] |
287 | + for i in range(self._info.contents.ncrtc): |
288 | + for crtc in self._screen.crtcs: |
289 | + if crtc.xid == self._info.contents.crtcs[i]: |
290 | + crtcs.append(crtc) |
291 | + return crtcs |
292 | + |
293 | + def get_available_rotations(self): |
294 | + """Returns a binary flag of the supported rotations of the output or |
295 | + 0 if the output is disabled""" |
296 | + rotations = RR_ROTATE_0 |
297 | + found = False |
298 | + if self.is_active(): |
299 | + # Get the rotations supported by all crtcs to make assigning |
300 | + # crtcs easier. Furthermore there don't seem to be so many |
301 | + # cards which show another behavior |
302 | + for crtc in self.get_crtcs(): |
303 | + # Set rotations to the value of the first found crtc and |
304 | + # then create a subset only for all other crtcs |
305 | + if not found: |
306 | + rotations = crtc.get_available_rotations() |
307 | + found = True |
308 | + else: |
309 | + rotations = rotations & crtc.get_available_rotations() |
310 | + return rotations |
311 | + |
312 | + def get_available_modes(self): |
313 | + """Returns the list of supported mode lines (resolution, refresh rate) |
314 | + that are supported by the connected device""" |
315 | + modes = [] |
316 | + for m in range(self._info.contents.nmode): |
317 | + output_modes = self._info.contents.modes |
318 | + for s in range(self._screen._resources.contents.nmode): |
319 | + screen_modes = self._screen._resources.contents.modes |
320 | + if screen_modes[s].id == output_modes[m]: |
321 | + modes.append(screen_modes[s]) |
322 | + return modes |
323 | + |
324 | + def get_preferred_mode(self): |
325 | + """Returns an index that refers to the list of available modes and |
326 | + points to the preferred mode of the connected device""" |
327 | + return self._info.contents.npreferred |
328 | + |
329 | + def is_active(self): |
330 | + """Returns True if the output is attached to a hardware pipe, is |
331 | + enabled""" |
332 | + return self._info.contents.crtc != 0 |
333 | + |
334 | + def is_connected(self): |
335 | + """Return True if a device is detected at the output""" |
336 | + if self._info.contents.connection in (RR_CONNECTED, |
337 | + RR_UNKOWN_CONNECTION): |
338 | + return True |
339 | + return False |
340 | + |
341 | + def disable(self): |
342 | + """Disables the output""" |
343 | + if not self.is_active(): |
344 | + return |
345 | + self._mode = None |
346 | + self._crtc._outputs.remove(self) |
347 | + self._crtc = None |
348 | + self._changes = self._changes | CHANGES_CRTC | CHANGES_MODE |
349 | + |
350 | + def set_to_mode(self, mode): |
351 | + modes = self.get_available_modes() |
352 | + if mode in range(len(modes)): |
353 | + self._mode = modes[mode].id |
354 | + return |
355 | + raise RRError("Mode is not available") |
356 | + |
357 | + def set_to_preferred_mode(self): |
358 | + modes = self.get_available_modes() |
359 | + mode = modes[self.get_preferred_mode()] |
360 | + if mode != None: |
361 | + self._mode = mode.id |
362 | + return |
363 | + raise RRError("Preferred mode is not available") |
364 | + |
365 | + def get_clones(self): |
366 | + """Returns the xids of the outputs which can be clones of the output""" |
367 | + clones = [] |
368 | + for i in range(self._info.contents.nclone): |
369 | + id = self._info.contents.clones[i] |
370 | + o = self._screen.get_output_by_id(id) |
371 | + clones.append(o) |
372 | + return clones |
373 | + |
374 | + def set_relation(self, relative, relation, offset=0): |
375 | + """Sets the position of the output in relation to the given one""" |
376 | + rel = self._screen.get_output_by_name(relative) |
377 | + if rel and relation in (RELATION_LEFT_OF, RELATION_RIGHT_OF, |
378 | + RELATION_ABOVE, RELATION_BELOW, |
379 | + RELATION_SAME_AS): |
380 | + self._relation = relation |
381 | + self._relative_to = rel |
382 | + self._relation_offset = offset |
383 | + self._changes = self._changes | CHANGES_RELATION |
384 | + else: |
385 | + raise RRError("The given relative output or relation is not " |
386 | + "available") |
387 | + |
388 | + def has_changed(self, changes=None): |
389 | + """Checks if the output has changed: Either for a specific change or |
390 | + generally""" |
391 | + if changes: |
392 | + return self._changes & changes |
393 | + else: |
394 | + return self._changes != CHANGES_NONE |
395 | + |
396 | + |
397 | +class Crtc: |
398 | + """The crtc is a reference to a hardware pipe that is provided by the |
399 | + graphics device. Outputs can be attached to crtcs""" |
400 | + |
401 | + def __init__(self, info, xid, screen): |
402 | + """Initializes the hardware pipe object""" |
403 | + self._info = info |
404 | + self.xid = xid |
405 | + self._screen = screen |
406 | + self._outputs = [] |
407 | + |
408 | + def __del__(self): |
409 | + """Frees the reference to the rendering pipe if the instance gets |
410 | + removed""" |
411 | + rr.XRRFreeCrtcConfigInfo(self._info) |
412 | + |
413 | + def get_xid(self): |
414 | + """Returns the internal id of the crtc from the X server""" |
415 | + return self.xid |
416 | + |
417 | + def get_available_rotations(self): |
418 | + """Returns a binary flag that contains the supported rotations of the |
419 | + hardware pipe""" |
420 | + return self._info.contents.rotations |
421 | + |
422 | + def set_config(self, x, y, mode, outputs, rotation=RR_ROTATE_0): |
423 | + """Configures the render pipe with the given mode and outputs. X and y |
424 | + set the position of the crtc output in the screen""" |
425 | + rr.XRRSetCrtcConfig(self._screen._display, |
426 | + self._screen._resources, |
427 | + self.xid, |
428 | + self._screen.get_timestamp(), |
429 | + c_int(x), c_int(y), |
430 | + mode, |
431 | + rotation, |
432 | + _array_conv(outputs, RROutput, lambda x: x.id), |
433 | + len(outputs)) |
434 | + |
435 | + def apply_changes(self): |
436 | + """Applies the stored changes""" |
437 | + if len(self._outputs) > 0: |
438 | + output = self._outputs[0] |
439 | + self.set_config(output._x, output._y, output._mode, |
440 | + self._outputs, output._rotation) |
441 | + else: |
442 | + self.disable() |
443 | + |
444 | + def disable(self): |
445 | + """Turns off all outputs on the crtc""" |
446 | + rr.XRRSetCrtcConfig(self._screen._display, |
447 | + self._screen._resources, |
448 | + self.xid, |
449 | + self._screen.get_timestamp(), |
450 | + 0, 0, |
451 | + None, |
452 | + RR_ROTATE_0, |
453 | + 0, 0) |
454 | + |
455 | + #FIXME: support gamma settings |
456 | + """ |
457 | + def get_gamma_size(self): |
458 | + return rr.XRRGetCrtcGammaSize(self._screen._display, self.id) |
459 | + def get_gamma(self): |
460 | + result = rr.XRRGetCrtcGamma(self._screen._display, self.id) |
461 | + return _from_gamma(result) |
462 | + def set_gamma(self, gamma): |
463 | + g = _to_gamma(gamma) |
464 | + rr.XRRSetCrtcGamma(self._screen._display, self.id, g) |
465 | + rr.XRRFreeGamma(g) |
466 | + gamma = property(get_gamma, set_gamma)""" |
467 | + |
468 | + def load_outputs(self): |
469 | + """Get the currently assigned outputs""" |
470 | + outputs = [] |
471 | + for i in range(self._info.contents.noutput): |
472 | + id = self._info.contents.outputs[i] |
473 | + o = self._screen.get_output_by_id(id) |
474 | + outputs.append(o) |
475 | + self._outputs = outputs |
476 | + |
477 | + def get_outputs(self): |
478 | + """Returns the list of attached outputs""" |
479 | + return self._outputs |
480 | + |
481 | + def add_output(self, output): |
482 | + """Adds the specified output to the crtc""" |
483 | + output._crtc = self |
484 | + self._outputs.append(output) |
485 | + |
486 | + def supports_output(self, output): |
487 | + """Check if the output can be used by the crtc. |
488 | + See check_crtc_for_output in xrandr.c""" |
489 | + if not self.xid in [c.xid for c in output.get_crtcs()]: |
490 | + return False |
491 | + if len(self._outputs): |
492 | + for other in self._outputs: |
493 | + if other == output: |
494 | + continue |
495 | + if other._x != output._x: |
496 | + return False |
497 | + if other._y != output._y: |
498 | + return False |
499 | + if other._mode != output._mode: |
500 | + return False |
501 | + if other._rotation != output._rotation: |
502 | + return False |
503 | + #FIXME: pick_crtc is still missing |
504 | + elif self._info.contents.noutput > 0: |
505 | + if self._info.contents.x != output._x: |
506 | + return False |
507 | + if self._info.contents.y != output._y: |
508 | + return False |
509 | + if self._info.contents.mode_info != output._mode: |
510 | + return False |
511 | + if self._info.rotation != output._rotation: |
512 | + return False |
513 | + return True |
514 | + |
515 | + def supports_rotation(self, rotation): |
516 | + """Check if the given rotation is supported by the crtc""" |
517 | + rotations = self._info.contents.rotations |
518 | + dir = rotation & (RR_ROTATE_0 | RR_ROTATE_90 | RR_ROTATE_180 | |
519 | + RR_ROTATE_270) |
520 | + reflect = rotation & (RR_REFLECT_X | RR_REFLECT_Y) |
521 | + if (((rotations & dir) != 0) and ((rotations & reflect) == reflect)): |
522 | + return True |
523 | + return False |
524 | + |
525 | + def has_changed(self): |
526 | + """Check if there are any new outputs assigned to the crtc or any |
527 | + outputs with a changed mode or position""" |
528 | + if len(self._outputs) != self._info.contents.noutput: |
529 | + return True |
530 | + for i in range(self._info.contents.noutput): |
531 | + id = self._info.contents.outputs[i] |
532 | + output = self._screen.get_output_by_id(id) |
533 | + if not output in self._outputs: |
534 | + return True |
535 | + if output.has_changed(): |
536 | + return True |
537 | + return False |
538 | + |
539 | + |
540 | +class Screen: |
541 | + def __init__(self, dpy, screen=-1): |
542 | + """Initializes the screen""" |
543 | + # Some sane default values |
544 | + self.outputs = {} |
545 | + self.crtcs = [] |
546 | + self._width = 0 |
547 | + self._height = 0 |
548 | + self._width_max = 0 |
549 | + self._height_max = 0 |
550 | + self._width_min = 0 |
551 | + self._height_min = 0 |
552 | + self._width_mm = 0 |
553 | + self._height_mm = 0 |
554 | + |
555 | + self._display = dpy |
556 | + if not -1 <= screen < xlib.XScreenCount(dpy): |
557 | + raise RRError("The chosen screen is not available", screen) |
558 | + elif screen == -1: |
559 | + self._screen = xlib.XDefaultScreen(dpy) |
560 | + else: |
561 | + self._screen = screen |
562 | + self._root = xlib.XDefaultRootWindow(self._display, self._screen) |
563 | + self._id = rr.XRRRootToScreen(self._display, self._root) |
564 | + |
565 | + self._load_resources() |
566 | + self._load_config() |
567 | + (self._width, self._height, |
568 | + self._width_mm, self._height_mm) = self.get_size() |
569 | + if XRANDR_VERSION >= (1, 2): |
570 | + self._load_screen_size_range() |
571 | + self._load_crtcs() |
572 | + self._load_outputs() |
573 | + |
574 | + # Store XRandR 1.0 changes here |
575 | + self._rate = self.get_current_rate() |
576 | + self._rotation = self.get_current_rotation() |
577 | + self._size_index = self.get_current_size_index() |
578 | + |
579 | + def __del__(self): |
580 | + """Free the reference to the interal screen config if the screen |
581 | + gets removed""" |
582 | + rr.XRRFreeScreenConfigInfo(self._config) |
583 | + |
584 | + def _load_config(self): |
585 | + """Loads the screen configuration. Only needed privately by the |
586 | + the bindings""" |
587 | + class XRRScreenConfiguration(Structure): |
588 | + " private to Xrandr " |
589 | + pass |
590 | + gsi = rr.XRRGetScreenInfo |
591 | + gsi.restype = POINTER(XRRScreenConfiguration) |
592 | + self._config = gsi(self._display, self._root) |
593 | + |
594 | + def _load_screen_size_range(self): |
595 | + """Detects the dimensionios of the screen""" |
596 | + minWidth = c_int() |
597 | + minHeight = c_int() |
598 | + maxWidth = c_int() |
599 | + maxHeight = c_int() |
600 | + res = rr.XRRGetScreenSizeRange(self._display, self._root, |
601 | + byref(minWidth), byref(minHeight), |
602 | + byref(maxWidth), byref(maxHeight)) |
603 | + if res: |
604 | + self._width_max = maxWidth.value |
605 | + self._width_min = minWidth.value |
606 | + self._height_max = maxHeight.value |
607 | + self._height_min = minHeight.value |
608 | + |
609 | + def _load_resources(self): |
610 | + """Loads the screen resources. Only needed privately for the |
611 | + bindings""" |
612 | + gsr = rr.XRRGetScreenResources |
613 | + gsr.restype = POINTER(_XRRScreenResources) |
614 | + self._resources = gsr(self._display, self._root) |
615 | + |
616 | + def _load_crtcs(self): |
617 | + """Loads the available XRandR 1.2 crtcs (hardware pipes) of |
618 | + the screen""" |
619 | + gci = rr.XRRGetCrtcInfo |
620 | + gci.restype = POINTER(_XRRCrtcInfo) |
621 | + c = self._resources.contents.crtcs |
622 | + for i in range(self._resources.contents.ncrtc): |
623 | + xrrcrtcinfo = gci(self._display, self._resources, c[i]) |
624 | + self.crtcs.append(Crtc(xrrcrtcinfo, c[i], self)) |
625 | + |
626 | + def _load_outputs(self): |
627 | + """Loads the available XRandR 1.2 outputs of the screen""" |
628 | + goi = rr.XRRGetOutputInfo |
629 | + goi.restype = POINTER(_XRROutputInfo) |
630 | + o = self._resources.contents.outputs |
631 | + for i in range(self._resources.contents.noutput): |
632 | + xrroutputinfo = goi(self._display, self._resources, o[i]) |
633 | + output = Output(xrroutputinfo, o[i], self) |
634 | + self.outputs[xrroutputinfo.contents.name] = output |
635 | + # Store the mode of the crtc in the output instance |
636 | + crtc = self.get_crtc_by_xid(output.get_crtc()) |
637 | + if crtc: |
638 | + output._mode = crtc._info.contents.mode |
639 | + crtc.add_output(output) |
640 | + |
641 | + def get_size(self): |
642 | + """Returns the current pixel and physical size of the screen""" |
643 | + width = xlib.XDisplayWidth(self._display, self._screen) |
644 | + width_mm = xlib.XDisplayWidthMM(self._display, self._screen) |
645 | + height = xlib.XDisplayHeight(self._display, self._screen) |
646 | + height_mm = xlib.XDisplayHeightMM(self._display, self._screen) |
647 | + return width, height, width_mm, height_mm |
648 | + |
649 | + def get_timestamp(self): |
650 | + """Creates a X timestamp that must be used when applying changes, since |
651 | + they can be delayed""" |
652 | + config_timestamp = Time() |
653 | + rr.XRRTimes.restpye = c_ulong |
654 | + return rr.XRRTimes(self._display, self._id, byref(config_timestamp)) |
655 | + |
656 | + def get_crtc_by_xid(self, xid): |
657 | + """Returns the crtc with the given xid or None""" |
658 | + for crtc in self.crtcs: |
659 | + if crtc.xid == xid: |
660 | + return crtc |
661 | + return None |
662 | + |
663 | + def get_current_rate(self): |
664 | + """Returns the currently used refresh rate""" |
665 | + _check_required_version((1, 0)) |
666 | + xccr = rr.XRRConfigCurrentRate |
667 | + xccr.restype = c_int |
668 | + return xccr(self._config) |
669 | + |
670 | + def get_available_rates_for_size_index(self, size_index): |
671 | + """Returns the refresh rates that are supported by the screen for |
672 | + the given resolution. See get_available_sizes for the resolution to |
673 | + which size_index points""" |
674 | + _check_required_version((1, 0)) |
675 | + rates = [] |
676 | + nrates = c_int() |
677 | + rr.XRRConfigRates.restype = POINTER(c_ushort) |
678 | + _rates = rr.XRRConfigRates(self._config, size_index, byref(nrates)) |
679 | + for r in range(nrates.value): |
680 | + rates.append(_rates[r]) |
681 | + return rates |
682 | + |
683 | + def get_current_rotation(self): |
684 | + """Returns the currently used rotation. Can be RR_ROTATE_0, |
685 | + RR_ROTATE_90, RR_ROTATE_180 or RR_ROTATE_270""" |
686 | + _check_required_version((1, 0)) |
687 | + current = c_ushort() |
688 | + rr.XRRConfigRotations(self._config, byref(current)) |
689 | + return current.value |
690 | + |
691 | + def get_available_rotations(self): |
692 | + """Returns a binary flag that holds the available resolutions""" |
693 | + _check_required_version((1, 0)) |
694 | + current = c_ushort() |
695 | + rotations = rr.XRRConfigRotations(self._config, byref(current)) |
696 | + return rotations |
697 | + |
698 | + def get_current_size_index(self): |
699 | + """Returns the position of the currently used resolution size in the |
700 | + list of available resolutions. See get_available_sizes""" |
701 | + _check_required_version((1, 0)) |
702 | + rotation = c_ushort() |
703 | + size = rr.XRRConfigCurrentConfiguration(self._config, |
704 | + byref(rotation)) |
705 | + return size |
706 | + |
707 | + def get_available_sizes(self): |
708 | + """Returns the available resolution sizes of the screen. The size |
709 | + index points to the corresponding resolution of this list""" |
710 | + _check_required_version((1, 0)) |
711 | + sizes = [] |
712 | + nsizes = c_int() |
713 | + xcs = rr.XRRConfigSizes |
714 | + xcs.restype = POINTER(_XRRScreenSize) |
715 | + _sizes = xcs(self._config, byref(nsizes)) |
716 | + for r in range(nsizes.value): |
717 | + sizes.append(_sizes[r]) |
718 | + return sizes |
719 | + |
720 | + def set_config(self, size_index, rate, rotation): |
721 | + """Configures the screen with the given resolution at the given size |
722 | + index, rotation and refresh rate. To get in effect call |
723 | + Screen.apply_config()""" |
724 | + _check_required_version((1, 0)) |
725 | + self.set_size_index(size_index) |
726 | + self.set_refresh_rate(rate) |
727 | + self.set_rotation(rotation) |
728 | + |
729 | + def set_size_index(self, index): |
730 | + """Sets the reoslution of the screen. To get in effect call |
731 | + Screen.apply_config()""" |
732 | + if index in range(len(self.get_available_sizes())): |
733 | + self._size_index = index |
734 | + else: |
735 | + raise RRError("There isn't any size associated " |
736 | + "to the index %s" % index) |
737 | + |
738 | + def set_rotation(self, rotation): |
739 | + """Sets the rotation of the screen. To get in effect call |
740 | + Screen.apply_config()""" |
741 | + if self.get_available_rotations() & rotation: |
742 | + self._rotation = rotation |
743 | + else: |
744 | + raise RRError("The chosen rotation is not supported") |
745 | + |
746 | + def set_refresh_rate(self, rate): |
747 | + """Sets the refresh rate of the screen. To get in effect call |
748 | + Screen.apply_config()""" |
749 | + if rate in self.get_available_rates_for_size_index(self._size_index): |
750 | + self._rate = rate |
751 | + else: |
752 | + raise RRError("The chosen refresh rate %s is not " |
753 | + "supported" % rate) |
754 | + |
755 | + def get_mode_by_xid(self, xid): |
756 | + """Returns the mode of the given xid""" |
757 | + screen_modes = self._resources.contents.modes |
758 | + for s in range(self._resources.contents.nmode): |
759 | + if screen_modes[s].id == xid: |
760 | + return screen_modes[s] |
761 | + return None |
762 | + |
763 | + def get_output_by_name(self, name): |
764 | + """Returns the output of the screen with the given name or None""" |
765 | + if name in self.outputs: |
766 | + return self.outputs[name] |
767 | + else: |
768 | + return None |
769 | + |
770 | + def get_output_by_id(self, id): |
771 | + """Returns the output of the screen with the given xid or None""" |
772 | + for o in list(self.outputs.values()): |
773 | + if o.id == id: |
774 | + return o |
775 | + return None |
776 | + |
777 | + def print_info(self, verbose=False): |
778 | + """Prints some information about the detected screen and its outputs""" |
779 | + _check_required_version((1, 0)) |
780 | + print("Screen %s: minimum %s x %s, current %s x %s, maximum %s x %s" %\ |
781 | + (self._screen, |
782 | + self._width_min, self._height_min, |
783 | + self._width, self._height, |
784 | + self._width_max, self._height_max)) |
785 | + print(" %smm x %smm" % (self._width_mm, self._height_mm)) |
786 | + print("Crtcs: %s" % len(self.crtcs)) |
787 | + if verbose: |
788 | + print("Modes (%s):" % self._resources.contents.nmode) |
789 | + modes = self._resources.contents.modes |
790 | + for i in range(self._resources.contents.nmode): |
791 | + print(" %s - %sx%s" % (modes[i].name, |
792 | + modes[i].width, |
793 | + modes[i].height)) |
794 | + i = 0 |
795 | + print("Sizes @ Refresh Rates:") |
796 | + for s in self.get_available_sizes(): |
797 | + print(" [%s] %s x %s @ %s" % ( |
798 | + i, s.width, s.height, |
799 | + self.get_available_rates_for_size_index(i))) |
800 | + i += 1 |
801 | + print("Rotations:") |
802 | + rots = self.get_available_rotations() |
803 | + if rots & RR_ROTATE_0: |
804 | + print("normal") |
805 | + if rots & RR_ROTATE_90: |
806 | + print("right") |
807 | + if rots & RR_ROTATE_180: |
808 | + print("inverted") |
809 | + if rots & RR_ROTATE_270: |
810 | + print("left") |
811 | + print("") |
812 | + print("Outputs:") |
813 | + for o in list(self.outputs.keys()): |
814 | + output = self.outputs[o] |
815 | + print(" %s" % o) |
816 | + if output.is_connected(): |
817 | + print("(%smm x %smm)" % (output.get_physical_width(), |
818 | + output.get_physical_height())) |
819 | + modes = output.get_available_modes() |
820 | + print(" Modes:") |
821 | + for m in range(len(modes)): |
822 | + mode = modes[m] |
823 | + refresh = mode.dotClock / (mode.hTotal * mode.vTotal) |
824 | + print(" [%s] %s x %s @ %s" % (m, |
825 | + mode.width, |
826 | + mode.height, |
827 | + refresh)) |
828 | + if mode.id == output._mode: |
829 | + print("*") |
830 | + if m == output.get_preferred_mode(): |
831 | + print("(preferred)") |
832 | + print("") |
833 | + print(" Rotations:") |
834 | + rots = output.get_available_rotations() |
835 | + if rots & RR_ROTATE_0: |
836 | + print("normal") |
837 | + if rots & RR_ROTATE_90: |
838 | + print("right") |
839 | + if rots & RR_ROTATE_180: |
840 | + print("inverted") |
841 | + if rots & RR_ROTATE_270: |
842 | + print("left") |
843 | + print("") |
844 | + else: |
845 | + print("(not connected)") |
846 | + if verbose: |
847 | + print(" Core properties:") |
848 | + for (f, t) in output._info.contents._fields_: |
849 | + print(" %s: %s" % ( |
850 | + f, getattr(output._info.contents, f))) |
851 | + |
852 | + def get_outputs(self): |
853 | + """Returns the outputs of the screen""" |
854 | + _check_required_version((1, 2)) |
855 | + return list(self.outputs.values()) |
856 | + |
857 | + def get_output_names(self): |
858 | + _check_required_version((1, 2)) |
859 | + return list(self.outputs.keys()) |
860 | + |
861 | + def set_size(self, width, height, width_mm, height_mm): |
862 | + """Apply the given pixel and physical size to the screen""" |
863 | + _check_required_version((1, 2)) |
864 | + # Check if we really need to apply the changes |
865 | + if (width, height, width_mm, height_mm) == self.get_size(): |
866 | + return |
867 | + rr.XRRSetScreenSize(self._display, self._root, |
868 | + c_int(width), c_int(height), |
869 | + c_int(width_mm), c_int(height_mm)) |
870 | + |
871 | + def apply_output_config(self): |
872 | + """Used for instantly applying RandR 1.2 changes""" |
873 | + _check_required_version((1, 2)) |
874 | + self._arrange_outputs() |
875 | + self._calculate_size() |
876 | + self.set_size(self._width, self._height, |
877 | + self._width_mm, self._height_mm) |
878 | + |
879 | + # Assign all active outputs to crtcs |
880 | + for output in list(self.outputs.values()): |
881 | + if not output._mode or output._crtc: |
882 | + continue |
883 | + for crtc in output.get_crtcs(): |
884 | + if crtc and crtc.supports_output(output): |
885 | + crtc.add_output(output) |
886 | + output._changes = output._changes | CHANGES_CRTC |
887 | + break |
888 | + if not output._crtc: |
889 | + #FIXME: Take a look at the pick_crtc code in xrandr.c |
890 | + raise RRError("There is no matching crtc for the output") |
891 | + |
892 | + # Apply stored changes of crtcs |
893 | + for crtc in self.crtcs: |
894 | + if crtc.has_changed(): |
895 | + crtc.apply_changes() |
896 | + |
897 | + def apply_config(self): |
898 | + """Used for instantly applying RandR 1.0 changes""" |
899 | + _check_required_version((1, 0)) |
900 | + status = rr.XRRSetScreenConfigAndRate(self._display, |
901 | + self._config, |
902 | + self._root, |
903 | + self._size_index, |
904 | + self._rotation, |
905 | + self._rate, |
906 | + self.get_timestamp()) |
907 | + return status |
908 | + |
909 | + def _arrange_outputs(self): |
910 | + """Arrange all output positions according to their relative position""" |
911 | + for output in self.get_outputs(): |
912 | + # Skip not changed and not used outputs |
913 | + if not output.has_changed(CHANGES_RELATION) or \ |
914 | + output._mode == None: |
915 | + continue |
916 | + relative = output._relative_to |
917 | + mode = self.get_mode_by_xid(output._mode) |
918 | + mode_relative = self.get_mode_by_xid(relative._mode) |
919 | + if not relative or not relative._mode: |
920 | + output._x = 0 |
921 | + output._y = 0 |
922 | + output._changes = output._changes | CHANGES_POSITION |
923 | + if output._relation == RELATION_LEFT_OF: |
924 | + output._y = relative._y + output._relation_offset |
925 | + output._x = relative._x - \ |
926 | + get_mode_width(mode, output._rotation) |
927 | + elif output._relation == RELATION_RIGHT_OF: |
928 | + output._y = relative._y + output._relation_offset |
929 | + output._x = relative._x + get_mode_width(mode_relative, |
930 | + output._rotation) |
931 | + elif output._relation == RELATION_ABOVE: |
932 | + output._y = relative._y - get_mode_height(mode, |
933 | + output._rotation) |
934 | + output._x = relative._x + output._relation_offset |
935 | + elif output._relation == RELATION_BELOW: |
936 | + output._y = relative._y + get_mode_height(mode_relative, |
937 | + output._rotation) |
938 | + output._x = relative._x + output._relation_offset |
939 | + elif output._relation == RELATION_SAME_AS: |
940 | + output._y = relative._y + output._relation_offset |
941 | + output._x = relative._x + output._relation_offset |
942 | + output._changes = output._changes | CHANGES_POSITION |
943 | + # Normalize the postions so to the upper left cornor of all outputs |
944 | + # is at 0,0 |
945 | + min_x = 32768 |
946 | + min_y = 32768 |
947 | + for output in self.get_outputs(): |
948 | + if output._mode == None: |
949 | + continue |
950 | + if output._x < min_x: |
951 | + min_x = output._x |
952 | + if output._y < min_y: |
953 | + min_y = output._y |
954 | + for output in self.get_outputs(): |
955 | + if output._mode == None: |
956 | + continue |
957 | + output._x -= min_x |
958 | + output._y -= min_y |
959 | + output._changes = output._changes | CHANGES_POSITION |
960 | + |
961 | + def _calculate_size(self): |
962 | + """Recalculate the pixel and physical size of the screen so that |
963 | + it covers all outputs""" |
964 | + width = self._width |
965 | + height = self._height |
966 | + for output in self.get_outputs(): |
967 | + if not output._mode: |
968 | + continue |
969 | + mode = self.get_mode_by_xid(output._mode) |
970 | + x = output._x |
971 | + y = output._y |
972 | + w = get_mode_width(mode, output._rotation) |
973 | + h = get_mode_height(mode, output._rotation) |
974 | + if x + w > width: |
975 | + width = x + w |
976 | + if y + h > height: |
977 | + height = y + h |
978 | + if width > self._width_max or height > self._height_max: |
979 | + raise RRError("The required size is not supported", |
980 | + (width, height), (self._width_max, self._width_min)) |
981 | + else: |
982 | + if height < self._height_min: |
983 | + self._fb_height = self._height_min |
984 | + else: |
985 | + self._height = height |
986 | + if width < self._width_min: |
987 | + self._width = self._width_min |
988 | + else: |
989 | + self._width = width |
990 | + #FIXME: Physical size is missing |
991 | + |
992 | + |
993 | +def get_current_display(): |
994 | + """Returns the currently used display""" |
995 | + display_url = os.getenv("DISPLAY") |
996 | + open_display = xlib.XOpenDisplay |
997 | + # Set .argtypes and .restype, to ensure proper |
998 | + # type check and conversion |
999 | + open_display.restype = c_void_p |
1000 | + open_display.argtypes = [c_char_p] |
1001 | + # XOpenDisplay accepts a char*, but |
1002 | + # display_url is a unicode string therefore |
1003 | + # we convert it to a bytes string |
1004 | + dpy = open_display(display_url.encode('utf-8')) |
1005 | + return dpy |
1006 | + |
1007 | + |
1008 | +def get_current_screen(): |
1009 | + """Returns the currently used screen""" |
1010 | + screen = Screen(get_current_display()) |
1011 | + return screen |
1012 | + |
1013 | + |
1014 | +def get_screen_of_display(display, count): |
1015 | + """Returns the screen of the given display""" |
1016 | + dpy = xlib.XOpenDisplay(display) |
1017 | + return Screen(dpy, count) |
1018 | + |
1019 | + |
1020 | +def get_version(): |
1021 | + """Returns a tuple containing the major and minor version of the xrandr |
1022 | + extension or None if the extension is not available""" |
1023 | + major = c_int() |
1024 | + minor = c_int() |
1025 | + res = rr.XRRQueryVersion(get_current_display(), |
1026 | + byref(major), byref(minor)) |
1027 | + if res: |
1028 | + return (major.value, minor.value) |
1029 | + return None |
1030 | + |
1031 | + |
1032 | +def has_extension(): |
1033 | + """Returns True if the xrandr extension is available""" |
1034 | + if XRANDR_VERSION: |
1035 | + return True |
1036 | + return False |
1037 | + |
1038 | + |
1039 | +def _to_gamma(gamma): |
1040 | + g = rr.XRRAllocGamma(len(gamma[0])) |
1041 | + for i in range(gamma[0]): |
1042 | + g.red[i] = gamma[0][i] |
1043 | + g.green[i] = gamma[1][i] |
1044 | + g.blue[i] = gamma[2][i] |
1045 | + return g |
1046 | + |
1047 | + |
1048 | +def _from_gamma(g): |
1049 | + gamma = ([], [], []) |
1050 | + for i in range(g.size): |
1051 | + gamma[0].append(g.red[i]) |
1052 | + gamma[1].append(g.green[i]) |
1053 | + gamma[2].append(g.blue[i]) |
1054 | + rr.XRRFreeGamma(g) |
1055 | + |
1056 | + |
1057 | +def _check_required_version(version): |
1058 | + """Raises an exception if the given or a later version of xrandr is not |
1059 | + available""" |
1060 | + if XRANDR_VERSION == None or XRANDR_VERSION < version: |
1061 | + raise UnsupportedRRError(version, XRANDR_VERSION) |
1062 | + |
1063 | + |
1064 | +def get_mode_height(mode, rotation): |
1065 | + """Return the height of the given mode taking the rotation into account""" |
1066 | + if rotation & (RR_ROTATE_0 | RR_ROTATE_180): |
1067 | + return mode.height |
1068 | + elif rotation & (RR_ROTATE_90 | RR_ROTATE_270): |
1069 | + return mode.width |
1070 | + else: |
1071 | + return 0 |
1072 | + |
1073 | + |
1074 | +def get_mode_width(mode, rotation): |
1075 | + """Return the width of the given mode taking the rotation into account""" |
1076 | + if rotation & (RR_ROTATE_0 | RR_ROTATE_180): |
1077 | + return mode.width |
1078 | + elif rotation & (RR_ROTATE_90 | RR_ROTATE_270): |
1079 | + return mode.height |
1080 | + else: |
1081 | + return 0 |
1082 | + |
1083 | + |
1084 | +XRANDR_VERSION = get_version() |
1085 | + |
1086 | +# vim:ts=4:sw=4:et |
1087 | |
1088 | === added directory 'checkbox-support/checkbox_support/dbus' |
1089 | === added file 'checkbox-support/checkbox_support/dbus/__init__.py' |
1090 | --- checkbox-support/checkbox_support/dbus/__init__.py 1970-01-01 00:00:00 +0000 |
1091 | +++ checkbox-support/checkbox_support/dbus/__init__.py 2014-01-07 13:44:32 +0000 |
1092 | @@ -0,0 +1,89 @@ |
1093 | +# This file is part of Checkbox. |
1094 | +# |
1095 | +# Copyright 2012 Canonical Ltd. |
1096 | +# Written by: |
1097 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1098 | +# |
1099 | +# Checkbox is free software: you can redistribute it and/or modify |
1100 | +# it under the terms of the GNU General Public License version 3, |
1101 | +# as published by the Free Software Foundation. |
1102 | + |
1103 | +# |
1104 | +# Checkbox is distributed in the hope that it will be useful, |
1105 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1106 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1107 | +# GNU General Public License for more details. |
1108 | +# |
1109 | +# You should have received a copy of the GNU General Public License |
1110 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1111 | +# |
1112 | +""" |
1113 | +checkbox_support.dbus |
1114 | +============= |
1115 | + |
1116 | +Utility modules for working with various things accessible over dbus |
1117 | +""" |
1118 | + |
1119 | +import logging |
1120 | + |
1121 | +from dbus import SystemBus |
1122 | +from dbus.mainloop.glib import DBusGMainLoop |
1123 | +from dbus import (Array, Boolean, Byte, Dictionary, Double, Int16, Int32, |
1124 | + Int64, ObjectPath, String, Struct, UInt16, UInt32, UInt64) |
1125 | +from gi.repository import GObject |
1126 | + |
1127 | + |
1128 | +def connect_to_system_bus(): |
1129 | + """ |
1130 | + Connect to the system bus properly. |
1131 | + |
1132 | + Returns a tuple (system_bus, loop) where loop is a GObject.MainLoop |
1133 | + instance. The loop is there so that you can listen to signals. |
1134 | + """ |
1135 | + # We'll need an event loop to observe signals. We will need the instance |
1136 | + # later below so let's keep it. Note that we're not passing it directly |
1137 | + # below as DBus needs specific API. The DBusGMainLoop class that we |
1138 | + # instantiate and pass is going to work with this instance transparently. |
1139 | + # |
1140 | + # NOTE: DBus tutorial suggests that we should create the loop _before_ |
1141 | + # connecting to the bus. |
1142 | + logging.debug("Setting up glib-based event loop") |
1143 | + loop = GObject.MainLoop() |
1144 | + # Let's get the system bus object. We need that to access UDisks2 object |
1145 | + logging.debug("Connecting to DBus system bus") |
1146 | + system_bus = SystemBus(mainloop=DBusGMainLoop()) |
1147 | + return system_bus, loop |
1148 | + |
1149 | + |
1150 | +def drop_dbus_type(value): |
1151 | + """ |
1152 | + Convert types from the DBus bindings to their python counterparts. |
1153 | + |
1154 | + This function is mostly lossless, except for arrays of bytes (DBus |
1155 | + signature "y") that are transparently converted to strings, assuming |
1156 | + an UTF-8 encoded string. |
1157 | + |
1158 | + The point of this function is to simplify printing of nested DBus data that |
1159 | + gets displayed in a rather illegible way. |
1160 | + """ |
1161 | + if isinstance(value, Array) and value.signature == "y": |
1162 | + # Some other things are reported as array of bytes that are just |
1163 | + # strings but due to Unix heritage the encoding is not known. |
1164 | + # In practice it is better to treat them as UTF-8 strings |
1165 | + return bytes(value).decode("UTF-8", "replace").strip("\0") |
1166 | + elif isinstance(value, (Struct, Array)): |
1167 | + return [drop_dbus_type(item) for item in value] |
1168 | + elif isinstance(value, (Dictionary)): |
1169 | + return {drop_dbus_type(dict_key): drop_dbus_type(dict_value) |
1170 | + for dict_key, dict_value in value.items()} |
1171 | + elif isinstance(value, (String, ObjectPath)): |
1172 | + return str(value) |
1173 | + elif isinstance(value, (Byte, UInt16, UInt32, UInt64, |
1174 | + Int16, Int32, Int64)): |
1175 | + return int(value) |
1176 | + elif isinstance(value, Boolean): |
1177 | + return bool(value) |
1178 | + elif isinstance(value, Double): |
1179 | + return float(value) |
1180 | + else: |
1181 | + return value |
1182 | |
1183 | === added file 'checkbox-support/checkbox_support/dbus/udisks2.py' |
1184 | --- checkbox-support/checkbox_support/dbus/udisks2.py 1970-01-01 00:00:00 +0000 |
1185 | +++ checkbox-support/checkbox_support/dbus/udisks2.py 2014-01-07 13:44:32 +0000 |
1186 | @@ -0,0 +1,479 @@ |
1187 | +# Copyright 2012 Canonical Ltd. |
1188 | +# Written by: |
1189 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1190 | +# |
1191 | +# This program is free software: you can redistribute it and/or modify |
1192 | +# it under the terms of the GNU General Public License version 3, |
1193 | +# as published by the Free Software Foundation. |
1194 | +# |
1195 | +# This program is distributed in the hope that it will be useful, |
1196 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1197 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1198 | +# GNU General Public License for more details. |
1199 | +# |
1200 | +# You should have received a copy of the GNU General Public License |
1201 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
1202 | + |
1203 | +""" |
1204 | +checkbox_support.dbus.udisks2 |
1205 | +===================== |
1206 | + |
1207 | +Module for working with UDisks2 from python. |
1208 | + |
1209 | +There are two main classes that are interesting here. |
1210 | + |
1211 | +The first class is UDisksObserver, which is easy to setup and has pythonic API |
1212 | +to all of the stuff that happens in UDisks2. It offers simple signal handlers |
1213 | +for any changes that occur in UDisks2 that were advertised by DBus. |
1214 | + |
1215 | +The second class is UDisksModel, that builds on the observer class to offer |
1216 | +persistent collection of objects managed by UDisks2. |
1217 | + |
1218 | +To work with this model you will likely want to look at: |
1219 | + http://udisks.freedesktop.org/docs/latest/ref-dbus.html |
1220 | +""" |
1221 | + |
1222 | +import logging |
1223 | + |
1224 | +from dbus import Interface, PROPERTIES_IFACE |
1225 | +from dbus.exceptions import DBusException |
1226 | + |
1227 | +from checkbox_support.dbus import drop_dbus_type |
1228 | + |
1229 | +__all__ = ['UDisks2Observer', 'UDisks2Model', 'Signal', 'is_udisks2_supported', |
1230 | + 'lookup_udev_device'] |
1231 | + |
1232 | + |
1233 | +def is_udisks2_supported(system_bus): |
1234 | + """ |
1235 | + Check if udisks2 is available on the system bus. |
1236 | + |
1237 | + ..note:: |
1238 | + Calling this _may_ trigger activation of the UDisks2 daemon but it |
1239 | + should only happen on systems where it is already expected to run all |
1240 | + the time. |
1241 | + """ |
1242 | + observer = UDisks2Observer() |
1243 | + try: |
1244 | + logging.debug("Trying to connect to UDisks2...") |
1245 | + observer.connect_to_bus(system_bus) |
1246 | + except DBusException as exc: |
1247 | + if exc.get_dbus_name() == "org.freedesktop.DBus.Error.ServiceUnknown": |
1248 | + logging.debug("No UDisks2 on the system bus") |
1249 | + return False |
1250 | + else: |
1251 | + raise |
1252 | + else: |
1253 | + logging.debug("Got UDisks2 connection") |
1254 | + return True |
1255 | + |
1256 | + |
1257 | +def map_udisks1_connection_bus(udisks1_connection_bus): |
1258 | + """ |
1259 | + Map the value of udisks1 ConnectionBus property to the corresponding values |
1260 | + in udisks2. This a lossy function as some values are no longer supported. |
1261 | + |
1262 | + Incorrect values raise LookupError |
1263 | + """ |
1264 | + return { |
1265 | + 'ata_serial_esata': '', # gone from udisks2 |
1266 | + 'firewire': 'ieee1394', # renamed |
1267 | + 'scsi': '', # gone from udisks2 |
1268 | + 'sdio': 'sdio', # as-is |
1269 | + 'usb': 'usb', # as-is |
1270 | + }[udisks1_connection_bus] |
1271 | + |
1272 | + |
1273 | +def lookup_udev_device(udisks2_object, udev_devices): |
1274 | + """ |
1275 | + Find the udev_device that corresponds to the udisks2 object |
1276 | + |
1277 | + Devices are matched by unix filesystem path of the special file (device). |
1278 | + The udisks2_object must implement the block device interface (so that the |
1279 | + block device path can be determined) or a ValueError is raised. |
1280 | + |
1281 | + The udisks2_object must be the dictionary that maps from interface names to |
1282 | + dictionaries of properties. For compatible data see |
1283 | + UDisks2Model.managed_objects The udev_devices must be a list of udev |
1284 | + device, as returned from GUdev. |
1285 | + |
1286 | + If there is no match, LookupError is raised with the unix block device |
1287 | + path. |
1288 | + """ |
1289 | + try: |
1290 | + block_props = udisks2_object[UDISKS2_BLOCK_INTERFACE] |
1291 | + except KeyError: |
1292 | + raise ValueError("udisks2_object must be a block device") |
1293 | + else: |
1294 | + block_dev = block_props['Device'] |
1295 | + for udev_device in udev_devices: |
1296 | + if udev_device.get_device_file() == block_dev: |
1297 | + return udev_device |
1298 | + raise LookupError(block_dev) |
1299 | + |
1300 | + |
1301 | +# The well-known name for the ObjectManager interface, sadly it is not a part |
1302 | +# of the python binding along with the rest of well-known names. |
1303 | +OBJECT_MANAGER_INTERFACE = "org.freedesktop.DBus.ObjectManager" |
1304 | + |
1305 | +# The well-known name of the filesystem interface implemented by certain |
1306 | +# objects exposed by UDisks2 |
1307 | +UDISKS2_FILESYSTEM_INTERFACE = "org.freedesktop.UDisks2.Filesystem" |
1308 | + |
1309 | +# The well-known name of the block (device) interface implemented by certain |
1310 | +# objects exposed by UDisks2 |
1311 | +UDISKS2_BLOCK_INTERFACE = "org.freedesktop.UDisks2.Block" |
1312 | + |
1313 | +# The well-known name of the drive interface implemented by certain objects |
1314 | +# exposed by UDisks2 |
1315 | +UDISKS2_DRIVE_INTERFACE = "org.freedesktop.UDisks2.Drive" |
1316 | + |
1317 | + |
1318 | +class Signal: |
1319 | + """ |
1320 | + Basic signal that supports arbitrary listeners. |
1321 | + |
1322 | + While this class can be used directly it is best used with the helper |
1323 | + decorator Signal.define on a member function. The function body is ignored, |
1324 | + apart from the documentation. |
1325 | + |
1326 | + The function name then becomes a unique (per encapsulating class instance) |
1327 | + object (an instance of this Signal class) that is created on demand. |
1328 | + |
1329 | + In practice you just have a documentation and use |
1330 | + object.signal_name.connect() and object.signal_name(*args, **kwargs) to |
1331 | + fire it. |
1332 | + """ |
1333 | + |
1334 | + def __init__(self, signal_name): |
1335 | + """ |
1336 | + Construct a signal with the given name |
1337 | + """ |
1338 | + self._listeners = [] |
1339 | + self._signal_name = signal_name |
1340 | + |
1341 | + def connect(self, listener): |
1342 | + """ |
1343 | + Connect a new listener to this signal |
1344 | + |
1345 | + That listener will be called whenever fire() is invoked on the signal |
1346 | + """ |
1347 | + self._listeners.append(listener) |
1348 | + |
1349 | + def disconnect(self, listener): |
1350 | + """ |
1351 | + Disconnect an existing listener from this signal |
1352 | + """ |
1353 | + self._listeners.remove(listener) |
1354 | + |
1355 | + def fire(self, args, kwargs): |
1356 | + """ |
1357 | + Fire this signal with the specified arguments and keyword arguments. |
1358 | + |
1359 | + Typically this is used by using __call__() on this object which is more |
1360 | + natural as it does all the argument packing/unpacking transparently. |
1361 | + """ |
1362 | + for listener in self._listeners: |
1363 | + listener(*args, **kwargs) |
1364 | + |
1365 | + def __call__(self, *args, **kwargs): |
1366 | + """ |
1367 | + Call fire() with all arguments forwarded transparently |
1368 | + """ |
1369 | + self.fire(args, kwargs) |
1370 | + |
1371 | + @classmethod |
1372 | + def define(cls, dummy_func): |
1373 | + """ |
1374 | + Helper decorator to define a signal descriptor in a class |
1375 | + |
1376 | + The decorated function is never called but is used to get |
1377 | + documentation. |
1378 | + """ |
1379 | + return _SignalDescriptor(dummy_func) |
1380 | + |
1381 | + |
1382 | +class _SignalDescriptor: |
1383 | + """ |
1384 | + Descriptor for convenient signal access. |
1385 | + |
1386 | + Typically this class is used indirectly, when accessed from Signal.define |
1387 | + method decorator. It is used to do all the magic required when accessing |
1388 | + signal name on a class or instance. |
1389 | + """ |
1390 | + |
1391 | + def __init__(self, dummy_func): |
1392 | + self.signal_name = dummy_func.__name__ |
1393 | + self.__doc__ = dummy_func.__doc__ |
1394 | + |
1395 | + def __repr__(self): |
1396 | + return "<SignalDecorator for signal: %r>" % self.signal_name |
1397 | + |
1398 | + def __get__(self, instance, owner): |
1399 | + if instance is None: |
1400 | + return self |
1401 | + # Ensure that the instance has __signals__ property |
1402 | + if not hasattr(instance, "__signals__"): |
1403 | + instance.__signals__ = {} |
1404 | + if self.signal_name not in instance.__signals__: |
1405 | + instance.__signals__[self.signal_name] = Signal(self.signal_name) |
1406 | + return instance.__signals__[self.signal_name] |
1407 | + |
1408 | + def __set__(self, instance, value): |
1409 | + raise AttributeError("You cannot overwrite signals") |
1410 | + |
1411 | + def __delete__(self, instance): |
1412 | + raise AttributeError("You cannot delete signals") |
1413 | + |
1414 | + |
1415 | +class UDisks2Observer: |
1416 | + """ |
1417 | + Class for observing ongoing changes in UDisks2 |
1418 | + """ |
1419 | + |
1420 | + def __init__(self): |
1421 | + """ |
1422 | + Create a UDisks2 model. |
1423 | + |
1424 | + The model must be connected to a bus before it is first used, see |
1425 | + connect() |
1426 | + """ |
1427 | + # Proxy to the UDisks2 object |
1428 | + self._udisks2_obj = None |
1429 | + # Proxy to the ObjectManager interface exposed by UDisks2 object |
1430 | + self._udisks2_obj_manager = None |
1431 | + |
1432 | + @Signal.define |
1433 | + def on_initial_objects(self, managed_objects): |
1434 | + """ |
1435 | + Signal fired when the initial list of objects becomes available |
1436 | + """ |
1437 | + |
1438 | + @Signal.define |
1439 | + def on_interfaces_added(self, object_path, interfaces_and_properties): |
1440 | + """ |
1441 | + Signal fired when one or more interfaces gets added to a specific |
1442 | + object. |
1443 | + """ |
1444 | + |
1445 | + @Signal.define |
1446 | + def on_interfaces_removed(self, object_path, interfaces): |
1447 | + """ |
1448 | + Signal fired when one or more interface gets removed from a specific |
1449 | + object |
1450 | + """ |
1451 | + |
1452 | + @Signal.define |
1453 | + def on_properties_changed(self, interface_name, changed_properties, |
1454 | + invalidated_properties, sender=None): |
1455 | + """ |
1456 | + Signal fired when one or more property changes value or becomes |
1457 | + invalidated. |
1458 | + """ |
1459 | + |
1460 | + def connect_to_bus(self, bus): |
1461 | + """ |
1462 | + Establish initial connection to UDisks2 on the specified DBus bus. |
1463 | + |
1464 | + This will also load the initial set of objects from UDisks2 and thus |
1465 | + fire the on_initial_objects() signal from the model. Please call this |
1466 | + method only after connecting that signal if you want to observe that |
1467 | + event. |
1468 | + """ |
1469 | + # Once everything is ready connect to udisks2 |
1470 | + self._connect_to_udisks2(bus) |
1471 | + # And read all the initial objects and setup |
1472 | + # change event handlers |
1473 | + self._get_initial_objects() |
1474 | + |
1475 | + def _connect_to_udisks2(self, bus): |
1476 | + """ |
1477 | + Setup the initial connection to UDisks2 |
1478 | + |
1479 | + This step can fail if UDisks2 is not available and cannot be |
1480 | + service-activated. |
1481 | + """ |
1482 | + # Access the /org/freedesktop/UDisks2 object sitting on the |
1483 | + # org.freedesktop.UDisks2 bus name. This will trigger the necessary |
1484 | + # activation if udisksd is not running for any reason |
1485 | + logging.debug("Accessing main UDisks2 object") |
1486 | + self._udisks2_obj = bus.get_object( |
1487 | + "org.freedesktop.UDisks2", "/org/freedesktop/UDisks2") |
1488 | + # Now extract the standard ObjectManager interface so that we can |
1489 | + # observe and iterate the collection of objects that UDisks2 provides. |
1490 | + logging.debug("Accessing ObjectManager interface on UDisks2 object") |
1491 | + self._udisks2_obj_manager = Interface( |
1492 | + self._udisks2_obj, OBJECT_MANAGER_INTERFACE) |
1493 | + # Connect to the PropertiesChanged signal. Here unlike before we want |
1494 | + # to listen to all signals, regardless of who was sending them in the |
1495 | + # first place. |
1496 | + logging.debug("Setting up DBus signal handler for PropertiesChanged") |
1497 | + bus.add_signal_receiver( |
1498 | + self._on_properties_changed, |
1499 | + signal_name="PropertiesChanged", |
1500 | + dbus_interface=PROPERTIES_IFACE, |
1501 | + # Use the sender_keyword keyword argument to indicate that we wish |
1502 | + # to know the sender of each signal. For consistency with other |
1503 | + # signals we choose to use the 'object_path' keyword argument. |
1504 | + sender_keyword='sender') |
1505 | + |
1506 | + def _get_initial_objects(self): |
1507 | + """ |
1508 | + Get the initial collection of objects. |
1509 | + |
1510 | + Needs to be called before the first signals from DBus are observed. |
1511 | + Requires a working connection to UDisks2. |
1512 | + """ |
1513 | + # Having this interface we can now peek at the existing objects. |
1514 | + # We can use the standard method GetManagedObjects() to do that |
1515 | + logging.debug("Accessing GetManagedObjects() on UDisks2 object") |
1516 | + managed_objects = self._udisks2_obj_manager.GetManagedObjects() |
1517 | + managed_objects = drop_dbus_type(managed_objects) |
1518 | + # Fire the public signal for getting initial objects |
1519 | + self.on_initial_objects(managed_objects) |
1520 | + # Connect our internal handles to the DBus signal handlers |
1521 | + logging.debug("Setting up DBus signal handler for InterfacesAdded") |
1522 | + self._udisks2_obj_manager.connect_to_signal( |
1523 | + "InterfacesAdded", self._on_interfaces_added) |
1524 | + logging.debug("Setting up DBus signal handler for InterfacesRemoved") |
1525 | + self._udisks2_obj_manager.connect_to_signal( |
1526 | + "InterfacesRemoved", self._on_interfaces_removed) |
1527 | + |
1528 | + def _on_interfaces_added(self, object_path, interfaces_and_properties): |
1529 | + """ |
1530 | + Internal callback that is called by DBus |
1531 | + |
1532 | + This function is responsible for firing the public signal |
1533 | + """ |
1534 | + # Convert from dbus types |
1535 | + object_path = drop_dbus_type(object_path) |
1536 | + interfaces_and_properties = drop_dbus_type(interfaces_and_properties) |
1537 | + # Log what's going on |
1538 | + logging.debug("The object %r has gained the following interfaces and " |
1539 | + "properties: %r", object_path, interfaces_and_properties) |
1540 | + # Call the signal handler |
1541 | + self.on_interfaces_added(object_path, interfaces_and_properties) |
1542 | + |
1543 | + def _on_interfaces_removed(self, object_path, interfaces): |
1544 | + """ |
1545 | + Internal callback that is called by DBus |
1546 | + |
1547 | + This function is responsible for firing the public signal |
1548 | + """ |
1549 | + # Convert from dbus types |
1550 | + object_path = drop_dbus_type(object_path) |
1551 | + interfaces = drop_dbus_type(interfaces) |
1552 | + # Log what's going on |
1553 | + logging.debug("The object %r has lost the following interfaces: %r", |
1554 | + object_path, interfaces) |
1555 | + # Call the signal handler |
1556 | + self.on_interfaces_removed(object_path, interfaces) |
1557 | + |
1558 | + def _on_properties_changed(self, interface_name, changed_properties, |
1559 | + invalidated_properties, sender=None): |
1560 | + """ |
1561 | + Internal callback that is called by DBus |
1562 | + |
1563 | + This function is responsible for firing the public signal |
1564 | + """ |
1565 | + # Convert from dbus types |
1566 | + interface_name = drop_dbus_type(interface_name) |
1567 | + changed_properties = drop_dbus_type(changed_properties) |
1568 | + invalidated_properties = drop_dbus_type(invalidated_properties) |
1569 | + sender = drop_dbus_type(sender) |
1570 | + # Log what's going on |
1571 | + logging.debug("Some object with the interface %r has changed " |
1572 | + "properties: %r and invalidated properties %r " |
1573 | + "(sender: %s)", |
1574 | + interface_name, changed_properties, |
1575 | + invalidated_properties, sender) |
1576 | + # Call the signal handler |
1577 | + self.on_properties_changed(interface_name, changed_properties, |
1578 | + invalidated_properties, sender) |
1579 | + |
1580 | + |
1581 | +class UDisks2Model: |
1582 | + """ |
1583 | + Model for working with UDisks2 |
1584 | + |
1585 | + This class maintains a persistent model of what UDisks2 knows about, based |
1586 | + on the UDisks2Observer class and the signals it offers. |
1587 | + """ |
1588 | + |
1589 | + def __init__(self, observer): |
1590 | + """ |
1591 | + Create a UDisks2 model. |
1592 | + |
1593 | + The model will track changes using the specified observer (which is |
1594 | + expected to be a UDisks2Observer instance) |
1595 | + |
1596 | + You should only connect the observer to the bus after creating the |
1597 | + model otherwise the initial objects will not be detected. |
1598 | + """ |
1599 | + # Local state, everything that UDisks2 tells us |
1600 | + self._managed_objects = {} |
1601 | + self._observer = observer |
1602 | + # Connect all the signals to the observer |
1603 | + self._observer.on_initial_objects.connect(self._on_initial_objects) |
1604 | + self._observer.on_interfaces_added.connect(self._on_interfaces_added) |
1605 | + self._observer.on_interfaces_removed.connect( |
1606 | + self._on_interfaces_removed) |
1607 | + self._observer.on_properties_changed.connect( |
1608 | + self._on_properties_changed) |
1609 | + |
1610 | + @Signal.define |
1611 | + def on_change(self): |
1612 | + """ |
1613 | + Signal sent whenever the collection of managed object changes |
1614 | + |
1615 | + Note that this signal is fired _after_ the change has occurred |
1616 | + """ |
1617 | + |
1618 | + @property |
1619 | + def managed_objects(self): |
1620 | + """ |
1621 | + A collection of objects that is managed by this model. All changes as |
1622 | + well as the initial state, are reflected here. |
1623 | + """ |
1624 | + return self._managed_objects |
1625 | + |
1626 | + def _on_initial_objects(self, managed_objects): |
1627 | + """ |
1628 | + Internal callback called when we get the initial collection of objects |
1629 | + """ |
1630 | + self._managed_objects = drop_dbus_type(managed_objects) |
1631 | + |
1632 | + def _on_interfaces_added(self, object_path, interfaces_and_properties): |
1633 | + """ |
1634 | + Internal callback called when an interface is added to certain object |
1635 | + """ |
1636 | + # Update internal state |
1637 | + if object_path not in self._managed_objects: |
1638 | + self._managed_objects[object_path] = {} |
1639 | + obj = self._managed_objects[object_path] |
1640 | + obj.update(interfaces_and_properties) |
1641 | + # Fire the change signal |
1642 | + self.on_change() |
1643 | + |
1644 | + def _on_interfaces_removed(self, object_path, interfaces): |
1645 | + """ |
1646 | + Internal callback called when an interface is removed from a certain |
1647 | + object |
1648 | + """ |
1649 | + # Update internal state |
1650 | + if object_path in self._managed_objects: |
1651 | + obj = self._managed_objects[object_path] |
1652 | + for interface in interfaces: |
1653 | + if interface in obj: |
1654 | + del obj[interface] |
1655 | + # Fire the change signal |
1656 | + self.on_change() |
1657 | + |
1658 | + def _on_properties_changed(self, interface_name, changed_properties, |
1659 | + invalidated_properties, sender=None): |
1660 | + # XXX: This is a workaround the fact that we cannot |
1661 | + # properly track changes to all properties :-( |
1662 | + self._managed_objects = drop_dbus_type( |
1663 | + self._observer._udisks2_obj_manager.GetManagedObjects()) |
1664 | + # Fire the change signal() |
1665 | + self.on_change() |
1666 | |
1667 | === added directory 'checkbox-support/checkbox_support/heuristics' |
1668 | === added file 'checkbox-support/checkbox_support/heuristics/__init__.py' |
1669 | --- checkbox-support/checkbox_support/heuristics/__init__.py 1970-01-01 00:00:00 +0000 |
1670 | +++ checkbox-support/checkbox_support/heuristics/__init__.py 2014-01-07 13:44:32 +0000 |
1671 | @@ -0,0 +1,56 @@ |
1672 | +# This file is part of Checkbox. |
1673 | +# |
1674 | +# Copyright 2012 Canonical Ltd. |
1675 | +# Written by: |
1676 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1677 | +# |
1678 | +# Checkbox is free software: you can redistribute it and/or modify |
1679 | +# it under the terms of the GNU General Public License version 3, |
1680 | +# as published by the Free Software Foundation. |
1681 | + |
1682 | +# |
1683 | +# Checkbox is distributed in the hope that it will be useful, |
1684 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1685 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1686 | +# GNU General Public License for more details. |
1687 | +# |
1688 | +# You should have received a copy of the GNU General Public License |
1689 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1690 | + |
1691 | +""" |
1692 | +checkbox_support.heuristics |
1693 | +=================== |
1694 | + |
1695 | +This module contains implementations behind various heuristics used throughout |
1696 | +the code. The intent of this module is twofold: |
1697 | + |
1698 | + 1) To encourage code reuse so that developers can use one implementation of |
1699 | + "guesswork" that is sometimes needed in our test. This reduces duplicate |
1700 | + bugs where many scripts do similar things in a different way. |
1701 | + |
1702 | + 2) To identify missing features in plumbing layer APIs such as |
1703 | + udev/udisks/dbus etc. Ideally no program should have to guess this, the |
1704 | + plumbing layer should be able to provide this meta data to allow |
1705 | + application developers deliver consistent behavior across userspace. |
1706 | + |
1707 | +Heuristics should be reusable from both python and shell. To make that possible |
1708 | +each heuristics needs to be constrained to serializable input and output. This |
1709 | +levels the playing field and allows both shell developers and python developers |
1710 | +to reuse the same function. |
1711 | + |
1712 | +Additionally heuristics should try to avoid accessing thick APIs (such as |
1713 | +objects returned by various libraries. This is meant to decrease the likelihood |
1714 | +that updates to those libraries break this code. As an added side effect this |
1715 | +also should make the implementation more explicit and easier to understand. |
1716 | + |
1717 | +In the long term each heuristic should be discussed with upstream developers of |
1718 | +the particular problem area (udev, udisks, etc) to see if that subsystem can |
1719 | +provide the required information directly, without us having to guess and fill |
1720 | +the gaps. |
1721 | + |
1722 | +Things to consider when adding entries to this package: |
1723 | + |
1724 | + 1) File a bug on the upstream package about missing feature. |
1725 | + |
1726 | + 2) File a bug on checkbox to de-duplicate similar heuristics |
1727 | +""" |
1728 | |
1729 | === added directory 'checkbox-support/checkbox_support/heuristics/tests' |
1730 | === added file 'checkbox-support/checkbox_support/heuristics/tests/__init__.py' |
1731 | === added file 'checkbox-support/checkbox_support/heuristics/tests/test_udisks2.py' |
1732 | --- checkbox-support/checkbox_support/heuristics/tests/test_udisks2.py 1970-01-01 00:00:00 +0000 |
1733 | +++ checkbox-support/checkbox_support/heuristics/tests/test_udisks2.py 2014-01-07 13:44:32 +0000 |
1734 | @@ -0,0 +1,40 @@ |
1735 | +# This file is part of Checkbox. |
1736 | +# |
1737 | +# Copyright 2012 Canonical Ltd. |
1738 | +# Written by: |
1739 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1740 | +# |
1741 | +# Checkbox is free software: you can redistribute it and/or modify |
1742 | +# it under the terms of the GNU General Public License version 3, |
1743 | +# as published by the Free Software Foundation. |
1744 | + |
1745 | +# |
1746 | +# Checkbox is distributed in the hope that it will be useful, |
1747 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1748 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1749 | +# GNU General Public License for more details. |
1750 | +# |
1751 | +# You should have received a copy of the GNU General Public License |
1752 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1753 | + |
1754 | +""" |
1755 | + |
1756 | +checkbox_support.heuristics.tests.test_udisks2 |
1757 | +====================================== |
1758 | + |
1759 | +Unit tests for checkbox_support.heuristics.udisks2 module |
1760 | +""" |
1761 | + |
1762 | +from unittest import TestCase |
1763 | + |
1764 | +from checkbox_support.heuristics.udisks2 import is_memory_card |
1765 | + |
1766 | + |
1767 | +class TestIsMemoryCard(TestCase): |
1768 | + |
1769 | + def test_generic(self): |
1770 | + """ |
1771 | + Device with vendor string "GENERIC" is a memory card |
1772 | + """ |
1773 | + self.assertTrue( |
1774 | + is_memory_card(vendor="Generic", model="", udisks2_media=None)) |
1775 | |
1776 | === added file 'checkbox-support/checkbox_support/heuristics/udev.py' |
1777 | --- checkbox-support/checkbox_support/heuristics/udev.py 1970-01-01 00:00:00 +0000 |
1778 | +++ checkbox-support/checkbox_support/heuristics/udev.py 2014-01-07 13:44:32 +0000 |
1779 | @@ -0,0 +1,44 @@ |
1780 | +# This file is part of Checkbox. |
1781 | +# |
1782 | +# Copyright 2012 Canonical Ltd. |
1783 | +# Written by: |
1784 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1785 | +# |
1786 | +# Checkbox is free software: you can redistribute it and/or modify |
1787 | +# it under the terms of the GNU General Public License version 3, |
1788 | +# as published by the Free Software Foundation. |
1789 | + |
1790 | +# |
1791 | +# Checkbox is distributed in the hope that it will be useful, |
1792 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1793 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1794 | +# GNU General Public License for more details. |
1795 | +# |
1796 | +# You should have received a copy of the GNU General Public License |
1797 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1798 | + |
1799 | +""" |
1800 | +checkbox_support.heuristics.dev |
1801 | +======================= |
1802 | + |
1803 | +Heuristics for udev. |
1804 | + |
1805 | + Documentation: http://udisks.freedesktop.org/docs/latest/ |
1806 | + Source code: http://cgit.freedesktop.org/systemd/systemd/ (src/udev) |
1807 | + Bug tracker: http://bugs.freedesktop.org/ (using systemd product) |
1808 | +""" |
1809 | + |
1810 | + |
1811 | +def is_virtual_device(device_file): |
1812 | + """ |
1813 | + Given a device name like /dev/ramX, /dev/sdX or /dev/loopX determine if |
1814 | + this is a virtual device. Virtual devices are typically uninteresting to |
1815 | + users. The only exception may be nonempty loopback device. |
1816 | + |
1817 | + Possible prior art: gnome-disks, palimpset (precursor, suffering from this |
1818 | + flaw and showing all the /dev/ram devices by default) |
1819 | + """ |
1820 | + for part in device_file.split("/"): |
1821 | + if part.startswith("ram") or part.startswith("loop"): |
1822 | + return True |
1823 | + return False |
1824 | |
1825 | === added file 'checkbox-support/checkbox_support/heuristics/udisks2.py' |
1826 | --- checkbox-support/checkbox_support/heuristics/udisks2.py 1970-01-01 00:00:00 +0000 |
1827 | +++ checkbox-support/checkbox_support/heuristics/udisks2.py 2014-01-07 13:44:32 +0000 |
1828 | @@ -0,0 +1,62 @@ |
1829 | +# This file is part of Checkbox. |
1830 | +# |
1831 | +# Copyright 2012 Canonical Ltd. |
1832 | +# Written by: |
1833 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
1834 | +# |
1835 | +# Checkbox is free software: you can redistribute it and/or modify |
1836 | +# it under the terms of the GNU General Public License version 3, |
1837 | +# as published by the Free Software Foundation. |
1838 | + |
1839 | +# |
1840 | +# Checkbox is distributed in the hope that it will be useful, |
1841 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1842 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1843 | +# GNU General Public License for more details. |
1844 | +# |
1845 | +# You should have received a copy of the GNU General Public License |
1846 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1847 | + |
1848 | +""" |
1849 | +checkbox_support.heuristics.udisks2 |
1850 | +=========================== |
1851 | + |
1852 | +Heuristics for udisks2. |
1853 | + |
1854 | + Documentation: http://udisks.freedesktop.org/docs/latest/ |
1855 | + Source code: http://cgit.freedesktop.org/systemd/systemd/ (src/udev) |
1856 | + Bug tracker: http://bugs.freedesktop.org/ (using systemd product) |
1857 | +""" |
1858 | + |
1859 | +from checkbox_support.parsers.udevadm import CARD_READER_RE, GENERIC_RE, FLASH_RE |
1860 | + |
1861 | + |
1862 | +def is_memory_card(vendor, model, udisks2_media): |
1863 | + """ |
1864 | + Check if the device seems to be a memory card |
1865 | + |
1866 | + The vendor and model arguments are _strings_, not integers. |
1867 | + The udisks2_media argument is the value of org.freedesktop.UDisks2.Drive/ |
1868 | + |
1869 | + |
1870 | + This is rather fuzzy, sadly udev and udisks2 don't do a very good job and |
1871 | + mostly don't specify the "media" property (it has a few useful values, such |
1872 | + as flash_cf, flash_ms, flash_sm, flash_sd, flash_sdhc, flash_sdxc and |
1873 | + flash_mmc but I have yet to see a device that reports such values) |
1874 | + """ |
1875 | + # Treat any udisks2_media that contains 'flash' as a memory card |
1876 | + if udisks2_media is not None and FLASH_RE.search(udisks2_media): |
1877 | + return True |
1878 | + # Treat any device that match model name to the following regular |
1879 | + # expression as a memory card reader. |
1880 | + if CARD_READER_RE.search(model): |
1881 | + return True |
1882 | + # Treat any device that contains the word 'Generic' in the vendor string as |
1883 | + # a memory card reader. |
1884 | + # |
1885 | + # XXX: This seems odd but strangely enough seems to gets the job done. I |
1886 | + # guess if I should start filing tons of bugs/patches on udev/udisks2 to |
1887 | + # just have a few more rules and make this rule obsolete. |
1888 | + if GENERIC_RE.search(vendor): |
1889 | + return True |
1890 | + return False |
1891 | |
1892 | === added directory 'checkbox-support/checkbox_support/lib' |
1893 | === added file 'checkbox-support/checkbox_support/lib/__init__.py' |
1894 | === added file 'checkbox-support/checkbox_support/lib/bit.py' |
1895 | --- checkbox-support/checkbox_support/lib/bit.py 1970-01-01 00:00:00 +0000 |
1896 | +++ checkbox-support/checkbox_support/lib/bit.py 2014-01-07 13:44:32 +0000 |
1897 | @@ -0,0 +1,46 @@ |
1898 | +# |
1899 | +# This file is part of Checkbox. |
1900 | +# |
1901 | +# Copyright 2008 Canonical Ltd. |
1902 | +# |
1903 | +# Checkbox is free software: you can redistribute it and/or modify |
1904 | +# it under the terms of the GNU General Public License version 3, |
1905 | +# as published by the Free Software Foundation. |
1906 | + |
1907 | +# |
1908 | +# Checkbox is distributed in the hope that it will be useful, |
1909 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1910 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1911 | +# GNU General Public License for more details. |
1912 | +# |
1913 | +# You should have received a copy of the GNU General Public License |
1914 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1915 | +# |
1916 | +from struct import calcsize |
1917 | + |
1918 | + |
1919 | +def get_bitmask(key): |
1920 | + bitmask = [] |
1921 | + for value in reversed(key.split()): |
1922 | + value = int(value, 16) |
1923 | + bitmask.append(value) |
1924 | + |
1925 | + return bitmask |
1926 | + |
1927 | +def get_bitcount(bitmask): |
1928 | + bitcount = 0 |
1929 | + for value in bitmask: |
1930 | + while value: |
1931 | + bitcount += 1 |
1932 | + value &= (value - 1) |
1933 | + |
1934 | + return bitcount |
1935 | + |
1936 | +def test_bit(bit, bitmask, bits=None): |
1937 | + if bits is None: |
1938 | + bits = calcsize("l") * 8 |
1939 | + offset = bit % bits |
1940 | + long = int(bit / bits) |
1941 | + if long >= len(bitmask): |
1942 | + return 0 |
1943 | + return (bitmask[long] >> offset) & 1 |
1944 | |
1945 | === added file 'checkbox-support/checkbox_support/lib/conversion.py' |
1946 | --- checkbox-support/checkbox_support/lib/conversion.py 1970-01-01 00:00:00 +0000 |
1947 | +++ checkbox-support/checkbox_support/lib/conversion.py 2014-01-07 13:44:32 +0000 |
1948 | @@ -0,0 +1,172 @@ |
1949 | +# |
1950 | +# This file is part of Checkbox. |
1951 | +# |
1952 | +# Copyright 2008 Canonical Ltd. |
1953 | +# |
1954 | +# Checkbox is free software: you can redistribute it and/or modify |
1955 | +# it under the terms of the GNU General Public License version 3, |
1956 | +# as published by the Free Software Foundation. |
1957 | + |
1958 | +# |
1959 | +# Checkbox is distributed in the hope that it will be useful, |
1960 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
1961 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1962 | +# GNU General Public License for more details. |
1963 | +# |
1964 | +# You should have received a copy of the GNU General Public License |
1965 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
1966 | +# |
1967 | +import re |
1968 | + |
1969 | +from datetime import ( |
1970 | + datetime, |
1971 | + timedelta, |
1972 | + ) |
1973 | + |
1974 | +from checkbox_support.lib.tz import tzutc |
1975 | + |
1976 | + |
1977 | +DATETIME_RE = re.compile(r""" |
1978 | + ^(?P<year>\d\d\d\d)-?(?P<month>\d\d)-?(?P<day>\d\d) |
1979 | + T(?P<hour>\d\d):?(?P<minute>\d\d):?(?P<second>\d\d) |
1980 | + (?:\.(?P<second_fraction>\d{0,6}))? |
1981 | + (?P<tz> |
1982 | + (?:(?P<tz_sign>[-+])(?P<tz_hour>\d\d):(?P<tz_minute>\d\d)) |
1983 | + | Z)?$ |
1984 | + """, re.VERBOSE) |
1985 | + |
1986 | +TYPE_FORMATS = ( |
1987 | + (r"(yes|true)", lambda v: True), |
1988 | + (r"(no|false)", lambda v: False), |
1989 | + (r"-?\d+", lambda v: int(v.group(0))), |
1990 | + (r"-?\d+\.\d+", lambda v: float(v.group(0))), |
1991 | + (r"(-?\d+) ?([kmgt]?b?)", lambda v: int(v.group(1))), |
1992 | + (r"(-?\d+\.\d+) ?([kmgt]?b?)", lambda v: float(v.group(1))), |
1993 | + (r"(-?\d+) ?([kmgt]?hz)", lambda v: int(v.group(1))), |
1994 | + (r"(-?\d+\.\d+) ?([kmgt]?hz)", lambda v: float(v.group(1)))) |
1995 | +TYPE_FORMATS = tuple( |
1996 | + (re.compile(r"^%s$" % pattern, re.IGNORECASE), format) |
1997 | + for pattern, format in TYPE_FORMATS) |
1998 | + |
1999 | +TYPE_MULTIPLIERS = ( |
2000 | + (r"b", 1), |
2001 | + (r"kb?", 1024), |
2002 | + (r"mb?", 1024 * 1024), |
2003 | + (r"gb?", 1024 * 1024 * 1024), |
2004 | + (r"tb?", 1024 * 1024 * 1024 * 1024), |
2005 | + (r"hz", 1), |
2006 | + (r"khz?", 1024), |
2007 | + (r"mhz?", 1024 * 1024), |
2008 | + (r"ghz?", 1024 * 1024 * 1024), |
2009 | + (r"thz?", 1024 * 1024 * 1024 * 1024)) |
2010 | +TYPE_MULTIPLIERS = tuple( |
2011 | + (re.compile(r"^%s$" % pattern, re.IGNORECASE), multiplier) |
2012 | + for pattern, multiplier in TYPE_MULTIPLIERS) |
2013 | + |
2014 | + |
2015 | +def datetime_to_string(dt): |
2016 | + """Return a consistent string representation for a given datetime. |
2017 | + |
2018 | + :param dt: The datetime object. |
2019 | + """ |
2020 | + return dt.isoformat() |
2021 | + |
2022 | + |
2023 | +def string_to_datetime(string): |
2024 | + """Return a datetime object from a consistent string representation. |
2025 | + |
2026 | + :param string: The string representation. |
2027 | + """ |
2028 | + # we cannot use time.strptime: this function accepts neither fractions |
2029 | + # of a second nor a time zone given e.g. as '+02:30'. |
2030 | + match = DATETIME_RE.match(string) |
2031 | + |
2032 | + # The Relax NG schema allows a leading minus sign and year numbers |
2033 | + # with more than four digits, which are not "covered" by _time_regex. |
2034 | + if not match: |
2035 | + raise ValueError("Datetime with unreasonable value: %s" % string) |
2036 | + |
2037 | + time_parts = match.groupdict() |
2038 | + |
2039 | + year = int(time_parts['year']) |
2040 | + month = int(time_parts['month']) |
2041 | + day = int(time_parts['day']) |
2042 | + hour = int(time_parts['hour']) |
2043 | + minute = int(time_parts['minute']) |
2044 | + second = int(time_parts['second']) |
2045 | + second_fraction = time_parts['second_fraction'] |
2046 | + if second_fraction is not None: |
2047 | + milliseconds = second_fraction + '0' * (6 - len(second_fraction)) |
2048 | + milliseconds = int(milliseconds) |
2049 | + else: |
2050 | + milliseconds = 0 |
2051 | + |
2052 | + # The Relax NG validator accepts leap seconds, but the datetime |
2053 | + # constructor rejects them. The time values submitted by the HWDB |
2054 | + # client are not necessarily very precise, hence we can round down |
2055 | + # to 59.999999 seconds without losing any real precision. |
2056 | + if second > 59: |
2057 | + second = 59 |
2058 | + milliseconds = 999999 |
2059 | + |
2060 | + dt = datetime( |
2061 | + year, month, day, hour, minute, second, milliseconds, tzinfo=tzutc) |
2062 | + |
2063 | + tz_sign = time_parts['tz_sign'] |
2064 | + tz_hour = time_parts['tz_hour'] |
2065 | + tz_minute = time_parts['tz_minute'] |
2066 | + if tz_sign in ('-', '+'): |
2067 | + delta = timedelta(hours=int(tz_hour), minutes=int(tz_minute)) |
2068 | + if tz_sign == '-': |
2069 | + dt = dt + delta |
2070 | + else: |
2071 | + dt = dt - delta |
2072 | + |
2073 | + return dt |
2074 | + |
2075 | + |
2076 | +def sizeof_bytes(bytes): |
2077 | + for x in ["bytes", "KB", "MB", "GB", "TB"]: |
2078 | + string = "%3.1f%s" % (bytes, x) |
2079 | + if bytes < 1024.0: |
2080 | + break |
2081 | + bytes /= 1024.0 |
2082 | + |
2083 | + return string |
2084 | + |
2085 | + |
2086 | +def sizeof_hertz(hertz): |
2087 | + for x in ["Hz", "KHz", "MHz", "GHz"]: |
2088 | + string = "%3.1f%s" % (hertz, x) |
2089 | + if hertz < 1000.0: |
2090 | + break |
2091 | + hertz /= 1000.0 |
2092 | + |
2093 | + return string |
2094 | + |
2095 | + |
2096 | +def string_to_type(string): |
2097 | + """Return a typed representation for the given string. |
2098 | + |
2099 | + The result might be a bool, int or float. The string might also be |
2100 | + supplemented by a multiplier like KB which would return an int or |
2101 | + float multiplied by 1024 for example. |
2102 | + |
2103 | + :param string: The string representation. |
2104 | + """ |
2105 | + for regex, formatter in TYPE_FORMATS: |
2106 | + match = regex.match(string) |
2107 | + if match: |
2108 | + string = formatter(match) |
2109 | + if len(match.groups()) > 1: |
2110 | + unit = match.group(2) |
2111 | + for regex, multiplier in TYPE_MULTIPLIERS: |
2112 | + match = regex.match(unit) |
2113 | + if match: |
2114 | + string *= multiplier |
2115 | + break |
2116 | + else: |
2117 | + raise ValueError("Unknown multiplier: %s" % unit) |
2118 | + break |
2119 | + |
2120 | + return string |
2121 | |
2122 | === added file 'checkbox-support/checkbox_support/lib/dmi.py' |
2123 | --- checkbox-support/checkbox_support/lib/dmi.py 1970-01-01 00:00:00 +0000 |
2124 | +++ checkbox-support/checkbox_support/lib/dmi.py 2014-01-07 13:44:32 +0000 |
2125 | @@ -0,0 +1,241 @@ |
2126 | +# |
2127 | +# This file is part of Checkbox. |
2128 | +# |
2129 | +# Copyright 2008 Canonical Ltd. |
2130 | +# |
2131 | +# Checkbox is free software: you can redistribute it and/or modify |
2132 | +# it under the terms of the GNU General Public License version 3, |
2133 | +# as published by the Free Software Foundation. |
2134 | + |
2135 | +# |
2136 | +# Checkbox is distributed in the hope that it will be useful, |
2137 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2138 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2139 | +# GNU General Public License for more details. |
2140 | +# |
2141 | +# You should have received a copy of the GNU General Public License |
2142 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
2143 | +# |
2144 | +import os |
2145 | + |
2146 | +from checkbox_support.lib.conversion import string_to_type |
2147 | + |
2148 | +# See also 3.3.4.1 of the "System Management BIOS Reference Specification, |
2149 | +# Version 2.6.1" (Preliminary Standard) document, available from |
2150 | +# http://www.dmtf.org/standards/smbios. |
2151 | +class Dmi: |
2152 | + chassis = ( |
2153 | + ("Undefined", "unknown"), # 0x00 |
2154 | + ("Other", "unknown"), |
2155 | + ("Unknown", "unknown"), |
2156 | + ("Desktop", "desktop"), |
2157 | + ("Low Profile Desktop", "desktop"), |
2158 | + ("Pizza Box", "server"), |
2159 | + ("Mini Tower", "desktop"), |
2160 | + ("Tower", "desktop"), |
2161 | + ("Portable", "laptop"), |
2162 | + ("Laptop", "laptop"), |
2163 | + ("Notebook", "laptop"), |
2164 | + ("Hand Held", "handheld"), |
2165 | + ("Docking Station", "laptop"), |
2166 | + ("All In One", "unknown"), |
2167 | + ("Sub Notebook", "laptop"), |
2168 | + ("Space-saving", "desktop"), |
2169 | + ("Lunch Box", "unknown"), |
2170 | + ("Main Server Chassis", "server"), |
2171 | + ("Expansion Chassis", "unknown"), |
2172 | + ("Sub Chassis", "unknown"), |
2173 | + ("Bus Expansion Chassis", "unknown"), |
2174 | + ("Peripheral Chassis", "unknown"), |
2175 | + ("RAID Chassis", "unknown"), |
2176 | + ("Rack Mount Chassis", "unknown"), |
2177 | + ("Sealed-case PC", "unknown"), |
2178 | + ("Multi-system", "unknown"), |
2179 | + ("CompactPCI", "unknonw"), |
2180 | + ("AdvancedTCA", "unknown"), |
2181 | + ("Blade", "server"), |
2182 | + ("Blade Enclosure", "unknown")) |
2183 | + |
2184 | + chassis_names = tuple(c[0] for c in chassis) |
2185 | + chassis_types = tuple(c[1] for c in chassis) |
2186 | + chassis_name_to_type = dict(chassis) |
2187 | + |
2188 | + type_names = ( |
2189 | + "BIOS", # 0x00 |
2190 | + "System", |
2191 | + "Base Board", |
2192 | + "Chassis", |
2193 | + "Processor", |
2194 | + "Memory Controller", |
2195 | + "Memory Module", |
2196 | + "Cache", |
2197 | + "Port Connector", |
2198 | + "System Slots", |
2199 | + "On Board Devices", |
2200 | + "OEM Strings", |
2201 | + "System Configuration Options", |
2202 | + "BIOS Language", |
2203 | + "Group Associations", |
2204 | + "System Event Log", |
2205 | + "Physical Memory Array", |
2206 | + "Memory Device", |
2207 | + "32-bit Memory Error", |
2208 | + "Memory Array Mapped Address", |
2209 | + "Memory Device Mapped Address", |
2210 | + "Built-in Pointing Device", |
2211 | + "Portable Battery", |
2212 | + "System Reset", |
2213 | + "Hardware Security", |
2214 | + "System Power Controls", |
2215 | + "Voltage Probe", |
2216 | + "Cooling Device", |
2217 | + "Temperature Probe", |
2218 | + "Electrical Current Probe", |
2219 | + "Out-of-band Remote Access", |
2220 | + "Boot Integrity Services", |
2221 | + "System Boot", |
2222 | + "64-bit Memory Error", |
2223 | + "Management Device", |
2224 | + "Management Device Component", |
2225 | + "Management Device Threshold Data", |
2226 | + "Memory Channel", |
2227 | + "IPMI Device", |
2228 | + "Power Supply", |
2229 | + ) |
2230 | + |
2231 | + |
2232 | +class DmiDevice: |
2233 | + |
2234 | + bus = "dmi" |
2235 | + driver = None |
2236 | + product_id = None |
2237 | + vendor_id = None |
2238 | + |
2239 | + _product_blacklist = ( |
2240 | + "<BAD INDEX>", |
2241 | + "N/A", |
2242 | + "Not Available", |
2243 | + "INVALID", |
2244 | + "OEM", |
2245 | + "Product Name", |
2246 | + "System Product Name", |
2247 | + "To be filled by O.E.M.", |
2248 | + "To Be Filled By O.E.M.", |
2249 | + "To Be Filled By O.E.M. by More String", |
2250 | + "Unknown", |
2251 | + "Uknown", |
2252 | + "Unknow", |
2253 | + "xxxxxxxxxxxxxx", |
2254 | + ) |
2255 | + _vendor_blacklist = ( |
2256 | + "<BAD INDEX>", |
2257 | + "Not Available", |
2258 | + "OEM", |
2259 | + "OEM Manufacturer", |
2260 | + "System manufacturer", |
2261 | + "System Manufacturer", |
2262 | + "System Name", |
2263 | + "To be filled by O.E.M.", |
2264 | + "To Be Filled By O.E.M.", |
2265 | + "To Be Filled By O.E.M. by More String", |
2266 | + "Unknow", # XXX This is correct mispelling |
2267 | + "Unknown", |
2268 | + ) |
2269 | + _serial_blacklist = ( |
2270 | + "0", |
2271 | + "00000000", |
2272 | + "00 00 00 00 00 00 00 00", |
2273 | + "0123456789", |
2274 | + "Base Board Serial Number", |
2275 | + "Chassis Serial Number", |
2276 | + "N/A", |
2277 | + "None", |
2278 | + "Not Applicable", |
2279 | + "Not Available", |
2280 | + "Not Specified", |
2281 | + "OEM", |
2282 | + "System Serial Number", |
2283 | + ) |
2284 | + _version_blacklist = ( |
2285 | + "-1", |
2286 | + "<BAD INDEX>", |
2287 | + "N/A", |
2288 | + "None", |
2289 | + "Not Applicable", |
2290 | + "Not Available", |
2291 | + "Not Specified", |
2292 | + "OEM", |
2293 | + "System Version", |
2294 | + "Unknown", |
2295 | + "x.x", |
2296 | + ) |
2297 | + |
2298 | + def __init__(self, attributes, category): |
2299 | + self._attributes = attributes |
2300 | + self.category = category |
2301 | + |
2302 | + @property |
2303 | + def path(self): |
2304 | + path = "/devices/virtual/dmi/id" |
2305 | + return os.path.join(path, self.category.lower()) |
2306 | + |
2307 | + @property |
2308 | + def product(self): |
2309 | + if self.category == "CHASSIS": |
2310 | + type_string = self._attributes.get("chassis_type", "0") |
2311 | + try: |
2312 | + type_index = int(type_string) |
2313 | + return Dmi.chassis_names[type_index] |
2314 | + except ValueError: |
2315 | + return type_string |
2316 | + |
2317 | + for name in "name", "version": |
2318 | + attribute = "%s_%s" % (self.category.lower(), name) |
2319 | + product = self._attributes.get(attribute) |
2320 | + if product and product not in self._product_blacklist: |
2321 | + return product |
2322 | + |
2323 | + return None |
2324 | + |
2325 | + @property |
2326 | + def vendor(self): |
2327 | + for name in "manufacturer", "vendor": |
2328 | + attribute = "%s_%s" % (self.category.lower(), name) |
2329 | + vendor = self._attributes.get(attribute) |
2330 | + if vendor and vendor not in self._vendor_blacklist: |
2331 | + return vendor |
2332 | + |
2333 | + return None |
2334 | + |
2335 | + @property |
2336 | + def serial(self): |
2337 | + attribute = "%s_serial" % self.category.lower() |
2338 | + serial = self._attributes.get(attribute) |
2339 | + if serial and serial not in self._serial_blacklist: |
2340 | + return serial |
2341 | + |
2342 | + return None |
2343 | + |
2344 | + @property |
2345 | + def version(self): |
2346 | + attribute = "%s_version" % self.category.lower() |
2347 | + version = self._attributes.get(attribute) |
2348 | + if version and version not in self._version_blacklist: |
2349 | + return version |
2350 | + |
2351 | + return None |
2352 | + |
2353 | + @property |
2354 | + def size(self): |
2355 | + attribute = "%s_size" % self.category.lower() |
2356 | + size = self._attributes.get(attribute) |
2357 | + |
2358 | + if size: |
2359 | + size = string_to_type(size) |
2360 | + |
2361 | + return size |
2362 | + |
2363 | + @property |
2364 | + def form(self): |
2365 | + attribute = "%s_form" % self.category.lower() |
2366 | + return self._attributes.get(attribute) |
2367 | |
2368 | === added file 'checkbox-support/checkbox_support/lib/input.py' |
2369 | --- checkbox-support/checkbox_support/lib/input.py 1970-01-01 00:00:00 +0000 |
2370 | +++ checkbox-support/checkbox_support/lib/input.py 2014-01-07 13:44:32 +0000 |
2371 | @@ -0,0 +1,585 @@ |
2372 | +# |
2373 | +# This file is part of Checkbox. |
2374 | +# |
2375 | +# Copyright 2008 Canonical Ltd. |
2376 | +# |
2377 | +# Checkbox is free software: you can redistribute it and/or modify |
2378 | +# it under the terms of the GNU General Public License version 3, |
2379 | +# as published by the Free Software Foundation. |
2380 | + |
2381 | +# |
2382 | +# Checkbox is distributed in the hope that it will be useful, |
2383 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2384 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2385 | +# GNU General Public License for more details. |
2386 | +# |
2387 | +# You should have received a copy of the GNU General Public License |
2388 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
2389 | +# |
2390 | + |
2391 | +# See linux/input.h |
2392 | +class Input: |
2393 | + KEY_RESERVED = 0 |
2394 | + KEY_ESC = 1 |
2395 | + KEY_1 = 2 |
2396 | + KEY_2 = 3 |
2397 | + KEY_3 = 4 |
2398 | + KEY_4 = 5 |
2399 | + KEY_5 = 6 |
2400 | + KEY_6 = 7 |
2401 | + KEY_7 = 8 |
2402 | + KEY_8 = 9 |
2403 | + KEY_9 = 10 |
2404 | + KEY_0 = 11 |
2405 | + KEY_MINUS = 12 |
2406 | + KEY_EQUAL = 13 |
2407 | + KEY_BACKSPACE = 14 |
2408 | + KEY_TAB = 15 |
2409 | + KEY_Q = 16 |
2410 | + KEY_W = 17 |
2411 | + KEY_E = 18 |
2412 | + KEY_R = 19 |
2413 | + KEY_T = 20 |
2414 | + KEY_Y = 21 |
2415 | + KEY_U = 22 |
2416 | + KEY_I = 23 |
2417 | + KEY_O = 24 |
2418 | + KEY_P = 25 |
2419 | + KEY_LEFTBRACE = 26 |
2420 | + KEY_RIGHTBRACE = 27 |
2421 | + KEY_ENTER = 28 |
2422 | + KEY_LEFTCTRL = 29 |
2423 | + KEY_A = 30 |
2424 | + KEY_S = 31 |
2425 | + KEY_D = 32 |
2426 | + KEY_F = 33 |
2427 | + KEY_G = 34 |
2428 | + KEY_H = 35 |
2429 | + KEY_J = 36 |
2430 | + KEY_K = 37 |
2431 | + KEY_L = 38 |
2432 | + KEY_SEMICOLON = 39 |
2433 | + KEY_APOSTROPHE = 40 |
2434 | + KEY_GRAVE = 41 |
2435 | + KEY_LEFTSHIFT = 42 |
2436 | + KEY_BACKSLASH = 43 |
2437 | + KEY_Z = 44 |
2438 | + KEY_X = 45 |
2439 | + KEY_C = 46 |
2440 | + KEY_V = 47 |
2441 | + KEY_B = 48 |
2442 | + KEY_N = 49 |
2443 | + KEY_M = 50 |
2444 | + KEY_COMMA = 51 |
2445 | + KEY_DOT = 52 |
2446 | + KEY_SLASH = 53 |
2447 | + KEY_RIGHTSHIFT = 54 |
2448 | + KEY_KPASTERISK = 55 |
2449 | + KEY_LEFTALT = 56 |
2450 | + KEY_SPACE = 57 |
2451 | + KEY_CAPSLOCK = 58 |
2452 | + KEY_F1 = 59 |
2453 | + KEY_F2 = 60 |
2454 | + KEY_F3 = 61 |
2455 | + KEY_F4 = 62 |
2456 | + KEY_F5 = 63 |
2457 | + KEY_F6 = 64 |
2458 | + KEY_F7 = 65 |
2459 | + KEY_F8 = 66 |
2460 | + KEY_F9 = 67 |
2461 | + KEY_F10 = 68 |
2462 | + KEY_NUMLOCK = 69 |
2463 | + KEY_SCROLLLOCK = 70 |
2464 | + KEY_KP7 = 71 |
2465 | + KEY_KP8 = 72 |
2466 | + KEY_KP9 = 73 |
2467 | + KEY_KPMINUS = 74 |
2468 | + KEY_KP4 = 75 |
2469 | + KEY_KP5 = 76 |
2470 | + KEY_KP6 = 77 |
2471 | + KEY_KPPLUS = 78 |
2472 | + KEY_KP1 = 79 |
2473 | + KEY_KP2 = 80 |
2474 | + KEY_KP3 = 81 |
2475 | + KEY_KP0 = 82 |
2476 | + KEY_KPDOT = 83 |
2477 | + |
2478 | + KEY_ZENKAKUHANKAKU = 85 |
2479 | + KEY_102ND = 86 |
2480 | + KEY_F11 = 87 |
2481 | + KEY_F12 = 88 |
2482 | + KEY_RO = 89 |
2483 | + KEY_KATAKANA = 90 |
2484 | + KEY_HIRAGANA = 91 |
2485 | + KEY_HENKAN = 92 |
2486 | + KEY_KATAKANAHIRAGANA = 93 |
2487 | + KEY_MUHENKAN = 94 |
2488 | + KEY_KPJPCOMMA = 95 |
2489 | + KEY_KPENTER = 96 |
2490 | + KEY_RIGHTCTRL = 97 |
2491 | + KEY_KPSLASH = 98 |
2492 | + KEY_SYSRQ = 99 |
2493 | + KEY_RIGHTALT = 100 |
2494 | + KEY_LINEFEED = 101 |
2495 | + KEY_HOME = 102 |
2496 | + KEY_UP = 103 |
2497 | + KEY_PAGEUP = 104 |
2498 | + KEY_LEFT = 105 |
2499 | + KEY_RIGHT = 106 |
2500 | + KEY_END = 107 |
2501 | + KEY_DOWN = 108 |
2502 | + KEY_PAGEDOWN = 109 |
2503 | + KEY_INSERT = 110 |
2504 | + KEY_DELETE = 111 |
2505 | + KEY_MACRO = 112 |
2506 | + KEY_MUTE = 113 |
2507 | + KEY_VOLUMEDOWN = 114 |
2508 | + KEY_VOLUMEUP = 115 |
2509 | + KEY_POWER = 116 # SC System Power Down |
2510 | + KEY_KPEQUAL = 117 |
2511 | + KEY_KPPLUSMINUS = 118 |
2512 | + KEY_PAUSE = 119 |
2513 | + KEY_SCALE = 120 # AL Compiz Scale (Expose) |
2514 | + |
2515 | + KEY_KPCOMMA = 121 |
2516 | + KEY_HANGEUL = 122 |
2517 | + KEY_HANGUEL = KEY_HANGEUL |
2518 | + KEY_HANJA = 123 |
2519 | + KEY_YEN = 124 |
2520 | + KEY_LEFTMETA = 125 |
2521 | + KEY_RIGHTMETA = 126 |
2522 | + KEY_COMPOSE = 127 |
2523 | + |
2524 | + KEY_STOP = 128 # AC Stop |
2525 | + KEY_AGAIN = 129 |
2526 | + KEY_PROPS = 130 # AC Properties |
2527 | + KEY_UNDO = 131 # AC Undo |
2528 | + KEY_FRONT = 132 |
2529 | + KEY_COPY = 133 # AC Copy |
2530 | + KEY_OPEN = 134 # AC Open |
2531 | + KEY_PASTE = 135 # AC Paste |
2532 | + KEY_FIND = 136 # AC Search |
2533 | + KEY_CUT = 137 # AC Cut |
2534 | + KEY_HELP = 138 # AL Integrated Help Center |
2535 | + KEY_MENU = 139 # Menu (show menu) |
2536 | + KEY_CALC = 140 # AL Calculator |
2537 | + KEY_SETUP = 141 |
2538 | + KEY_SLEEP = 142 # SC System Sleep |
2539 | + KEY_WAKEUP = 143 # System Wake Up |
2540 | + KEY_FILE = 144 # AL Local Machine Browser |
2541 | + KEY_SENDFILE = 145 |
2542 | + KEY_DELETEFILE = 146 |
2543 | + KEY_XFER = 147 |
2544 | + KEY_PROG1 = 148 |
2545 | + KEY_PROG2 = 149 |
2546 | + KEY_WWW = 150 # AL Internet Browser |
2547 | + KEY_MSDOS = 151 |
2548 | + KEY_COFFEE = 152 # AL Terminal Lock/Screensaver |
2549 | + KEY_SCREENLOCK = KEY_COFFEE |
2550 | + KEY_DIRECTION = 153 |
2551 | + KEY_CYCLEWINDOWS = 154 |
2552 | + KEY_MAIL = 155 |
2553 | + KEY_BOOKMARKS = 156 # AC Bookmarks |
2554 | + KEY_COMPUTER = 157 |
2555 | + KEY_BACK = 158 # AC Back |
2556 | + KEY_FORWARD = 159 # AC Forward |
2557 | + KEY_CLOSECD = 160 |
2558 | + KEY_EJECTCD = 161 |
2559 | + KEY_EJECTCLOSECD = 162 |
2560 | + KEY_NEXTSONG = 163 |
2561 | + KEY_PLAYPAUSE = 164 |
2562 | + KEY_PREVIOUSSONG = 165 |
2563 | + KEY_STOPCD = 166 |
2564 | + KEY_RECORD = 167 |
2565 | + KEY_REWIND = 168 |
2566 | + KEY_PHONE = 169 # Media Select Telephone |
2567 | + KEY_ISO = 170 |
2568 | + KEY_CONFIG = 171 # AL Consumer Control Configuration |
2569 | + KEY_HOMEPAGE = 172 # AC Home |
2570 | + KEY_REFRESH = 173 # AC Refresh |
2571 | + KEY_EXIT = 174 # AC Exit |
2572 | + KEY_MOVE = 175 |
2573 | + KEY_EDIT = 176 |
2574 | + KEY_SCROLLUP = 177 |
2575 | + KEY_SCROLLDOWN = 178 |
2576 | + KEY_KPLEFTPAREN = 179 |
2577 | + KEY_KPRIGHTPAREN = 180 |
2578 | + KEY_NEW = 181 # AC New |
2579 | + KEY_REDO = 182 # AC Redo/Repeat |
2580 | + |
2581 | + KEY_F13 = 183 |
2582 | + KEY_F14 = 184 |
2583 | + KEY_F15 = 185 |
2584 | + KEY_F16 = 186 |
2585 | + KEY_F17 = 187 |
2586 | + KEY_F18 = 188 |
2587 | + KEY_F19 = 189 |
2588 | + KEY_F20 = 190 |
2589 | + KEY_F21 = 191 |
2590 | + KEY_F22 = 192 |
2591 | + KEY_F23 = 193 |
2592 | + KEY_F24 = 194 |
2593 | + |
2594 | + KEY_PLAYCD = 200 |
2595 | + KEY_PAUSECD = 201 |
2596 | + KEY_PROG3 = 202 |
2597 | + KEY_PROG4 = 203 |
2598 | + KEY_DASHBOARD = 204 # AL Dashboard |
2599 | + KEY_SUSPEND = 205 |
2600 | + KEY_CLOSE = 206 # AC Close |
2601 | + KEY_PLAY = 207 |
2602 | + KEY_FASTFORWARD = 208 |
2603 | + KEY_BASSBOOST = 209 |
2604 | + KEY_PRINT = 210 # AC Print |
2605 | + KEY_HP = 211 |
2606 | + KEY_CAMERA = 212 |
2607 | + KEY_SOUND = 213 |
2608 | + KEY_QUESTION = 214 |
2609 | + KEY_EMAIL = 215 |
2610 | + KEY_CHAT = 216 |
2611 | + KEY_SEARCH = 217 |
2612 | + KEY_CONNECT = 218 |
2613 | + KEY_FINANCE = 219 # AL Checkbook/Finance |
2614 | + KEY_SPORT = 220 |
2615 | + KEY_SHOP = 221 |
2616 | + KEY_ALTERASE = 222 |
2617 | + KEY_CANCEL = 223 # AC Cancel |
2618 | + KEY_BRIGHTNESSDOWN = 224 |
2619 | + KEY_BRIGHTNESSUP = 225 |
2620 | + KEY_MEDIA = 226 |
2621 | + |
2622 | + KEY_SWITCHVIDEOMODE = 227 # Cycle between available video |
2623 | + # outputs (Monitor/LCD/TV-out/etc) |
2624 | + KEY_KBDILLUMTOGGLE = 228 |
2625 | + KEY_KBDILLUMDOWN = 229 |
2626 | + KEY_KBDILLUMUP = 230 |
2627 | + |
2628 | + KEY_SEND = 231 # AC Send |
2629 | + KEY_REPLY = 232 # AC Reply |
2630 | + KEY_FORWARDMAIL = 233 # AC Forward Msg |
2631 | + KEY_SAVE = 234 # AC Save |
2632 | + KEY_DOCUMENTS = 235 |
2633 | + |
2634 | + KEY_BATTERY = 236 |
2635 | + |
2636 | + KEY_BLUETOOTH = 237 |
2637 | + KEY_WLAN = 238 |
2638 | + KEY_UWB = 239 |
2639 | + |
2640 | + KEY_UNKNOWN = 240 |
2641 | + |
2642 | + KEY_VIDEO_NEXT = 241 # drive next video source |
2643 | + KEY_VIDEO_PREV = 242 # drive previous video source |
2644 | + KEY_BRIGHTNESS_CYCLE = 243 # brightness up, after max is min |
2645 | + KEY_BRIGHTNESS_ZERO = 244 # brightness off, use ambient |
2646 | + KEY_DISPLAY_OFF = 245 # display device to off state |
2647 | + |
2648 | + KEY_WIMAX = 246 |
2649 | + |
2650 | + # Range = 248 - 255 is reserved for special needs of AT keyboard driver |
2651 | + |
2652 | + BTN_MISC = 0x100 |
2653 | + BTN_0 = 0x100 |
2654 | + BTN_1 = 0x101 |
2655 | + BTN_2 = 0x102 |
2656 | + BTN_3 = 0x103 |
2657 | + BTN_4 = 0x104 |
2658 | + BTN_5 = 0x105 |
2659 | + BTN_6 = 0x106 |
2660 | + BTN_7 = 0x107 |
2661 | + BTN_8 = 0x108 |
2662 | + BTN_9 = 0x109 |
2663 | + |
2664 | + BTN_MOUSE = 0x110 |
2665 | + BTN_LEFT = 0x110 |
2666 | + BTN_RIGHT = 0x111 |
2667 | + BTN_MIDDLE = 0x112 |
2668 | + BTN_SIDE = 0x113 |
2669 | + BTN_EXTRA = 0x114 |
2670 | + BTN_FORWARD = 0x115 |
2671 | + BTN_BACK = 0x116 |
2672 | + BTN_TASK = 0x117 |
2673 | + |
2674 | + BTN_JOYSTICK = 0x120 |
2675 | + BTN_TRIGGER = 0x120 |
2676 | + BTN_THUMB = 0x121 |
2677 | + BTN_THUMB2 = 0x122 |
2678 | + BTN_TOP = 0x123 |
2679 | + BTN_TOP2 = 0x124 |
2680 | + BTN_PINKIE = 0x125 |
2681 | + BTN_BASE = 0x126 |
2682 | + BTN_BASE2 = 0x127 |
2683 | + BTN_BASE3 = 0x128 |
2684 | + BTN_BASE4 = 0x129 |
2685 | + BTN_BASE5 = 0x12a |
2686 | + BTN_BASE6 = 0x12b |
2687 | + BTN_DEAD = 0x12f |
2688 | + |
2689 | + BTN_GAMEPAD = 0x130 |
2690 | + BTN_A = 0x130 |
2691 | + BTN_B = 0x131 |
2692 | + BTN_C = 0x132 |
2693 | + BTN_X = 0x133 |
2694 | + BTN_Y = 0x134 |
2695 | + BTN_Z = 0x135 |
2696 | + BTN_TL = 0x136 |
2697 | + BTN_TR = 0x137 |
2698 | + BTN_TL2 = 0x138 |
2699 | + BTN_TR2 = 0x139 |
2700 | + BTN_SELECT = 0x13a |
2701 | + BTN_START = 0x13b |
2702 | + BTN_MODE = 0x13c |
2703 | + BTN_THUMBL = 0x13d |
2704 | + BTN_THUMBR = 0x13e |
2705 | + |
2706 | + BTN_DIGI = 0x140 |
2707 | + BTN_TOOL_PEN = 0x140 |
2708 | + BTN_TOOL_RUBBER = 0x141 |
2709 | + BTN_TOOL_BRUSH = 0x142 |
2710 | + BTN_TOOL_PENCIL = 0x143 |
2711 | + BTN_TOOL_AIRBRUSH = 0x144 |
2712 | + BTN_TOOL_FINGER = 0x145 |
2713 | + BTN_TOOL_MOUSE = 0x146 |
2714 | + BTN_TOOL_LENS = 0x147 |
2715 | + BTN_TOUCH = 0x14a |
2716 | + BTN_STYLUS = 0x14b |
2717 | + BTN_STYLUS2 = 0x14c |
2718 | + BTN_TOOL_DOUBLETAP = 0x14d |
2719 | + BTN_TOOL_TRIPLETAP = 0x14e |
2720 | + |
2721 | + BTN_WHEEL = 0x150 |
2722 | + BTN_GEAR_DOWN = 0x150 |
2723 | + BTN_GEAR_UP = 0x151 |
2724 | + |
2725 | + KEY_OK = 0x160 |
2726 | + KEY_SELECT = 0x161 |
2727 | + KEY_GOTO = 0x162 |
2728 | + KEY_CLEAR = 0x163 |
2729 | + KEY_POWER2 = 0x164 |
2730 | + KEY_OPTION = 0x165 |
2731 | + KEY_INFO = 0x166 # AL OEM Features/Tips/Tutorial |
2732 | + KEY_TIME = 0x167 |
2733 | + KEY_VENDOR = 0x168 |
2734 | + KEY_ARCHIVE = 0x169 |
2735 | + KEY_PROGRAM = 0x16a # Media Select Program Guide |
2736 | + KEY_CHANNEL = 0x16b |
2737 | + KEY_FAVORITES = 0x16c |
2738 | + KEY_EPG = 0x16d |
2739 | + KEY_PVR = 0x16e # Media Select Home |
2740 | + KEY_MHP = 0x16f |
2741 | + KEY_LANGUAGE = 0x170 |
2742 | + KEY_TITLE = 0x171 |
2743 | + KEY_SUBTITLE = 0x172 |
2744 | + KEY_ANGLE = 0x173 |
2745 | + KEY_ZOOM = 0x174 |
2746 | + KEY_MODE = 0x175 |
2747 | + KEY_KEYBOARD = 0x176 |
2748 | + KEY_SCREEN = 0x177 |
2749 | + KEY_PC = 0x178 # Media Select Computer |
2750 | + KEY_TV = 0x179 # Media Select TV |
2751 | + KEY_TV2 = 0x17a # Media Select Cable |
2752 | + KEY_VCR = 0x17b # Media Select VCR |
2753 | + KEY_VCR2 = 0x17c # VCR Plus |
2754 | + KEY_SAT = 0x17d # Media Select Satellite |
2755 | + KEY_SAT2 = 0x17e |
2756 | + KEY_CD = 0x17f # Media Select CD |
2757 | + KEY_TAPE = 0x180 # Media Select Tape |
2758 | + KEY_RADIO = 0x181 |
2759 | + KEY_TUNER = 0x182 # Media Select Tuner |
2760 | + KEY_PLAYER = 0x183 |
2761 | + KEY_TEXT = 0x184 |
2762 | + KEY_DVD = 0x185 # Media Select DVD |
2763 | + KEY_AUX = 0x186 |
2764 | + KEY_MP3 = 0x187 |
2765 | + KEY_AUDIO = 0x188 |
2766 | + KEY_VIDEO = 0x189 |
2767 | + KEY_DIRECTORY = 0x18a |
2768 | + KEY_LIST = 0x18b |
2769 | + KEY_MEMO = 0x18c # Media Select Messages |
2770 | + KEY_CALENDAR = 0x18d |
2771 | + KEY_RED = 0x18e |
2772 | + KEY_GREEN = 0x18f |
2773 | + KEY_YELLOW = 0x190 |
2774 | + KEY_BLUE = 0x191 |
2775 | + KEY_CHANNELUP = 0x192 # Channel Increment |
2776 | + KEY_CHANNELDOWN = 0x193 # Channel Decrement |
2777 | + KEY_FIRST = 0x194 |
2778 | + KEY_LAST = 0x195 # Recall Last |
2779 | + KEY_AB = 0x196 |
2780 | + KEY_NEXT = 0x197 |
2781 | + KEY_RESTART = 0x198 |
2782 | + KEY_SLOW = 0x199 |
2783 | + KEY_SHUFFLE = 0x19a |
2784 | + KEY_BREAK = 0x19b |
2785 | + KEY_PREVIOUS = 0x19c |
2786 | + KEY_DIGITS = 0x19d |
2787 | + KEY_TEEN = 0x19e |
2788 | + KEY_TWEN = 0x19f |
2789 | + KEY_VIDEOPHONE = 0x1a0 # Media Select Video Phone |
2790 | + KEY_GAMES = 0x1a1 # Media Select Games |
2791 | + KEY_ZOOMIN = 0x1a2 # AC Zoom In |
2792 | + KEY_ZOOMOUT = 0x1a3 # AC Zoom Out |
2793 | + KEY_ZOOMRESET = 0x1a4 # AC Zoom |
2794 | + KEY_WORDPROCESSOR = 0x1a5 # AL Word Processor |
2795 | + KEY_EDITOR = 0x1a6 # AL Text Editor |
2796 | + KEY_SPREADSHEET = 0x1a7 # AL Spreadsheet |
2797 | + KEY_GRAPHICSEDITOR = 0x1a8 # AL Graphics Editor |
2798 | + KEY_PRESENTATION = 0x1a9 # AL Presentation App |
2799 | + KEY_DATABASE = 0x1aa # AL Database App |
2800 | + KEY_NEWS = 0x1ab # AL Newsreader |
2801 | + KEY_VOICEMAIL = 0x1ac # AL Voicemail |
2802 | + KEY_ADDRESSBOOK = 0x1ad # AL Contacts/Address Book |
2803 | + KEY_MESSENGER = 0x1ae # AL Instant Messaging |
2804 | + KEY_DISPLAYTOGGLE = 0x1af # Turn display (LCD) on and off |
2805 | + KEY_SPELLCHECK = 0x1b0 # AL Spell Check |
2806 | + KEY_LOGOFF = 0x1b1 # AL Logoff |
2807 | + |
2808 | + KEY_DOLLAR = 0x1b2 |
2809 | + KEY_EURO = 0x1b3 |
2810 | + |
2811 | + KEY_FRAMEBACK = 0x1b4 # Consumer - transport controls |
2812 | + KEY_FRAMEFORWARD = 0x1b5 |
2813 | + KEY_CONTEXT_MENU = 0x1b6 # GenDesc - system context menu |
2814 | + KEY_MEDIA_REPEAT = 0x1b7 # Consumer - transport control |
2815 | + |
2816 | + KEY_DEL_EOL = 0x1c0 |
2817 | + KEY_DEL_EOS = 0x1c1 |
2818 | + KEY_INS_LINE = 0x1c2 |
2819 | + KEY_DEL_LINE = 0x1c3 |
2820 | + |
2821 | + KEY_FN = 0x1d0 |
2822 | + KEY_FN_ESC = 0x1d1 |
2823 | + KEY_FN_F1 = 0x1d2 |
2824 | + KEY_FN_F2 = 0x1d3 |
2825 | + KEY_FN_F3 = 0x1d4 |
2826 | + KEY_FN_F4 = 0x1d5 |
2827 | + KEY_FN_F5 = 0x1d6 |
2828 | + KEY_FN_F6 = 0x1d7 |
2829 | + KEY_FN_F7 = 0x1d8 |
2830 | + KEY_FN_F8 = 0x1d9 |
2831 | + KEY_FN_F9 = 0x1da |
2832 | + KEY_FN_F10 = 0x1db |
2833 | + KEY_FN_F11 = 0x1dc |
2834 | + KEY_FN_F12 = 0x1dd |
2835 | + KEY_FN_1 = 0x1de |
2836 | + KEY_FN_2 = 0x1df |
2837 | + KEY_FN_D = 0x1e0 |
2838 | + KEY_FN_E = 0x1e1 |
2839 | + KEY_FN_F = 0x1e2 |
2840 | + KEY_FN_S = 0x1e3 |
2841 | + KEY_FN_B = 0x1e4 |
2842 | + |
2843 | + KEY_BRL_DOT1 = 0x1f1 |
2844 | + KEY_BRL_DOT2 = 0x1f2 |
2845 | + KEY_BRL_DOT3 = 0x1f3 |
2846 | + KEY_BRL_DOT4 = 0x1f4 |
2847 | + KEY_BRL_DOT5 = 0x1f5 |
2848 | + KEY_BRL_DOT6 = 0x1f6 |
2849 | + KEY_BRL_DOT7 = 0x1f7 |
2850 | + KEY_BRL_DOT8 = 0x1f8 |
2851 | + KEY_BRL_DOT9 = 0x1f9 |
2852 | + KEY_BRL_DOT10 = 0x1fa |
2853 | + |
2854 | + KEY_NUMERIC_0 = 0x200 # used by phones, remote controls, |
2855 | + KEY_NUMERIC_1 = 0x201 # and other keypads |
2856 | + KEY_NUMERIC_2 = 0x202 |
2857 | + KEY_NUMERIC_3 = 0x203 |
2858 | + KEY_NUMERIC_4 = 0x204 |
2859 | + KEY_NUMERIC_5 = 0x205 |
2860 | + KEY_NUMERIC_6 = 0x206 |
2861 | + KEY_NUMERIC_7 = 0x207 |
2862 | + KEY_NUMERIC_8 = 0x208 |
2863 | + KEY_NUMERIC_9 = 0x209 |
2864 | + KEY_NUMERIC_STAR = 0x20a |
2865 | + KEY_NUMERIC_POUND = 0x20b |
2866 | + |
2867 | + # Relative axes |
2868 | + |
2869 | + REL_X = 0x00 |
2870 | + REL_Y = 0x01 |
2871 | + REL_Z = 0x02 |
2872 | + REL_RX = 0x03 |
2873 | + REL_RY = 0x04 |
2874 | + REL_RZ = 0x05 |
2875 | + REL_HWHEEL = 0x06 |
2876 | + REL_DIAL = 0x07 |
2877 | + REL_WHEEL = 0x08 |
2878 | + REL_MISC = 0x09 |
2879 | + REL_MAX = 0x0f |
2880 | + REL_CNT = REL_MAX+1 |
2881 | + |
2882 | + # Absolute axes |
2883 | + |
2884 | + ABS_X = 0x00 |
2885 | + ABS_Y = 0x01 |
2886 | + ABS_Z = 0x02 |
2887 | + ABS_RX = 0x03 |
2888 | + ABS_RY = 0x04 |
2889 | + ABS_RZ = 0x05 |
2890 | + ABS_THROTTLE = 0x06 |
2891 | + ABS_RUDDER = 0x07 |
2892 | + ABS_WHEEL = 0x08 |
2893 | + ABS_GAS = 0x09 |
2894 | + ABS_BRAKE = 0x0a |
2895 | + ABS_HAT0X = 0x10 |
2896 | + ABS_HAT0Y = 0x11 |
2897 | + ABS_HAT1X = 0x12 |
2898 | + ABS_HAT1Y = 0x13 |
2899 | + ABS_HAT2X = 0x14 |
2900 | + ABS_HAT2Y = 0x15 |
2901 | + ABS_HAT3X = 0x16 |
2902 | + ABS_HAT3Y = 0x17 |
2903 | + ABS_PRESSURE = 0x18 |
2904 | + ABS_DISTANCE = 0x19 |
2905 | + ABS_TILT_X = 0x1a |
2906 | + ABS_TILT_Y = 0x1b |
2907 | + ABS_TOOL_WIDTH = 0x1c |
2908 | + ABS_VOLUME = 0x20 |
2909 | + ABS_MISC = 0x28 |
2910 | + ABS_MAX = 0x3f |
2911 | + ABS_CNT = ABS_MAX+1 |
2912 | + |
2913 | + # Switch events |
2914 | + |
2915 | + SW_LID = 0x00 # set = lid shut |
2916 | + SW_TABLET_MODE = 0x01 # set = tablet mode |
2917 | + SW_HEADPHONE_INSERT = 0x02 # set = inserted |
2918 | + SW_RFKILL_ALL = 0x03 # rfkill master switch, type "any" |
2919 | + # set = radio enabled |
2920 | + SW_RADIO = SW_RFKILL_ALL # deprecated |
2921 | + SW_MICROPHONE_INSERT = 0x04 # set = inserted |
2922 | + SW_DOCK = 0x05 # set = plugged into dock |
2923 | + SW_MAX = 0x0f |
2924 | + SW_CNT = SW_MAX+1 |
2925 | + |
2926 | + # Misc events |
2927 | + |
2928 | + MSC_SERIAL = 0x00 |
2929 | + MSC_PULSELED = 0x01 |
2930 | + MSC_GESTURE = 0x02 |
2931 | + MSC_RAW = 0x03 |
2932 | + MSC_SCAN = 0x04 |
2933 | + MSC_MAX = 0x07 |
2934 | + MSC_CNT = MSC_MAX+1 |
2935 | + |
2936 | + # LEDs |
2937 | + |
2938 | + LED_NUML = 0x00 |
2939 | + LED_CAPSL = 0x01 |
2940 | + LED_SCROLLL = 0x02 |
2941 | + LED_COMPOSE = 0x03 |
2942 | + LED_KANA = 0x04 |
2943 | + LED_SLEEP = 0x05 |
2944 | + LED_SUSPEND = 0x06 |
2945 | + LED_MUTE = 0x07 |
2946 | + LED_MISC = 0x08 |
2947 | + LED_MAIL = 0x09 |
2948 | + LED_CHARGING = 0x0a |
2949 | + LED_MAX = 0x0f |
2950 | + LED_CNT = LED_MAX+1 |
2951 | + |
2952 | + # Autorepeat values |
2953 | + |
2954 | + REP_DELAY = 0x00 |
2955 | + REP_PERIOD = 0x01 |
2956 | + REP_MAX = 0x01 |
2957 | |
2958 | === added file 'checkbox-support/checkbox_support/lib/path.py' |
2959 | --- checkbox-support/checkbox_support/lib/path.py 1970-01-01 00:00:00 +0000 |
2960 | +++ checkbox-support/checkbox_support/lib/path.py 2014-01-07 13:44:32 +0000 |
2961 | @@ -0,0 +1,62 @@ |
2962 | +# |
2963 | +# This file is part of Checkbox. |
2964 | +# |
2965 | +# Copyright 2008 Canonical Ltd. |
2966 | +# |
2967 | +# Checkbox is free software: you can redistribute it and/or modify |
2968 | +# it under the terms of the GNU General Public License version 3, |
2969 | +# as published by the Free Software Foundation. |
2970 | + |
2971 | +# |
2972 | +# Checkbox is distributed in the hope that it will be useful, |
2973 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
2974 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2975 | +# GNU General Public License for more details. |
2976 | +# |
2977 | +# You should have received a copy of the GNU General Public License |
2978 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
2979 | +# |
2980 | +import os |
2981 | + |
2982 | +from glob import glob |
2983 | + |
2984 | + |
2985 | +def path_split(path): |
2986 | + return path.split(os.path.sep) |
2987 | + |
2988 | +def path_common(l1, l2, common=[]): |
2989 | + if len(l1) < 1: |
2990 | + return (common, l1, l2) |
2991 | + |
2992 | + if len(l2) < 1: |
2993 | + return (common, l1, l2) |
2994 | + |
2995 | + if l1[0] != l2[0]: |
2996 | + return (common, l1, l2) |
2997 | + |
2998 | + return path_common(l1[1:], l2[1:], common + [l1[0]]) |
2999 | + |
3000 | +def path_relative(p1, p2): |
3001 | + (common, l1, l2) = path_common(path_split(p1), path_split(p2)) |
3002 | + p = [] |
3003 | + if len(l1) > 0: |
3004 | + p = ["..%s" % os.path.sep * len(l1)] |
3005 | + |
3006 | + p = p + l2 |
3007 | + return os.path.join( *p ) |
3008 | + |
3009 | +def path_expand(path): |
3010 | + path = os.path.expanduser(path) |
3011 | + return glob(path) |
3012 | + |
3013 | +def path_expand_recursive(path): |
3014 | + paths = [] |
3015 | + for path in path_expand(path): |
3016 | + if os.path.isdir(path): |
3017 | + for dirpath, dirnames, filenames in os.walk(path): |
3018 | + for filename in filenames: |
3019 | + paths.append(os.path.join(dirpath, filename)) |
3020 | + else: |
3021 | + paths.append(path) |
3022 | + |
3023 | + return paths |
3024 | |
3025 | === added file 'checkbox-support/checkbox_support/lib/pci.py' |
3026 | --- checkbox-support/checkbox_support/lib/pci.py 1970-01-01 00:00:00 +0000 |
3027 | +++ checkbox-support/checkbox_support/lib/pci.py 2014-01-07 13:44:32 +0000 |
3028 | @@ -0,0 +1,89 @@ |
3029 | +# |
3030 | +# This file is part of Checkbox. |
3031 | +# |
3032 | +# Copyright 2008 Canonical Ltd. |
3033 | +# |
3034 | +# Checkbox is free software: you can redistribute it and/or modify |
3035 | +# it under the terms of the GNU General Public License version 3, |
3036 | +# as published by the Free Software Foundation. |
3037 | + |
3038 | +# |
3039 | +# Checkbox is distributed in the hope that it will be useful, |
3040 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3041 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3042 | +# GNU General Public License for more details. |
3043 | +# |
3044 | +# You should have received a copy of the GNU General Public License |
3045 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3046 | +# |
3047 | + |
3048 | +# See http://pciids.sourceforge.net/pci.ids.bz2 |
3049 | +class Pci: |
3050 | + |
3051 | + BASE_CLASS_STORAGE = 1 |
3052 | + CLASS_STORAGE_SCSI = 0 |
3053 | + CLASS_STORAGE_IDE = 1 |
3054 | + CLASS_STORAGE_FLOPPY = 2 |
3055 | + CLASS_STORAGE_IPI = 3 |
3056 | + CLASS_STORAGE_RAID = 4 |
3057 | + CLASS_STORAGE_OTHER = 80 |
3058 | + |
3059 | + BASE_CLASS_NETWORK = 2 |
3060 | + CLASS_NETWORK_ETHERNET = 0 |
3061 | + CLASS_NETWORK_TOKEN_RING = 1 |
3062 | + CLASS_NETWORK_FDDI = 2 |
3063 | + CLASS_NETWORK_ATM = 3 |
3064 | + CLASS_NETWORK_OTHER = 80 |
3065 | + CLASS_NETWORK_WIRELESS = 128 |
3066 | + |
3067 | + BASE_CLASS_DISPLAY = 3 |
3068 | + CLASS_DISPLAY_VGA = 0 |
3069 | + CLASS_DISPLAY_XGA = 1 |
3070 | + CLASS_DISPLAY_3D = 2 |
3071 | + CLASS_DISPLAY_OTHER = 80 |
3072 | + |
3073 | + BASE_CLASS_MULTIMEDIA = 4 |
3074 | + CLASS_MULTIMEDIA_VIDEO = 0 |
3075 | + CLASS_MULTIMEDIA_AUDIO = 1 |
3076 | + CLASS_MULTIMEDIA_PHONE = 2 |
3077 | + CLASS_MULTIMEDIA_AUDIO_DEVICE = 3 |
3078 | + CLASS_MULTIMEDIA_OTHER = 80 |
3079 | + |
3080 | + BASE_CLASS_BRIDGE = 6 |
3081 | + CLASS_BRIDGE_HOST = 0 |
3082 | + CLASS_BRIDGE_ISA = 1 |
3083 | + CLASS_BRIDGE_EISA = 2 |
3084 | + CLASS_BRIDGE_MC = 3 |
3085 | + CLASS_BRIDGE_PCI = 4 |
3086 | + CLASS_BRIDGE_PCMCIA = 5 |
3087 | + CLASS_BRIDGE_NUBUS = 6 |
3088 | + CLASS_BRIDGE_CARDBUS = 7 |
3089 | + CLASS_BRIDGE_RACEWAY = 8 |
3090 | + CLASS_BRIDGE_OTHER = 80 |
3091 | + |
3092 | + BASE_CLASS_COMMUNICATION = 7 |
3093 | + CLASS_COMMUNICATION_SERIAL = 0 |
3094 | + CLASS_COMMUNICATION_PARALLEL = 1 |
3095 | + CLASS_COMMUNICATION_MULTISERIAL = 2 |
3096 | + CLASS_COMMUNICATION_MODEM = 3 |
3097 | + CLASS_COMMUNICATION_OTHER = 80 |
3098 | + |
3099 | + BASE_CLASS_INPUT = 9 |
3100 | + CLASS_INPUT_KEYBOARD = 0 |
3101 | + CLASS_INPUT_PEN = 1 |
3102 | + CLASS_INPUT_MOUSE = 2 |
3103 | + CLASS_INPUT_SCANNER = 3 |
3104 | + CLASS_INPUT_GAMEPORT = 4 |
3105 | + CLASS_INPUT_OTHER = 80 |
3106 | + |
3107 | + BASE_CLASS_SERIAL = 12 |
3108 | + CLASS_SERIAL_FIREWIRE = 0 |
3109 | + CLASS_SERIAL_ACCESS = 1 |
3110 | + |
3111 | + BASE_CLASS_WIRELESS = 13 |
3112 | + CLASS_WIRELESS_BLUETOOTH = 17 |
3113 | + |
3114 | + CLASS_SERIAL_SSA = 2 |
3115 | + CLASS_SERIAL_USB = 3 |
3116 | + CLASS_SERIAL_FIBER = 4 |
3117 | + CLASS_SERIAL_SMBUS = 5 |
3118 | |
3119 | === added file 'checkbox-support/checkbox_support/lib/template.py' |
3120 | --- checkbox-support/checkbox_support/lib/template.py 1970-01-01 00:00:00 +0000 |
3121 | +++ checkbox-support/checkbox_support/lib/template.py 2014-01-07 13:44:32 +0000 |
3122 | @@ -0,0 +1,143 @@ |
3123 | +# |
3124 | +# This file is part of Checkbox. |
3125 | +# |
3126 | +# Copyright 2008 Canonical Ltd. |
3127 | +# |
3128 | +# Checkbox is free software: you can redistribute it and/or modify |
3129 | +# it under the terms of the GNU General Public License version 3, |
3130 | +# as published by the Free Software Foundation. |
3131 | + |
3132 | +# |
3133 | +# Checkbox is distributed in the hope that it will be useful, |
3134 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3135 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3136 | +# GNU General Public License for more details. |
3137 | +# |
3138 | +# You should have received a copy of the GNU General Public License |
3139 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3140 | +# |
3141 | +import re |
3142 | +import logging |
3143 | + |
3144 | + |
3145 | +EXTENDED_STRING = "_extended" |
3146 | + |
3147 | + |
3148 | +class Template: |
3149 | + |
3150 | + def _reader(self, file, size=4096, delimiter=r"\n{2,}"): |
3151 | + buffer_old = "" |
3152 | + while True: |
3153 | + buffer_new = file.read(size) |
3154 | + if not buffer_new: |
3155 | + break |
3156 | + |
3157 | + lines = re.split(delimiter, buffer_old + buffer_new) |
3158 | + buffer_old = lines.pop(-1) |
3159 | + |
3160 | + for line in lines: |
3161 | + yield line |
3162 | + |
3163 | + yield buffer_old |
3164 | + |
3165 | + def dump_file(self, elements, file, filename="<stream>"): |
3166 | + for element in elements: |
3167 | + for long_key in list(element.keys()): |
3168 | + if long_key.endswith(EXTENDED_STRING): |
3169 | + short_key = long_key.replace(EXTENDED_STRING, "") |
3170 | + del element[short_key] |
3171 | + |
3172 | + for key, value in element.items(): |
3173 | + if key.endswith(EXTENDED_STRING): |
3174 | + key = key.replace(EXTENDED_STRING, "") |
3175 | + file.write("%s:\n" % key) |
3176 | + for line in value.split("\n"): |
3177 | + file.write(" %s\n" % line) |
3178 | + elif isinstance(value, (list, tuple)): |
3179 | + file.write("%s:\n" % key) |
3180 | + for v in value: |
3181 | + file.write(" %s\n" % v) |
3182 | + else: |
3183 | + file.write("%s: %s\n" % (key, value)) |
3184 | + |
3185 | + file.write("\n") |
3186 | + |
3187 | + def dump_filename(self, elements, filename): |
3188 | + logging.info("Dumping elements to filename: %s", filename) |
3189 | + |
3190 | + with open(filename, "w") as stream: |
3191 | + return self.dump_file(elements, stream, filename) |
3192 | + |
3193 | + def load_file(self, file, filename="<stream>"): |
3194 | + elements = [] |
3195 | + for string in self._reader(file): |
3196 | + if not string: |
3197 | + break |
3198 | + |
3199 | + element = {} |
3200 | + |
3201 | + def _save(field, value, extended): |
3202 | + extended = extended.rstrip("\n") |
3203 | + if field: |
3204 | + if field in element: |
3205 | + raise Exception("Template %s has a duplicate " |
3206 | + "field '%s' with a new value '%s'." |
3207 | + % (filename, field, value)) |
3208 | + element[field] = value |
3209 | + if extended: |
3210 | + element["%s%s" % (field, EXTENDED_STRING)] = extended |
3211 | + |
3212 | + string = string.strip("\n") |
3213 | + field = value = extended = "" |
3214 | + for line in string.split("\n"): |
3215 | + line.strip() |
3216 | + if line.startswith("#"): |
3217 | + continue |
3218 | + |
3219 | + match = re.search(r"^([-_.A-Za-z0-9@]*):\s?(.*)", line) |
3220 | + if match: |
3221 | + _save(field, value, extended) |
3222 | + field = match.groups()[0] |
3223 | + value = match.groups()[1].rstrip() |
3224 | + extended = "" |
3225 | + continue |
3226 | + |
3227 | + if re.search(r"^\s\.$", line): |
3228 | + extended += "\n\n" |
3229 | + continue |
3230 | + |
3231 | + match = re.search(r"^\s(\s+.*)", line) |
3232 | + if match: |
3233 | + bit = match.groups()[0].rstrip() |
3234 | + if len(extended) and not re.search(r"[\n ]$", extended): |
3235 | + extended += "\n" |
3236 | + |
3237 | + extended += bit + "\n" |
3238 | + continue |
3239 | + |
3240 | + match = re.search(r"^\s(.*)", line) |
3241 | + if match: |
3242 | + bit = match.groups()[0].rstrip() |
3243 | + if len(extended) and not re.search(r"[\n ]$", extended): |
3244 | + if extended.endswith("\\"): |
3245 | + extended = extended[:-1].rstrip() + " " |
3246 | + else: |
3247 | + extended += "\n" |
3248 | + |
3249 | + extended += bit |
3250 | + continue |
3251 | + |
3252 | + raise Exception("Template %s parse error at: %s" \ |
3253 | + % (filename, line)) |
3254 | + |
3255 | + _save(field, value, extended) |
3256 | + |
3257 | + elements.append(element) |
3258 | + |
3259 | + return elements |
3260 | + |
3261 | + def load_filename(self, filename): |
3262 | + logging.info("Loading elements from filename: %s", filename) |
3263 | + |
3264 | + with open(filename, "r", encoding="utf-8") as stream: |
3265 | + return self.load_file(stream, filename) |
3266 | |
3267 | === added file 'checkbox-support/checkbox_support/lib/tz.py' |
3268 | --- checkbox-support/checkbox_support/lib/tz.py 1970-01-01 00:00:00 +0000 |
3269 | +++ checkbox-support/checkbox_support/lib/tz.py 2014-01-07 13:44:32 +0000 |
3270 | @@ -0,0 +1,55 @@ |
3271 | +# |
3272 | +# This file is part of Checkbox. |
3273 | +# |
3274 | +# Copyright 2012 Canonical Ltd. |
3275 | +# |
3276 | +# Checkbox is free software: you can redistribute it and/or modify |
3277 | +# it under the terms of the GNU General Public License version 3, |
3278 | +# as published by the Free Software Foundation. |
3279 | + |
3280 | +# |
3281 | +# Checkbox is distributed in the hope that it will be useful, |
3282 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3283 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3284 | +# GNU General Public License for more details. |
3285 | +# |
3286 | +# You should have received a copy of the GNU General Public License |
3287 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3288 | +# |
3289 | +__all__ = [ |
3290 | + "tzutc", |
3291 | + ] |
3292 | + |
3293 | +from datetime import ( |
3294 | + timedelta, |
3295 | + tzinfo, |
3296 | + ) |
3297 | + |
3298 | + |
3299 | +ZERO = timedelta(0) |
3300 | + |
3301 | + |
3302 | +class _tzutc(tzinfo): |
3303 | + |
3304 | + def utcoffset(self, dt): |
3305 | + return ZERO |
3306 | + |
3307 | + def dst(self, dt): |
3308 | + return ZERO |
3309 | + |
3310 | + def tzname(self, dt): |
3311 | + return "UTC" |
3312 | + |
3313 | + def __eq__(self, other): |
3314 | + return isinstance(other, tzutc) |
3315 | + |
3316 | + def __ne__(self, other): |
3317 | + return not self.__eq__(other) |
3318 | + |
3319 | + def __repr__(self): |
3320 | + return "%s()" % self.__class__.__name__ |
3321 | + |
3322 | + __reduce__ = object.__reduce__ |
3323 | + |
3324 | + |
3325 | +tzutc = _tzutc() |
3326 | |
3327 | === added file 'checkbox-support/checkbox_support/lib/usb.py' |
3328 | --- checkbox-support/checkbox_support/lib/usb.py 1970-01-01 00:00:00 +0000 |
3329 | +++ checkbox-support/checkbox_support/lib/usb.py 2014-01-07 13:44:32 +0000 |
3330 | @@ -0,0 +1,59 @@ |
3331 | +# |
3332 | +# This file is part of Checkbox. |
3333 | +# |
3334 | +# Copyright 2008 Canonical Ltd. |
3335 | +# |
3336 | +# Checkbox is free software: you can redistribute it and/or modify |
3337 | +# it under the terms of the GNU General Public License version 3, |
3338 | +# as published by the Free Software Foundation. |
3339 | + |
3340 | +# |
3341 | +# Checkbox is distributed in the hope that it will be useful, |
3342 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3343 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3344 | +# GNU General Public License for more details. |
3345 | +# |
3346 | +# You should have received a copy of the GNU General Public License |
3347 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3348 | +# |
3349 | + |
3350 | +# See http://www.linux-usb.org/usb.ids |
3351 | +class Usb: |
3352 | + |
3353 | + BASE_CLASS_INTERFACE = 0 |
3354 | + |
3355 | + BASE_CLASS_AUDIO = 1 |
3356 | + CLASS_AUDIO_CONTROL_DEVICE = 1 |
3357 | + CLASS_AUDIO_STREAMING = 2 |
3358 | + CLASS_AUDIO_MIDI_STREAMING = 3 |
3359 | + |
3360 | + BASE_CLASS_COMMUNICATIONS = 2 |
3361 | + CLASS_COMMUNICATIONS_DIRECT_LINE = 1 |
3362 | + CLASS_COMMUNICATIONS_ABSTRACT = 2 |
3363 | + CLASS_COMMUNICATIONS_TELEPHONE = 3 |
3364 | + |
3365 | + BASE_CLASS_PRINTER = 7 |
3366 | + CLASS_PRINTER_OTHER = 1 |
3367 | + |
3368 | + BASE_CLASS_STORAGE = 8 |
3369 | + CLASS_STORAGE_RBC = 1 |
3370 | + CLASS_STORAGE_SFF = 2 |
3371 | + CLASS_STORAGE_QIC = 3 |
3372 | + CLASS_STORAGE_FLOPPY = 4 |
3373 | + CLASS_STORAGE_SFF = 5 |
3374 | + CLASS_STORAGE_SCSI = 6 |
3375 | + |
3376 | + BASE_CLASS_HUB = 9 |
3377 | + CLASS_HUB_UNUSED = 0 |
3378 | + |
3379 | + BASE_CLASS_VIDEO = 14 |
3380 | + CLASS_VIDEO_UNDEFINED = 0 |
3381 | + CLASS_VIDEO_CONTROL = 1 |
3382 | + CLASS_VIDEO_STREAMING = 2 |
3383 | + CLASS_VIDEO_INTERFACE_COLLECTION = 3 |
3384 | + |
3385 | + BASE_CLASS_WIRELESS = 224 |
3386 | + CLASS_WIRELESS_RADIO_FREQUENCY = 1 |
3387 | + CLASS_WIRELESS_USB_ADAPTER = 2 |
3388 | + |
3389 | + PROTOCOL_BLUETOOTH = 1 |
3390 | |
3391 | === added directory 'checkbox-support/checkbox_support/parsers' |
3392 | === added file 'checkbox-support/checkbox_support/parsers/__init__.py' |
3393 | === added file 'checkbox-support/checkbox_support/parsers/cpuinfo.py' |
3394 | --- checkbox-support/checkbox_support/parsers/cpuinfo.py 1970-01-01 00:00:00 +0000 |
3395 | +++ checkbox-support/checkbox_support/parsers/cpuinfo.py 2014-01-07 13:44:32 +0000 |
3396 | @@ -0,0 +1,180 @@ |
3397 | +# |
3398 | +# This file is part of Checkbox. |
3399 | +# |
3400 | +# Copyright 2011 Canonical Ltd. |
3401 | +# |
3402 | +# Checkbox is free software: you can redistribute it and/or modify |
3403 | +# it under the terms of the GNU General Public License version 3, |
3404 | +# as published by the Free Software Foundation. |
3405 | + |
3406 | +# |
3407 | +# Checkbox is distributed in the hope that it will be useful, |
3408 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3409 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3410 | +# GNU General Public License for more details. |
3411 | +# |
3412 | +# You should have received a copy of the GNU General Public License |
3413 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3414 | +# |
3415 | +import re |
3416 | + |
3417 | +from os import uname |
3418 | + |
3419 | +from checkbox_support.lib.conversion import string_to_type |
3420 | + |
3421 | + |
3422 | +class CpuinfoParser: |
3423 | + """Parser for the /proc/cpuinfo file.""" |
3424 | + |
3425 | + def __init__(self, stream, machine=None): |
3426 | + self.stream = stream |
3427 | + self.machine = machine or uname()[4].lower() |
3428 | + |
3429 | + def getAttributes(self): |
3430 | + count = 0 |
3431 | + attributes = {} |
3432 | + cpuinfo = self.stream.read() |
3433 | + for block in re.split(r"\n{2,}", cpuinfo): |
3434 | + block = block.strip() |
3435 | + if not block: |
3436 | + continue |
3437 | + |
3438 | + for line in block.split("\n"): |
3439 | + if not line: |
3440 | + continue |
3441 | + key, value = line.split(":", 1) |
3442 | + key, value = key.strip(), value.strip() |
3443 | + |
3444 | + if key == 'processor': |
3445 | + count += 1 |
3446 | + |
3447 | + # Handle bogomips on sparc |
3448 | + if key.endswith("Bogo"): |
3449 | + key = "bogomips" |
3450 | + |
3451 | + attributes[key] = value |
3452 | + |
3453 | + if attributes: |
3454 | + attributes["count"] = count |
3455 | + |
3456 | + return attributes |
3457 | + |
3458 | + def run(self, result): |
3459 | + attributes = self.getAttributes() |
3460 | + if not attributes: |
3461 | + return |
3462 | + |
3463 | + # Default values |
3464 | + machine = self.machine |
3465 | + processor = { |
3466 | + "platform": machine, |
3467 | + "count": 1, |
3468 | + "type": machine, |
3469 | + "model": machine, |
3470 | + "model_number": "", |
3471 | + "model_version": "", |
3472 | + "model_revision": "", |
3473 | + "cache": 0, |
3474 | + "bogomips": 0, |
3475 | + "speed": -1, |
3476 | + "other": ""} |
3477 | + |
3478 | + # Conversion table |
3479 | + platform_to_conversion = { |
3480 | + ("i386", "i486", "i586", "i686", "x86_64",): { |
3481 | + "type": "vendor_id", |
3482 | + "model": "model name", |
3483 | + "model_number": "cpu family", |
3484 | + "model_version": "model", |
3485 | + "model_revision": "stepping", |
3486 | + "cache": "cache size", |
3487 | + "other": "flags", |
3488 | + "speed": "cpu MHz"}, |
3489 | + ("alpha", "alphaev6",): { |
3490 | + "count": "cpus detected", |
3491 | + "type": "cpu", |
3492 | + "model": "cpu model", |
3493 | + "model_number": "cpu variation", |
3494 | + "model_version": ("system type", "system variation",), |
3495 | + "model_revision": "cpu revision", |
3496 | + "other": "platform string", |
3497 | + "speed": "cycle frequency [Hz]"}, |
3498 | + ("armv7l",): { |
3499 | + "type": "Hardware", |
3500 | + "model": "Processor", |
3501 | + "model_number": "CPU variant", |
3502 | + "model_version": "CPU architecture", |
3503 | + "model_revision": "CPU revision", |
3504 | + "other": "Features", |
3505 | + "bogomips": "BogoMIPS"}, |
3506 | + ("ia64",): { |
3507 | + "type": "vendor", |
3508 | + "model": "family", |
3509 | + "model_version": "archrev", |
3510 | + "model_revision": "revision", |
3511 | + "other": "features", |
3512 | + "speed": "cpu mhz"}, |
3513 | + ("ppc64", "ppc",): { |
3514 | + "type": "platform", |
3515 | + "model": "cpu", |
3516 | + "model_version": "revision", |
3517 | + "speed": "clock"}, |
3518 | + ("sparc64", "sparc",): { |
3519 | + "count": "ncpus probed", |
3520 | + "type": "type", |
3521 | + "model": "cpu", |
3522 | + "model_version": "type", |
3523 | + "speed": "bogomips"}} |
3524 | + |
3525 | + for key in processor: |
3526 | + if attributes.get(key): |
3527 | + processor[key] = attributes.get(key) |
3528 | + |
3529 | + for platform, conversion in platform_to_conversion.items(): |
3530 | + if machine in platform: |
3531 | + for pkey, ckey in conversion.items(): |
3532 | + if isinstance(ckey, (list, tuple)): |
3533 | + processor[pkey] = "/".join([attributes[k] |
3534 | + for k in ckey]) |
3535 | + elif ckey in attributes: |
3536 | + processor[pkey] = attributes[ckey] |
3537 | + |
3538 | + # Adjust platform |
3539 | + if machine[0] == "i" and machine[-2:] == "86": |
3540 | + processor["platform"] = "i386" |
3541 | + elif machine[:5] == "alpha": |
3542 | + processor["platform"] = "alpha" |
3543 | + |
3544 | + # Adjust cache |
3545 | + if processor["cache"]: |
3546 | + processor["cache"] = string_to_type(processor["cache"]) |
3547 | + |
3548 | + # Adjust speed |
3549 | + try: |
3550 | + if machine[:5] == "alpha": |
3551 | + speed = processor["speed"].split()[0] |
3552 | + processor["speed"] = int(round(float(speed))) / 1000000 |
3553 | + elif machine[:5] == "sparc": |
3554 | + speed = processor["speed"] |
3555 | + processor["speed"] = int(round(float(speed))) / 2 |
3556 | + elif machine[:3] == "ppc": |
3557 | + # String is appended with "mhz" |
3558 | + speed = processor["speed"][:-3] |
3559 | + except ValueError: |
3560 | + processor["speed"] = -1 |
3561 | + |
3562 | + # Make sure speed and bogomips are integers |
3563 | + processor["speed"] = int(round(float(processor["speed"])) - 1) |
3564 | + processor["bogomips"] = int(round(float(processor["bogomips"]))) |
3565 | + |
3566 | + # Adjust count |
3567 | + try: |
3568 | + processor["count"] = int(processor["count"]) |
3569 | + except ValueError: |
3570 | + processor["count"] = 1 |
3571 | + else: |
3572 | + # There is at least one processor |
3573 | + if processor["count"] == 0: |
3574 | + processor["count"] = 1 |
3575 | + |
3576 | + result.setProcessor(processor) |
3577 | |
3578 | === added file 'checkbox-support/checkbox_support/parsers/dmidecode.py' |
3579 | --- checkbox-support/checkbox_support/parsers/dmidecode.py 1970-01-01 00:00:00 +0000 |
3580 | +++ checkbox-support/checkbox_support/parsers/dmidecode.py 2014-01-07 13:44:32 +0000 |
3581 | @@ -0,0 +1,126 @@ |
3582 | +# |
3583 | +# This file is part of Checkbox. |
3584 | +# |
3585 | +# Copyright 2011 Canonical Ltd. |
3586 | +# |
3587 | +# Checkbox is free software: you can redistribute it and/or modify |
3588 | +# it under the terms of the GNU General Public License version 3, |
3589 | +# as published by the Free Software Foundation. |
3590 | + |
3591 | +# |
3592 | +# Checkbox is distributed in the hope that it will be useful, |
3593 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3594 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3595 | +# GNU General Public License for more details. |
3596 | +# |
3597 | +# You should have received a copy of the GNU General Public License |
3598 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3599 | +# |
3600 | +import re |
3601 | + |
3602 | +from string import ( |
3603 | + hexdigits, |
3604 | + ascii_uppercase, |
3605 | + ) |
3606 | + |
3607 | +from checkbox_support.lib.dmi import ( |
3608 | + Dmi, |
3609 | + DmiDevice, |
3610 | + ) |
3611 | + |
3612 | + |
3613 | +HANDLE_RE = re.compile( |
3614 | + r"^Handle (?P<handle>0x[%s]{4}), " |
3615 | + r"DMI type (?P<type>\d+), " |
3616 | + r"(?P<size>\d+) bytes$" |
3617 | + % hexdigits) |
3618 | +KEY_VALUE_RE = re.compile( |
3619 | + r"^\t(?P<key>[%s].+):( (?P<value>.+))?$" |
3620 | + % ascii_uppercase) |
3621 | + |
3622 | + |
3623 | +class DmidecodeParser: |
3624 | + """Parser for the dmidecode command.""" |
3625 | + |
3626 | + _key_map = { |
3627 | + "ID": "serial", |
3628 | + "Manufacturer": "vendor", |
3629 | + "Product Name": "name", |
3630 | + "Serial Number": "serial", |
3631 | + "Type": "type", |
3632 | + "Vendor": "vendor", |
3633 | + "Version": "version", |
3634 | + "Size": "size", |
3635 | + "Form Factor": "form", |
3636 | + } |
3637 | + |
3638 | + def __init__(self, stream): |
3639 | + self.stream = stream |
3640 | + |
3641 | + def _parseKey(self, key): |
3642 | + return self._key_map.get(key) |
3643 | + |
3644 | + def _parseValue(self, value): |
3645 | + if value is not None: |
3646 | + value = value.strip() |
3647 | + if not value: |
3648 | + value = None |
3649 | + |
3650 | + return value |
3651 | + |
3652 | + def run(self, result): |
3653 | + output = self.stream.read() |
3654 | + for record in re.split(r"\n{2,}", output): |
3655 | + record = record.strip() |
3656 | + # Skip empty records |
3657 | + if not record: |
3658 | + continue |
3659 | + |
3660 | + # Skip header record |
3661 | + lines = record.split("\n") |
3662 | + line = lines.pop(0) |
3663 | + if line.startswith("#"): |
3664 | + continue |
3665 | + |
3666 | + # Skip records with an unsupported handle |
3667 | + match = HANDLE_RE.match(line) |
3668 | + if not match: |
3669 | + continue |
3670 | + |
3671 | + # Skip records that are empty or inactive |
3672 | + if not lines or lines.pop(0) == "Inactive": |
3673 | + continue |
3674 | + |
3675 | + # Skip disabled entries and end-of-table marker |
3676 | + type_index = int(match.group("type")) |
3677 | + if type_index >= len(Dmi.type_names): |
3678 | + continue |
3679 | + |
3680 | + category = Dmi.type_names[type_index] |
3681 | + category = category.upper().split(" ")[-1] |
3682 | + if category not in ( |
3683 | + "BOARD", "BIOS", "CHASSIS", "DEVICE", "PROCESSOR", "SYSTEM"): |
3684 | + continue |
3685 | + |
3686 | + # Parse attributes |
3687 | + attributes = {} |
3688 | + |
3689 | + for line in lines: |
3690 | + # Skip lines with an unsupported key/value pair |
3691 | + match = KEY_VALUE_RE.match(line) |
3692 | + if not match: |
3693 | + continue |
3694 | + |
3695 | + # Skip lines with an unsupported key |
3696 | + key = self._parseKey(match.group("key")) |
3697 | + if not key: |
3698 | + continue |
3699 | + |
3700 | + key = "%s_%s" % (category.lower(), key) |
3701 | + value = self._parseValue(match.group("value")) |
3702 | + attributes[key] = value |
3703 | + |
3704 | + device = DmiDevice(attributes, category) |
3705 | + result.addDmiDevice(device) |
3706 | + |
3707 | + return result |
3708 | |
3709 | === added file 'checkbox-support/checkbox_support/parsers/efi.py' |
3710 | --- checkbox-support/checkbox_support/parsers/efi.py 1970-01-01 00:00:00 +0000 |
3711 | +++ checkbox-support/checkbox_support/parsers/efi.py 2014-01-07 13:44:32 +0000 |
3712 | @@ -0,0 +1,52 @@ |
3713 | +# |
3714 | +# This file is part of Checkbox. |
3715 | +# |
3716 | +# Copyright 2011 Canonical Ltd. |
3717 | +# |
3718 | +# Checkbox is free software: you can redistribute it and/or modify |
3719 | +# it under the terms of the GNU General Public License version 3, |
3720 | +# as published by the Free Software Foundation. |
3721 | + |
3722 | +# |
3723 | +# Checkbox is distributed in the hope that it will be useful, |
3724 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3725 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3726 | +# GNU General Public License for more details. |
3727 | +# |
3728 | +# You should have received a copy of the GNU General Public License |
3729 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3730 | +# |
3731 | +import re |
3732 | + |
3733 | + |
3734 | +class EfiDevice: |
3735 | + |
3736 | + path = "/sys/class/dmi/id/bios_version" |
3737 | + category = "EFI" |
3738 | + |
3739 | + def __init__(self, product, vendor=None): |
3740 | + self.product = product |
3741 | + self.vendor = vendor |
3742 | + |
3743 | + |
3744 | +class EfiParser: |
3745 | + """Parser for EFI information.""" |
3746 | + |
3747 | + def __init__(self, stream): |
3748 | + self.stream = stream |
3749 | + |
3750 | + def run(self, result): |
3751 | + vendor_product_pattern = re.compile( |
3752 | + r"^(?P<vendor>.*)\s+by\s+(?P<product>.*)$") |
3753 | + |
3754 | + for line in self.stream.readlines(): |
3755 | + line = line.strip() |
3756 | + match = vendor_product_pattern.match(line) |
3757 | + if match: |
3758 | + product = match.group("product") |
3759 | + vendor = match.group("vendor") |
3760 | + device = EfiDevice(product, vendor) |
3761 | + else: |
3762 | + device = EfiDevice(line) |
3763 | + |
3764 | + result.setEfiDevice(device) |
3765 | |
3766 | === added file 'checkbox-support/checkbox_support/parsers/lshwjson.py' |
3767 | --- checkbox-support/checkbox_support/parsers/lshwjson.py 1970-01-01 00:00:00 +0000 |
3768 | +++ checkbox-support/checkbox_support/parsers/lshwjson.py 2014-01-07 13:44:32 +0000 |
3769 | @@ -0,0 +1,23 @@ |
3770 | +import sys |
3771 | +import json |
3772 | + |
3773 | +class LshwJsonParser: |
3774 | + |
3775 | + def __init__(self, stream_or_string): |
3776 | + self.stream_or_string = stream_or_string |
3777 | + |
3778 | + def _parse_lshw(self, lshw, result): |
3779 | + if 'children' in lshw.keys(): |
3780 | + for child in lshw['children']: |
3781 | + self._parse_lshw(child, result) |
3782 | + del lshw['children'] |
3783 | + |
3784 | + result.addHardware(lshw) |
3785 | + |
3786 | + def run(self, result): |
3787 | + try: |
3788 | + lshw = json.loads(self.stream_or_string) |
3789 | + except: |
3790 | + print('not valid json') |
3791 | + |
3792 | + self._parse_lshw(lshw, result) |
3793 | |
3794 | === added file 'checkbox-support/checkbox_support/parsers/meminfo.py' |
3795 | --- checkbox-support/checkbox_support/parsers/meminfo.py 1970-01-01 00:00:00 +0000 |
3796 | +++ checkbox-support/checkbox_support/parsers/meminfo.py 2014-01-07 13:44:32 +0000 |
3797 | @@ -0,0 +1,46 @@ |
3798 | +# |
3799 | +# This file is part of Checkbox. |
3800 | +# |
3801 | +# Copyright 2011 Canonical Ltd. |
3802 | +# |
3803 | +# Checkbox is free software: you can redistribute it and/or modify |
3804 | +# it under the terms of the GNU General Public License version 3, |
3805 | +# as published by the Free Software Foundation. |
3806 | + |
3807 | +# |
3808 | +# Checkbox is distributed in the hope that it will be useful, |
3809 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3810 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3811 | +# GNU General Public License for more details. |
3812 | +# |
3813 | +# You should have received a copy of the GNU General Public License |
3814 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3815 | +# |
3816 | +import re |
3817 | + |
3818 | + |
3819 | +class MeminfoParser: |
3820 | + """Parser for the /proc/meminfo file.""" |
3821 | + |
3822 | + def __init__(self, stream): |
3823 | + self.stream = stream |
3824 | + |
3825 | + def run(self, result): |
3826 | + key_value_pattern = re.compile(r"(?P<key>.*):\s+(?P<value>.*)") |
3827 | + meminfo_map = { |
3828 | + "MemTotal": "total", |
3829 | + "SwapTotal": "swap"} |
3830 | + |
3831 | + meminfo = {} |
3832 | + for line in self.stream.readlines(): |
3833 | + line = line.strip() |
3834 | + match = key_value_pattern.match(line) |
3835 | + if match: |
3836 | + key = match.group("key") |
3837 | + if key in meminfo_map: |
3838 | + key = meminfo_map[key] |
3839 | + value = match.group("value") |
3840 | + (integer, factor) = value.split() |
3841 | + meminfo[key] = int(integer) * 1024 |
3842 | + |
3843 | + result.setMemory(meminfo) |
3844 | |
3845 | === added file 'checkbox-support/checkbox_support/parsers/modinfo.py' |
3846 | --- checkbox-support/checkbox_support/parsers/modinfo.py 1970-01-01 00:00:00 +0000 |
3847 | +++ checkbox-support/checkbox_support/parsers/modinfo.py 2014-01-07 13:44:32 +0000 |
3848 | @@ -0,0 +1,89 @@ |
3849 | +# |
3850 | +# This file is part of Checkbox. |
3851 | +# |
3852 | +# Copyright 2011 Canonical Ltd. |
3853 | +# |
3854 | +# Checkbox is free software: you can redistribute it and/or modify |
3855 | +# it under the terms of the GNU General Public License version 3, |
3856 | +# as published by the Free Software Foundation. |
3857 | + |
3858 | +# |
3859 | +# Checkbox is distributed in the hope that it will be useful, |
3860 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3861 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3862 | +# GNU General Public License for more details. |
3863 | +# |
3864 | +# You should have received a copy of the GNU General Public License |
3865 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3866 | +# |
3867 | + |
3868 | + |
3869 | +class ModinfoParser: |
3870 | + """ |
3871 | + Parser for modinfo information. |
3872 | + This will take the stdout for modinfo output and return a dict populated |
3873 | + with each field. |
3874 | + |
3875 | + Basic usage in your script: |
3876 | + try: |
3877 | + output = subprocess.check_output('/sbin/modinfo e1000e', |
3878 | + stderr=subprocess.STDOUT, |
3879 | + universal_newlines=True) |
3880 | + except CalledProcessError as err: |
3881 | + print("Error while running modinfo") |
3882 | + print(err.output) |
3883 | + return err.returncode |
3884 | + |
3885 | + parser = ModinfoParser(output) |
3886 | + all_fields = parser.get_all() |
3887 | + one_field = parser.get_field(field) |
3888 | + """ |
3889 | + |
3890 | + def __init__(self, stream): |
3891 | + self._modinfo = {'alias': [], |
3892 | + 'author': '', |
3893 | + 'depends': [], |
3894 | + 'description': '', |
3895 | + 'filename': '', |
3896 | + 'firmware': [], |
3897 | + 'intree': '', |
3898 | + 'license': '', |
3899 | + 'parm': [], |
3900 | + 'srcversion': '', |
3901 | + 'vermagic': '', |
3902 | + 'version': ''} |
3903 | + self._get_info(stream) |
3904 | + |
3905 | + def _get_info(self, stream): |
3906 | + for line in stream.splitlines(): |
3907 | + # At this point, stream should be the stdout from the modinfo |
3908 | + # command, in a list. |
3909 | + try: |
3910 | + key, data = line.split(':', 1) |
3911 | + except ValueError: |
3912 | + # Most likely this will be caused by a blank line in the |
3913 | + # stream, so we just ignore it and move on. |
3914 | + continue |
3915 | + else: |
3916 | + key = key.strip() |
3917 | + data = data.strip() |
3918 | + # First, we need to handle alias, parm, firmware, and depends |
3919 | + # because there can be multiple lines of output for these. |
3920 | + if key in ('alias', 'depend', 'firmware', 'parm',): |
3921 | + self._modinfo[key].append(data) |
3922 | + # Now handle unknown keys |
3923 | + elif key not in self._modinfo.keys(): |
3924 | + self._modinfo[key] = ("WARNING: Unknown Key %s providing " |
3925 | + "data: %s") % (key, data) |
3926 | + # And finally known keys |
3927 | + else: |
3928 | + self._modinfo[key] = data |
3929 | + |
3930 | + def get_all(self): |
3931 | + return self._modinfo |
3932 | + |
3933 | + def get_field(self, field): |
3934 | + if field not in self._modinfo.keys(): |
3935 | + raise Exception("Key not found: %s" % field) |
3936 | + else: |
3937 | + return self._modinfo[field] |
3938 | |
3939 | === added file 'checkbox-support/checkbox_support/parsers/pactl.py' |
3940 | --- checkbox-support/checkbox_support/parsers/pactl.py 1970-01-01 00:00:00 +0000 |
3941 | +++ checkbox-support/checkbox_support/parsers/pactl.py 2014-01-07 13:44:32 +0000 |
3942 | @@ -0,0 +1,543 @@ |
3943 | +# This file is part of Checkbox. |
3944 | +# |
3945 | +# Copyright 2013 Canonical Ltd. |
3946 | +# Written by: |
3947 | +# Zygmunt Krynicki <zygmunt.krynicki@canonical.com> |
3948 | +# |
3949 | +# Checkbox is free software: you can redistribute it and/or modify |
3950 | +# it under the terms of the GNU General Public License version 3, |
3951 | +# as published by the Free Software Foundation. |
3952 | + |
3953 | +# |
3954 | +# Checkbox is distributed in the hope that it will be useful, |
3955 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
3956 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3957 | +# GNU General Public License for more details. |
3958 | +# |
3959 | +# You should have received a copy of the GNU General Public License |
3960 | +# along with Checkbox. If not, see <http://www.gnu.org/licenses/>. |
3961 | + |
3962 | +""" |
3963 | +:mod:`checkbox_support.parsers.pactl` -- `pactl list` parser |
3964 | +==================================================== |
3965 | + |
3966 | +Parser for the output of ``pactl list`` syntax. |
3967 | + |
3968 | +The abstract syntax tree of 'pactl list' is as follows:: |
3969 | + |
3970 | + Document: Record + ('\n' + Record)* |
3971 | + |
3972 | + Record: RECORD-NAME ':' Attribute+ |
3973 | + |
3974 | + Attribute: ATTRIBUTE-NAME ':' AttributeValue |
3975 | + |
3976 | + AttributeValue: SIMPLE-VALUE '\n' |
3977 | + | PropertyValue |
3978 | + | VOLUME-VALUE |
3979 | + | BASE-VOLUME-VALUE |
3980 | + | PORT+ |
3981 | + | PORT-WITH-PROFILE+ |
3982 | + |
3983 | + PropertyValue: PROPERTY-NAME '=' PROPERTY-VALUE |
3984 | + |
3985 | + (other all-upsercase values are not specified in detail) |
3986 | + |
3987 | +Some parts of the output are always localized while others depend on the |
3988 | +locale of the current user. This is caused by the fact that ``pactl`` talks to |
3989 | +pulse audio server over DBus. Some of the data obtained from pulse that was is |
3990 | +localized and it is difficult to influence. This should be of no problem for |
3991 | +the parser but actual usage of the data can be more difficult. |
3992 | +""" |
3993 | + |
3994 | +from collections import OrderedDict |
3995 | +from inspect import isroutine |
3996 | + |
3997 | +import pyparsing as p |
3998 | + |
3999 | + |
4000 | +# Enable packrat paring. |
4001 | +# |
4002 | +# This reduces the complexity of the parser |
4003 | +# from O(2**N) to O(N) at the cost of memory O(N) vs O(1). |
4004 | +p.ParserElement.enablePackrat() |
4005 | + |
4006 | +# XXX: Hack, changes global stuff |
4007 | +# |
4008 | +# This makes pyparsing not so ignorant to whitespace. Normally pyparsing is |
4009 | +# happily treating newlines, tabs, spaces and carriage returns as irrelevant |
4010 | +# spacers between tokens. Because pactl syntax is so whitespace-sensitive this |
4011 | +# is globally turned off. A proper solution would apply this on a |
4012 | +# per-ParserElement level |
4013 | +p.ParserElement.DEFAULT_WHITE_CHARS = " " |
4014 | + |
4015 | + |
4016 | +class NodeMeta(type): |
4017 | + """ |
4018 | + Metaclass for all Node types. |
4019 | + |
4020 | + Helps to setup the `Syntax` attribute using the special `__syntax__` |
4021 | + attribute. It also calls from_tokens() with the appropriate class. |
4022 | + """ |
4023 | + |
4024 | + def __new__(mcls, name, bases, ns): |
4025 | + cls = type.__new__(mcls, name, bases, ns) |
4026 | + if hasattr(cls, '__syntax__'): |
4027 | + cls.Syntax = ( |
4028 | + cls.__syntax__ |
4029 | + ).setParseAction( |
4030 | + cls.from_tokens |
4031 | + ).parseWithTabs() |
4032 | + return cls |
4033 | + |
4034 | + |
4035 | +class Node(metaclass=NodeMeta): |
4036 | + """ |
4037 | + Base class for things parsed by pyparsing. |
4038 | + |
4039 | + Defines sensible __repr__(), __init__() and from_tokens(). That |
4040 | + last class method uses __fragments__ to pick things from pyparsing |
4041 | + ParseResults and assign them to attributes of the Node instance. |
4042 | + |
4043 | + This serves as a buffer between pyparsing and external code, so that |
4044 | + anything we do to the syntax is irrelevant as long as the tree of |
4045 | + Nodes remains the same. |
4046 | + """ |
4047 | + |
4048 | + __fragments__ = {} |
4049 | + |
4050 | + def __init__(self, **kwargs): |
4051 | + for attr, value in kwargs.items(): |
4052 | + setattr(self, attr, value) |
4053 | + |
4054 | + def __repr__(self): |
4055 | + return "{}({})".format( |
4056 | + type(self).__name__, ", ".join([ |
4057 | + "{}={!r}".format(attr, getattr(self, attr)) |
4058 | + for attr in self.__fragments__])) |
4059 | + |
4060 | + @classmethod |
4061 | + def from_tokens(cls, tokens): |
4062 | + """ |
4063 | + Create a node from tokens that were matched from __syntax__ |
4064 | + """ |
4065 | + data = { |
4066 | + attr: mapper(tokens) if isroutine(mapper) else tokens[mapper] |
4067 | + for attr, mapper in cls.__fragments__.items() |
4068 | + } |
4069 | + return cls(**data) |
4070 | + |
4071 | + |
4072 | +class Property(Node): |
4073 | + """ |
4074 | + A key=value pair. |
4075 | + |
4076 | + A list of properties is a possible syntax for Attribute value. |
4077 | + """ |
4078 | + |
4079 | + __fragments__ = { |
4080 | + 'name': 'property-name', |
4081 | + 'value': 'property-value' |
4082 | + } |
4083 | + |
4084 | + __syntax__ = ( |
4085 | + p.Word(p.alphanums + "-_.").setResultsName("property-name") |
4086 | + + p.Suppress('=') |
4087 | + + p.QuotedString('"').setResultsName("property-value") |
4088 | + ).setResultsName('property') |
4089 | + |
4090 | + |
4091 | +class Profile(Node): |
4092 | + """ |
4093 | + Description of a pulseaudio profile. |
4094 | + """ |
4095 | + |
4096 | + __fragments__ = { |
4097 | + 'name': 'profile-name', |
4098 | + 'label': 'profile-label', |
4099 | + 'sink_cnt': 'profile-sink-count', |
4100 | + 'source_cnt': 'profile-source-count', |
4101 | + 'priority': 'profile-priority', |
4102 | + } |
4103 | + |
4104 | + __syntax__ = ( |
4105 | + p.Word(p.alphanums + "+-:").setParseAction( |
4106 | + lambda t: t[0].rstrip(':') |
4107 | + ).setResultsName("profile-name") |
4108 | + + p.delimitedList( |
4109 | + p.Literal("(HDMI)") | p.Literal("(IEC958)") | p.Regex('[^ (\n]+'), |
4110 | + ' ', combine=True |
4111 | + ).setResultsName('profile-label') |
4112 | + + p.Suppress('(') |
4113 | + + p.Keyword('sinks').suppress() |
4114 | + + p.Suppress(':') |
4115 | + + p.Word(p.nums).setParseAction( |
4116 | + lambda t: int(t[0]) |
4117 | + ).setResultsName('profile-sink-count') |
4118 | + + p.Suppress(',') |
4119 | + + p.Keyword('sources').suppress() |
4120 | + + p.Suppress(':') |
4121 | + + p.Word(p.nums).setParseAction( |
4122 | + lambda t: int(t[0]) |
4123 | + ).setResultsName('profile-source-count') |
4124 | + + p.Suppress(',') |
4125 | + + p.Keyword('priority').suppress() |
4126 | + + p.MatchFirst([ |
4127 | + p.Suppress('.'), |
4128 | + # http://cgit.freedesktop.org/pulseaudio/pulseaudio/commit/src/utils/pactl.c?id=83c3cf0a65fb05900f81bd2dbb38e6956eb23935 |
4129 | + p.Suppress(':'), |
4130 | + ]) |
4131 | + + p.Word(p.nums).setParseAction( |
4132 | + lambda t: int(t[0]) |
4133 | + ).setResultsName('profile-priority') |
4134 | + + p.Suppress(')') |
4135 | + ).setResultsName("profile") |
4136 | + |
4137 | + |
4138 | +class Port(Node): |
4139 | + """ |
4140 | + Description of a port on a sink |
4141 | + """ |
4142 | + |
4143 | + __fragments__ = { |
4144 | + 'name': 'port-name', |
4145 | + 'label': 'port-label', |
4146 | + 'priority': 'port-priority', |
4147 | + 'availability': 'port-availability' |
4148 | + } |
4149 | + |
4150 | + __syntax__ = ( |
4151 | + p.Word(p.alphanums + "-;").setResultsName('port-name') |
4152 | + + p.Suppress(':') |
4153 | + # This part was very tricky to write. The label is basically |
4154 | + # arbitrary localized Unicode text. We want to grab all of it in |
4155 | + # one go but without consuming the upcoming '(' character or the |
4156 | + # space that comes immediately before. |
4157 | + # |
4158 | + # The syntax here combines a sequence of words, as defined by |
4159 | + # anything other than a space and '(', delimited by a single |
4160 | + # whitespace. |
4161 | + + p.delimitedList( |
4162 | + p.Regex('[^ (\n]+'), ' ', combine=True |
4163 | + ).setResultsName('port-label') |
4164 | + + p.Suppress('(') |
4165 | + + p.Keyword('priority').suppress() |
4166 | + + p.Suppress(':') |
4167 | + + p.Word(p.nums).setParseAction( |
4168 | + lambda t: int(t[0]) |
4169 | + ).setResultsName('port-priority') |
4170 | + + p.MatchFirst([ |
4171 | + p.Suppress(',') + p.Literal('not available'), |
4172 | + p.Suppress(',') + p.Literal('available'), |
4173 | + p.Empty().setParseAction(lambda t: '') |
4174 | + ]).setResultsName('port-availability') |
4175 | + + p.Suppress(')') |
4176 | + ).setResultsName("port") |
4177 | + |
4178 | + |
4179 | +# ================= |
4180 | +# Shared Attributes |
4181 | +# ================= |
4182 | + |
4183 | +PropertyAttributeValue = ( |
4184 | + p.Group( |
4185 | + p.OneOrMore( |
4186 | + p.LineStart().suppress() |
4187 | + + p.Optional(p.White('\t')).suppress() |
4188 | + + p.Optional(Property.Syntax) |
4189 | + + p.LineEnd().suppress() |
4190 | + ) |
4191 | + ).setResultsName("attribute-value")) |
4192 | + |
4193 | + |
4194 | +class PortWithProfile(Node): |
4195 | + """ |
4196 | + Variant of :class:`Port` that is used by "card" records inside |
4197 | + the "Ports" property. It differs from the normal port syntax by having |
4198 | + different entries inside the last section. Availability is not listed |
4199 | + here, only priority. Priority does not have a colon before the actual |
4200 | + number. This port is followed by profile assignment. |
4201 | + """ |
4202 | + __fragments__ = { |
4203 | + 'name': 'port-name', |
4204 | + 'label': 'port-label', |
4205 | + 'priority': 'port-priority', |
4206 | + 'latency_offset': 'port-latency-offset', |
4207 | + 'availability': 'port-availability', |
4208 | + 'properties': lambda t: t['port-properties'].asList(), |
4209 | + 'profile_list': lambda t: t['port-profile-list'].asList(), |
4210 | + } |
4211 | + |
4212 | + __syntax__ = ( |
4213 | + p.Word(p.alphanums + "-;").setResultsName('port-name') |
4214 | + + p.Suppress(':') |
4215 | + # This part was very tricky to write. The label is basically arbitrary |
4216 | + # localized Unicode text. We want to grab all of it in one go but |
4217 | + # without consuming the upcoming and latest '(' character or the space |
4218 | + # that comes immediately before. |
4219 | + # |
4220 | + # The syntax here combines a sequence of words, as defined by anything |
4221 | + # other than a space and '(', delimited by a single whitespace. |
4222 | + + p.Combine( |
4223 | + p.OneOrMore( |
4224 | + ~p.FollowedBy( |
4225 | + p.Regex('\(.+?\)') |
4226 | + + p.LineEnd() |
4227 | + ) |
4228 | + + p.Regex('[^ \n]+') |
4229 | + + p.White().suppress() |
4230 | + ), |
4231 | + ' ' |
4232 | + ).setResultsName('port-label') |
4233 | + + p.Suppress('(') |
4234 | + + p.Keyword('priority').suppress() |
4235 | + + p.Optional( |
4236 | + p.Suppress(':') |
4237 | + ) |
4238 | + + p.Word(p.nums).setParseAction( |
4239 | + lambda t: int(t[0]) |
4240 | + ).setResultsName('port-priority') |
4241 | + + p.Optional( |
4242 | + p.MatchFirst([ |
4243 | + p.Suppress(',') + p.Keyword('latency offset:').suppress() |
4244 | + + p.Word(p.nums).setParseAction(lambda t: int(t[0])) |
4245 | + + p.Literal("usec").suppress(), |
4246 | + p.Empty().setParseAction(lambda t: '') |
4247 | + ]).setResultsName('port-latency-offset') |
4248 | + ) |
4249 | + + p.Optional( |
4250 | + p.MatchFirst([ |
4251 | + p.Suppress(',') + p.Literal('not available'), |
4252 | + p.Suppress(',') + p.Literal('available'), |
4253 | + p.Empty().setParseAction(lambda t: '') |
4254 | + ]).setResultsName('port-availability') |
4255 | + ) |
4256 | + + p.Suppress(')') |
4257 | + + p.LineEnd().suppress() |
4258 | + + p.Optional( |
4259 | + p.MatchFirst([ |
4260 | + p.LineStart().suppress() |
4261 | + + p.NotAny(p.White(' ')) |
4262 | + + p.White('\t').suppress() |
4263 | + + p.Keyword('Properties:').suppress() |
4264 | + + p.LineEnd().suppress() |
4265 | + + PropertyAttributeValue, |
4266 | + p.Empty().setParseAction(lambda t: []) |
4267 | + ]).setResultsName('port-properties') |
4268 | + ) |
4269 | + + p.White('\t', max=3).suppress() |
4270 | + + p.Literal("Part of profile(s)").suppress() |
4271 | + + p.Suppress(":") |
4272 | + + p.delimitedList( |
4273 | + p.Word(p.alphanums + "+-:"), ", " |
4274 | + ).setResultsName("port-profile-list") |
4275 | + ).setResultsName("port") |
4276 | + |
4277 | + |
4278 | +# ========================= |
4279 | +# Non-collection attributes |
4280 | +# ========================= |
4281 | + |
4282 | +AttributeName = p.Regex("[a-zA-Z][^:\n]+").setResultsName("attribute-name") |
4283 | + |
4284 | + |
4285 | +VolumeAttributeValue = ( |
4286 | + p.Combine( |
4287 | + p.Or([ |
4288 | + p.Literal("(invalid)"), |
4289 | + p.Regex("([0-9]+: +[0-9]+% ?)+") |
4290 | + ]) |
4291 | + + p.LineEnd() |
4292 | + + p.Optional(p.White('\t').suppress()) |
4293 | + + p.Or([ |
4294 | + p.Literal("(invalid)"), |
4295 | + p.Regex("([0-9]+: -?[0-9]+\.[0-9]+ dB ?)+") |
4296 | + ]) |
4297 | + + p.LineEnd() |
4298 | + + p.Optional(p.White('\t').suppress()) |
4299 | + + p.Regex("balance [0-9]+\.[0-9]+") |
4300 | + + p.LineEnd(), |
4301 | + adjacent=False |
4302 | + ).setResultsName("attribute-value") |
4303 | +) |
4304 | + |
4305 | + |
4306 | +BaseVolumeAttributeValue = ( |
4307 | + p.Combine( |
4308 | + p.Regex("[0-9]+%") |
4309 | + + p.LineEnd() |
4310 | + + p.Optional(p.White('\t').suppress()) |
4311 | + + p.Regex("-?[0-9]+\.[0-9]+ dB") |
4312 | + + p.LineEnd(), |
4313 | + adjacent=False |
4314 | + ).setResultsName("attribute-value") |
4315 | +) |
4316 | + |
4317 | + |
4318 | +SimpleAttributeValue = ( |
4319 | + p.Regex("[^\n]*").setResultsName("attribute-value") |
4320 | + + p.LineEnd().suppress()) |
4321 | + |
4322 | +# simple values |
4323 | +GenericSimpleAttributeValue = p.MatchFirst([ |
4324 | + VolumeAttributeValue, |
4325 | + BaseVolumeAttributeValue, |
4326 | + SimpleAttributeValue, |
4327 | +]) |
4328 | + |
4329 | + |
4330 | +class GenericSimpleAttribute(Node): |
4331 | + |
4332 | + __fragments__ = { |
4333 | + 'name': 'attribute-name', |
4334 | + 'value': 'attribute-value', |
4335 | + } |
4336 | + |
4337 | + __syntax__ = ( |
4338 | + p.LineStart().suppress() |
4339 | + + p.NotAny(p.White(' ')) |
4340 | + + p.Optional(p.White('\t')).suppress() |
4341 | + + AttributeName |
4342 | + + p.Literal(':').suppress() |
4343 | + + GenericSimpleAttributeValue |
4344 | + ).setResultsName("attribute") |
4345 | + |
4346 | + |
4347 | +# ===================== |
4348 | +# Collection Attributes |
4349 | +# ===================== |
4350 | + |
4351 | +PortsAttributeValue = ( |
4352 | + p.Group( |
4353 | + p.OneOrMore( |
4354 | + p.LineStart().suppress() |
4355 | + + p.Optional(p.White('\t')).suppress() |
4356 | + + Port.Syntax |
4357 | + + p.LineEnd().suppress()) |
4358 | + ).setResultsName("attribute-value")) |
4359 | + |
4360 | +PortsWithProfilesAttributeValue = ( |
4361 | + p.Group( |
4362 | + p.OneOrMore( |
4363 | + p.LineStart().suppress() |
4364 | + + p.Optional(p.White('\t')).suppress() |
4365 | + + PortWithProfile.Syntax |
4366 | + + p.LineEnd().suppress()) |
4367 | + ).setResultsName("attribute-value")) |
4368 | + |
4369 | +FormatsAttributeValue = ( |
4370 | + p.Group( |
4371 | + p.OneOrMore( |
4372 | + p.LineStart().suppress() |
4373 | + + p.Optional(p.White('\t')).suppress() |
4374 | + + p.Word(p.alphas) |
4375 | + + p.LineEnd().suppress()) |
4376 | + ).setResultsName("attribute-value")) |
4377 | + |
4378 | +ProfilesAttributeValue = ( |
4379 | + p.Group( |
4380 | + p.OneOrMore( |
4381 | + p.LineStart().suppress() |
4382 | + + p.Optional(p.White('\t')).suppress() |
4383 | + + Profile.Syntax |
4384 | + + p.LineEnd().suppress()) |
4385 | + ).setResultsName("attribute-value")) |
4386 | + |
4387 | + |
4388 | +GenericListAttributeValue = p.MatchFirst([ |
4389 | + PortsAttributeValue, |
4390 | + PropertyAttributeValue, |
4391 | + PortsWithProfilesAttributeValue, |
4392 | + ProfilesAttributeValue, |
4393 | + FormatsAttributeValue, |
4394 | +]) |
4395 | + |
4396 | + |
4397 | +class GenericListAttribute(Node): |
4398 | + |
4399 | + __fragments__ = { |
4400 | + 'name': 'attribute-name', |
4401 | + 'value': lambda t: t['attribute-value'].asList() |
4402 | + } |
4403 | + |
4404 | + __syntax__ = ( |
4405 | + p.LineStart().suppress() |
4406 | + + p.NotAny(p.White(' ')) |
4407 | + + p.Optional(p.White('\t')).suppress() |
4408 | + + AttributeName |
4409 | + + p.Literal(':').suppress() |
4410 | + + p.LineEnd().suppress() |
4411 | + + GenericListAttributeValue |
4412 | + ).setResultsName("attribute") |
4413 | + |
4414 | + |
4415 | +class Record(Node): |
4416 | + """ |
4417 | + Single standalone entry of `pactl list`. |
4418 | + |
4419 | + The record is composed of a name and a list of attributes. Pulseaudio |
4420 | + exposes objects such as cards, sinks and sources as separate records. |
4421 | + |
4422 | + Each attribute may be of a different type. Some attributes are simple |
4423 | + values while others have finer structure, including lits and even |
4424 | + additional recursive attributes. |
4425 | + """ |
4426 | + |
4427 | + __fragments__ = { |
4428 | + 'name': 'record-name', |
4429 | + 'attribute_list': lambda t: t['record-attributes'].asList(), |
4430 | + 'attribute_map': lambda t: OrderedDict( |
4431 | + (attr.name, attr) |
4432 | + for attr in t['record-attributes'].asList()), |
4433 | + } |
4434 | + |
4435 | + __syntax__ = ( |
4436 | + p.LineStart() |
4437 | + + p.NotAny(p.White(' \t')) |
4438 | + + p.Regex("[A-Z][a-zA-Z ]+ #[0-9]+").setResultsName("record-name") |
4439 | + + p.LineEnd().suppress() |
4440 | + + p.OneOrMore( |
4441 | + p.Or([ |
4442 | + GenericListAttribute.Syntax, |
4443 | + GenericSimpleAttribute.Syntax, |
4444 | + ]) |
4445 | + ).setResultsName("record-attributes") |
4446 | + ).setResultsName("record") |
4447 | + |
4448 | + def as_json(self): |
4449 | + return { |
4450 | + 'name': self.name, |
4451 | + 'attribute_list': self.attribute_list, |
4452 | + } |
4453 | + |
4454 | + def __repr__(self): |
4455 | + # Custom __repr__ that skips attribute_map |
4456 | + return "{}({})".format( |
4457 | + type(self).__name__, ", ".join([ |
4458 | + "{}={!r}".format(attr, getattr(self, attr)) |
4459 | + for attr in ['name', 'attribute_list']])) |
4460 | + |
4461 | + |
4462 | +class Document(Node): |
4463 | + """ |
4464 | + Encompasses whole output of `pactl list` |
4465 | + The document is composed of a list of :class:`Record` objects |
4466 | + """ |
4467 | + |
4468 | + __fragments__ = { |
4469 | + 'record_list': lambda t: t['record-list'].asList(), |
4470 | + } |
4471 | + |
4472 | + __syntax__ = ( |
4473 | + p.OneOrMore( |
4474 | + Record.Syntax + p.Optional("\n").suppress() |
4475 | + ).setResultsName("record-list") |
4476 | + ).parseWithTabs() |
4477 | + |
4478 | + |
4479 | +def parse_pactl_output(output): |
4480 | + """ |
4481 | + Parse output of `LANG=C pactl list` |
4482 | + |
4483 | + :returns: :class:`Document` object that corresponds to the parsed input |
4484 | + """ |
4485 | + return Document.Syntax.parseString(output, parseAll=True)[0] |
4486 | |
4487 | === added directory 'checkbox-support/checkbox_support/parsers/tests' |
4488 | === added file 'checkbox-support/checkbox_support/parsers/tests/__init__.py' |
4489 | === added directory 'checkbox-support/checkbox_support/parsers/tests/fixtures' |
4490 | === added file 'checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_quantal.txt' |
4491 | --- checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_quantal.txt 1970-01-01 00:00:00 +0000 |
4492 | +++ checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_quantal.txt 2014-01-07 13:44:32 +0000 |
4493 | @@ -0,0 +1,143 @@ |
4494 | +⎡ Virtual core pointer id=2 [master pointer (3)] |
4495 | +Reporting 4 classes: |
4496 | +Class originated from: 10. Type: XIButtonClass |
4497 | +Buttons supported: 10 |
4498 | +Button labels: "Button Unknown" "Button Unknown" "Button Unknown" |
4499 | +"Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button |
4500 | +Horiz Wheel Right" None None None |
4501 | +Button state: |
4502 | +Class originated from: 10. Type: XIValuatorClass |
4503 | +Detail for Valuator 0: |
4504 | +Label: Abs MT Position X |
4505 | +Range: 0.000000 - 1919.000000 |
4506 | +Resolution: 0 units/m |
4507 | +Mode: absolute |
4508 | +Current value: 1664.000000 |
4509 | +Class originated from: 10. Type: XIValuatorClass |
4510 | +Detail for Valuator 1: |
4511 | +Label: Abs MT Position Y |
4512 | +Range: 0.000000 - 1079.000000 |
4513 | +Resolution: 0 units/m |
4514 | +Mode: absolute |
4515 | +Current value: 932.000000 |
4516 | +Class originated from: 0. Type: XITouchClass |
4517 | +Touch mode: direct |
4518 | +Max number of touches: 17 |
4519 | + |
4520 | +⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] |
4521 | +Reporting 3 classes: |
4522 | +Class originated from: 4. Type: XIButtonClass |
4523 | +Buttons supported: 10 |
4524 | +Button labels: "Button Left" "Button Middle" "Button Right" "Button |
4525 | +Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz |
4526 | +Wheel Right" None None None |
4527 | +Button state: |
4528 | +Class originated from: 4. Type: XIValuatorClass |
4529 | +Detail for Valuator 0: |
4530 | +Label: Rel X |
4531 | +Range: -1.000000 - -1.000000 |
4532 | +Resolution: 0 units/m |
4533 | +Mode: relative |
4534 | +Class originated from: 4. Type: XIValuatorClass |
4535 | +Detail for Valuator 1: |
4536 | +Label: Rel Y |
4537 | +Range: -1.000000 - -1.000000 |
4538 | +Resolution: 0 units/m |
4539 | +Mode: relative |
4540 | + |
4541 | +⎜ ↳ Quanta OpticalTouchScreen id=10 [slave pointer (2)] |
4542 | +Reporting 4 classes: |
4543 | +Class originated from: 10. Type: XIButtonClass |
4544 | +Buttons supported: 5 |
4545 | +Button labels: "Button Unknown" "Button Unknown" "Button Unknown" |
4546 | +"Button Wheel Up" "Button Wheel Down" |
4547 | +Button state: |
4548 | +Class originated from: 10. Type: XIValuatorClass |
4549 | +Detail for Valuator 0: |
4550 | +Label: Abs MT Position X |
4551 | +Range: 0.000000 - 1919.000000 |
4552 | +Resolution: 0 units/m |
4553 | +Mode: absolute |
4554 | +Current value: 1664.000000 |
4555 | +Class originated from: 10. Type: XIValuatorClass |
4556 | +Detail for Valuator 1: |
4557 | +Label: Abs MT Position Y |
4558 | +Range: 0.000000 - 1079.000000 |
4559 | +Resolution: 0 units/m |
4560 | +Mode: absolute |
4561 | +Current value: 932.000000 |
4562 | +Class originated from: 0. Type: XITouchClass |
4563 | +Touch mode: direct |
4564 | +Max number of touches: 9 |
4565 | + |
4566 | +⎜ ↳ MCE IR Keyboard/Mouse (nuvoton-cir) id=12 [slave pointer (2)] |
4567 | +Reporting 4 classes: |
4568 | +Class originated from: 12. Type: XIButtonClass |
4569 | +Buttons supported: 5 |
4570 | +Button labels: "Button Left" "Button Unknown" "Button Right" "Button |
4571 | +Wheel Up" "Button Wheel Down" |
4572 | +Button state: |
4573 | +Class originated from: 12. Type: XIKeyClass |
4574 | +Keycodes supported: 248 |
4575 | +Class originated from: 12. Type: XIValuatorClass |
4576 | +Detail for Valuator 0: |
4577 | +Label: Rel X |
4578 | +Range: -1.000000 - -1.000000 |
4579 | +Resolution: 1 units/m |
4580 | +Mode: relative |
4581 | +Class originated from: 12. Type: XIValuatorClass |
4582 | +Detail for Valuator 1: |
4583 | +Label: Rel Y |
4584 | +Range: -1.000000 - -1.000000 |
4585 | +Resolution: 1 units/m |
4586 | +Mode: relative |
4587 | + |
4588 | +⎣ Virtual core keyboard id=3 [master keyboard (2)] |
4589 | +Reporting 1 classes: |
4590 | +Class originated from: 14. Type: XIKeyClass |
4591 | +Keycodes supported: 248 |
4592 | + |
4593 | +↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] |
4594 | +Reporting 1 classes: |
4595 | +Class originated from: 5. Type: XIKeyClass |
4596 | +Keycodes supported: 248 |
4597 | + |
4598 | +↳ Power Button id=6 [slave keyboard (3)] |
4599 | +Reporting 1 classes: |
4600 | +Class originated from: 6. Type: XIKeyClass |
4601 | +Keycodes supported: 248 |
4602 | + |
4603 | +↳ Power Button id=7 [slave keyboard (3)] |
4604 | +Reporting 1 classes: |
4605 | +Class originated from: 7. Type: XIKeyClass |
4606 | +Keycodes supported: 248 |
4607 | + |
4608 | +↳ Laptop_Integrated_Webcam_2M id=8 [slave keyboard (3)] |
4609 | +Reporting 1 classes: |
4610 | +Class originated from: 8. Type: XIKeyClass |
4611 | +Keycodes supported: 248 |
4612 | + |
4613 | +↳ HID 413c:8161 id=9 [slave keyboard (3)] |
4614 | +Reporting 1 classes: |
4615 | +Class originated from: 9. Type: XIKeyClass |
4616 | +Keycodes supported: 248 |
4617 | + |
4618 | +↳ Nuvoton w836x7hg Infrared Remote Transceiver id=11 [slave keyboard (3)] |
4619 | +Reporting 1 classes: |
4620 | +Class originated from: 11. Type: XIKeyClass |
4621 | +Keycodes supported: 248 |
4622 | + |
4623 | +↳ Dell AIO WMI hotkeys id=13 [slave keyboard (3)] |
4624 | +Reporting 1 classes: |
4625 | +Class originated from: 13. Type: XIKeyClass |
4626 | +Keycodes supported: 248 |
4627 | + |
4628 | +↳ Chicony USB Keyboard id=14 [slave keyboard (3)] |
4629 | +Reporting 1 classes: |
4630 | +Class originated from: 14. Type: XIKeyClass |
4631 | +Keycodes supported: 248 |
4632 | + |
4633 | +↳ Chicony USB Keyboard id=15 [slave keyboard (3)] |
4634 | +Reporting 1 classes: |
4635 | +Class originated from: 15. Type: XIKeyClass |
4636 | +Keycodes supported: 248 |
4637 | |
4638 | === added file 'checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_toshiba.txt' |
4639 | --- checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_toshiba.txt 1970-01-01 00:00:00 +0000 |
4640 | +++ checkbox-support/checkbox_support/parsers/tests/fixtures/xinput_toshiba.txt 2014-01-07 13:44:32 +0000 |
4641 | @@ -0,0 +1,166 @@ |
4642 | +⎡ Virtual core pointer id=2 [master pointer (3)] |
4643 | + Reporting 8 classes: |
4644 | + Class originated from: 12. Type: XIButtonClass |
4645 | + Buttons supported: 12 |
4646 | + Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right" None None None None None |
4647 | + Button state: |
4648 | + Class originated from: 12. Type: XIValuatorClass |
4649 | + Detail for Valuator 0: |
4650 | + Label: Rel X |
4651 | + Range: 0.000000 - 2000.000000 |
4652 | + Resolution: 0 units/m |
4653 | + Mode: relative |
4654 | + Class originated from: 12. Type: XIValuatorClass |
4655 | + Detail for Valuator 1: |
4656 | + Label: Rel Y |
4657 | + Range: 0.000000 - 1400.000000 |
4658 | + Resolution: 0 units/m |
4659 | + Mode: relative |
4660 | + Class originated from: 12. Type: XIValuatorClass |
4661 | + Detail for Valuator 2: |
4662 | + Label: Rel Horiz Scroll |
4663 | + Range: 0.000000 - -1.000000 |
4664 | + Resolution: 0 units/m |
4665 | + Mode: relative |
4666 | + Class originated from: 12. Type: XIValuatorClass |
4667 | + Detail for Valuator 3: |
4668 | + Label: Rel Vert Scroll |
4669 | + Range: 0.000000 - -1.000000 |
4670 | + Resolution: 0 units/m |
4671 | + Mode: relative |
4672 | + Class originated from: 12. Type: XIScrollClass |
4673 | + Scroll info for Valuator 2 |
4674 | + type: 2 (horizontal) |
4675 | + increment: 48.000000 |
4676 | + flags: 0x0 |
4677 | + Class originated from: 12. Type: XIScrollClass |
4678 | + Scroll info for Valuator 3 |
4679 | + type: 1 (vertical) |
4680 | + increment: 48.000000 |
4681 | + flags: 0x0 |
4682 | + Class originated from: 0. Type: XITouchClass |
4683 | + Touch mode: dependent |
4684 | + Max number of touches: 2 |
4685 | + |
4686 | +⎜ ↳ Virtual core XTEST pointer id=4 [slave pointer (2)] |
4687 | + Reporting 3 classes: |
4688 | + Class originated from: 4. Type: XIButtonClass |
4689 | + Buttons supported: 10 |
4690 | + Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right" None None None |
4691 | + Button state: |
4692 | + Class originated from: 4. Type: XIValuatorClass |
4693 | + Detail for Valuator 0: |
4694 | + Label: Rel X |
4695 | + Range: -1.000000 - -1.000000 |
4696 | + Resolution: 0 units/m |
4697 | + Mode: relative |
4698 | + Class originated from: 4. Type: XIValuatorClass |
4699 | + Detail for Valuator 1: |
4700 | + Label: Rel Y |
4701 | + Range: -1.000000 - -1.000000 |
4702 | + Resolution: 0 units/m |
4703 | + Mode: relative |
4704 | + |
4705 | +⎜ ↳ DualPoint Stick id=11 [slave pointer (2)] |
4706 | + Reporting 3 classes: |
4707 | + Class originated from: 11. Type: XIButtonClass |
4708 | + Buttons supported: 7 |
4709 | + Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right" |
4710 | + Button state: |
4711 | + Class originated from: 11. Type: XIValuatorClass |
4712 | + Detail for Valuator 0: |
4713 | + Label: Rel X |
4714 | + Range: -1.000000 - -1.000000 |
4715 | + Resolution: 1 units/m |
4716 | + Mode: relative |
4717 | + Class originated from: 11. Type: XIValuatorClass |
4718 | + Detail for Valuator 1: |
4719 | + Label: Rel Y |
4720 | + Range: -1.000000 - -1.000000 |
4721 | + Resolution: 1 units/m |
4722 | + Mode: relative |
4723 | + |
4724 | +⎜ ↳ AlpsPS/2 ALPS DualPoint TouchPad id=12 [slave pointer (2)] |
4725 | + Reporting 8 classes: |
4726 | + Class originated from: 12. Type: XIButtonClass |
4727 | + Buttons supported: 12 |
4728 | + Button labels: "Button Left" "Button Middle" "Button Right" "Button Wheel Up" "Button Wheel Down" "Button Horiz Wheel Left" "Button Horiz Wheel Right" None None None None None |
4729 | + Button state: |
4730 | + Class originated from: 12. Type: XIValuatorClass |
4731 | + Detail for Valuator 0: |
4732 | + Label: Rel X |
4733 | + Range: 0.000000 - 2000.000000 |
4734 | + Resolution: 0 units/m |
4735 | + Mode: relative |
4736 | + Class originated from: 12. Type: XIValuatorClass |
4737 | + Detail for Valuator 1: |
4738 | + Label: Rel Y |
4739 | + Range: 0.000000 - 1400.000000 |
4740 | + Resolution: 0 units/m |
4741 | + Mode: relative |
4742 | + Class originated from: 12. Type: XIValuatorClass |
4743 | + Detail for Valuator 2: |
4744 | + Label: Rel Horiz Scroll |
4745 | + Range: 0.000000 - -1.000000 |
4746 | + Resolution: 0 units/m |
4747 | + Mode: relative |
4748 | + Class originated from: 12. Type: XIValuatorClass |
4749 | + Detail for Valuator 3: |
4750 | + Label: Rel Vert Scroll |
4751 | + Range: 0.000000 - -1.000000 |
4752 | + Resolution: 0 units/m |
4753 | + Mode: relative |
4754 | + Class originated from: 12. Type: XIScrollClass |
4755 | + Scroll info for Valuator 2 |
4756 | + type: 2 (horizontal) |
4757 | + increment: 48.000000 |
4758 | + flags: 0x0 |
4759 | + Class originated from: 12. Type: XIScrollClass |
4760 | + Scroll info for Valuator 3 |
4761 | + type: 1 (vertical) |
4762 | + increment: 48.000000 |
4763 | + flags: 0x0 |
4764 | + Class originated from: 0. Type: XITouchClass |
4765 | + Touch mode: dependent |
4766 | + Max number of touches: 2 |
4767 | + |
4768 | +⎣ Virtual core keyboard id=3 [master keyboard (2)] |
4769 | + Reporting 1 classes: |
4770 | + Class originated from: 10. Type: XIKeyClass |
4771 | + Keycodes supported: 248 |
4772 | + |
4773 | + ↳ Virtual core XTEST keyboard id=5 [slave keyboard (3)] |
4774 | + Reporting 1 classes: |
4775 | + Class originated from: 5. Type: XIKeyClass |
4776 | + Keycodes supported: 248 |
4777 | + |
4778 | + ↳ Power Button id=6 [slave keyboard (3)] |
4779 | + Reporting 1 classes: |
4780 | + Class originated from: 6. Type: XIKeyClass |
4781 | + Keycodes supported: 248 |
4782 | + |
4783 | + ↳ Video Bus id=7 [slave keyboard (3)] |
4784 | + Reporting 1 classes: |
4785 | + Class originated from: 7. Type: XIKeyClass |
4786 | + Keycodes supported: 248 |
4787 | + |
4788 | + ↳ Power Button id=8 [slave keyboard (3)] |
4789 | + Reporting 1 classes: |
4790 | + Class originated from: 8. Type: XIKeyClass |
4791 | + Keycodes supported: 248 |
4792 | + |
4793 | + ↳ CNF9055 id=9 [slave keyboard (3)] |
4794 | + Reporting 1 classes: |
4795 | + Class originated from: 9. Type: XIKeyClass |
4796 | + Keycodes supported: 248 |
4797 | + |
4798 | + ↳ AT Translated Set 2 keyboard id=10 [slave keyboard (3)] |
4799 | + Reporting 1 classes: |
4800 | + Class originated from: 10. Type: XIKeyClass |
4801 | + Keycodes supported: 248 |
4802 | + |
4803 | + ↳ Toshiba input device id=13 [slave keyboard (3)] |
4804 | + Reporting 1 classes: |
4805 | + Class originated from: 13. Type: XIKeyClass |
4806 | + Keycodes supported: 248 |
4807 | + |
4808 | |
4809 | === added directory 'checkbox-support/checkbox_support/parsers/tests/pactl_data' |
4810 | === added file 'checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-0.txt' |
4811 | --- checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-0.txt 1970-01-01 00:00:00 +0000 |
4812 | +++ checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-0.txt 2014-01-07 13:44:32 +0000 |
4813 | @@ -0,0 +1,33 @@ |
4814 | +Card #0 |
4815 | + Name: alsa_card.pci-0000_02_00.1 |
4816 | + Driver: module-alsa-card.c |
4817 | + Owner Module: 4 |
4818 | + Properties: |
4819 | + alsa.card = "2" |
4820 | + alsa.card_name = "HDA NVidia" |
4821 | + alsa.long_card_name = "HDA NVidia at 0xfbdfc000 irq 34" |
4822 | + alsa.driver_name = "snd_hda_intel" |
4823 | + device.bus_path = "pci-0000:02:00.1" |
4824 | + sysfs.path = "/devices/pci0000:00/0000:00:03.0/0000:02:00.1/sound/card2" |
4825 | + device.bus = "pci" |
4826 | + device.vendor.id = "10de" |
4827 | + device.vendor.name = "NVIDIA Corporation" |
4828 | + device.string = "2" |
4829 | + device.description = "HDA NVidia" |
4830 | + module-udev-detect.discovered = "1" |
4831 | + device.icon_name = "audio-card-pci" |
4832 | + Profiles: |
4833 | + output:hdmi-stereo: Wyjście Digital Stereo (HDMI) (sinks: 1, sources: 0, priority. 5400) |
4834 | + output:hdmi-surround: Wyjście Digital Surround 5.1 (HDMI) (sinks: 1, sources: 0, priority. 300) |
4835 | + output:hdmi-stereo-extra1: Wyjście Digital Stereo (HDMI) (sinks: 1, sources: 0, priority. 5200) |
4836 | + output:hdmi-stereo-extra2: Wyjście Digital Stereo (HDMI) (sinks: 1, sources: 0, priority. 5200) |
4837 | + output:hdmi-surround-extra2: Wyjście Digital Surround 5.1 (HDMI) (sinks: 1, sources: 0, priority. 100) |
4838 | + off: Wyłącz (sinks: 0, sources: 0, priority. 0) |
4839 | + Active Profile: output:hdmi-stereo-extra1 |
4840 | + Ports: |
4841 | + hdmi-output-0: HDMI / DisplayPort (priority 5900) |
4842 | + Part of profile(s): output:hdmi-stereo, output:hdmi-surround |
4843 | + hdmi-output-1: HDMI / DisplayPort 2 (priority 5800) |
4844 | + Part of profile(s): output:hdmi-stereo-extra1 |
4845 | + hdmi-output-2: HDMI / DisplayPort 3 (priority 5700) |
4846 | + Part of profile(s): output:hdmi-stereo-extra2, output:hdmi-surround-extra2 |
4847 | |
4848 | === added file 'checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-1.txt' |
4849 | --- checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-1.txt 1970-01-01 00:00:00 +0000 |
4850 | +++ checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-1.txt 2014-01-07 13:44:32 +0000 |
4851 | @@ -0,0 +1,51 @@ |
4852 | +Card #1 |
4853 | + Name: alsa_card.pci-0000_00_1b.0 |
4854 | + Driver: module-alsa-card.c |
4855 | + Owner Module: 5 |
4856 | + Properties: |
4857 | + alsa.card = "0" |
4858 | + alsa.card_name = "HDA Intel" |
4859 | + alsa.long_card_name = "HDA Intel at 0xf9ff8000 irq 70" |
4860 | + alsa.driver_name = "snd_hda_intel" |
4861 | + device.bus_path = "pci-0000:00:1b.0" |
4862 | + sysfs.path = "/devices/pci0000:00/0000:00:1b.0/sound/card0" |
4863 | + device.bus = "pci" |
4864 | + device.vendor.id = "8086" |
4865 | + device.vendor.name = "Intel Corporation" |
4866 | + device.product.name = "82801JI (ICH10 Family) HD Audio Controller" |
4867 | + device.form_factor = "internal" |
4868 | + device.string = "0" |
4869 | + device.description = "Wbudowany dźwięk" |
4870 | + module-udev-detect.discovered = "1" |
4871 | + device.icon_name = "audio-card-pci" |
4872 | + Profiles: |
4873 | + output:analog-stereo: Wyjście Analogowe stereo (sinks: 1, sources: 0, priority. 6000) |
4874 | + output:analog-stereo+input:analog-stereo: Analogowy dupleks stereo (sinks: 1, sources: 1, priority. 6060) |
4875 | + output:analog-surround-40: Wyjście Analogowe surround 4.0 (sinks: 1, sources: 0, priority. 700) |
4876 | + output:analog-surround-40+input:analog-stereo: Wyjście Analogowe surround 4.0 + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 760) |
4877 | + output:analog-surround-41: Wyjście Analogowe surround 4.1 (sinks: 1, sources: 0, priority. 800) |
4878 | + output:analog-surround-41+input:analog-stereo: Wyjście Analogowe surround 4.1 + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 860) |
4879 | + output:analog-surround-50: Wyjście Analogowe surround 5.0 (sinks: 1, sources: 0, priority. 700) |
4880 | + output:analog-surround-50+input:analog-stereo: Wyjście Analogowe surround 5.0 + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 760) |
4881 | + output:analog-surround-51: Wyjście Analogowe surround 5.1 (sinks: 1, sources: 0, priority. 800) |
4882 | + output:analog-surround-51+input:analog-stereo: Wyjście Analogowe surround 5.1 + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 860) |
4883 | + output:analog-surround-71: Wyjście Analog Surround 7.1 (sinks: 1, sources: 0, priority. 700) |
4884 | + output:analog-surround-71+input:analog-stereo: Wyjście Analog Surround 7.1 + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 760) |
4885 | + output:iec958-stereo: Wyjście Cyfrowe stereo (IEC958) (sinks: 1, sources: 0, priority. 5500) |
4886 | + output:iec958-stereo+input:analog-stereo: Wyjście Cyfrowe stereo (IEC958) + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 5560) |
4887 | + input:analog-stereo: Wejście Analogowe stereo (sinks: 0, sources: 1, priority. 60) |
4888 | + off: Wyłącz (sinks: 0, sources: 0, priority. 0) |
4889 | + Active Profile: output:analog-stereo+input:analog-stereo |
4890 | + Ports: |
4891 | + analog-output: Wyjście analogowe (priority 9900) |
4892 | + Part of profile(s): output:analog-stereo, output:analog-stereo+input:analog-stereo, output:analog-surround-40, output:analog-surround-40+input:analog-stereo, output:analog-surround-41, output:analog-surround-41+input:analog-stereo, output:analog-surround-50, output:analog-surround-50+input:analog-stereo, output:analog-surround-51, output:analog-surround-51+input:analog-stereo, output:analog-surround-71, output:analog-surround-71+input:analog-stereo |
4893 | + analog-output-headphones: Słuchawki (priority 9000) |
4894 | + Part of profile(s): output:analog-stereo, output:analog-stereo+input:analog-stereo |
4895 | + analog-input-microphone-front: Przedni mikrofon (priority 8500) |
4896 | + Part of profile(s): output:analog-stereo+input:analog-stereo, output:analog-surround-40+input:analog-stereo, output:analog-surround-41+input:analog-stereo, output:analog-surround-50+input:analog-stereo, output:analog-surround-51+input:analog-stereo, output:analog-surround-71+input:analog-stereo, output:iec958-stereo+input:analog-stereo, input:analog-stereo |
4897 | + analog-input-microphone-rear: Tylny mikrofon (priority 8200) |
4898 | + Part of profile(s): output:analog-stereo+input:analog-stereo, output:analog-surround-40+input:analog-stereo, output:analog-surround-41+input:analog-stereo, output:analog-surround-50+input:analog-stereo, output:analog-surround-51+input:analog-stereo, output:analog-surround-71+input:analog-stereo, output:iec958-stereo+input:analog-stereo, input:analog-stereo |
4899 | + analog-input-linein: Wejście liniowe (priority 8100) |
4900 | + Part of profile(s): output:analog-stereo+input:analog-stereo, output:analog-surround-40+input:analog-stereo, output:analog-surround-41+input:analog-stereo, output:analog-surround-50+input:analog-stereo, output:analog-surround-51+input:analog-stereo, output:analog-surround-71+input:analog-stereo, output:iec958-stereo+input:analog-stereo, input:analog-stereo |
4901 | + iec958-stereo-output: Wyjście cyfrowe (S/PDIF) (priority 0) |
4902 | + Part of profile(s): output:iec958-stereo, output:iec958-stereo+input:analog-stereo |
4903 | |
4904 | === added file 'checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-2.txt' |
4905 | --- checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-2.txt 1970-01-01 00:00:00 +0000 |
4906 | +++ checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise-2.txt 2014-01-07 13:44:32 +0000 |
4907 | @@ -0,0 +1,30 @@ |
4908 | +Card #2 |
4909 | + Name: alsa_card.usb-046d_0825_0E3A8A10-02-U0x46d0x825 |
4910 | + Driver: module-alsa-card.c |
4911 | + Owner Module: 6 |
4912 | + Properties: |
4913 | + alsa.card = "1" |
4914 | + alsa.card_name = "USB Device 0x46d:0x825" |
4915 | + alsa.long_card_name = "USB Device 0x46d:0x825 at usb-0000:00:1d.7-1, high speed" |
4916 | + alsa.driver_name = "snd_usb_audio" |
4917 | + device.bus_path = "pci-0000:00:1d.7-usb-0:1:1.2" |
4918 | + sysfs.path = "/devices/pci0000:00/0000:00:1d.7/usb2/2-1/2-1:1.2/sound/card1" |
4919 | + udev.id = "usb-046d_0825_0E3A8A10-02-U0x46d0x825" |
4920 | + device.bus = "usb" |
4921 | + device.vendor.id = "046d" |
4922 | + device.vendor.name = "Logitech, Inc." |
4923 | + device.product.id = "0825" |
4924 | + device.product.name = "Webcam C270" |
4925 | + device.serial = "046d_0825_0E3A8A10" |
4926 | + device.form_factor = "webcam" |
4927 | + device.string = "1" |
4928 | + device.description = "Webcam C270" |
4929 | + module-udev-detect.discovered = "1" |
4930 | + device.icon_name = "camera-web-usb" |
4931 | + Profiles: |
4932 | + input:analog-mono: Wejście Analogowe mono (sinks: 0, sources: 1, priority. 1) |
4933 | + off: Wyłącz (sinks: 0, sources: 0, priority. 0) |
4934 | + Active Profile: input:analog-mono |
4935 | + Ports: |
4936 | + analog-input-microphone: Mikrofon (priority 8700) |
4937 | + Part of profile(s): input:analog-mono |
4938 | |
4939 | === added file 'checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise.txt' |
4940 | --- checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise.txt 1970-01-01 00:00:00 +0000 |
4941 | +++ checkbox-support/checkbox_support/parsers/tests/pactl_data/cards-desktop-precise.txt 2014-01-07 13:44:32 +0000 |
4942 | @@ -0,0 +1,41 @@ |
4943 | +Card #0 |
4944 | + Name: alsa_card.pci-0000_00_1b.0 |
4945 | + Driver: module-alsa-card.c |
4946 | + Owner Module: 4 |
4947 | + Properties: |
4948 | + alsa.card = "0" |
4949 | + alsa.card_name = "HDA Intel PCH" |
4950 | + alsa.long_card_name = "HDA Intel PCH at 0xe0610000 irq 46" |
4951 | + alsa.driver_name = "snd_hda_intel" |
4952 | + device.bus_path = "pci-0000:00:1b.0" |
4953 | + sysfs.path = "/devices/pci0000:00/0000:00:1b.0/sound/card0" |
4954 | + device.bus = "pci" |
4955 | + device.vendor.id = "8086" |
4956 | + device.vendor.name = "Intel Corporation" |
4957 | + device.product.name = "Panther Point High Definition Audio Controller" |
4958 | + device.form_factor = "internal" |
4959 | + device.string = "0" |
4960 | + device.description = "Wbudowany dźwięk" |
4961 | + module-udev-detect.discovered = "1" |
4962 | + device.icon_name = "audio-card-pci" |
4963 | + Profiles: |
4964 | + output:analog-stereo: Wyjście Analogowe stereo (sinks: 1, sources: 0, priority. 6000) |
4965 | + output:analog-stereo+input:analog-stereo: Analogowy dupleks stereo (sinks: 1, sources: 1, priority. 6060) |
4966 | + output:hdmi-stereo: Wyjście Digital Stereo (HDMI) (sinks: 1, sources: 0, priority. 5400) |
4967 | + output:hdmi-stereo+input:analog-stereo: Wyjście Digital Stereo (HDMI) + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 5460) |
4968 | + output:hdmi-surround: Wyjście Digital Surround 5.1 (HDMI) (sinks: 1, sources: 0, priority. 300) |
4969 | + output:hdmi-surround+input:analog-stereo: Wyjście Digital Surround 5.1 (HDMI) + Wejście Analogowe stereo (sinks: 1, sources: 1, priority. 360) |
4970 | + input:analog-stereo: Wejście Analogowe stereo (sinks: 0, sources: 1, priority. 60) |
4971 | + off: Wyłącz (sinks: 0, sources: 0, priority. 0) |
4972 | + Active Profile: output:analog-stereo+input:analog-stereo |
4973 | + Ports: |
4974 | + analog-output-speaker: Głośniki (priority 10000) |
4975 | + Part of profile(s): output:analog-stereo, output:analog-stereo+input:analog-stereo |
4976 | + analog-output-headphones: Słuchawki (priority 9000) |
4977 | + Part of profile(s): output:analog-stereo, output:analog-stereo+input:analog-stereo |
4978 | + analog-input-microphone-internal: Wewnętrzny mikrofon (priority 8900) |
4979 | + Part of profile(s): output:analog-stereo+input:analog-stereo, output:hdmi-stereo+input:analog-stereo, output:hdmi-surround+input:analog-stereo, input:analog-stereo |
4980 | + analog-input-microphone: Mikrofon (priority 8700) |
4981 | + Part of profile(s): output:analog-stereo+input:analog-stereo, output:hdmi-stereo+input:analog-stereo, output:hdmi-surround+input:analog-stereo, input:analog-stereo |
4982 | + hdmi-output-0: HDMI / DisplayPort (priority 5900) |
4983 | + Part of profile(s): output:hdmi-stereo, output:hdmi-stereo+input:analog-stereo, output:hdmi-surround, output:hdmi-surround+input:analog-stereo |
4984 | |
4985 | === added file 'checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise-radeon-hdmi-available.txt' |
4986 | --- checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise-radeon-hdmi-available.txt 1970-01-01 00:00:00 +0000 |
4987 | +++ checkbox-support/checkbox_support/parsers/tests/pactl_data/desktop-precise-radeon-hdmi-available.txt 2014-01-07 13:44:32 +0000 |
4988 | @@ -0,0 +1,608 @@ |
4989 | +Module #0 |
4990 | + Name: module-device-restore |
4991 | + Argument: |
4992 | + Usage counter: n/a |
4993 | + Properties: |
4994 | + module.author = "Lennart Poettering" |
4995 | + module.description = "Automatically restore the volume/mute state of devices" |
4996 | + module.version = "1.1" |
4997 | + |
4998 | +Module #1 |
4999 | + Name: module-stream-restore |
5000 | + Argument: |
The diff has been truncated for viewing.
ensure that all commits have correct, scoped first line (summary)