]> git.alsa-project.org Git - alsa.git/commitdiff
hda-analyzer: more work on the graph window
authorJaroslav Kysela <perex@perex.cz>
Sat, 10 Jul 2010 12:56:03 +0000 (14:56 +0200)
committerJaroslav Kysela <perex@perex.cz>
Sat, 10 Jul 2010 12:56:03 +0000 (14:56 +0200)
- added hda-change signals
- added statusbar to the graph window for route information
- changed handling of the popup window for node information
- use more colors and widths to identify the route states
- added progress bar when the graph routes are rendered

Signed-off-by: Jaroslav Kysela <perex@perex.cz>
hda-analyzer/hda_analyzer.py
hda-analyzer/hda_codec.py
hda-analyzer/hda_graph.py
hda-analyzer/hda_guilib.py

index 284e82cec5f1811be8a585aa08f44c75e07f1c2e..ecfbb7e7cf8b70749ced77588fd588c7e57774d1 100755 (executable)
@@ -33,8 +33,6 @@ import gobject
 import gtk
 import pango
 
-DIFF_FILE = "/tmp/hda-analyze.diff"
-
 from hda_codec import HDACodec, HDA_card_list, \
                       EAPDBTL_BITS, PIN_WIDGET_CONTROL_BITS, \
                       PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \
@@ -43,9 +41,6 @@ from hda_proc import DecodeProcFile, DecodeAlsaInfoFile, HDACodecProc
 from hda_guilib import *
 from hda_graph import create_graph
 
-CODEC_TREE = {}
-DIFF_TREE = {}
-
 def gethttpfile(url, size=1024*1024):
   from urllib import splithost
   from httplib import HTTP
@@ -127,28 +122,6 @@ def read_nodes(proc_files):
       cnt += 1
   return cnt    
 
-def do_diff1(codec, diff1):
-  from difflib import unified_diff
-  diff = unified_diff(diff1.split('\n'), codec.dump().split('\n'), n=8, lineterm='')
-  diff = '\n'.join(list(diff))
-  if len(diff) > 0:
-    diff = 'Diff for codec %i/%i (%s):\n' % (codec.card, codec.device, codec.name) + diff
-  return diff
-
-def do_diff():
-  diff = ''
-  hw = 0
-  for card in CODEC_TREE:
-    for codec in CODEC_TREE[card]:
-      c = CODEC_TREE[card][codec]
-      if c.hwaccess:
-        hw += 1
-      diff += do_diff1(c, DIFF_TREE[card][codec])
-  if len(diff) > 0:
-    open(DIFF_FILE, "w+").write(diff + '\n')
-    print "Diff was stored to: %s" % DIFF_FILE
-  return (diff and hw > 0) and True or False
-
 (
     TITLE_COLUMN,
     CARD_COLUMN,
@@ -216,21 +189,6 @@ class HDAAnalyzer(gtk.Window):
     TRACKER.add(self)
 
   def __destroy(self, widget):
-    if do_diff():      
-      dialog = gtk.MessageDialog(self,
-                      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                      gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
-                      "HDA-Analyzer: Would you like to revert\n"
-                      "settings for all HDA codecs?")
-      response = dialog.run()
-      dialog.destroy()
-    
-      if response == gtk.RESPONSE_YES:
-        for card in CODEC_TREE:
-          for codec in CODEC_TREE[card]:
-            CODEC_TREE[card][codec].revert()
-        print "Settings for all codecs were reverted..."
-    
     TRACKER.close(self)
 
   def simple_dialog(self, type, msg):
@@ -361,8 +319,9 @@ mailing list, too.
     else:
       n = codec.get_node(node)
 
-    for child in self.node_window.get_children():
+    for child in self.node_window.get_children()[:]:
       self.node_window.remove(child)
+      child.destroy()
 
     if not n:
       if not codec:
@@ -516,7 +475,8 @@ def main(argv):
       for card in CODEC_TREE:
         for codec in CODEC_TREE:
           create_graph(CODEC_TREE[card][codec])
-    HDAAnalyzer()
+    else:
+      HDAAnalyzer()
     gtk.main()
   return 1
 
index 8ff9267a2a2bfaee73b200cf770cce644202f3c3..8c7b14d2f120828c1308c0ba8d5ce3d87174389b 100644 (file)
@@ -239,28 +239,77 @@ class HDAAmpCaps:
     self.codec = codec
     self.nid = nid
     self.dir = dir
+    self.cloned = False
     self.reread()
     
   def reread(self):
     caps = self.codec.param_read(self.nid,
           PARAMS[self.dir == HDA_OUTPUT and 'AMP_OUT_CAP' or 'AMP_IN_CAP'])
     if caps == ~0 or caps == 0:
-      self.ofs = None
-      self.nsteps = None
-      self.stepsize = None
-      self.mute = None
+      if self.dir == HDA_INPUT:
+        ccaps = self.codec.amp_caps_in
+      else:
+        ccaps = self.codec.amp_caps_out
+      if ccaps:
+        ccaps.clone(self)
+      else:
+        self.ofs = self.nsteps = self.stepsize = self.mute = None
     else:
       self.ofs = caps & 0x7f
       self.nsteps = (caps >> 8) & 0x7f
       self.stepsize = (caps >> 16) & 0x7f
       self.mute = (caps >> 31) & 1 and True or False
 
+  def clone(self, ampcaps):
+    ampcaps.ofs = self.ofs
+    ampcaps.nsteps = self.steps
+    ampcaps.stepsize = self.stepsize
+    ampcaps.mute = self.mute
+    ampcaps.cloned = True
+
+  def get_val_db(self, val):
+    if self.ofs is None:
+      return None
+    if val & 0x80:
+      return -999999
+    range = (self.stepsize + 1) * 25
+    off = -self.ofs * range
+    if val >= self.nsteps:
+      db = off + self.nsteps * range
+      if val != 0 or self.nsteps != 0:
+        print "val > nsteps? for nid 0x%02x" % self.nid, val, self.nsteps
+    else:
+      db = off + val * range
+    return db
+
+  def get_val_perc(self, val):
+    if self.ofs is None:
+      return None
+    if self.nsteps == 0:
+      return 0
+    return (val * 100) / self.nsteps
+
+  def get_val_str(self, val):
+    if self.ofs is None:
+      return "0x%02x" % val
+    else:
+      db = self.get_val_db(val & 0x7f)
+      res = val & 0x80 and "{mute-" or "{"
+      res += "0x%02x" % (val & 0x7f)
+      res += ":%02i.%idB" % (db / 100, db % 100)
+      res += ":%i%%}" % (self.get_val_perc(val & 0x7f))
+      return res
+
 class HDAAmpVal:
 
-  def __init__(self, codec, node, dir):
+  def __init__(self, codec, node, dir, caps):
     self.codec = codec
     self.node = node
     self.dir = dir
+    if caps.ofs == None:
+      self.caps = dir == HDA_INPUT and codec.amp_caps_in or codec.amp_caps_out
+    else:
+      self.caps = caps
     self.nid = node.nid
     self.stereo = node.stereo
     self.indices = 1
@@ -283,15 +332,20 @@ class HDAAmpVal:
   def set_mute(self, idx, mute):
     val = self.vals[idx]
     if mute:
+      changed = (self.vals[idx] & 0x80) == 0
       self.vals[idx] |= 0x80
     else:
+      changed = (self.vals[idx] & 0x80) == 0x80
       self.vals[idx] &= ~0x80
     self.__write_val(idx)
+    return changed
     
   def set_value(self, idx, val):
+    changed = (self.vals[idx] & 0x7f) != val
     self.vals[idx] &= ~0x7f
     self.vals[idx] |= val & 0x7f
     self.__write_val(idx)
+    return changed
     
   def reread(self):
     dir = self.dir == HDA_OUTPUT and (1<<15) or (0<<15)
@@ -311,6 +365,27 @@ class HDAAmpVal:
     for idx in range(len(self.vals)):
       self.__write_val(idx)
 
+  def get_val(self, idx):
+    if self.stereo:
+      return [self.vals[idx*2], self.vals[idx*2+1]]
+    return self.vals[idx]
+
+  def get_val_db(self, idx):
+    vals = self.get_val(idx)
+    res = []
+    for val in vals:
+      res.append(self.caps.get_val_db(val))
+    return res
+
+  def get_val_str(self, idx):
+
+    def niceval(val):
+      return self.caps.get_val_str(val)
+
+    if self.stereo:
+      return '[' + niceval(self.vals[idx*2]) + ' ' + niceval(self.vals[idx*2+1]) + ']'
+    return niceval(self.vals[idx])
+
 class HDARootNode:
 
   def __init__(self, codec, _name):
@@ -386,9 +461,12 @@ class HDANode:
     return self.wtype_name() + " [0x%02x]" % self.nid
 
   def set_active_connection(self, val):
+    changed = False
     if self.active_connection != None:
+      changed = self.active_connection != val 
       self.codec.rw(self.nid, VERBS['SET_CONNECT_SEL'], val)
       self.active_connection = self.codec.rw(self.nid, VERBS['GET_CONNECT_SEL'], 0)
+    return changed
     
   def reread(self):
   
@@ -428,10 +506,10 @@ class HDANode:
           self.origin_active_connection = self.active_connection
     if self.in_amp:
       self.amp_caps_in = HDAAmpCaps(self.codec, self.nid, HDA_INPUT)
-      self.amp_vals_in = HDAAmpVal(self.codec, self, HDA_INPUT)
+      self.amp_vals_in = HDAAmpVal(self.codec, self, HDA_INPUT, self.amp_caps_in)
     if self.out_amp:
       self.amp_caps_out = HDAAmpCaps(self.codec, self.nid, HDA_OUTPUT)
-      self.amp_vals_out = HDAAmpVal(self.codec, self, HDA_OUTPUT)
+      self.amp_vals_out = HDAAmpVal(self.codec, self, HDA_OUTPUT, self.amp_caps_out)
     if self.wtype_id == 'PIN':
       jack_conns = ["Jack", "N/A", "Fixed", "Both"]
       jack_types = ["Line Out", "Speaker", "HP Out", "CD", "SPDIF Out",
@@ -537,12 +615,15 @@ class HDANode:
 
   def eapdbtl_set_value(self, name, value):
     mask = 1 << EAPDBTL_BITS[name]
+    value = value and True or False
+    changed = (self.pincap_eapdbtls & mask) and not value or value
     if value:
       self.pincap_eapdbtls |= mask
     else:
       self.pincap_eapdbtls &= ~mask
     self.codec.rw(self.nid, VERBS['SET_EAPD_BTLENABLE'], self.pincap_eapdbtls)
     self.reread_eapdbtl()
+    return changed
 
   def reread_pin_widget_control(self):
     pinctls = self.codec.rw(self.nid, VERBS['GET_PIN_WIDGET_CONTROL'], 0)
@@ -560,16 +641,20 @@ class HDANode:
   def pin_widget_control_set_value(self, name, value):
     if name in PIN_WIDGET_CONTROL_BITS:
       mask = 1 << PIN_WIDGET_CONTROL_BITS[name]
+      value = value and True or False
+      changed = (self.pinctls & mask) and not value or value
       if value:
         self.pinctls |= mask
       else:
         self.pinctls &= ~mask
     elif name == 'vref' and self.pincap_vref:
       idx = PIN_WIDGET_CONTROL_VREF.index(value)
+      changed = (self.pinctls & 0x07) != idx
       self.pinctls &= ~0x07
       self.pinctls |= idx
     self.codec.rw(self.nid, VERBS['SET_PIN_WIDGET_CONTROL'], self.pinctls)
     self.reread_pin_widget_control()
+    return changed
 
   def reread_vol_knb(self):
     cap = self.codec.rw(self.nid, VERBS['GET_VOLUME_KNOB_CONTROL'], 0)
@@ -581,15 +666,19 @@ class HDANode:
     
   def vol_knb_set_value(self, name, value):
     if name == 'direct':
+      value = value and True or False
+      changed = (self.vol_knb & (1 << 7)) and not value or value      
       if value:
         self.vol_knb |= (1 << 7)
       else:
         self.vol_knb &= ~(1 << 7)
     elif name == 'value':
-      self.vol_knb &= 0x7f
+      changed = (self.vol_knb & 0x7f) != value
+      self.vol_knb &= ~0x7f
       self.vol_knb |= value
     self.codec.rw(self.nid, VERBS['SET_VOLUME_KNOB_CONTROL'], self.vol_knb)
-    self.reread_vol_knb() 
+    self.reread_vol_knb()
+    return changed
 
   def reread_sdi_select(self):
     self.sdi_select = None
@@ -600,10 +689,13 @@ class HDANode:
         self.origin_sdi_select = sdi
 
   def sdi_select_set_value(self, value):
+    changed = False
     if self.sdi_select != None:
+      changed = (self.sdi_select & 0x0f) != value
       self.sdi_select = value & 0x0f
       self.codec.rw(self.nid, VERBS['SET_SDI_SELECT'], self.sdi_select)
       self.reread_sdi_select()
+    return changed
 
   def reread_dig1(self):
     self.dig1 = []
@@ -621,17 +713,21 @@ class HDANode:
 
   def dig1_set_value(self, name, value):
     if name == 'category':
+      changed = ((self.digi1 >> 8) & 0x7f) != (value & 0x7f)
       self.digi1 &= ~0x7f00
       self.digi1 |= (value & 0x7f) << 8
       self.codec.rw(self.nid, VERBS['SET_DIGI_CONVERT_2'], (self.digi1 >> 8) & 0xff)
     else:
       mask = 1 << DIG1_BITS[name]
+      value = value and True or False
+      changed = (self.digi1 & mask) and not value or value      
       if value:
         self.digi1 |= mask
       else:
         self.digi1 &= ~mask
       self.codec.rw(self.nid, VERBS['SET_DIGI_CONVERT_1'], self.digi1 & 0xff)
     self.reread_dig1()
+    return changed
 
   def revert(self):
     if self.origin_active_connection != None:
@@ -659,6 +755,57 @@ class HDANode:
   def get_controls(self):
     return self.codec.get_controls(self.nid)
 
+  def get_conn_amp_vals_str(self, dst_node):
+    # return amp values for connection between this and dst_node
+    res = []
+    if self.out_amp:
+      res.append(self.amp_vals_out.get_val_str(0))
+    else:
+      res.append(None)
+    if dst_node.in_amp:
+      idx = 0
+      if dst_node.amp_vals_in.indices == dst_node.connections:
+        if not self.nid in dst_node.connections:
+          raise ValueError, "nid 0x%02x is not connected to nid 0x%02x (%s, %s)" % (dst_node.nid, self.nid, repr(self.connections), repr(dst_node.connections))
+        idx = dst_node.connections.index(self.nid)
+      res.append(dst_node.amp_vals_in.get_val_str(idx))
+    else:
+      res.append(None)
+    return res
+
+  def is_conn_active(self, dst_node):
+    # disabled route for PIN widgets
+    if self.wtype_id == 'PIN' and not 'IN' in self.pinctl:
+      return None
+    if dst_node.wtype_id == 'PIN' and not 'OUT' in dst_node.pinctl:
+      return None
+    res = [None, None]
+    if self.out_amp:
+      vals = self.amp_vals_out.get_val_db(0)
+      for idx in range(len(vals)):
+        if res[idx]:
+          res[idx] += vals[idx]
+        else:
+          res[idx] = vals[idx]
+    if dst_node.in_amp:
+      idx = 0
+      if dst_node.amp_vals_in.indices == dst_node.connections:
+        if not self.nid in dst_node.connections:
+          raise ValueError, "nid 0x%02x is not connected to nid 0x%02x (%s, %s)" % (dst_node.nid, self.nid, repr(self.connections), repr(dst_node.connections))
+        idx = dst_node.connections.index(self.nid)
+      vals = dst_node.amp_vals_in.get_val_db(idx)
+      for idx in range(len(vals)):
+        if res[idx]:
+          res[idx] += vals[idx]
+        else:
+          res[idx] = vals[idx]
+    if res[0] is None and res[1] is None:
+      return True
+    for r in res:
+      if r >= -1200:
+        return True
+    return False
+
 class HDAGPIO:
 
   def __init__(self, codec, nid):
@@ -691,8 +838,9 @@ class HDAGPIO:
     else:
       self.val[name] &= ~(1 << bit)
     if old == self.test(name, bit):
-      return
+      return False
     self.write(name)
+    return True
 
   def revert(self):
     for i in GPIO_IDS:
index 43bfed5a3597ddb1558f24c82106debe4bb7b1ac..0461b34c3a039e58cfee0dfd00784f313f6778be 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+4#!/usr/bin/env python
 #
 # Copyright (c) 2008-2010 by Jaroslav Kysela <perex@perex.cz>
 #
@@ -37,6 +37,9 @@ class Node:
     self.dst_routes = []
     self.win = None
 
+  def longdesc(self):
+    return "0x%02x" % self.node.nid
+
   def expose(self, cr, event, graph):
 
     width = self.myarea[2]
@@ -135,7 +138,7 @@ class Route:
     self.highlight = False
     self.marked = False
 
-  def shortdest(self):
+  def shortdesc(self):
     return "0x%02x->0x%02x" % (self.src.node.nid, self.dst.node.nid)
 
   def longdesc(self):
@@ -144,6 +147,22 @@ class Route:
     return "%s 0x%02x -> %s 0x%02x" % (src.wtype_id.replace('_', '-'),
                         src.nid, dst.wtype_id.replace('_', '-'), dst.nid)
 
+  def statusdesc(self):
+
+    def niceprint(prefix, val):
+      if val is None:
+        return prefix
+      return ' ' + prefix + ' ' + val
+
+    src = self.src.node
+    dst = self.dst.node
+    vals = src.get_conn_amp_vals_str(dst)
+    src = "%s 0x%02x" % (src.wtype_id.replace('_', '-'), src.nid)
+    dst = "%s 0x%02x" % (dst.wtype_id.replace('_', '-'), dst.nid)
+    res = niceprint("SRC " + src, vals[0]) + ' -> ' + \
+          niceprint("DST " + dst, vals[1])
+    return res
+
   def expose(self, cr, event):
     width = self.src.myarea[2]
     height = self.src.myarea[3]
@@ -163,10 +182,16 @@ class Route:
         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)
+        inactive = self.src.node.is_conn_active(self.dst.node)
+        if inactive is None:
+          cr.set_line_width(0.35)
+          cr.set_source_rgb(0, 0, 0)
+        elif inactive is False:
+          cr.set_line_width(0.35)
+          cr.set_source_rgb(0, 0, 1)
+        else:
+          cr.set_line_width(1.5)
+          cr.set_source_rgb(0, 0, 1)
       cr.move_to(line[0], line[1])
       cr.line_to(line[2], line[3])
       cr.stroke()
@@ -398,10 +423,10 @@ class Route:
       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 x1 == x2 and abs(x1 - x) < 3:
         if y1 <= y and y2 >= y:
           return True
-      elif y1 == y2 and abs(y1 - y) < 2:
+      elif y1 == y2 and abs(y1 - y) < 3:
         if x1 <= x and x2 >= x:
           return True
 
@@ -412,24 +437,30 @@ class Route:
 
 class CodecGraphLayout(gtk.Layout):
 
-  def __init__(self, adj1, adj2, codec, mytitle):
+  def __init__(self, adj1, adj2, codec, mytitle, statusbar):
     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)
+                    gtk.gdk.BUTTON_PRESS_MASK | gtk.gdk.BUTTON_RELEASE_MASK |
+                    gtk.gdk.LEAVE_NOTIFY_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.mouse_leave_handler = self.connect("leave-notify-event", self.mouse_leave)
 
     self.popup_win = None
     self.popup = None
+    self.statusbar = statusbar
 
     self.codec = codec
     self.mytitle = mytitle
     self.graph = codec.graph(dump=False)
     self.startnode = None
     self.endnode = None
+
+    self.changed_handler = HDA_SIGNAL.connect("hda-node-changed", self.hda_node_changed)
+
     ok = False
     for extra in [150, 200, 300]:
       if self.build(extra):
@@ -443,7 +474,7 @@ class CodecGraphLayout(gtk.Layout):
     if self.popup_win:
       self.popup_win.destroy()
 
-  def build(self, extra=50):
+  def __build(self, extra=50):
     self.nodes = []
     self.routes = []
     maxconns = 0
@@ -465,6 +496,18 @@ class CodecGraphLayout(gtk.Layout):
     sx = len(self.graph[0])*(nodesize+extra)+extra
     sy = len(self.graph)*(nodesize+extra)+extra
     self.set_size(sx, sy)
+    total = 0
+    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:
+            total += 1
+            break
+    total *= 2
+    total += 1
+    position = 0
     for node in self.nodes:
       if not node.node.connections:
         continue
@@ -473,12 +516,16 @@ class CodecGraphLayout(gtk.Layout):
           if node1.node.nid == conn:
             r = Route(self.codec, node1, node, self.routes, self.nodes)
             self.routes.append(r)
+            position += 1
+            self.pdialog.set_fraction(float(position) / total)
             break
     res = True
     for route in self.routes:
       if not route.finish(self.routes, self.nodes):
         res = False
         break
+      position += 1
+      self.pdialog.set_fraction(float(position) / total)
     if not res:
       return
     # final step - optimize drawings
@@ -487,6 +534,8 @@ class CodecGraphLayout(gtk.Layout):
       if not size:
         break
       sx -= size
+    position += 1
+    self.pdialog.set_fraction(float(position) / total)
     while 1:
       size = self.compressy(sy)
       if not size:
@@ -495,6 +544,14 @@ class CodecGraphLayout(gtk.Layout):
     self.set_size(sx, sy)
     return res
 
+  def build(self, extra=50):
+    self.pdialog = SimpleProgressDialog("Rendering routes")
+    self.pdialog.show_all()
+    res = self.__build(extra)
+    self.pdialog.destroy()
+    self.pdialog = None
+    return res
+
   def expose(self, widget, event):
     if not self.flags() & gtk.REALIZED:
       return
@@ -567,6 +624,10 @@ class CodecGraphLayout(gtk.Layout):
         return size
     return None
 
+  def hda_node_changed(self, obj, widget, node):
+    if widget != self:
+      self.queue_draw()
+
   def find_node(self, event):
     for node in self.nodes:
       what = node.in_area(event.x, event.y)
@@ -581,7 +642,7 @@ class CodecGraphLayout(gtk.Layout):
 
   def show_popup(self, event):
     screen_width = gtk.gdk.screen_width()
-    screeen_height = gtk.gdk.screen_height()
+    screen_height = gtk.gdk.screen_height()
 
     if self.popup_win:
       self.popup_win.destroy()
@@ -590,23 +651,22 @@ class CodecGraphLayout(gtk.Layout):
     label.modify_font(get_fixed_font())
     label.set_text(self.popup)
     self.popup_win.add(label)
+    self.popup_win.move(screen_width + 10, screen_height + 10)
+    self.popup_win.show_all()
     popup_width, popup_height = self.popup_win.get_size()
 
-    rootwin = self.get_screen().get_root_window()
-    x, y, mods = rootwin.get_pointer()
+    #rootwin = self.get_screen().get_root_window()
+    #x, y, mods = rootwin.get_pointer()
 
-    pos_x = x - popup_width/2
+    pos_x = screen_width - popup_width
     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                                                                                                                                                 
+    pos_y = screen_height - popup_height
+    if pos_y < 0:
+      pox_y = 0
 
     self.popup_win.move(int(pos_x), int(pos_y))
-    self.popup_win.show_all()
+    #self.popup_win.show_all()
 
   def mouse_move(self, widget, event):
     oldpopup = self.popup
@@ -619,13 +679,19 @@ class CodecGraphLayout(gtk.Layout):
         route.highlight = False
     for node in self.nodes:
       if node.mouse_move(event.x, event.y, self):
+        self.statusbar.pop(1)
+        self.statusbar.push(1, node.longdesc())
         found = redraw = True
         break
     if not found:
       for route in self.routes:
         if route.mouse_move(event.x, event.y, self):
+          self.statusbar.pop(1)
+          self.statusbar.push(1, route.statusdesc())
           found = redraw = True
           break
+    if not found:
+      self.statusbar.pop(1)
     if redraw:
       self.queue_draw()
     if self.popup:
@@ -634,6 +700,16 @@ class CodecGraphLayout(gtk.Layout):
     else:
       if self.popup_win:
         self.popup_win.destroy()
+        self.popup_win = None
+
+  def mouse_leave(self, widget, data=None):
+    for route in self.routes:
+      if route.highlight:
+        redraw = True
+        route.highlight = False
+    if self.popup_win:
+      self.popup_win.destroy()
+      self.popup_win = None
 
   def mark_it(self, widget, node, what, enable):
     if what == "start":
@@ -799,10 +875,11 @@ class CodecGraph(gtk.Window):
     self.set_title(self.__class__.__name__ + ' ' + self.codec.name)
     self.set_border_width(0)
 
-    table = gtk.Table(2, 2, False)
+    table = gtk.Table(2, 3, False)
     self.add(table)
 
-    self.layout = CodecGraphLayout(None, None, codec, self.get_title())
+    statusbar = gtk.Statusbar()
+    self.layout = CodecGraphLayout(None, None, codec, self.get_title(), statusbar)
     table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
                  gtk.FILL|gtk.EXPAND, 0, 0)
     vScrollbar = gtk.VScrollbar(None)
@@ -815,6 +892,8 @@ class CodecGraph(gtk.Window):
     vScrollbar.set_adjustment(vAdjust)
     hAdjust = self.layout.get_hadjustment()
     hScrollbar.set_adjustment(hAdjust)
+    table.attach(statusbar, 0, 2, 2, 3, gtk.FILL|gtk.SHRINK,
+                 gtk.FILL|gtk.SHRINK, 0, 0)
     self.show_all()
     GRAPH_WINDOWS[codec] = self
     TRACKER.add(self)
index 841cacb846f59619adb7c1c75146c3cba7de37f0..4fe84573c6a1410a0675bf377ce6f14ff1331bac 100644 (file)
@@ -21,23 +21,90 @@ from hda_codec import HDACodec, HDA_card_list, \
                       PIN_WIDGET_CONTROL_VREF, DIG1_BITS, GPIO_IDS, \
                       HDA_INPUT, HDA_OUTPUT
 
+DIFF_FILE = "/tmp/hda-analyze.diff"
+
+CODEC_TREE = {}
+DIFF_TREE = {}
+
 def get_fixed_font():
   return pango.FontDescription("Misc Fixed,Courier Bold 9")
 
+class HDASignal(gobject.GObject):
+
+  def __init__(self):
+    self.__gobject_init__()
+
+gobject.signal_new("hda-codec-changed", HDASignal,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT))
+gobject.signal_new("hda-node-changed", HDASignal,
+                   gobject.SIGNAL_RUN_FIRST,
+                   gobject.TYPE_NONE,
+                   (gobject.TYPE_PYOBJECT,gobject.TYPE_PYOBJECT))
+
+HDA_SIGNAL = HDASignal()
+
+def do_diff1(codec, diff1):
+  from difflib import unified_diff
+  diff = unified_diff(diff1.split('\n'), codec.dump().split('\n'), n=8, lineterm='')
+  diff = '\n'.join(list(diff))
+  if len(diff) > 0:
+    diff = 'Diff for codec %i/%i (%s):\n' % (codec.card, codec.device, codec.name) + diff
+  return diff
+
+def do_diff():
+  diff = ''
+  hw = 0
+  for card in CODEC_TREE:
+    for codec in CODEC_TREE[card]:
+      c = CODEC_TREE[card][codec]
+      if c.hwaccess:
+        hw += 1
+      diff += do_diff1(c, DIFF_TREE[card][codec])
+  if len(diff) > 0:
+    open(DIFF_FILE, "w+").write(diff + '\n')
+    print "Diff was stored to: %s" % DIFF_FILE
+  return (diff and hw > 0) and True or False
+
 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)    
+    self.set_shadow_type(gtk.SHADOW_IN)
+    self.read_all = self.__read_all_none
+    self.node = None
+    self.codec = None
     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.connect("destroy", self.__destroy)
+    self.codec_changed = HDA_SIGNAL.connect("hda-codec-changed", self.hda_codec_changed)
+    self.node_changed = HDA_SIGNAL.connect("hda-node-changed", self.hda_node_changed)
+    self.read_all()
     self.show_all()
 
+  def __destroy(self, widget):
+    HDA_SIGNAL.handler_disconnect(self.codec_changed)
+    HDA_SIGNAL.handler_disconnect(self.node_changed)
+
+  def __read_all_none(self):
+    pass
+
+  def hda_codec_changed(self, obj, widget, codec):
+    if widget != self:
+      if self.read_all and self.codec == codec:
+        self.read_all()
+    
+  def hda_node_changed(self, obj, widget, node):
+    if widget != self:
+      if self.read_all and self.node == node:
+        self.read_all()
+
   def __create_text(self, callback):
     scrolled_window = gtk.ScrolledWindow()
     scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
@@ -88,7 +155,8 @@ class NodeGui(gtk.ScrolledWindow):
   def __node_connection_toggled(self, widget, row, data):
     model, node = data
     if not model[row][0]:
-      node.set_active_connection(int(row))
+      if node.set_active_connection(int(row)):
+        HDA_SIGNAL.emit("hda-node-changed", self, node)
     for r in model:
       r[0] = False
     idx = 0
@@ -115,18 +183,19 @@ class NodeGui(gtk.ScrolledWindow):
         model.set(iter, 0, node.active_connection == idx,
                         1, node1.name())
         idx += 1
+      self.connection_model = model
       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:
+      if not node.active_connection is 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)
+      column = gtk.TreeViewColumn("Source Node", renderer, text=1, editable=False)
       treeview.append_column(column)
       sw.add(treeview)
     return frame
@@ -134,21 +203,21 @@ class NodeGui(gtk.ScrolledWindow):
   def __amp_mute_toggled(self, button, data):
     caps, vals, idx = data
     val = button.get_active()
-    vals.set_mute(idx, val)
+    if vals.set_mute(idx, val):
+      HDA_SIGNAL.emit("hda-node-changed", self, vals.node)
     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)
+    if vals.set_value(idx, val):
+      HDA_SIGNAL.emit("hda-node-changed", self, vals.node)
     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
+      if caps and caps.cloned:
         title += ' (Global)'
       frame = gtk.Frame(title)
       frame.set_border_width(4)
@@ -162,6 +231,8 @@ class NodeGui(gtk.ScrolledWindow):
         idx = 0
         frame1 = None
         vbox1 = None
+        self.amp_checkbuttons[caps.dir] = []
+        self.amp_adjs[caps.dir] = []
         for val in vals.vals:
           if vals.stereo and idx & 1 == 0:
             frame1 = gtk.Frame()
@@ -173,16 +244,21 @@ class NodeGui(gtk.ScrolledWindow):
           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))
+            self.amp_checkbuttons[caps.dir].append(checkbutton)
             hbox.pack_start(checkbutton, False, False)
+          else:
+            self.amp_checkbuttons[caps.dir].append(None)
           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))
+            self.amp_adjs[caps.dir].append(adj)
             hbox.pack_start(scale, True, True)
+          else:
+            self.amp_adjs[caps.dir].append(None)
           if vbox1:
             vbox1.pack_start(hbox, False, False)
           else:
@@ -191,6 +267,8 @@ class NodeGui(gtk.ScrolledWindow):
       frame.add(vbox)
       return frame
 
+    self.amp_checkbuttons = {}
+    self.amp_adjs = {}
     hbox = gtk.HBox(False, 0)
     c = build_caps('Input Amplifier',
                     node.in_amp and node.amp_caps_in or None,
@@ -205,12 +283,14 @@ class NodeGui(gtk.ScrolledWindow):
 
   def __pincap_eapdbtl_toggled(self, button, data):
     node, name = data
-    node.eapdbtl_set_value(name, button.get_active())
+    if node.eapdbtl_set_value(name, button.get_active()):
+      HDA_SIGNAL.emit("hda-node-changed", self, node)
     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())
+    if node.pin_widget_control_set_value(name, button.get_active()):
+      HDA_SIGNAL.emit("hda-node-changed", self, node)
     button.set_active(name in node.pinctl)
 
   def __pinctls_vref_change(self, combobox, node):
@@ -219,7 +299,8 @@ class NodeGui(gtk.ScrolledWindow):
     for name in PIN_WIDGET_CONTROL_VREF:
       if not name: continue
       if idx1 == index:
-        node.pin_widget_control_set_value('vref', name)
+        if node.pin_widget_control_set_value('vref', name):
+          HDA_SIGNAL.emit("hda-node-changed", self, node)
         break
       idx1 += 1
     idx = idx1 = 0
@@ -249,10 +330,11 @@ class NodeGui(gtk.ScrolledWindow):
         frame = gtk.Frame('EAPD')
         frame.set_border_width(4)
         vbox1 = gtk.VBox(False, 0)
+        self.pincap_eapdbtls_checkbuttons = []
         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))
+          self.pincap_eapdbtls_checkbuttons.append(checkbutton)
           vbox1.pack_start(checkbutton, False, False)
         frame.add(vbox1)
         vbox.pack_start(frame, False, False)
@@ -276,22 +358,19 @@ class NodeGui(gtk.ScrolledWindow):
     frame = gtk.Frame('Widget Control')
     frame.set_border_width(4)
     vbox1 = gtk.VBox(False, 0)
+    self.pin_checkbuttons = []
     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))
+      self.pin_checkbuttons.append(checkbutton)
       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)
+      self.pincap_vref_combobox = combobox
       hbox1 = gtk.HBox(False, 0)
       label = gtk.Label('VREF')
       hbox1.pack_start(label, False, False)
@@ -309,13 +388,15 @@ class NodeGui(gtk.ScrolledWindow):
 
   def __sdi_select_changed(self, adj, node):
     val = int(adj.get_value())
-    node.sdi_select_set_value(val)
+    if node.sdi_select_set_value(val):
+      HDA_SIGNAL.emit("hda-node-changed", self, node)
     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)
+    if node.dig1_set_value(name, val):
+      HDA_SIGNAL.emit("hda-node-changed", self, node)
     button.set_active(name in node.dig1)
 
   def __dig1_category_activate(self, entry, node):
@@ -328,7 +409,8 @@ class NodeGui(gtk.ScrolledWindow):
       except:
         print "Unknown category value '%s'" % val
         return
-    node.dig1_set_value('category', val)
+    if node.dig1_set_value('category', val):
+      HDA_SIGNAL.emit("hda-node-changed", self, node)
     entry.set_text("0x%02x" % node.dig1_category)
 
   def __build_aud(self, node):
@@ -357,6 +439,7 @@ class NodeGui(gtk.ScrolledWindow):
       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)
+      self.sdi_select_adj = adj
       scale = gtk.HScale(adj)
       scale.set_digits(0)
       scale.set_value_pos(gtk.POS_LEFT)
@@ -370,10 +453,11 @@ class NodeGui(gtk.ScrolledWindow):
       hbox1 = gtk.HBox(False, 0)
       frame = gtk.Frame('Digital Converter')
       vbox1 = gtk.VBox(False, 0)
+      self.digital_checkbuttons = []
       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))
+        self.digital_checkbuttons.append(checkbutton)
         vbox1.pack_start(checkbutton, False, False)
       frame.add(vbox1)
       hbox1.pack_start(frame)
@@ -431,7 +515,60 @@ class NodeGui(gtk.ScrolledWindow):
     frame.add(self.__new_text_view(text=str))
     return frame
 
+  def __read_all_node(self):
+    node = self.node
+    if node.wtype_id in ['AUD_IN', 'AUD_OUT']:
+      if not node.sdi_select is None:
+        self.sdi_select_adj.set_value(node.sdi_select)
+      if node.digital:
+        idx = 0
+        for name in DIG1_BITS:
+          checkbutton = self.digital_checkbuttons[idx]
+          checkbutton.set_active(node.digi1 & (1 << DIG1_BITS[name]))
+          idx += 1
+    elif node.wtype_id == 'PIN':
+      if 'EAPD' in node.pincap:
+        idx = 0
+        for name in EAPDBTL_BITS:
+          checkbutton = self.pincap_eapdbtls_checkbuttons[idx]
+          checkbutton.set_active(node.pincap_eapdbtls & (1 << EAPDBTL_BITS[name]))
+          idx += 1
+      idx = 0
+      for name in PIN_WIDGET_CONTROL_BITS:
+        checkbutton = self.pin_checkbuttons[idx]
+        checkbutton.set_active(node.pinctls & (1 << PIN_WIDGET_CONTROL_BITS[name]))
+        idx += 1
+      idx = active = 0
+      for name in PIN_WIDGET_CONTROL_VREF:
+        if name == node.pinctl_vref: active = idx
+        if name: idx += 1
+      if node.pincap_vref:
+        self.pincap_vref_combobox.set_active(active)
+      a = []
+      if node.in_amp:
+        a.append((HDA_INPUT, node.amp_caps_in, node.amp_vals_in))
+      if node.out_amp:
+        a.append((HDA_OUTPUT, node.amp_caps_out, node.amp_vals_out))
+      for dir, caps, vals in a:
+        for idx in range(len(vals.vals)):
+          val = vals.vals[idx]
+          checkbutton = self.amp_checkbuttons[dir][idx]
+          if checkbutton:
+            checkbutton.set_active(val & 0x80 and True or False)
+          adj = self.amp_adjs[dir][idx]
+          if adj:
+            adj.set_value((val & 0x7f) % (caps.nsteps+1))
+          idx += 1
+    if hasattr(self, 'connection_model'):
+      for r in self.connection_model:
+        r[0] = False
+      idx = 0
+      for r in self.connection_model:
+        r[0] = node.active_connection == idx
+        idx += 1
+
   def __build_node(self, node, doframe=False):
+    self.node = node
     self.mytitle = node.name()
     if doframe:
       mframe = gtk.Frame(self.mytitle)
@@ -465,6 +602,8 @@ class NodeGui(gtk.ScrolledWindow):
     mframe.add(vbox)
     self.add_with_viewport(mframe)
 
+    self.read_all = self.__read_all_node
+
   def __build_codec_info(self, codec):
     vbox = gtk.VBox(False, 0)
 
@@ -512,7 +651,8 @@ class NodeGui(gtk.ScrolledWindow):
     return hbox
 
   def __gpio_toggled(self, button, (codec, id, idx)):
-    codec.gpio.set(id, idx, button.get_active())
+    if codec.gpio.set(id, idx, button.get_active()):
+      HDA_SIGNAL.emit("hda-codec-changed", self, codec)
     button.set_active(codec.gpio.test(id, idx))
 
   def __build_codec_gpio(self, codec):
@@ -526,21 +666,31 @@ class NodeGui(gtk.ScrolledWindow):
     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)
+    self.gpio_checkbuttons = []
     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)
+      self.gpio_checkbuttons.append([])
       for i in range(codec.gpio_max):
-        checkbutton = gtk.CheckButton('[%d]' % i)
-        checkbutton.set_active(codec.gpio.test(id, i))
+        checkbutton = checkbutton = gtk.CheckButton('[%d]' % i)
         checkbutton.connect("toggled", self.__gpio_toggled, (codec, id, i))
+        self.gpio_checkbuttons[-1].append(checkbutton)
         vbox1.pack_start(checkbutton, False, False)
       frame1.add(vbox1)
       hbox.pack_start(frame1, False, False)
     return frame
 
+  def __read_all_codec(self):
+    idx = 0
+    for id in GPIO_IDS:
+      for i in range(self.codec.gpio_max):
+        self.gpio_checkbuttons[idx][i].set_active(self.codec.gpio.test(id, i))
+      idx += 1
+
   def __build_codec(self, codec, doframe=False):
+    self.codec = codec
     self.mytitle = codec.name
     if doframe:
       mframe = gtk.Frame(self.mytitle)
@@ -554,6 +704,7 @@ class NodeGui(gtk.ScrolledWindow):
     vbox.pack_start(self.__build_codec_gpio(codec), False, False)
     mframe.add(vbox)
     self.add_with_viewport(mframe)
+    self.read_all = self.__read_all_codec
 
   def __build_card_info(self, card):
     str =  'Card:       %s\n' % card.card
@@ -576,6 +727,23 @@ class NodeGui(gtk.ScrolledWindow):
     mframe.add(vbox)
     self.add_with_viewport(mframe)
 
+class SimpleProgressDialog(gtk.Dialog):
+
+  def __init__(self, title):
+    gtk.Dialog.__init__(self, title, None, gtk.DIALOG_MODAL, None)
+    self.set_deletable(False)
+
+    box = self.get_child()
+
+    box.pack_start(gtk.Label(), False, False, 0)
+    self.progressbar = gtk.ProgressBar()
+    box.pack_start(self.progressbar, False, False, 0)    
+
+  def set_fraction(self, fraction):
+    self.progressbar.set_fraction(fraction)
+    while gtk.events_pending():   
+      gtk.main_iteration_do(False)
+                          
 class TrackWindows:
 
   def __init__(self):
@@ -589,6 +757,23 @@ class TrackWindows:
     if win in self.windows:
       self.windows.remove(win)
       if not self.windows:
+        self.do_diff(win)
         gtk.main_quit()
 
+  def do_diff(self, widget):
+    if do_diff():      
+      dialog = gtk.MessageDialog(widget,
+                      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
+                      gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
+                      "HDA-Analyzer: Would you like to revert\n"
+                      "settings for all HDA codecs?")
+      response = dialog.run()
+      dialog.destroy()
+    
+      if response == gtk.RESPONSE_YES:
+        for card in CODEC_TREE:
+          for codec in CODEC_TREE[card]:
+            CODEC_TREE[card][codec].revert()
+        print "Settings for all codecs were reverted..."
+
 TRACKER = TrackWindows()