/usr/lib/2013.com.canonical.certification:checkbox/bin/pulse-active-port-change is in plainbox-provider-checkbox 0.4-1.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #!/usr/bin/env python3
# This file is part of Checkbox.
#
# Copyright 2014 Canonical Ltd.
# Written by:
# Zygmunt Krynicki <zygmunt.krynicki@canonical.com>
#
# Checkbox is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3,
# as published by the Free Software Foundation.
#
# Checkbox is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Checkbox. If not, see <http://www.gnu.org/licenses/>.
"""
pulse-active-port-change
========================
This script checks if the active port on either sinks (speakers or headphones)
or sources (microphones, webcams) is changed after an appropriate device is
plugged into the DUT. The script is fully automatic and either times out after
30 seconds or returns as soon as the change is detected.
The script monitors pulse audio events with `pactl subscribe`. Any changes to
sinks (or sources, depending on the mode) are treated as a possible match. A
match is verified by running `pactl list sinks` (or `pactl list sources`) and
constructing a set of pairs (sink-source-name, sink-source-active-port). Any
change to the computed set, as compared to the initially computed set, is
considered a match.
Due to the algorithm used, it will also detect things like USB headsets, HDMI
monitors/speakers, webcams, etc.
The script depends on:
python3-checkbox-support
Which depends on:
python3-pyparsing
"""
import argparse
import os
import pty
import signal
import subprocess
from checkbox_support.parsers.pactl import parse_pactl_output
class AudioPlugDetection:
def __init__(self, timeout, mode):
# store parameters
self.timeout = timeout
self.mode = mode
# get the un-localized environment
env = dict(os.environb)
env[b'LANG'] = b''
env[b'LANGUAGE'] = b''
env[b'LC_ALL'] = b'C.UTF-8'
self.unlocalized_env = env
# set SIGALRM handler
signal.signal(signal.SIGALRM, self.on_timeout)
def get_sound_config(self):
text = subprocess.check_output(
["pactl", "list", self.mode], # either 'sources' or 'sinks'
env=self.unlocalized_env, universal_newlines=True)
doc = parse_pactl_output(text)
cfg = set()
for record in doc.record_list:
for attr in record.attribute_list:
if attr.name == "Active Port":
cfg.add((record.name, attr.value))
return cfg
def on_timeout(self, signum, frame):
print("Time is up")
raise SystemExit(1)
@classmethod
def main(cls):
parser = argparse.ArgumentParser(
description=__doc__.split("")[0],
epilog=__doc__.split("")[1],
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument(
'mode', choices=['sinks', 'sources'],
help='Monitor either sinks or sources')
parser.add_argument(
'-t', '--timeout', type=int, default=30,
help='Timeout after which the script fails')
ns = parser.parse_args()
return cls(ns.timeout, ns.mode).run()
def run(self):
found = False
if self.mode == 'sinks':
look_for = "Event 'change' on sink #"
elif self.mode == 'sources':
look_for = "Event 'change' on source #"
else:
assert False
# Get the initial / baseline configuration
initial_cfg = self.get_sound_config()
print("Starting with config: {}".format(initial_cfg))
print("You have {} seconds to plug something".format(self.timeout))
# Start the timer
signal.alarm(self.timeout)
# run subscribe in a pty as it doesn't fflush() after every event
pid, master_fd = pty.fork()
if pid == 0:
os.execlpe("pactl", "pactl", "subscribe", self.unlocalized_env)
else:
child_stream = os.fdopen(master_fd, "rt", encoding='UTF-8')
try:
for line in child_stream:
if line.startswith(look_for):
new_cfg = self.get_sound_config()
print("Now using config: {}".format(new_cfg))
if new_cfg != initial_cfg:
print("It seems to work!")
found = True
break
except KeyboardInterrupt:
pass
finally:
os.kill(pid, signal.SIGTERM)
os.close(master_fd)
return 0 if found else 1
if __name__ == "__main__":
raise SystemExit(AudioPlugDetection.main())
|