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