]> git.alsa-project.org Git - alsa.git/commitdiff
hda-analyzer: initial version to show interactive HDA codec node graph
authorJaroslav Kysela <perex@perex.cz>
Tue, 6 Jul 2010 10:53:44 +0000 (12:53 +0200)
committerJaroslav Kysela <perex@perex.cz>
Tue, 6 Jul 2010 10:53:44 +0000 (12:53 +0200)
- it's a preview version - more features will follow

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
alsatool
comments.py
config.py
drivermerge.py
hda-analyzer/hda_analyzer.py
hda-analyzer/hda_codec.py
hda-analyzer/hda_graph.py [new file with mode: 0755]
hda-analyzer/hda_guilib.py [new file with mode: 0644]
hda-analyzer/run.py

index 9cdc074f6e921f6fa82c17773c327fa43ab77ead..e8c3ef5b7eae1378cc3a350e04ad385683f4e76c 100755 (executable)
--- a/alsatool
+++ b/alsatool
@@ -602,6 +602,12 @@ def changes(argv):
                                                             continue
                                                     if l[:15].lower() == "signed-off-by: ":
                                                             continue
+                                                    if l[:10].lower() == "acked-by: ":
+                                                            continue
+                                                    if l[:6].lower() == "from: ":
+                                                            continue
+                                                    if l[:4].lower() == "cc: ":
+                                                            continue
                                                     print(': %s %s' % (first, esc(l)))
                                                     first = " "
     print('</text>')
index ecd09af0905a21217bc0febedae46d2152fd550a..19b5fd898db3ad12ce3bb55693110042b71895bd 100644 (file)
@@ -253,6 +253,8 @@ COMMENT_MAP = {
         ['/soc/codecs/Kconfig', 'SoC Layer'],
         ['/soc/codecs/l3.(c|h)', 'SoC L3 bus'],
         ['/soc/codecs/ac97.(c|h)', 'SoC Codec AC97'],
+        ['/include/wm2000.h', 'SoC Codec WM2000'],
+        ['/soc/codecs/wm2000.(c|h)', 'SoC Codec WM2000'],
         ['/soc/codecs/wm8350.(c|h)', 'SoC Codec WM8350'],
         ['/soc/codecs/wm8400.(c|h)', 'SoC Codec WM8400'],
         ['/soc/codecs/wm8510.(c|h)', 'SoC Codec WM8510'],
@@ -268,21 +270,25 @@ COMMENT_MAP = {
         ['/soc/codecs/wm8776.(c|h)', 'SoC Codec WM8776'],
         ['/soc/codecs/wm8794.(c|h)', 'SoC Codec WM8794'],
         ['/soc/codecs/wm8900.(c|h)', 'SoC Codec WM8900'],
+        ['/include/wm8903.h', 'SoC Codec WM8903'],
         ['/soc/codecs/wm8903.(c|h)', 'SoC Codec WM8903'],
         ['/include/wm8904.h', 'SoC Codec WM8904'],
         ['/soc/codecs/wm8904.(c|h)', 'SoC Codec WM8904'],
         ['/soc/codecs/wm8940.(c|h)', 'SoC Codec WM8940'],
         ['/include/wm8955.h', 'SoC Codec WM8955'],
         ['/soc/codecs/wm8955.(c|h)', 'SoC Codec WM8955'],
+        ['/include/wm8960.h', 'SoC Codec WM8960'],
         ['/soc/codecs/wm8960.(c|h)', 'SoC Codec WM8960'],
         ['/soc/codecs/wm8961.(c|h)', 'SoC Codec WM8961'],
         ['/soc/codecs/wm8971.(c|h)', 'SoC Codec WM8971'],
         ['/soc/codecs/wm8974.(c|h)', 'SoC Codec WM8974'],
+        ['/soc/codecs/wm8978.(c|h)', 'SoC Codec WM8978'],
         ['/soc/codecs/wm8988.(c|h)', 'SoC Codec WM8988'],
         ['/soc/codecs/wm8990.(c|h)', 'SoC Codec WM8990'],
         ['/soc/codecs/wm_hubs.(c|h)', 'SoC Codec WM8993/4'],
         ['/include/wm8993.h', 'SoC Codec WM8993/4'],
         ['/soc/codecs/wm8993.(c|h)', 'SoC Codec WM8993/4'],
+        ['/soc/codecs/wm8994.(c|h)', 'SoC Codec WM8994'],
         ['/include/wm9081.h', 'SoC Codec WM9081'],
         ['/soc/codecs/wm9081.(c|h)', 'SoC Codec WM9081'],
         ['/soc/codecs/wm9705.(c|h)', 'SoC Codec WM9705'],
@@ -291,6 +297,7 @@ COMMENT_MAP = {
         ['/soc/codecs/cs4270.(c|h)', 'SoC Codec CS4270'],
         ['/soc/codecs/ad1836.(c|h)', 'SoC Codec AD1836'],
         ['/soc/codecs/ad1938.(c|h)', 'SoC Codec AD1938'],
+        ['/soc/codecs/ad193x.(c|h)', 'SoC Codec AD193X'],
         ['/soc/codecs/ad1963.(c|h)', 'SoC Codec AD1963'],
         ['/soc/codecs/ad1980.(c|h)', 'SoC Codec AD1980'],
         ['/soc/codecs/tlv320aic23.(c|h)', 'SoC Codec TLV320AIC23'],
@@ -307,6 +314,7 @@ COMMENT_MAP = {
         ['/soc/codecs/ssm2602.(c|h)', 'SoC Codec SSM2602'],
         ['/soc/codecs/ad73311.(c|h)', 'SoC Codec AD73311'],
         ['/soc/codecs/twl4030.(c|h)', 'SoC Codec TWL4030'],
+        ['/soc/codecs/twl6040.(c|h)', 'SoC Codec TWL6040'],
         ['/soc/codecs/pcm3008.(c|h)', 'SoC Codec PCM3008'],
         ['/soc/codecs/cx20442.(c|h)', 'SoC Codec CX20442'],
         ['/soc/codecs/max9877.(c|h)', 'SoC Codec MAX9877'],
@@ -314,6 +322,7 @@ COMMENT_MAP = {
         ['/soc/codecs/spdif_transciever.(c|h)', 'SoC Codec DIT SPDI/F'],
         ['/soc/codecs/ads117x.(c|h)', 'SoC Codec ads1174/8'],
         ['/soc/codecs/da7210.(c|h)', 'SoC Codec DA7210'],
+        ['/soc/codecs/cq93vc.(c|h)', 'SoC Codec CQ0093 Voice'],
         ['/include/tpa6130a2-plat.h', 'SoC Codec TPA6130A2'],
         ['/soc/codecs/tpa6130a2.(c|h)', 'SoC Codec TPA6130A2'],
         ['/soc/codecs', 'ERROR'],
@@ -322,6 +331,7 @@ COMMENT_MAP = {
         ['/soc/at91/.*', 'SoC Audio for the Atmel AT91 System-on-Chip'],
         ['/soc/imx/.*', 'SoC Audio for Freecale i.MX1x i.MX2x CPUs'],
         ['/soc/txx9/.*', 'SoC Audio for TXx9'],
+        ['/soc/pxa/z2.c', 'SoC PXA2xx Aeronix Zipit Z2'],
         ['/soc/pxa/spitz.c', 'SoC PXA2xx Spitz'],
         ['/soc/pxa/corgi.c', 'SoC PXA2xx Corgi'],
         ['/soc/pxa/poodle.c', 'SoC PXA2xx Poodle'],
@@ -356,15 +366,30 @@ COMMENT_MAP = {
         ['/soc', 'ERROR'],
         ['/usb/usx2y/.*', 'USB USX2Y'],
         ['/usb/caiaq/.*', 'USB caiaq'],
+        ['/usb/misc/ua101.(c|h)', 'USB Edirol UA101 driver'],
         ['/usb/Kconfig', 'USB'],
         ['/usb/Makefile', 'USB'],
-        ['/usb/usbaudio.(c|h|inc|patch)', 'USB generic driver'],
+        ['/usb/card.(c|h|inc|patch)', 'USB generic driver'],
+        ['/usb/midi.(c|h|inc|patch)', 'USB generic driver'],
         ['/usb/usbmidi.(c|h|inc|patch)', 'USB generic driver'],
-        ['/usb/usbmixer.(c|h|patch)', 'USB generic driver'],
-        ['/usb/usbquirks.(c|h)', 'USB generic driver'],
-        ['/usb/usbmixer_maps.c', 'USB generic driver'],
-        ['/usb/usbcompat.h', 'USB generic driver'],
-        ['/usb/ua101.(c|h)', 'Edirol UA-101 driver'],
+        ['/usb/mixer.(c|h|inc|patch)', 'USB generic driver'],
+        ['/usb/usbmixer.(c|h|inc|patch)', 'USB generic driver'],
+        ['/usb/usbmixer_maps.(c|h|inc|patch)', 'USB generic driver'],
+        ['/usb/usbaudio.(c|h|inc|patch)', 'USB generic driver'],
+        ['/usb/mixer_quirks.(c|h|patch)', 'USB generic driver'],
+        ['/usb/quirks.(c|h|patch)', 'USB generic driver'],
+        ['/usb/quirks-table.(c|h|patch)', 'USB generic driver'],
+        ['/usb/usbquirks.(c|h|patch)', 'USB generic driver'],
+        ['/usb/endpoint.(c|h|patch)', 'USB generic driver'],
+        ['/usb/helper.(c|h|patch)', 'USB generic driver'],
+        ['/usb/debug.(c|h|patch)', 'USB generic driver'],
+        ['/usb/urb.(c|h|patch)', 'USB generic driver'],
+        ['/usb/pcm.(c|h)', 'USB generic driver'],
+        ['/usb/format.(c|h)', 'USB generic driver'],
+        ['/usb/proc.(c|h)', 'USB generic driver'],
+        ['/usb/mixer_maps.c', 'USB generic driver'],
+        ['/usb/usbcompat.(c|h)', 'USB generic driver'],
+        ['/usb/ua101.(c|h)', 'USB Edirol UA101 driver'],
         ['/usb', 'ERROR'],
         ['/include/atmel-abdac.h', 'Atmel on-chip Audio Bitstream DAC (ABDAC)'],
         ['/include/atmel-ac97c.h', 'Atmel on-chip Audio Bitstream DAC (ABDAC)'],
@@ -423,6 +448,7 @@ COMMENT_MAP = {
         ['/include/compat_22.h', 'ALSA Core'],
         ['/include/linux/pci_ids.h', 'ALSA Core'],
         ['/include/autoconf.*', 'ALSA Core'],
+        ['/include/alsa-autoconf.*', 'ALSA Core'],
         ['/include/platform_device_compat.h', 'ALSA Core'],
         ['/include/typedefs.h', 'ALSA Core'],
         ['/include/old/gf1.h', 'OLD GF1 header'],
@@ -497,6 +523,7 @@ COMMENT_MAP = {
         ['/sb16_csp/.*', 'sb16_csp'],
         ['/sscape_ctl/.*', 'sscape_ctl'],
         ['/usx2yloader/.*', 'usx2yloader'],
+        ['/hwmixvolume/.*', 'hwmixvolume'],
         ['/seq/sbiload/.*', 'sbiload'],
         ['/seq/cvscompile', 'Core'],
         ['/seq/hgcompile', 'Core'],
index 2e5e7747c4ab4a9a5ebb4e672417f28d3c326099..c6a7941e3821a2fb30b52d405e7cb4edb9aeba85 100644 (file)
--- a/config.py
+++ b/config.py
@@ -9,8 +9,8 @@ VERBOSE = False
 GERRORS = 0
 TMPDIR = '/dev/shm/alsatool'
 SMTP_SERVER = 'localhost'
-GIT_KERNEL_MERGE = 'v2.6.32'
-GIT_DRIVER_MERGE = 'v1.0.21'
+GIT_KERNEL_MERGE = 'v2.6.33'
+GIT_DRIVER_MERGE = 'v1.0.22'
 GIT_MERGE_REPOS = [
         ('git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git', 'master', 'linux-2.6', 'http://www.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git'),
         ('git://git.alsa-project.org/alsa-kernel.git', 'fixes', 'perex-fixes', 'http://git.alsa-project.org/http/alsa-kernel.git'),
index 753ebefedf0f3832c6bc10e3882d917da1ab5a08..4c4c626f82ed5f3604573ea15abc9ee481fa3cbb 100644 (file)
@@ -264,7 +264,8 @@ def compare_trees(driver_repo, driver_branch, kernel_repo, kernel_branch):
     rmtree("alsa-kernel-repo/Documentation/DocBook")
     mkdir("alsa-kernel-repo/Documentation/DocBook")
     system("mv alsa-kernel-repo/Documentation/alsa-driver-api.tmpl alsa-kernel-repo/Documentation/DocBook")
-    for i in ['.git-ok-commits', '.hgignore', '.hgtags', '.gitignore', 'kernel', 'scripts', 'oss']:
+    for i in ['.git-ok-commits', '.hgignore', '.hgtags', '.gitignore', 'kernel', 'scripts',
+              'oss', 'usb/usbmixer.h', 'usb/usbmixer_maps.c']:
         if isdir("alsa-kmirror-repo/%s" % i):
             rmtree("alsa-kmirror-repo/%s" % i)
         elif exists("alsa-kmirror-repo/%s" % i):
@@ -290,6 +291,8 @@ def compare_trees(driver_repo, driver_branch, kernel_repo, kernel_branch):
              'usb/caiaq/caiaq-device.c', 'usb/caiaq/caiaq-device.h',
              'usb/caiaq/caiaq-input.c', 'usb/caiaq/caiaq-input.h',
              'usb/caiaq/caiaq-midi.c', 'usb/caiaq/caiaq-midi.h',
+             'usb/usbmixer.h',
+             'usb/usbmixer_maps.c',
              'isa/wavefront/yss225.c'
              ]:
         if isdir("alsa-kernel-repo/%s" % i):
index 58d1c3ea3643fea071f4824a8e67e2ebd07632e4..8dec0eb08c9d81e488cd5d0647c2adbc4bba62ca 100755 (executable)
@@ -40,6 +40,8 @@ from hda_codec import HDACodec, HDA_card_list, \
                       PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \
                       HDA_INPUT, HDA_OUTPUT
 from hda_proc import DecodeProcFile, DecodeAlsaInfoFile, HDACodecProc
+from hda_guilib import *
+from hda_graph import create_graph
 
 CODEC_TREE = {}
 DIFF_TREE = {}
@@ -79,6 +81,7 @@ def read_nodes2(card, codec):
 def read_nodes3(card, codec, proc_file):
   c = HDACodecProc(card, codec, proc_file)
   c.analyze()
+  c.graph(dump=True)
   if not card in CODEC_TREE:
     CODEC_TREE[card] = {}
     DIFF_TREE[card] = {}
@@ -154,9 +157,6 @@ def do_diff():
     ITALIC_COLUMN
 ) = range(5)
 
-def get_fixed_font():
-  return pango.FontDescription("Misc Fixed,Courier Bold 9")
-
 class HDAAnalyzer(gtk.Window):
   info_buffer = None
   node_window = None
@@ -196,13 +196,17 @@ class HDAAnalyzer(gtk.Window):
     button.connect("clicked", self.__diff_clicked)
     self.tooltips.set_tip(button, "Show settings diff for selected codec.")
     hbox1.pack_start(button)
+    button = gtk.Button("Graph")
+    button.connect("clicked", self.__graph_clicked)
+    self.tooltips.set_tip(button, "Show graph for selected codec.")
+    hbox1.pack_start(button)
     vbox.pack_start(hbox1, False, False)
     hbox.pack_start(vbox, False, False)
     
     self.notebook = gtk.Notebook()
     hbox.pack_start(self.notebook, expand=True)
     
-    self.node_window = self.__create_node()
+    self.node_window = gtk.Table()
     self._new_notebook_page(self.node_window, '_Node editor')
 
     scrolled_window, self.info_buffer = self.__create_text(self.__dump_visibility)
@@ -228,6 +232,13 @@ class HDAAnalyzer(gtk.Window):
     
     gtk.main_quit()
 
+  def simple_dialog(self, type, msg):
+    dialog = gtk.MessageDialog(self,
+                      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                      type, gtk.BUTTONS_OK, msg)
+    dialog.run()
+    dialog.destroy()
+
   def __about_clicked(self, button):
     dialog = gtk.Dialog('About', self,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
@@ -270,14 +281,11 @@ mailing list, too.
       msg = "Setting for codec %s/%s (%s) was reverted!" % (self.codec.card, self.codec.device, self.codec.name)
       type = gtk.MESSAGE_INFO
 
-    dialog = gtk.MessageDialog(self,
-                      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                      type, gtk.BUTTONS_OK, msg)
-    dialog.run()
-    dialog.destroy()
+    self.simple_dialog(type, msg)
 
   def __diff_clicked(self, button):
     if not self.codec:
+      self.simple_dialog(gtk.MESSAGE_WARNING, "Please, select a codec in left codec/node tree.")
       return
     dialog = gtk.Dialog('Diff', self,
                         gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
@@ -300,6 +308,12 @@ mailing list, too.
     dialog.run()
     dialog.destroy()
     
+  def __graph_clicked(self, button):
+    if not self.codec:
+      self.simple_dialog(gtk.MESSAGE_WARNING, "Please, select a codec in left codec/node tree.")
+      return
+    create_graph(self.codec)
+    
   def __refresh(self):
     self.load()
     self.__dump_visibility(None, None)
@@ -354,13 +368,13 @@ mailing list, too.
         for i in CODEC_TREE[self.card]:
           card = CODEC_TREE[self.card][i].mcard
           break
-        self.__build_card(card)
+        self.node_window.add(NodeGui(card=card))
       elif codec:
-        self.__build_codec(codec)
+        self.node_window.add(NodeGui(codec=codec))
       else:
         return
     else:
-      self.__build_node(n)
+      self.node_window.add(NodeGui(node=n))
     self.node_window.show_all()
 
   def _new_notebook_page(self, widget, label):
@@ -444,524 +458,6 @@ mailing list, too.
     
     return scrolled_window, buffer
 
-  def __create_node(self):
-    scrolled_window = gtk.ScrolledWindow()
-    scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-    scrolled_window.set_shadow_type(gtk.SHADOW_IN)    
-    return scrolled_window
-
-  def __new_text_view(self, text=None):
-    text_view = gtk.TextView()
-    text_view.set_border_width(4)
-    fontName = get_fixed_font()
-    text_view.modify_font(fontName)
-    if not text is None:
-      buffer = gtk.TextBuffer(None)
-      iter = buffer.get_iter_at_offset(0)
-      if text[-1] == '\n':
-        text = text[:-1]
-      buffer.insert(iter, text)
-      text_view.set_buffer(buffer)
-      text_view.set_editable(False)
-      text_view.set_cursor_visible(False)
-    return text_view
-
-  def __build_node_caps(self, node):
-    frame = gtk.Frame('Node Caps')
-    frame.set_border_width(4)
-    if len(node.wcaps_list) == 0:
-      return frame
-    str = ''
-    for i in node.wcaps_list:
-      str += node.wcap_name(i) + '\n'
-    frame.add(self.__new_text_view(text=str))
-    return frame
-
-  def __node_connection_toggled(self, widget, row, data):
-    model, node = data
-    if not model[row][0]:
-      node.set_active_connection(int(row))
-    for r in model:
-      r[0] = False
-    idx = 0
-    for r in model:
-      r[0] = node.active_connection == idx
-      idx += 1
-
-  def __build_connection_list(self, node):
-    frame = gtk.Frame('Connection List')
-    frame.set_border_width(4)
-    sw = gtk.ScrolledWindow()
-    #sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
-    sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-    frame.add(sw)
-    if node.conn_list and node.connections:
-      model = gtk.ListStore(
-        gobject.TYPE_BOOLEAN,
-        gobject.TYPE_STRING
-      )
-      idx = 0
-      for i in node.connections:
-        iter = model.append()
-        node1 = self.codec.get_node(node.connections[idx])
-        model.set(iter, 0, node.active_connection == idx,
-                        1, node1.name())
-        idx += 1
-      treeview = gtk.TreeView(model)
-      treeview.set_rules_hint(True)
-      treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
-      treeview.set_size_request(300, 30 + len(node.connections) * 25)
-      renderer = gtk.CellRendererToggle()
-      renderer.set_radio(True)
-      if node.active_connection != None:
-        renderer.connect("toggled", self.__node_connection_toggled, (model, node))
-      column = gtk.TreeViewColumn("Active", renderer, active=0)
-      treeview.append_column(column)
-      renderer = gtk.CellRendererText()
-      column = gtk.TreeViewColumn("Destination Node", renderer, text=1, editable=False)
-      treeview.append_column(column)
-      sw.add(treeview)
-    return frame
-
-  def __amp_mute_toggled(self, button, data):
-    caps, vals, idx = data
-    val = button.get_active()
-    vals.set_mute(idx, val)
-    button.set_active(vals.vals[idx] & 0x80)
-
-  def __amp_value_changed(self, adj, data):
-    caps, vals, idx = data
-    val = int(adj.get_value())
-    vals.set_value(idx, val)
-    adj.set_value(vals.vals[idx] & 0x7f)
-
-  def __build_amps(self, node):
-
-    def build_caps(title, caps, vals):
-      if caps and caps.ofs is None:
-        caps = caps.dir == HDA_INPUT and \
-                    node.codec.amp_caps_in or node.codec.amp_caps_out
-        title += ' (Global)'
-      frame = gtk.Frame(title)
-      frame.set_border_width(4)
-      vbox = gtk.VBox(False, 0)
-      if caps:
-        str =  'Offset:          %d\n' % caps.ofs
-        str += 'Number of steps: %d\n' % caps.nsteps
-        str += 'Step size:       %d\n' % caps.stepsize
-        str += 'Mute:            %s\n' % (caps.mute and "True" or "False")
-        vbox.pack_start(self.__new_text_view(text=str), True, True, 0)
-        idx = 0
-        frame1 = None
-        vbox1 = None
-        for val in vals.vals:
-          if vals.stereo and idx & 1 == 0:
-            frame1 = gtk.Frame()
-            vbox.pack_start(frame1, False, False)
-            vbox1 = gtk.VBox(False, 0)
-            frame1.add(vbox1)
-          hbox = gtk.HBox(False, 0)
-          label = gtk.Label('Val[%d]' % idx)
-          hbox.pack_start(label, False, False)
-          if caps.mute:
-            checkbutton = gtk.CheckButton('Mute')
-            checkbutton.set_active(val & 0x80 and True or False)
-            checkbutton.connect("toggled", self.__amp_mute_toggled, (caps, vals, idx))
-            hbox.pack_start(checkbutton, False, False)
-          if caps.stepsize > 0:
-            adj = gtk.Adjustment((val & 0x7f) % (caps.nsteps+1), 0.0, caps.nsteps+1, 1.0, 1.0, 1.0)
-            scale = gtk.HScale(adj)
-            scale.set_digits(0)
-            scale.set_value_pos(gtk.POS_RIGHT)
-            adj.connect("value_changed", self.__amp_value_changed, (caps, vals, idx))
-            hbox.pack_start(scale, True, True)
-          if vbox1:
-            vbox1.pack_start(hbox, False, False)
-          else:
-            vbox.pack_start(hbox, False, False)
-          idx += 1
-      frame.add(vbox)
-      return frame
-
-    hbox = gtk.HBox(False, 0)
-    c = build_caps('Input Amplifier',
-                    node.in_amp and node.amp_caps_in or None,
-                    node.in_amp and node.amp_vals_in or None)
-    hbox.pack_start(c)
-    c = build_caps('Output Amplifier',
-                    node.out_amp and node.amp_caps_out or None,
-                    node.out_amp and node.amp_vals_out or None)
-    hbox.pack_start(c)
-
-    return hbox
-
-  def __pincap_eapdbtl_toggled(self, button, data):
-    node, name = data
-    node.eapdbtl_set_value(name, button.get_active())
-    button.set_active(name in node.pincap_eapdbtl)
-
-  def __pinctls_toggled(self, button, data):
-    node, name = data
-    node.pin_widget_control_set_value(name, button.get_active())
-    button.set_active(name in node.pinctl)
-
-  def __pinctls_vref_change(self, combobox, node):
-    index = combobox.get_active()
-    idx1 = 0
-    for name in PIN_WIDGET_CONTROL_VREF:
-      if not name: continue
-      if idx1 == index:
-        node.pin_widget_control_set_value('vref', name)
-        break
-      idx1 += 1
-    idx = idx1 = 0
-    for name in PIN_WIDGET_CONTROL_VREF:
-      if name == node.pinctl_vref:
-        combobox.set_active(idx1)
-        break
-      if name != None:
-        idx1 += 1
-
-  def __build_pin(self, node):
-    hbox = gtk.HBox(False, 0)
-
-    if node.pincap or node.pincap_vref or node.pincap_eapdbtl:
-      vbox = gtk.VBox(False, 0)
-      if node.pincap or node.pincap_vref:
-        frame = gtk.Frame('PIN Caps')
-        frame.set_border_width(4)
-        str = ''
-        for i in node.pincap:
-          str += node.pincap_name(i) + '\n'
-        for i in node.pincap_vref:
-          str += 'VREF_%s\n' % i
-        frame.add(self.__new_text_view(text=str))
-        vbox.pack_start(frame)
-      if 'EAPD' in node.pincap:
-        frame = gtk.Frame('EAPD')
-        frame.set_border_width(4)
-        vbox1 = gtk.VBox(False, 0)
-        for name in EAPDBTL_BITS:
-          checkbutton = gtk.CheckButton(name)
-          checkbutton.set_active(node.pincap_eapdbtls & (1 << EAPDBTL_BITS[name]))
-          checkbutton.connect("toggled", self.__pincap_eapdbtl_toggled, (node, name))
-          vbox1.pack_start(checkbutton, False, False)
-        frame.add(vbox1)
-        vbox.pack_start(frame, False, False)
-      hbox.pack_start(vbox)
-
-    vbox = gtk.VBox(False, 0)
-
-    frame = gtk.Frame('Config Default')
-    frame.set_border_width(4)
-    str =  'Jack connection: %s\n' % node.jack_conn_name
-    str += 'Jack type:       %s\n' % node.jack_type_name
-    str += 'Jack location:   %s\n' % node.jack_location_name
-    str += 'Jack location2:  %s\n' % node.jack_location2_name
-    str += 'Jack connector:  %s\n' % node.jack_connector_name
-    str += 'Jack color:      %s\n' % node.jack_color_name
-    if 'NO_PRESENCE' in node.defcfg_misc:
-      str += 'No presence\n'
-    frame.add(self.__new_text_view(text=str))
-    vbox.pack_start(frame)
-    
-    frame = gtk.Frame('Widget Control')
-    frame.set_border_width(4)
-    vbox1 = gtk.VBox(False, 0)
-    for name in PIN_WIDGET_CONTROL_BITS:
-      checkbutton = gtk.CheckButton(name)
-      checkbutton.set_active(node.pinctls & (1 << PIN_WIDGET_CONTROL_BITS[name]))
-      checkbutton.connect("toggled", self.__pinctls_toggled, (node, name))
-      vbox1.pack_start(checkbutton, False, False)
-    if node.pincap_vref:
-      combobox = gtk.combo_box_new_text()
-      idx = idx1 = active = 0
-      for name in PIN_WIDGET_CONTROL_VREF:
-        if name == node.pinctl_vref: active = idx1
-        if name:
-          combobox.append_text(name)
-          idx1 += 1
-        idx += 1
-      combobox.set_active(active)
-      combobox.connect("changed", self.__pinctls_vref_change, node)
-      hbox1 = gtk.HBox(False, 0)
-      label = gtk.Label('VREF')
-      hbox1.pack_start(label, False, False)
-      hbox1.pack_start(combobox)
-      vbox1.pack_start(hbox1, False, False)
-    frame.add(vbox1)
-    vbox.pack_start(frame, False, False)
-
-    hbox.pack_start(vbox)
-    return hbox
-
-  def __build_mix(self, node):
-    hbox = gtk.HBox(False, 0)
-    return hbox
-
-  def __sdi_select_changed(self, adj, node):
-    val = int(adj.get_value())
-    node.sdi_select_set_value(val)
-    adj.set_value(node.sdi_select)
-
-  def __dig1_toggled(self, button, data):
-    node, name = data
-    val = button.get_active()
-    node.dig1_set_value(name, val)
-    button.set_active(name in node.dig1)
-
-  def __dig1_category_activate(self, entry, node):
-    val = entry.get_text()
-    if val.lower().startswith('0x'):
-      val = int(val[2:], 16)
-    else:
-      try:
-        val = int(val)
-      except:
-        print "Unknown category value '%s'" % val
-        return
-    node.dig1_set_value('category', val)
-    entry.set_text("0x%02x" % node.dig1_category)
-
-  def __build_aud(self, node):
-    vbox = gtk.VBox(False, 0)
-
-    frame = gtk.Frame('Converter')
-    frame.set_border_width(4)
-    str = 'Audio Stream:\t%s\n' % node.aud_stream
-    str += 'Audio Channel:\t%s\n' % node.aud_channel
-    if node.format_ovrd:
-      str += 'Rates:\t\t%s\n' % node.pcm_rates[:6]
-      if len(node.pcm_rates) > 6:
-        str += '\t\t\t\t%s\n' % node.pcm_rates[6:]
-      str += 'Bits:\t\t%s\n' % node.pcm_bits
-      str += 'Streams:\t%s\n' % node.pcm_streams
-    else:
-      str += 'Global Rates:\t%s\n' % node.codec.pcm_rates[:6]
-      if len(node.codec.pcm_rates) > 6:
-        str += '\t\t%s\n' % node.codec.pcm_rates[6:]
-      str += 'Global Bits:\t%s\n' % node.codec.pcm_bits
-      str += 'Global Streams:\t%s\n' % node.codec.pcm_streams
-    frame.add(self.__new_text_view(text=str))
-    vbox.pack_start(frame)
-
-    if not node.sdi_select is None:
-      hbox1 = gtk.HBox(False, 0)
-      frame = gtk.Frame('SDI Select')
-      adj = gtk.Adjustment(node.sdi_select, 0.0, 16.0, 1.0, 1.0, 1.0)
-      scale = gtk.HScale(adj)
-      scale.set_digits(0)
-      scale.set_value_pos(gtk.POS_LEFT)
-      scale.set_size_request(200, 16)
-      adj.connect("value_changed", self.__sdi_select_changed, node)
-      frame.add(scale)
-      hbox1.pack_start(frame, False, False)
-      vbox.pack_start(hbox1, False, False)
-
-    if node.digital:
-      hbox1 = gtk.HBox(False, 0)
-      frame = gtk.Frame('Digital Converter')
-      vbox1 = gtk.VBox(False, 0)
-      for name in DIG1_BITS:
-        checkbutton = gtk.CheckButton(name)
-        checkbutton.set_active(node.digi1 & (1 << DIG1_BITS[name]))
-        checkbutton.connect("toggled", self.__dig1_toggled, (node, name))
-        vbox1.pack_start(checkbutton, False, False)
-      frame.add(vbox1)
-      hbox1.pack_start(frame)
-      frame = gtk.Frame('Digital Converter Category')
-      entry = gtk.Entry()
-      entry.set_text("0x%x" % node.dig1_category)
-      entry.set_width_chars(4)
-      entry.connect("activate", self.__dig1_category_activate, node)
-      frame.add(entry)
-      hbox1.pack_start(frame)
-      vbox.pack_start(hbox1, False, False)
-
-    return vbox
-
-  def __build_device(self, device):
-    vbox = gtk.VBox(False, 0)
-    frame = gtk.Frame('Device')
-    frame.set_border_width(4)
-    hbox = gtk.HBox(False, 0)
-    s = 'name=' + str(device.name) + ', type=' + \
-        str(device.type) + ', device=' + str(device.device)
-    label = gtk.Label(s)
-    hbox.pack_start(label, False, False)
-    frame.add(hbox)
-    vbox.pack_start(frame)
-    return vbox
-
-  def __build_controls(self, ctrls):
-    vbox = gtk.VBox(False, 0)
-    frame = gtk.Frame('Controls')
-    frame.set_border_width(4)
-    vbox1 = gtk.VBox(False, 0)
-    for ctrl in ctrls:
-      hbox1 = gtk.HBox(False, 0)
-      vbox1.pack_start(hbox1, False, False)
-      s = 'name=' + str(ctrl.name) + ', index=' + str(ctrl.index) + \
-          ', device=' + str(ctrl.device)
-      label = gtk.Label(s)
-      hbox1.pack_start(label, False, False)
-      if ctrl.amp_chs:
-        hbox1 = gtk.HBox(False, 0)
-        vbox1.pack_start(hbox1, False, False)
-        s = '  chs=' + str(ctrl.amp_chs) + ', dir=' + str(ctrl.amp_dir) + \
-            ', idx=' + str(ctrl.amp_idx) + ', ofs=' + str(ctrl.amp_ofs)
-        label = gtk.Label(s)
-        hbox1.pack_start(label, False, False)
-    frame.add(vbox1)
-    vbox.pack_start(frame)
-    return vbox
-
-  def __build_proc(self, node):
-    frame = gtk.Frame('Processing Caps')
-    frame.set_border_width(4)
-    str = 'benign=%i\nnumcoef=%i\n' % (node.proc_benign, node.proc_numcoef)
-    frame.add(self.__new_text_view(text=str))
-    return frame
-
-  def __build_node(self, node):
-    w = self.node_window
-
-    mframe = gtk.Frame(node.name())
-    mframe.set_border_width(4)
-
-    vbox = gtk.VBox(False, 0)
-    dev = node.get_device()
-    if not dev is None:
-      vbox.pack_start(self.__build_device(dev), False, False)
-    ctrls = node.get_controls()
-    if ctrls:
-      vbox.pack_start(self.__build_controls(ctrls), False, False)
-    hbox = gtk.HBox(False, 0)
-    hbox.pack_start(self.__build_node_caps(node))
-    hbox.pack_start(self.__build_connection_list(node))
-    vbox.pack_start(hbox, False, False)
-    if node.in_amp or node.out_amp:
-      vbox.pack_start(self.__build_amps(node), False, False)
-    if node.wtype_id == 'PIN':
-      vbox.pack_start(self.__build_pin(node), False, False)
-    elif node.wtype_id in ['AUD_IN', 'AUD_OUT']:
-      vbox.pack_start(self.__build_aud(node), False, False)
-    else:
-      if not node.wtype_id in ['AUD_MIX', 'BEEP', 'AUD_SEL']:
-        print 'Node type %s has no GUI support' % node.wtype_id
-    if node.proc_wid:
-      vbox.pack_start(self.__build_proc(node), False, False)
-
-    mframe.add(vbox)
-    w.add_with_viewport(mframe)
-
-  def __build_codec_info(self, codec):
-    vbox = gtk.VBox(False, 0)
-
-    frame = gtk.Frame('Codec Identification')
-    frame.set_border_width(4)
-    str = 'Audio Fcn Group: %s\n' % (codec.afg and "0x%02x" % codec.afg or "N/A")
-    str += 'Modem Fcn Group: %s\n' % (codec.mfg and "0x%02x" % codec.mfg or "N/A")
-    str += 'Vendor ID:\t 0x%08x\n' % codec.vendor_id
-    str += 'Subsystem ID:\t 0x%08x\n' % codec.subsystem_id
-    str += 'Revision ID:\t 0x%08x\n' % codec.revision_id
-    frame.add(self.__new_text_view(text=str))
-    vbox.pack_start(frame, False, False)
-
-    frame = gtk.Frame('PCM Global Capabilities')
-    frame.set_border_width(4)
-    str = 'Rates:\t\t %s\n' % codec.pcm_rates[:6]
-    if len(codec.pcm_rates) > 6:
-      str += '\t\t %s\n' % codec.pcm_rates[6:]
-    str += 'Bits:\t\t %s\n' % codec.pcm_bits
-    str += 'Streams:\t %s\n' % codec.pcm_streams
-    frame.add(self.__new_text_view(text=str))
-    vbox.pack_start(frame, False, False)
-
-    return vbox
-    
-  def __build_codec_amps(self, codec):
-
-    def build_caps(title, caps):
-      frame = gtk.Frame(title)
-      frame.set_border_width(4)
-      if caps and caps.ofs != None:
-        str = 'Offset:\t\t %d\n' % caps.ofs
-        str += 'Number of steps: %d\n' % caps.nsteps
-        str += 'Step size:\t %d\n' % caps.stepsize
-        str += 'Mute:\t\t %s\n' % (caps.mute and "True" or "False")
-        frame.add(self.__new_text_view(text=str))
-      return frame
-
-    hbox = gtk.HBox(False, 0)
-    c = build_caps('Global Input Amplifier Caps', codec.amp_caps_in)
-    hbox.pack_start(c)
-    c = build_caps('Global Output Amplifier Caps', codec.amp_caps_out)
-    hbox.pack_start(c)
-
-    return hbox
-
-  def __gpio_toggled(self, button, (codec, id, idx)):
-    codec.gpio.set(id, idx, button.get_active())
-    button.set_active(codec.gpio.test(id, idx))
-
-  def __build_codec_gpio(self, codec):
-    frame = gtk.Frame('GPIO')
-    frame.set_border_width(4)
-    hbox = gtk.HBox(False, 0)
-    str =  'IO Count:    %d\n' % codec.gpio_max
-    str += 'O Count:     %d\n' % codec.gpio_o
-    str += 'I Count:     %d\n' % codec.gpio_i
-    str += 'Unsolicited: %s\n' % (codec.gpio_unsol and "True" or "False")
-    str += 'Wake:        %s\n' % (codec.gpio_wake and "True" or "False")
-    hbox.pack_start(self.__new_text_view(text=str), False, False)
-    frame.add(hbox)
-    for id in GPIO_IDS:
-      id1 = id == 'direction' and 'out-dir' or id
-      frame1 = gtk.Frame(id1)
-      frame1.set_border_width(4)
-      vbox1 = gtk.VBox(False, 0)
-      for i in range(codec.gpio_max):
-        checkbutton = gtk.CheckButton('[%d]' % i)
-        checkbutton.set_active(codec.gpio.test(id, i))
-        checkbutton.connect("toggled", self.__gpio_toggled, (codec, id, i))
-        vbox1.pack_start(checkbutton, False, False)
-      frame1.add(vbox1)
-      hbox.pack_start(frame1, False, False)
-    return frame
-
-  def __build_codec(self, codec):
-    w = self.node_window
-
-    mframe = gtk.Frame(codec.name)
-    mframe.set_border_width(4)
-
-    vbox = gtk.VBox(False, 0)
-    vbox.pack_start(self.__build_codec_info(codec), False, False)
-    vbox.pack_start(self.__build_codec_amps(codec), False, False)
-    vbox.pack_start(self.__build_codec_gpio(codec), False, False)
-    mframe.add(vbox)
-    w.add_with_viewport(mframe)
-
-  def __build_card_info(self, card):
-    str =  'Card:       %s\n' % card.card
-    str += 'Id:         %s\n' % card.id
-    str += 'Driver:     %s\n' % card.driver
-    str += 'Name:       %s\n' % card.name
-    str += 'LongName:   %s\n' % card.longname
-    return self.__new_text_view(text=str)
-
-  def __build_card(self, card):
-    w = self.node_window
-
-    mframe = gtk.Frame(card.name)
-    mframe.set_border_width(4)
-
-    vbox = gtk.VBox(False, 0)
-    vbox.pack_start(self.__build_card_info(card), False, False)
-    mframe.add(vbox)
-    w.add_with_viewport(mframe)
-
 def monitor():
   from time import sleep
   print "Watching %s cards" % len(CODEC_TREE)
@@ -999,6 +495,9 @@ def main(argv):
   if len(argv) > 1 and argv[1] in ('-m', '-monitor', '--monitor'):
     cmd = 'monitor'
     del argv[1]
+  if len(argv) > 1 and argv[1] in ('-g', '-graph', '--graph'):
+    cmd = 'graph'
+    del argv[1]
   if read_nodes(sys.argv[1:]) == 0:
     print "No HDA codecs were found or insufficient priviledges for "
     print "/dev/snd/controlC* and /dev/snd/hwdepC*D* device files."
@@ -1012,6 +511,10 @@ def main(argv):
     if cmd == 'monitor':
       monitor()
       return 1
+    if cmd == 'graph':
+      for card in CODEC_TREE:
+        for codec in CODEC_TREE:
+          create_graph(CODEC_TREE[card][codec])
     HDAAnalyzer()
     gtk.main()
   return 1
index 2e7b25bda869fd8022333af7b4240f908aa6af16..bdf008dd90102fcacdedc4d724a30fe82a68849f 100644 (file)
@@ -1219,6 +1219,189 @@ class HDACodec:
       return self.proc_codec.get_controls(nid)
     return None
 
+  def connections(self, nid, dir=0):
+    if dir == 0:
+      if nid in self.nodes:
+        conn = self.nodes[nid].connections
+        if conn:
+          return len(conn)
+      return 0
+    res = 0
+    for nid in self.nodes:
+      node = self.nodes[nid]
+      if nid != nid and node.connections and nid in node.connections:
+        res += 1
+    return res
+
+  def graph(self, dump=False):
+  
+    def mfind(nid):
+      for y in range(len(res)):
+        if nid in res[y]:
+          return (y, res[y].index(nid))
+      return None, None
+
+    def doplace(nid, y, x):
+      node = self.nodes[nid]
+      if node.wtype_id == 'AUD_MIX':
+        if y == 0:
+          y += 1
+        while 1:
+          x += 1
+          if x >= len(res[0]) - 1:
+            x = 1
+            y += 1
+          if y >= len(res) - 1:
+            raise ValueError, "cannot place nid to graph matrix"
+          if res[y][x+1] is None and \
+             res[y][x-1] is None and \
+             res[y+1][x] is None and \
+             res[y-1][x] is None and \
+             res[y][x] is None:
+            res[y][x] = nid
+            return True
+      if y == 0:
+        for idx in range(len(res)-2):
+          if res[idx+1][x] is None:
+            res[idx+1][x] = nid
+            return True
+      elif y == len(res)-1:
+        for idx in reversed(range(len(res)-2)):
+          if res[idx+1][x] is None:
+            res[idx+1][x] = nid
+            return True
+      elif x == 0:
+        for idx in range(len(res[0])-2):
+          if res[y][idx+1] is None:
+            res[y][idx+1] = nid
+            return True
+      elif x == len(res)-1:
+        for idx in range(len(res[0])-2):
+          if res[y][idx+1] is None:
+            res[y][idx+1] = nid
+            return True
+      else:
+        if y+1 < len(res) and res[y+1][x] is None:
+          res[y+1][x] = nid
+          return True
+        if y-1 != 0 and res[y-1][x] is None:
+          res[y-1][x] = nid
+          return True
+        if x+1 < len(res[0]) and res[y][x+1] is None:
+          res[y][x+1] = nid
+          return True
+        if x-1 != 0 and res[y][x-1] is None:
+          res[y][x-1] = nid
+          return True
+        if y+1 < len(res):
+          return doplace(nid, y+1, 1)
+        if x+1 < len(res[0]):
+          return doplace(nid, 1, x+1)
+        raise ValueError, "cannot place nid to graph matrix"
+      return False
+  
+    res = []
+    unplaced = []
+    # determine all termination widgets
+    terms = {'AUD_IN':[], 'AUD_OUT':[], 'PIN_IN':[], 'PIN_OUT':[]}
+    for nid in self.nodes:
+      node = self.nodes[nid]
+      if node.wtype_id in ['AUD_IN', 'AUD_OUT', 'PIN']:
+        id = node.wtype_id
+        if id == 'PIN':
+          id = 'IN' in node.pinctl and 'PIN_IN' or 'PIN_OUT'
+        terms[id].append(nid)
+      else:
+        unplaced.append(nid)
+    for id in terms:
+      terms[id].sort()
+    # build the matrix
+    x = max(len(terms['AUD_IN']), len(terms['AUD_OUT'])) + 2
+    y = max(len(terms['PIN_IN']), len(terms['PIN_OUT'])) + 2
+    if x == 2 and y == 2:
+      return None
+    for a in range(y):
+      res.append([None]*x)
+    if 'PIN_IN' in terms:
+      for idx in range(len(terms['PIN_IN'])):
+        res[idx+1][0] = terms['PIN_IN'][idx]
+    if 'PIN_OUT' in terms:
+      for idx in range(len(terms['PIN_OUT'])):
+        res[idx+1][-1] = terms['PIN_OUT'][idx]
+    if 'AUD_IN' in terms:
+      idx = 1
+      for nid in terms['AUD_IN']:
+        res[0][idx] = nid
+        idx += 1
+    if 'AUD_OUT' in terms:
+      idx = 1
+      for nid in terms['AUD_OUT']:
+        res[-1][idx] = nid
+        idx += 1
+    # check and resize the matrix for unplaced nodes
+    while len(res)**len(res[0]) < len(unplaced):
+      res.insert(-2, [None]*x)
+    # assign unplaced nids - check connections
+    unplaced.sort()
+    while unplaced:
+      change = len(unplaced)
+      for idx in range(len(res)):
+        for idx1 in range(len(res[idx])):
+          nid = res[idx][idx1]
+          if nid is None:
+            continue
+          node = self.nodes[nid]
+          if not node or not node.connections:
+            continue
+          for conn in node.connections:
+            if conn in unplaced and doplace(conn, idx, idx1):
+              unplaced.remove(conn)
+      for nid in unplaced:
+        node = self.nodes[nid]
+        if not node.connections:
+          continue
+        for conn in node.connections:
+          placed = False
+          y, x = mfind(nid)
+          if doplace(nid, y, x):
+            unplaced.remove(nid)
+            break
+      if len(unplaced) == change:
+        break
+    y = len(res)
+    x = 0
+    for nid in unplaced:
+      if y >= len(res):
+        res.append([None]*len(res[0]))
+      res[y][x] = nid
+      x += 1
+      if x >= len(res[0]):
+        y += 1
+        x = 0
+    # do extra check
+    check = []
+    for y in range(len(res)):
+      for x in range(len(res[0])):
+        nid = res[y][x]
+        if not nid is None:
+          if nid in check:
+            raise ValueError, "double nid in graph matrix"
+          if not nid in self.nodes:
+            raise ValueError, "unknown nid in graph matrix"
+          check.append(nid)
+    if len(check) != len(self.nodes):
+      raise ValueError, "not all nids in graph matrix"
+    # do addition dump
+    if dump:
+      print "****", len(self.nodes)
+      for nodes in res:
+        str = ''
+        for node2 in nodes:
+          str += node2 is None and '--  ' or '%02x  ' % node2
+        print str
+      print "****"
+    return res
+
 def HDA_card_list():
   from dircache import listdir
   result = []
diff --git a/hda-analyzer/hda_graph.py b/hda-analyzer/hda_graph.py
new file mode 100755 (executable)
index 0000000..1ac6987
--- /dev/null
@@ -0,0 +1,797 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+
+import gobject
+import gtk
+from gtk import gdk
+import pango
+import cairo
+from hda_guilib import *
+
+from hda_codec import EAPDBTL_BITS, PIN_WIDGET_CONTROL_BITS, \
+                      PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \
+                      HDA_INPUT, HDA_OUTPUT
+
+GRAPH_WINDOWS = {}
+
+class Node:
+
+  def __init__(self, codec, node, x, y, nodesize, extra):
+    self.codec = codec
+    self.node = node
+    self.extra = extra
+    sx = sy = nodesize
+    self.myarea = [extra+x*(sx+extra), extra+y*(sy+extra), sx, sy]
+    self.src_routes = []
+    self.dst_routes = []
+    self.win = None
+
+  def expose(self, cr, event, graph):
+
+    width = self.myarea[2]
+    height = self.myarea[3]
+
+    cr.select_font_face("Misc Fixed", 
+                        cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_NORMAL)
+    cr.set_font_size(14)
+    if graph.startnode == self:
+      cr.set_line_width(1.8)
+      cr.set_source_rgb(0, 0, 1)
+    elif graph.endnode == self:
+      cr.set_line_width(1.8)
+      cr.set_source_rgb(1, 0, 0)
+    else:
+      cr.set_line_width(0.8)    
+      cr.set_source_rgb(0, 0, 0)
+    cr.rectangle(*self.myarea)
+    cr.stroke()
+
+    cr.set_line_width(0.4)
+    cr.move_to(self.myarea[0]+5, self.myarea[1]+13)
+    cr.text_path("0x%02x: %s" % (self.node.nid, self.node.wtype_id))
+    cr.stroke()
+
+    cr.set_line_width(0.2)
+    cr.rectangle(self.myarea[0], self.myarea[1] + (height/4)*3, width/2, height/4)
+    cr.rectangle(self.myarea[0]+width/2, self.myarea[1] + (height/4)*3, width/2, height/4)
+    cr.stroke()
+
+    cr.set_font_size(11)
+    cr.move_to(self.myarea[0]+20, self.myarea[1] + (height/4)*3+15)
+    cr.text_path('IN')
+    cr.stroke()
+    cr.move_to(self.myarea[0]+width/2+20, self.myarea[1] + (height/4)*3+15)
+    cr.text_path('OUT')
+    cr.stroke()
+
+  def has_x(self, x):
+    x1 = self.myarea[0]
+    x2 = x1 + self.myarea[2]
+    return x >= x1 and x <= x2
+    
+  def compressx(self, first, size):
+    if self.myarea[0] > first:
+      self.myarea[0] -= size
+
+  def has_y(self, y):
+    y1 = self.myarea[1]
+    y2 = y1 + self.myarea[3]
+    return y >= y1 and y <= y2
+    
+  def compressy(self, first, size):
+    if self.myarea[1] > first:
+      self.myarea[1] -= size
+
+  def in_area(self, x, y):
+    if x >= self.myarea[0] and \
+       y >= self.myarea[1] and \
+       x < self.myarea[0] + self.myarea[2] and \
+       y < self.myarea[1] + self.myarea[3]:
+      wherex = x - self.myarea[0]
+      wherey = y - self.myarea[1]
+      if wherey >= (self.myarea[3]/4) * 3:
+        if wherex >= self.myarea[2]/2:
+          return "dst"
+        else:
+          return "src"
+      else:
+        return "body"
+
+  def mouse_move(self, x, y, graph):
+    what = self.in_area(x, y)
+    if not what is None:
+      if what == "dst":
+        for r in self.dst_routes:
+          r.highlight = True
+      elif what == "src":
+        for r in self.src_routes:
+          r.highlight = True
+      else:
+        graph.popup = self.codec.dump_node(self.node)
+      return True
+
+class Route:
+
+  def __init__(self, codec, src_node, dst_node, routes, nodes):
+    self.codec = codec
+    self.src = src_node
+    self.dst = dst_node
+    self.lines = []
+    self.analyze_routes(routes, nodes)
+    src_node.dst_routes.append(self)
+    dst_node.src_routes.append(self)
+    self.highlight = False
+    self.marked = False
+
+  def shortdest(self):
+    return "0x%02x->0x%02x" % (self.src.node.nid, self.dst.node.nid)
+
+  def longdesc(self):
+    src = self.src.node
+    dst = self.dst.node
+    return "%s 0x%02x -> %s 0x%02x" % (src.wtype_id.replace('_', '-'),
+                        src.nid, dst.wtype_id.replace('_', '-'), dst.nid)
+
+  def expose(self, cr, event):
+    width = self.src.myarea[2]
+    height = self.src.myarea[3]
+
+    if 0: # direct green lines
+      cr.set_line_width(0.3)
+      cr.set_source_rgb(0.2, 1.0, 0.2)
+      cr.move_to(self.src.myarea[0]+(width/4)*3, self.src.myarea[1]+height)
+      cr.line_to(self.dst.myarea[0]+width/4, self.dst.myarea[1]+height)
+      cr.stroke()
+
+    for line in self.lines:
+      if self.marked:
+        cr.set_line_width(1.8)
+        cr.set_source_rgb(1, 0, 1)
+      elif self.highlight:
+        cr.set_line_width(1.5)
+        cr.set_source_rgb(1, 0, 0)
+      else:
+        cr.set_line_width(0.5)
+        cr.set_source_rgb(0, 0, 0)
+      if len(line) > 4:
+        cr.set_source_rgb(0, 1, 0)
+      cr.move_to(line[0], line[1])
+      cr.line_to(line[2], line[3])
+      cr.stroke()
+
+  def select_line(self, routes, nodes, possible):
+
+    def check_dot(posx, posy, line):
+      if posx == line[0] and posx == line[2]:
+        if line[1] < line[3]:
+          if posy >= line[1] and posy <= line[3]:
+            #print "Clash1", posx, posy, line
+            return True
+        else:
+          if posy >= line[3] and posy <= line[1]:
+            #print "Clash2", posx, posy, line
+            return True
+      if posy == line[1] and posy == line[3]:
+        if line[0] < line[2]:
+          if posx >= line[0] and posx <= line[2]:
+            #print "Clash3", posx, posy, line
+            return True
+        else:
+          if posx >= line[2] and posx <= line[0]:
+            #print "Clash4", posx, posy, line
+            return True
+      if posx == line[0] and posy == line[1]:
+        #print "Clash5", posx, posy, line
+        return True
+      if posx == line[2] and posy == line[3]:
+        #print "Clash6", posx, posy, line
+        return True
+      return False 
+
+    for p in possible:
+      found = False
+      for route in routes:
+        if found:
+          break
+        for line in route.lines:
+          if check_dot(line[0], line[1], p) or \
+             check_dot(line[2], line[3], p) or \
+             check_dot(p[0], p[1], line) or \
+             check_dot(p[2], p[3], line):
+            #print "Found1", p
+            found = True
+            break
+      if nodes and not found:
+        x1, y1, x2, y2 = p
+        if x1 > x2 or y1 > y2:
+          x2, y2, x1, y1 = p
+        for node in nodes:
+          xx1, yy1, xx2, yy2 = node.myarea
+          xx2 += xx1
+          yy2 += yy1
+          if x1 < xx2 and x2 >= xx1 and y1 < yy2 and y2 >= yy1:
+            #print "Found2", x1, y1, x2, y2, xx1, yy1, xx2, yy2
+            found = True
+            break
+      if not found:
+        #print "OK x1=%s,y1=%s,x2=%s,y2=%s" % (p[0], p[1], p[2], p[3])
+        return p
+
+  def analyze_routes(self, routes, nodes):
+    posx, posy, width, height = self.src.myarea
+    dposx, dposy, dwidth, dheight = self.dst.myarea
+    extra = self.src.extra
+    
+    possible = []
+    startx = posx >= dposx and posx - extra or posx + width
+    xrange = range(5, extra-1, 5)
+    if posx >= dposx:
+      xrange.reverse()
+      a = range(width+extra+5, width+extra*2-1, 5)
+      a.reverse()
+      xrange = xrange + a
+      for i in range(2, 10):
+        a = range(width*i+extra*i+5, width*i+extra*(i+1)-1, 5)
+        a.reverse()
+        xrange = xrange + a
+    else:
+      xrange += range(width+extra+5, width+extra*2-1, 5)
+      for i in range(2, 10):
+        xrange += range(width*i+extra*i+5, width*i+extra*(i+1)-1, 5)
+    for j in xrange:
+      possible.append([startx + j, posy + height + 5,
+                       startx + j, dposy + height + 5])
+    sel = self.select_line(routes, None, possible)
+    if not sel:
+      raise ValueError, "unable to route"
+
+    self.lines.append(sel)
+
+  def finish(self, routes, nodes):
+
+    if not self.lines:
+      return
+
+    posx, posy, width, height = self.src.myarea
+    dposx, dposy, dwidth, dheight = self.dst.myarea
+    extra = self.src.extra
+    sel = self.lines[0]
+    res = True
+
+    x = posx+(width/2)
+    y = posy+height
+    for tryit in range(3):
+      possible = []
+      fixup = sel[0] > posx and -1 or 1
+      r = range(tryit*extra, (tryit+1)*extra-5-1, 5)
+      if tryit == 2:
+        r = range(-height-extra+5, -height-5, 5)
+        r.reverse()
+      for i in range(tryit*extra, (tryit+1)*extra-5-1, 5):
+        possible.append([x+5+fixup, sel[1]+i, sel[0]-fixup, sel[1]+i])
+      sel1 = self.select_line(routes, nodes, possible)
+      if sel1:
+        sel1[0] -= fixup
+        sel1[2] += fixup
+        possible = []
+        for j in range(0, width/2-10, 5):
+          possible.append([sel1[0]+j, y, sel1[0]+j, sel1[1]])
+        sel2 = self.select_line(routes, nodes, possible)
+        if sel2:
+          sel1[0] = sel2[0]
+          self.lines[0][1] = sel1[1]
+          self.lines.append(sel1)
+          self.lines.append(sel2)
+          tryit = -1
+          break
+    if tryit >= 0:
+      self.lines.append([x+5, y, sel[0], sel[1], 1])
+      print "[1] displaced route 0x%x->0x%x %s %s" % (self.src.node.nid, self.dst.node.nid, repr(self.lines[-1]), repr(sel))
+      res = False
+
+    x = dposx
+    y = dposy+height
+    for tryit in range(3):
+      possible = []
+      fixup = sel[2] > posx and -1 or 1
+      r = range(tryit * extra, (tryit+1)*extra-5-1, 5)
+      if tryit == 2:
+        r = range(-height-extra+5, -height-5, 5)
+        r.reverse()
+      for i in r:
+        possible.append([x+(width/2-1)+fixup, sel[3]+i, sel[2]-fixup, sel[3]+i])
+      sel1 = self.select_line(routes, nodes, possible)
+      if sel1:
+        sel1[0] -= fixup + (width/2-1) - 5
+        sel1[2] += fixup
+        possible = []
+        for j in range(0, width/2-10, 5):
+          possible.append([sel1[0]+j, y, sel1[0]+j, sel1[1]])
+        sel2 = self.select_line(routes, nodes, possible)
+        if sel2:
+          sel1[0] = sel2[0]
+          self.lines[0][3] = sel1[3]
+          self.lines.append(sel1)
+          self.lines.append(sel2)
+          tryit = -1
+          break
+    if tryit >= 0:
+      self.lines.append([x+5, y, sel[2], sel[3], 1])
+      print "[2] displaced route 0x%x->0x%x %s %s" % (self.src.node.nid, self.dst.node.nid, repr(self.lines[-1]), repr(sel))
+      res = False
+      
+    return res
+
+  def has_x(self, x):
+    for line in self.lines:
+      if line[0] == x or line[2] == x:
+        return True
+    return False
+    
+  def compressx(self, first, size):
+    idx = 0
+    while idx < len(self.lines):
+      line = self.lines[idx]
+      if line[0] > first:
+        line[0] -= size
+        self.lines[idx] = line
+      if line[2] > first:
+        line[2] -= size
+        self.lines[idx] = line
+      idx += 1
+
+  def has_y(self, y):
+    for line in self.lines:
+      if line[1] == y or line[3] == y:
+        return True
+    return False
+    
+  def compressy(self, first, size):
+    idx = 0
+    while idx < len(self.lines):
+      line = self.lines[idx]
+      if line[1] > first:
+        line[1] -= size
+        self.lines[idx] = line
+      if line[3] > first:
+        line[3] -= size
+        self.lines[idx] = line
+      idx += 1
+
+  def in_area(self, x, y):
+    for line in self.lines:
+      x1, y1, x2, y2 = line
+      if x1 > x2 or y1 > y2:
+        x2, y2, x1, y1 = line
+      if x1 == x2 and abs(x1 - x) < 2:
+        if y1 <= y and y2 >= y:
+          return True
+      elif y1 == y2 and abs(y1 - y) < 2:
+        if x1 <= x and x2 >= x:
+          return True
+
+  def mouse_move(self, x, y, graph):
+    if self.in_area(x, y):
+      self.highlight = True
+      return True
+
+class CodecGraphLayout(gtk.Layout):
+
+  def __init__(self, adj1, adj2, codec, mytitle):
+    gtk.Layout.__init__(self, adj1, adj2)
+    self.set_events(0)
+    self.add_events(gtk.gdk.EXPOSURE_MASK | gtk.gdk.POINTER_MOTION_MASK |
+                    gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK)
+    self.expose_handler = self.connect("expose-event", self.expose)
+    self.click_handler = self.connect("button-press-event", self.button_click)
+    self.release_handler = self.connect("button-release-event", self.button_release)
+    self.motion_handler = self.connect("motion-notify-event", self.mouse_move)
+
+    self.popup_win = None
+    self.popup = None
+
+    self.codec = codec
+    self.mytitle = mytitle
+    self.graph = codec.graph(dump=False)
+    self.startnode = None
+    self.endnode = None
+    ok = False
+    for extra in [150, 200, 300]:
+      if self.build(extra):
+        ok = True
+        break
+      break
+    if not ok:
+      print "Not all routes are placed correctly!!!"
+
+  def __destroy(self, widget):
+    if self.popup_win:
+      self.popup_win.destroy()
+
+  def build(self, extra=50):
+    self.nodes = []
+    self.routes = []
+    maxconns = 0
+    for nid in self.codec.nodes:
+      node = self.codec.nodes[nid]
+      conns = max(self.codec.connections(nid, 0),
+                  self.codec.connections(nid, 1))
+      if conns > maxconns:
+        maxconns = conns
+    nodesize = max((maxconns * 5 + 10) * 2, 100)
+    if self.graph:
+      for y in range(len(self.graph)):
+        for x in range(len(self.graph[0])):
+          nid = self.graph[y][x]
+          if not nid is None:
+            node = self.codec.nodes[nid]
+            w = Node(self.codec, node, x, y, nodesize, extra)
+            self.nodes.append(w)
+    sx = len(self.graph[0])*(nodesize+extra)+extra
+    sy = len(self.graph)*(nodesize+extra)+extra
+    self.set_size(sx, sy)
+    for node in self.nodes:
+      if not node.node.connections:
+        continue
+      for conn in node.node.connections:
+        for node1 in self.nodes:
+          if node1.node.nid == conn:
+            r = Route(self.codec, node1, node, self.routes, self.nodes)
+            self.routes.append(r)
+            break
+    res = True
+    for route in self.routes:
+      if not route.finish(self.routes, self.nodes):
+        res = False
+        break
+    # final step - optimize drawings
+    while 1:
+      size = self.compressx(sx)
+      if not size:
+        break
+      sx -= size
+    while 1:
+      size = self.compressy(sy)
+      if not size:
+        break
+      sy -= size
+    self.set_size(sx, sy)
+    return res
+
+  def expose(self, widget, event):
+    if not self.flags() & gtk.REALIZED:
+      return
+
+    # background
+    cr = self.bin_window.cairo_create()
+    cr.set_source_rgb(1.0, 1.0, 1.0)
+    cr.rectangle(event.area.x, event.area.y,
+                 event.area.width, event.area.height)
+    cr.clip()
+    cr.paint()
+
+    # draw nodes
+    for node in self.nodes:
+      node.expose(cr, event, self)
+      
+    # draw routes
+    for route in self.routes:
+      route.expose(cr, event)
+
+  def compressx(self, sx):
+    first = None
+    for a in range(15, sx, 5):
+      found = False
+      for node in self.nodes:
+        if node.has_x(a):
+          found = True
+          break
+      if not found:
+        for route in self.routes:
+          if route.has_x(a):
+            found = True
+            break
+      if not found:
+        if first is None:
+          first = a
+        last = a
+      elif first is not None:
+        size = (last - first) + 5
+        for node in self.nodes:
+          node.compressx(first, size)
+        for route in self.routes:
+          route.compressx(first, size)
+        return size
+    return None
+
+  def compressy(self, sy):
+    first = None
+    for a in range(15, sy, 5):
+      found = False
+      for node in self.nodes:
+        if node.has_y(a):
+          found = True
+          break
+      if not found:
+        for route in self.routes:
+          if route.has_y(a):
+            found = True
+            break
+      if not found:
+        if first is None:
+          first = a
+        last = a
+      elif first is not None:
+        size = (last - first) + 5
+        for node in self.nodes:
+          node.compressy(first, size)
+        for route in self.routes:
+          route.compressy(first, size)
+        return size
+    return None
+
+  def find_node(self, event):
+    for node in self.nodes:
+      what = node.in_area(event.x, event.y)
+      if not what is None:
+        return (node, what)
+    return (None, None)
+    
+  def find_route(self, event):
+    for route in self.routes:
+      if route.in_area(event.x, event.y):
+        return route
+
+  def show_popup(self, event):
+    screen_width = gtk.gdk.screen_width()
+    screeen_height = gtk.gdk.screen_height()
+
+    if self.popup_win:
+      self.popup_win.destroy()
+    self.popup_win = gtk.Window(gtk.WINDOW_POPUP)
+    label = gtk.Label()
+    label.modify_font(get_fixed_font())
+    label.set_text(self.popup)
+    self.popup_win.add(label)
+    popup_width, popup_height = self.popup_win.get_size()
+
+    rootwin = self.get_screen().get_root_window()
+    x, y, mods = rootwin.get_pointer()
+
+    pos_x = x - popup_width/2
+    if pos_x < 0:
+      pos_x = 0
+    elif pos_x + popup_width > screen_width:
+      pos_x = screen_width - popup_width
+    
+    pos_y = y + 3
+    if pos_y + popup_height > screeen_height:
+      pos_y = event.y - 3 - popup_height                                                                                                                                                 
+
+    self.popup_win.move(int(pos_x), int(pos_y))
+    self.popup_win.show_all()
+
+  def mouse_move(self, widget, event):
+    oldpopup = self.popup
+    self.popup = None
+    for route in self.routes:
+      route.highlight = False
+    found = False
+    for node in self.nodes:
+      if node.mouse_move(event.x, event.y, self):
+        self.queue_draw()
+        found = True
+        break
+    if not found:
+      for route in self.routes:
+        if route.mouse_move(event.x, event.y, self):
+          self.queue_draw()
+          found = True
+          break
+    if self.popup:
+      if oldpopup != self.popup:
+        self.show_popup(event)
+    else:
+      if self.popup_win:
+        self.popup_win.destroy()
+
+  def mark_it(self, widget, node, what, enable):
+    if what == "start":
+      if enable:
+        if not self.startnode:
+          self.startnode = node
+          self.queue_draw()
+      else:
+        if self.startnode:
+          self.startnode = None
+          self.queue_draw()
+    elif what == "end":
+      if enable:
+        if not self.endnode:
+          self.endnode = node
+          self.queue_draw()
+      else:
+        if self.endnode:
+          self.endnode = None
+          self.queue_draw()
+
+  def mark_route(self, widget, route, what, enable):
+    if what == "mark":
+      if enable:
+        if not route.marked:
+          route.marked = enable
+          self.queue_draw()
+      else:
+        if route.marked:
+          route.marked = False
+          self.queue_draw()
+
+  def node_win_destroy(self, widget, node):
+    node.win = None
+
+  def open_node(self, widget, node):
+    if self.popup_win:
+      self.popup_win.destroy()
+    if not node.win:
+      win = gtk.Window()
+      win.set_default_size(500, 600)
+      gui = NodeGui(node=node.node)
+      win.set_title(self.mytitle + ' ' + gui.mytitle)
+      win.add(gui)
+      win.connect("destroy", self.node_win_destroy, node)
+      win.show_all()
+      node.win = win
+    else:
+      node.win.present()
+    
+  def button_click(self, widget, event):
+    if event.button != 3:
+      return False
+    node, what = self.find_node(event)
+    m = None
+    if node:
+      m = gtk.Menu()
+      i = gtk.MenuItem("Open")
+      i.connect("activate", self.open_node, node)
+      i.show()
+      m.append(i)
+      if what in ["src", "dst"]:
+        routes1 = node.src_routes
+        text = "Mark Route From"
+        if what == "dst":
+          routes1 = node.dst_routes
+          text = "Mark Route To"
+        routes = []
+        for route in routes1:
+          if not route.marked:
+            routes.append(route)
+        if routes:
+          i = None
+          if len(routes) == 1:
+            i = gtk.MenuItem(text + ' ' + routes[0].longdesc())
+            i.connect("activate", self.mark_route, routes[0], "mark", True)
+          else:
+            menu = gtk.Menu()
+            for route in routes:
+              i = gtk.MenuItem(route.longdesc())
+              i.connect("activate", self.mark_route, route, "mark", True)
+              i.show()
+              menu.append(i)
+            i = gtk.MenuItem(text)
+            i.set_submenu(menu)
+          if i:
+            i.show()
+            m.append(i)
+      if what in ["src", "dst"]:
+        routes1 = node.src_routes
+        text = "Unmark Route From"
+        if what == "dst":
+          routes1 = node.dst_routes
+          text = "Unmark Route To"
+        routes = []
+        for route in routes1:
+          if route.marked:
+            routes.append(route)
+        if routes:
+          i = None
+          if len(routes) == 1:
+            i = gtk.MenuItem(text + ' ' + routes[0].longdesc())
+            i.connect("activate", self.mark_route, routes[0], "mark", False)
+          else:
+            menu = gtk.Menu()
+            for route in routes:
+              i = gtk.MenuItem(route.longdesc())
+              i.connect("activate", self.mark_route, route, "mark", False)
+              i.show()
+              menu.append(i)
+            i = gtk.MenuItem(text)
+            i.set_submenu(menu)
+          if i:
+            i.show()
+            m.append(i)
+      if not self.startnode:
+        i = gtk.MenuItem("Mark as start point")
+        i.connect("activate", self.mark_it, node, "start", True)
+      else:
+        i = gtk.MenuItem("Clear start point")
+        i.connect("activate", self.mark_it, None, "start", False)
+      i.show()
+      m.append(i)
+      if not self.endnode:
+        i = gtk.MenuItem("Mark as finish point")
+        i.connect("activate", self.mark_it, node, "end", True)
+      else:
+        i = gtk.MenuItem("Clear finish point")
+        i.connect("activate", self.mark_it, None, "end", False)
+      i.show()
+      m.append(i)
+    else:
+      route = self.find_route(event)
+      if route:
+        m = gtk.Menu()
+        if not route.marked:
+          i = gtk.MenuItem("Mark selected route %s" % route.shortdesc())
+          i.connect("activate", self.mark_route, route, "mark", True)
+        else:
+          i = gtk.MenuItem("Clear selected route %s" % route.shortdesc())
+          i.connect("activate", self.mark_route, route, "mark", False)
+        i.show()
+        m.append(i)
+    if m:
+      m.popup(None, None, None, event.button, event.time, None)
+    return False 
+    
+  def button_release(self, widget, event):
+    
+    pass
+
+gobject.type_register(CodecGraphLayout)
+
+class CodecGraph(gtk.Window):
+
+  def __init__(self, codec):
+    gtk.Window.__init__(self)
+    self.codec = codec
+    self.connect('destroy', self.__destroy)
+    self.set_default_size(800, 600)
+    self.set_title(self.__class__.__name__ + ' ' + self.codec.name)
+    self.set_border_width(0)
+
+    table = gtk.Table(2, 2, False)
+    self.add(table)
+
+    self.layout = CodecGraphLayout(None, None, codec, self.get_title())
+    table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
+                 gtk.FILL|gtk.EXPAND, 0, 0)
+    vScrollbar = gtk.VScrollbar(None)
+    table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,
+                 gtk.FILL|gtk.SHRINK, 0, 0)
+    hScrollbar = gtk.HScrollbar(None)
+    table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,
+                 gtk.FILL|gtk.SHRINK, 0, 0)    
+    vAdjust = self.layout.get_vadjustment()
+    vScrollbar.set_adjustment(vAdjust)
+    hAdjust = self.layout.get_hadjustment()
+    hScrollbar.set_adjustment(hAdjust)
+    self.show_all()
+    GRAPH_WINDOWS[codec] = self
+
+  def __destroy(self, widget):
+    del GRAPH_WINDOWS[self.codec]
+
+def create_graph(codec):
+  if codec in GRAPH_WINDOWS:
+    GRAPH_WINDOWS[codec].present()
+  else:
+    CodecGraph(codec)
diff --git a/hda-analyzer/hda_guilib.py b/hda-analyzer/hda_guilib.py
new file mode 100644 (file)
index 0000000..532da33
--- /dev/null
@@ -0,0 +1,577 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+
+import gobject
+import gtk
+import pango
+
+from hda_codec import HDACodec, HDA_card_list, \
+                      EAPDBTL_BITS, PIN_WIDGET_CONTROL_BITS, \
+                      PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \
+                      HDA_INPUT, HDA_OUTPUT
+
+def get_fixed_font():
+  return pango.FontDescription("Misc Fixed,Courier Bold 9")
+
+class NodeGui(gtk.ScrolledWindow):
+
+  def __init__(self, card=None, codec=None, node=None, doframe=False):
+    gtk.ScrolledWindow.__init__(self)
+    self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+    self.set_shadow_type(gtk.SHADOW_IN)    
+    if card and not codec and not node:
+      self.__build_card(card, doframe)
+    elif codec and not card and not node:
+      self.__build_codec(codec, doframe)
+    elif node and not card and not codec:
+      self.__build_node(node, doframe)
+    self.show_all()
+
+  def __create_text(self, callback):
+    scrolled_window = gtk.ScrolledWindow()
+    scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+    scrolled_window.set_shadow_type(gtk.SHADOW_IN)
+    
+    text_view = gtk.TextView()
+    fontName = get_fixed_font()
+    text_view.modify_font(fontName)
+    scrolled_window.add(text_view)
+    
+    buffer = gtk.TextBuffer(None)
+    text_view.set_buffer(buffer)
+    text_view.set_editable(False)
+    text_view.set_cursor_visible(False)
+    text_view.connect("visibility-notify-event", callback)
+    
+    text_view.set_wrap_mode(True)
+    
+    return scrolled_window, buffer
+
+  def __new_text_view(self, text=None):
+    text_view = gtk.TextView()
+    text_view.set_border_width(4)
+    fontName = get_fixed_font()
+    text_view.modify_font(fontName)
+    if not text is None:
+      buffer = gtk.TextBuffer(None)
+      iter = buffer.get_iter_at_offset(0)
+      if text[-1] == '\n':
+        text = text[:-1]
+      buffer.insert(iter, text)
+      text_view.set_buffer(buffer)
+      text_view.set_editable(False)
+      text_view.set_cursor_visible(False)
+    return text_view
+
+  def __build_node_caps(self, node):
+    frame = gtk.Frame('Node Caps')
+    frame.set_border_width(4)
+    if len(node.wcaps_list) == 0:
+      return frame
+    str = ''
+    for i in node.wcaps_list:
+      str += node.wcap_name(i) + '\n'
+    frame.add(self.__new_text_view(text=str))
+    return frame
+
+  def __node_connection_toggled(self, widget, row, data):
+    model, node = data
+    if not model[row][0]:
+      node.set_active_connection(int(row))
+    for r in model:
+      r[0] = False
+    idx = 0
+    for r in model:
+      r[0] = node.active_connection == idx
+      idx += 1
+
+  def __build_connection_list(self, node):
+    frame = gtk.Frame('Connection List')
+    frame.set_border_width(4)
+    sw = gtk.ScrolledWindow()
+    #sw.set_shadow_type(gtk.SHADOW_ETCHED_IN)
+    sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+    frame.add(sw)
+    if node.conn_list and node.connections:
+      model = gtk.ListStore(
+        gobject.TYPE_BOOLEAN,
+        gobject.TYPE_STRING
+      )
+      idx = 0
+      for i in node.connections:
+        iter = model.append()
+        node1 = node.codec.get_node(node.connections[idx])
+        model.set(iter, 0, node.active_connection == idx,
+                        1, node1.name())
+        idx += 1
+      treeview = gtk.TreeView(model)
+      treeview.set_rules_hint(True)
+      treeview.get_selection().set_mode(gtk.SELECTION_SINGLE)
+      treeview.set_size_request(300, 30 + len(node.connections) * 25)
+      renderer = gtk.CellRendererToggle()
+      renderer.set_radio(True)
+      if node.active_connection != None:
+        renderer.connect("toggled", self.__node_connection_toggled, (model, node))
+      column = gtk.TreeViewColumn("Active", renderer, active=0)
+      treeview.append_column(column)
+      renderer = gtk.CellRendererText()
+      column = gtk.TreeViewColumn("Destination Node", renderer, text=1, editable=False)
+      treeview.append_column(column)
+      sw.add(treeview)
+    return frame
+
+  def __amp_mute_toggled(self, button, data):
+    caps, vals, idx = data
+    val = button.get_active()
+    vals.set_mute(idx, val)
+    button.set_active(vals.vals[idx] & 0x80)
+
+  def __amp_value_changed(self, adj, data):
+    caps, vals, idx = data
+    val = int(adj.get_value())
+    vals.set_value(idx, val)
+    adj.set_value(vals.vals[idx] & 0x7f)
+
+  def __build_amps(self, node):
+
+    def build_caps(title, caps, vals):
+      if caps and caps.ofs is None:
+        caps = caps.dir == HDA_INPUT and \
+                    node.codec.amp_caps_in or node.codec.amp_caps_out
+        title += ' (Global)'
+      frame = gtk.Frame(title)
+      frame.set_border_width(4)
+      vbox = gtk.VBox(False, 0)
+      if caps:
+        str =  'Offset:          %d\n' % caps.ofs
+        str += 'Number of steps: %d\n' % caps.nsteps
+        str += 'Step size:       %d\n' % caps.stepsize
+        str += 'Mute:            %s\n' % (caps.mute and "True" or "False")
+        vbox.pack_start(self.__new_text_view(text=str), True, True, 0)
+        idx = 0
+        frame1 = None
+        vbox1 = None
+        for val in vals.vals:
+          if vals.stereo and idx & 1 == 0:
+            frame1 = gtk.Frame()
+            vbox.pack_start(frame1, False, False)
+            vbox1 = gtk.VBox(False, 0)
+            frame1.add(vbox1)
+          hbox = gtk.HBox(False, 0)
+          label = gtk.Label('Val[%d]' % idx)
+          hbox.pack_start(label, False, False)
+          if caps.mute:
+            checkbutton = gtk.CheckButton('Mute')
+            checkbutton.set_active(val & 0x80 and True or False)
+            checkbutton.connect("toggled", self.__amp_mute_toggled, (caps, vals, idx))
+            hbox.pack_start(checkbutton, False, False)
+          if caps.stepsize > 0:
+            adj = gtk.Adjustment((val & 0x7f) % (caps.nsteps+1), 0.0, caps.nsteps+1, 1.0, 1.0, 1.0)
+            scale = gtk.HScale(adj)
+            scale.set_digits(0)
+            scale.set_value_pos(gtk.POS_RIGHT)
+            adj.connect("value_changed", self.__amp_value_changed, (caps, vals, idx))
+            hbox.pack_start(scale, True, True)
+          if vbox1:
+            vbox1.pack_start(hbox, False, False)
+          else:
+            vbox.pack_start(hbox, False, False)
+          idx += 1
+      frame.add(vbox)
+      return frame
+
+    hbox = gtk.HBox(False, 0)
+    c = build_caps('Input Amplifier',
+                    node.in_amp and node.amp_caps_in or None,
+                    node.in_amp and node.amp_vals_in or None)
+    hbox.pack_start(c)
+    c = build_caps('Output Amplifier',
+                    node.out_amp and node.amp_caps_out or None,
+                    node.out_amp and node.amp_vals_out or None)
+    hbox.pack_start(c)
+
+    return hbox
+
+  def __pincap_eapdbtl_toggled(self, button, data):
+    node, name = data
+    node.eapdbtl_set_value(name, button.get_active())
+    button.set_active(name in node.pincap_eapdbtl)
+
+  def __pinctls_toggled(self, button, data):
+    node, name = data
+    node.pin_widget_control_set_value(name, button.get_active())
+    button.set_active(name in node.pinctl)
+
+  def __pinctls_vref_change(self, combobox, node):
+    index = combobox.get_active()
+    idx1 = 0
+    for name in PIN_WIDGET_CONTROL_VREF:
+      if not name: continue
+      if idx1 == index:
+        node.pin_widget_control_set_value('vref', name)
+        break
+      idx1 += 1
+    idx = idx1 = 0
+    for name in PIN_WIDGET_CONTROL_VREF:
+      if name == node.pinctl_vref:
+        combobox.set_active(idx1)
+        break
+      if name != None:
+        idx1 += 1
+
+  def __build_pin(self, node):
+    hbox = gtk.HBox(False, 0)
+
+    if node.pincap or node.pincap_vref or node.pincap_eapdbtl:
+      vbox = gtk.VBox(False, 0)
+      if node.pincap or node.pincap_vref:
+        frame = gtk.Frame('PIN Caps')
+        frame.set_border_width(4)
+        str = ''
+        for i in node.pincap:
+          str += node.pincap_name(i) + '\n'
+        for i in node.pincap_vref:
+          str += 'VREF_%s\n' % i
+        frame.add(self.__new_text_view(text=str))
+        vbox.pack_start(frame)
+      if 'EAPD' in node.pincap:
+        frame = gtk.Frame('EAPD')
+        frame.set_border_width(4)
+        vbox1 = gtk.VBox(False, 0)
+        for name in EAPDBTL_BITS:
+          checkbutton = gtk.CheckButton(name)
+          checkbutton.set_active(node.pincap_eapdbtls & (1 << EAPDBTL_BITS[name]))
+          checkbutton.connect("toggled", self.__pincap_eapdbtl_toggled, (node, name))
+          vbox1.pack_start(checkbutton, False, False)
+        frame.add(vbox1)
+        vbox.pack_start(frame, False, False)
+      hbox.pack_start(vbox)
+
+    vbox = gtk.VBox(False, 0)
+
+    frame = gtk.Frame('Config Default')
+    frame.set_border_width(4)
+    str =  'Jack connection: %s\n' % node.jack_conn_name
+    str += 'Jack type:       %s\n' % node.jack_type_name
+    str += 'Jack location:   %s\n' % node.jack_location_name
+    str += 'Jack location2:  %s\n' % node.jack_location2_name
+    str += 'Jack connector:  %s\n' % node.jack_connector_name
+    str += 'Jack color:      %s\n' % node.jack_color_name
+    if 'NO_PRESENCE' in node.defcfg_misc:
+      str += 'No presence\n'
+    frame.add(self.__new_text_view(text=str))
+    vbox.pack_start(frame)
+    
+    frame = gtk.Frame('Widget Control')
+    frame.set_border_width(4)
+    vbox1 = gtk.VBox(False, 0)
+    for name in PIN_WIDGET_CONTROL_BITS:
+      checkbutton = gtk.CheckButton(name)
+      checkbutton.set_active(node.pinctls & (1 << PIN_WIDGET_CONTROL_BITS[name]))
+      checkbutton.connect("toggled", self.__pinctls_toggled, (node, name))
+      vbox1.pack_start(checkbutton, False, False)
+    if node.pincap_vref:
+      combobox = gtk.combo_box_new_text()
+      idx = idx1 = active = 0
+      for name in PIN_WIDGET_CONTROL_VREF:
+        if name == node.pinctl_vref: active = idx1
+        if name:
+          combobox.append_text(name)
+          idx1 += 1
+        idx += 1
+      combobox.set_active(active)
+      combobox.connect("changed", self.__pinctls_vref_change, node)
+      hbox1 = gtk.HBox(False, 0)
+      label = gtk.Label('VREF')
+      hbox1.pack_start(label, False, False)
+      hbox1.pack_start(combobox)
+      vbox1.pack_start(hbox1, False, False)
+    frame.add(vbox1)
+    vbox.pack_start(frame, False, False)
+
+    hbox.pack_start(vbox)
+    return hbox
+
+  def __build_mix(self, node):
+    hbox = gtk.HBox(False, 0)
+    return hbox
+
+  def __sdi_select_changed(self, adj, node):
+    val = int(adj.get_value())
+    node.sdi_select_set_value(val)
+    adj.set_value(node.sdi_select)
+
+  def __dig1_toggled(self, button, data):
+    node, name = data
+    val = button.get_active()
+    node.dig1_set_value(name, val)
+    button.set_active(name in node.dig1)
+
+  def __dig1_category_activate(self, entry, node):
+    val = entry.get_text()
+    if val.lower().startswith('0x'):
+      val = int(val[2:], 16)
+    else:
+      try:
+        val = int(val)
+      except:
+        print "Unknown category value '%s'" % val
+        return
+    node.dig1_set_value('category', val)
+    entry.set_text("0x%02x" % node.dig1_category)
+
+  def __build_aud(self, node):
+    vbox = gtk.VBox(False, 0)
+
+    frame = gtk.Frame('Converter')
+    frame.set_border_width(4)
+    str = 'Audio Stream:\t%s\n' % node.aud_stream
+    str += 'Audio Channel:\t%s\n' % node.aud_channel
+    if node.format_ovrd:
+      str += 'Rates:\t\t%s\n' % node.pcm_rates[:6]
+      if len(node.pcm_rates) > 6:
+        str += '\t\t\t\t%s\n' % node.pcm_rates[6:]
+      str += 'Bits:\t\t%s\n' % node.pcm_bits
+      str += 'Streams:\t%s\n' % node.pcm_streams
+    else:
+      str += 'Global Rates:\t%s\n' % node.codec.pcm_rates[:6]
+      if len(node.codec.pcm_rates) > 6:
+        str += '\t\t%s\n' % node.codec.pcm_rates[6:]
+      str += 'Global Bits:\t%s\n' % node.codec.pcm_bits
+      str += 'Global Streams:\t%s\n' % node.codec.pcm_streams
+    frame.add(self.__new_text_view(text=str))
+    vbox.pack_start(frame)
+
+    if not node.sdi_select is None:
+      hbox1 = gtk.HBox(False, 0)
+      frame = gtk.Frame('SDI Select')
+      adj = gtk.Adjustment(node.sdi_select, 0.0, 16.0, 1.0, 1.0, 1.0)
+      scale = gtk.HScale(adj)
+      scale.set_digits(0)
+      scale.set_value_pos(gtk.POS_LEFT)
+      scale.set_size_request(200, 16)
+      adj.connect("value_changed", self.__sdi_select_changed, node)
+      frame.add(scale)
+      hbox1.pack_start(frame, False, False)
+      vbox.pack_start(hbox1, False, False)
+
+    if node.digital:
+      hbox1 = gtk.HBox(False, 0)
+      frame = gtk.Frame('Digital Converter')
+      vbox1 = gtk.VBox(False, 0)
+      for name in DIG1_BITS:
+        checkbutton = gtk.CheckButton(name)
+        checkbutton.set_active(node.digi1 & (1 << DIG1_BITS[name]))
+        checkbutton.connect("toggled", self.__dig1_toggled, (node, name))
+        vbox1.pack_start(checkbutton, False, False)
+      frame.add(vbox1)
+      hbox1.pack_start(frame)
+      frame = gtk.Frame('Digital Converter Category')
+      entry = gtk.Entry()
+      entry.set_text("0x%x" % node.dig1_category)
+      entry.set_width_chars(4)
+      entry.connect("activate", self.__dig1_category_activate, node)
+      frame.add(entry)
+      hbox1.pack_start(frame)
+      vbox.pack_start(hbox1, False, False)
+
+    return vbox
+
+  def __build_device(self, device):
+    vbox = gtk.VBox(False, 0)
+    frame = gtk.Frame('Device')
+    frame.set_border_width(4)
+    hbox = gtk.HBox(False, 0)
+    s = 'name=' + str(device.name) + ', type=' + \
+        str(device.type) + ', device=' + str(device.device)
+    label = gtk.Label(s)
+    hbox.pack_start(label, False, False)
+    frame.add(hbox)
+    vbox.pack_start(frame)
+    return vbox
+
+  def __build_controls(self, ctrls):
+    vbox = gtk.VBox(False, 0)
+    frame = gtk.Frame('Controls')
+    frame.set_border_width(4)
+    vbox1 = gtk.VBox(False, 0)
+    for ctrl in ctrls:
+      hbox1 = gtk.HBox(False, 0)
+      vbox1.pack_start(hbox1, False, False)
+      s = 'name=' + str(ctrl.name) + ', index=' + str(ctrl.index) + \
+          ', device=' + str(ctrl.device)
+      label = gtk.Label(s)
+      hbox1.pack_start(label, False, False)
+      if ctrl.amp_chs:
+        hbox1 = gtk.HBox(False, 0)
+        vbox1.pack_start(hbox1, False, False)
+        s = '  chs=' + str(ctrl.amp_chs) + ', dir=' + str(ctrl.amp_dir) + \
+            ', idx=' + str(ctrl.amp_idx) + ', ofs=' + str(ctrl.amp_ofs)
+        label = gtk.Label(s)
+        hbox1.pack_start(label, False, False)
+    frame.add(vbox1)
+    vbox.pack_start(frame)
+    return vbox
+
+  def __build_proc(self, node):
+    frame = gtk.Frame('Processing Caps')
+    frame.set_border_width(4)
+    str = 'benign=%i\nnumcoef=%i\n' % (node.proc_benign, node.proc_numcoef)
+    frame.add(self.__new_text_view(text=str))
+    return frame
+
+  def __build_node(self, node, doframe=False):
+    self.mytitle = node.name()
+    if doframe:
+      mframe = gtk.Frame(self.mytitle)
+      mframe.set_border_width(4)
+    else:
+      mframe = gtk.Table()
+
+    vbox = gtk.VBox(False, 0)
+    dev = node.get_device()
+    if not dev is None:
+      vbox.pack_start(self.__build_device(dev), False, False)
+    ctrls = node.get_controls()
+    if ctrls:
+      vbox.pack_start(self.__build_controls(ctrls), False, False)
+    hbox = gtk.HBox(False, 0)
+    hbox.pack_start(self.__build_node_caps(node))
+    hbox.pack_start(self.__build_connection_list(node))
+    vbox.pack_start(hbox, False, False)
+    if node.in_amp or node.out_amp:
+      vbox.pack_start(self.__build_amps(node), False, False)
+    if node.wtype_id == 'PIN':
+      vbox.pack_start(self.__build_pin(node), False, False)
+    elif node.wtype_id in ['AUD_IN', 'AUD_OUT']:
+      vbox.pack_start(self.__build_aud(node), False, False)
+    else:
+      if not node.wtype_id in ['AUD_MIX', 'BEEP', 'AUD_SEL']:
+        print 'Node type %s has no GUI support' % node.wtype_id
+    if node.proc_wid:
+      vbox.pack_start(self.__build_proc(node), False, False)
+
+    mframe.add(vbox)
+    self.add_with_viewport(mframe)
+
+  def __build_codec_info(self, codec):
+    vbox = gtk.VBox(False, 0)
+
+    frame = gtk.Frame('Codec Identification')
+    frame.set_border_width(4)
+    str = 'Audio Fcn Group: %s\n' % (codec.afg and "0x%02x" % codec.afg or "N/A")
+    str += 'Modem Fcn Group: %s\n' % (codec.mfg and "0x%02x" % codec.mfg or "N/A")
+    str += 'Vendor ID:\t 0x%08x\n' % codec.vendor_id
+    str += 'Subsystem ID:\t 0x%08x\n' % codec.subsystem_id
+    str += 'Revision ID:\t 0x%08x\n' % codec.revision_id
+    frame.add(self.__new_text_view(text=str))
+    vbox.pack_start(frame, False, False)
+
+    frame = gtk.Frame('PCM Global Capabilities')
+    frame.set_border_width(4)
+    str = 'Rates:\t\t %s\n' % codec.pcm_rates[:6]
+    if len(codec.pcm_rates) > 6:
+      str += '\t\t %s\n' % codec.pcm_rates[6:]
+    str += 'Bits:\t\t %s\n' % codec.pcm_bits
+    str += 'Streams:\t %s\n' % codec.pcm_streams
+    frame.add(self.__new_text_view(text=str))
+    vbox.pack_start(frame, False, False)
+
+    return vbox
+    
+  def __build_codec_amps(self, codec):
+
+    def build_caps(title, caps):
+      frame = gtk.Frame(title)
+      frame.set_border_width(4)
+      if caps and caps.ofs != None:
+        str = 'Offset:\t\t %d\n' % caps.ofs
+        str += 'Number of steps: %d\n' % caps.nsteps
+        str += 'Step size:\t %d\n' % caps.stepsize
+        str += 'Mute:\t\t %s\n' % (caps.mute and "True" or "False")
+        frame.add(self.__new_text_view(text=str))
+      return frame
+
+    hbox = gtk.HBox(False, 0)
+    c = build_caps('Global Input Amplifier Caps', codec.amp_caps_in)
+    hbox.pack_start(c)
+    c = build_caps('Global Output Amplifier Caps', codec.amp_caps_out)
+    hbox.pack_start(c)
+
+    return hbox
+
+  def __gpio_toggled(self, button, (codec, id, idx)):
+    codec.gpio.set(id, idx, button.get_active())
+    button.set_active(codec.gpio.test(id, idx))
+
+  def __build_codec_gpio(self, codec):
+    frame = gtk.Frame('GPIO')
+    frame.set_border_width(4)
+    hbox = gtk.HBox(False, 0)
+    str =  'IO Count:    %d\n' % codec.gpio_max
+    str += 'O Count:     %d\n' % codec.gpio_o
+    str += 'I Count:     %d\n' % codec.gpio_i
+    str += 'Unsolicited: %s\n' % (codec.gpio_unsol and "True" or "False")
+    str += 'Wake:        %s\n' % (codec.gpio_wake and "True" or "False")
+    hbox.pack_start(self.__new_text_view(text=str), False, False)
+    frame.add(hbox)
+    for id in GPIO_IDS:
+      id1 = id == 'direction' and 'out-dir' or id
+      frame1 = gtk.Frame(id1)
+      frame1.set_border_width(4)
+      vbox1 = gtk.VBox(False, 0)
+      for i in range(codec.gpio_max):
+        checkbutton = gtk.CheckButton('[%d]' % i)
+        checkbutton.set_active(codec.gpio.test(id, i))
+        checkbutton.connect("toggled", self.__gpio_toggled, (codec, id, i))
+        vbox1.pack_start(checkbutton, False, False)
+      frame1.add(vbox1)
+      hbox.pack_start(frame1, False, False)
+    return frame
+
+  def __build_codec(self, codec, doframe=False):
+    self.mytitle = codec.name
+    if doframe:
+      mframe = gtk.Frame(self.mytitle)
+      mframe.set_border_width(4)
+    else:
+      mframe = gtk.Table()
+
+    vbox = gtk.VBox(False, 0)
+    vbox.pack_start(self.__build_codec_info(codec), False, False)
+    vbox.pack_start(self.__build_codec_amps(codec), False, False)
+    vbox.pack_start(self.__build_codec_gpio(codec), False, False)
+    mframe.add(vbox)
+    self.add_with_viewport(mframe)
+
+  def __build_card_info(self, card):
+    str =  'Card:       %s\n' % card.card
+    str += 'Id:         %s\n' % card.id
+    str += 'Driver:     %s\n' % card.driver
+    str += 'Name:       %s\n' % card.name
+    str += 'LongName:   %s\n' % card.longname
+    return self.__new_text_view(text=str)
+
+  def __build_card(self, card, doframe=False):
+    self.mytitle = card.name
+    if doframe:
+      mframe = gtk.Frame(self.mytitle)
+      mframe.set_border_width(4)
+    else:
+      mframe = gtk.Table()
+
+    vbox = gtk.VBox(False, 0)
+    vbox.pack_start(self.__build_card_info(card), False, False)
+    mframe.add(vbox)
+    self.add_with_viewport(mframe)
index 0fd48e4bd0d77f15564a156ec4d9a45397b7cbe9..59def3d87919674194256f82f9261d70a229d5b0 100755 (executable)
@@ -1,7 +1,8 @@
 #!/usr/bin/env python
 
 URL="http://git.alsa-project.org/?p=alsa.git;a=blob_plain;f=hda-analyzer/"
-FILES=["hda_analyzer.py", "hda_codec.py", "hda_proc.py"]
+FILES=["hda_analyzer.py", "hda_guilib.py", "hda_codec.py", "hda_proc.py",
+       "hda_graph.py"]
 
 try:
   import gobject