From e730ace23f6c45917f887e09d392cb9a1751d5be Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sat, 10 Jul 2010 14:56:03 +0200 Subject: [PATCH] hda-analyzer: more work on the graph window - 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 --- hda-analyzer/hda_analyzer.py | 48 +------ hda-analyzer/hda_codec.py | 168 ++++++++++++++++++++++-- hda-analyzer/hda_graph.py | 127 +++++++++++++++---- hda-analyzer/hda_guilib.py | 239 +++++++++++++++++++++++++++++++---- 4 files changed, 477 insertions(+), 105 deletions(-) diff --git a/hda-analyzer/hda_analyzer.py b/hda-analyzer/hda_analyzer.py index 284e82c..ecfbb7e 100755 --- a/hda-analyzer/hda_analyzer.py +++ b/hda-analyzer/hda_analyzer.py @@ -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 diff --git a/hda-analyzer/hda_codec.py b/hda-analyzer/hda_codec.py index 8ff9267..8c7b14d 100644 --- a/hda-analyzer/hda_codec.py +++ b/hda-analyzer/hda_codec.py @@ -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: diff --git a/hda-analyzer/hda_graph.py b/hda-analyzer/hda_graph.py index 43bfed5..0461b34 100755 --- a/hda-analyzer/hda_graph.py +++ b/hda-analyzer/hda_graph.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +4#!/usr/bin/env python # # Copyright (c) 2008-2010 by Jaroslav Kysela # @@ -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) diff --git a/hda-analyzer/hda_guilib.py b/hda-analyzer/hda_guilib.py index 841cacb..4fe8457 100644 --- a/hda-analyzer/hda_guilib.py +++ b/hda-analyzer/hda_guilib.py @@ -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() -- 2.47.1