bool, specifies if we should display addresses before instrs
create the graph objects in ctx
# File metasm/gui/dasm_graph.rb, line 1201 def build_ctx(ctx) # graph : block -> following blocks in same function block_rel = {} todo = ctx.root_addrs.dup done = [:default, Expression::Unknown] while a = todo.shift a = @dasm.normalize a next if done.include? a done << a next if not di = @dasm.di_at(a) if not di.block_head? block_rel[di.block.address] = [a] @dasm.split_block(a) end block_rel[a] = [] di.block.each_to_samefunc(@dasm) { |t| t = @dasm.normalize t next if not @dasm.di_at(t) todo << t block_rel[a] << t } block_rel[a].uniq! end # populate boxes addr2box = {} todo = ctx.root_addrs.dup todo.delete_if { |t| not @dasm.di_at(t) } # undefined func start done = [] while a = todo.shift next if done.include? a done << a if not ctx.keep_split.to_a.include?(a) and from = block_rel.keys.find_all { |ba| block_rel[ba].include? a } and from.length == 1 and block_rel[from.first].length == 1 and addr2box[from.first] and lst = @dasm.decoded[from.first].block.list.last and lst.next_addr == a and (not lst.opcode.props[:saveip] or lst.block.to_subfuncret) box = addr2box[from.first] else box = ctx.new_box a, :addresses => [], :line_text_col => [], :line_address => [] end @dasm.decoded[a].block.list.each { |di_| box[:addresses] << di_.address addr2box[di_.address] = box } todo.concat block_rel[a] end # link boxes ctx.box.each { |b| next if not di = @dasm.decoded[b[:addresses].last] a = di.block.address next if not block_rel[a] block_rel[a].each { |t| ctx.link_boxes(b.id, t) b.direct_to = t if t == di.next_addr } } # calc box dimensions/text ctx.box.each { |b| colstr = [] curaddr = nil line = 0 render = lambda { |str, col| colstr << [str, col] } nl = lambda { b[:line_address][line] = curaddr b[:line_text_col][line] = colstr colstr = [] line += 1 } b[:addresses].each { |addr| curaddr = addr if di = @dasm.di_at(curaddr) if di.block_head? # render dump_block_header, add a few colors b_header = '' ; @dasm.dump_block_header(di.block) { |l| b_header << l ; b_header << \n if b_header[-1] != \n } b_header.strip.each_line { |l| l.chomp! col = :comment col = :label if l[0, 2] != '//' and l[-1] == : render[l, col] nl[] } end render["#{Expression[curaddr]} ", :address] if @show_addresses render[di.instruction.to_s.ljust(di.comment ? 18 : 0), :instruction] render[' ; ' + di.comment.join(' ')[0, 64], :comment] if di.comment nl[] else # TODO real data display (dwords, xrefs, strings..) if label = @dasm.get_label_at(curaddr) render[label + ' ', :label] end s = @dasm.get_section_at(curaddr) render['db '+((s and s[0].data.length > s[0].ptr) ? Expression[s[0].read(1)[0]].to_s : '?'), :text] nl[] end } b.w = b[:line_text_col].map { |strc| strc.map { |s, c| s }.join.length }.max.to_i * @font_width + 2 b.w += 1 if b.w % 2 == 0 # ensure boxes have odd width -> vertical arrows are straight b.h = line * @font_height } end
# File metasm/gui/dasm_graph.rb, line 861 def click(x, y) @mousemove_origin = [x, y] if b = find_box_xy(x, y) @selected_boxes = [b] if not @selected_boxes.include? b @caret_box = b @caret_x = (view_x+x/@zoom-b.x-1).to_i / @font_width @caret_y = (view_y+y/@zoom-b.y-1).to_i / @font_height update_caret else @selected_boxes = [] @caret_box = nil end redraw end
# File metasm/gui/dasm_graph.rb, line 848 def click_ctrl(x, y) if b = find_box_xy(x, y) if @selected_boxes.include? b @selected_boxes.delete b else @selected_boxes << b end redraw else @mousemove_origin_ctrl = [x, y] end end
# File metasm/gui/dasm_graph.rb, line 1690 def current_address @caret_box ? @caret_box[:line_address][@caret_y] : @curcontext.root_addrs.first end
find a suitable array of graph roots, walking up from a block (function start/entrypoint)
# File metasm/gui/dasm_graph.rb, line 1566 def dasm_find_roots(addr) todo = [addr] done = [] roots = [] default_root = nil while a = todo.shift next if not di = @dasm.di_at(a) b = di.block a = b.address if done.include? a default_root ||= a next end done << a newf = [] b.each_from_samefunc(@dasm) { |f| newf << f } if newf.empty? roots << b.address else todo.concat newf end end roots << default_root if roots.empty? and default_root roots end
# File metasm/gui/dasm_graph.rb, line 906 def doubleclick(x, y) @mousemove_origin = nil if b = find_box_xy(x, y) if @hl_word and @zoom >= 0.90 and @zoom <= 1.1 @parent_widget.focus_addr(@hl_word) else @parent_widget.focus_addr((b[:addresses] || b[:line_address]).first) end elsif doubleclick_check_arrow(x, y) elsif @zoom == 1.0 zoom_all else @curcontext.view_x += (x/@zoom - x) @curcontext.view_y += (y/@zoom - y) @zoom = 1.0 end redraw end
check if the user clicked on the beginning/end of an arrow, if so focus on the other end
# File metasm/gui/dasm_graph.rb, line 926 def doubleclick_check_arrow(x, y) return if @margin*@zoom < 2 x = view_x+x/@zoom y = view_y+y/@zoom sx = nil if bt = @shown_boxes.to_a.reverse.find { |b| y >= b.y+b.h-1 and y <= b.y+b.h-1+@margin+2 and sx = b.x+b.w/2 - b.to.length/2 * @margin/2 and x >= sx-@margin/2 and x <= sx+b.to.length*@margin/2 # should be margin/4, but add a little comfort margin } idx = (x-sx+@margin/4).to_i / (@margin/2) idx = 0 if idx < 0 idx = bt.to.length-1 if idx >= bt.to.length if bt.to[idx] if @parent_widget @caret_box, @caret_y = bt, bt[:line_address].length-1 @parent_widget.focus_addr bt.to[idx][:line_address][0] else focus_xy(bt.to[idx].x, bt.to[idx].y) end end true elsif bf = @shown_boxes.to_a.reverse.find { |b| y >= b.y-@margin-2 and y <= b.y and sx = b.x+b.w/2 - b.from.length/2 * @margin/2 and x >= sx-@margin/2 and x <= sx+b.from.length*@margin/2 } idx = (x-sx+@margin/4).to_i / (@margin/2) idx = 0 if idx < 0 idx = bf.from.length-1 if idx >= bf.from.length if bf.from[idx] if @parent_widget @caret_box, @caret_y = bf, bf[:line_address].length-1 @parent_widget.focus_addr bf.from[idx][:line_address][-1] else focus_xy(bt.from[idx].x, bt.from[idx].y) end end true end end
# File metasm/gui/dasm_graph.rb, line 779 def find_box_xy(x, y) x = view_x+x/@zoom y = view_y+y/@zoom @shown_boxes.to_a.reverse.find { |b| b.x <= x and b.x+b.w > x and b.y <= y-1 and b.y+b.h > y+1 } end
focus on addr addr may be a dasm label, dasm address, dasm address in string form (eg “0DEADBEEFh”) addr must point to a decodedinstruction if the addr is not found in curcontext, the code flow is walked up until a function start or an entrypoint is found, then the graph is created from there will call #gui_update then
# File metasm/gui/dasm_graph.rb, line 1610 def focus_addr(addr, can_update_context=true) return if @parent_widget and not addr = @parent_widget.normalize(addr) return if not @dasm.di_at(addr) # move window / change curcontext if b = @curcontext.box.find { |b_| b_[:line_address].index(addr) } @caret_box, @caret_x, @caret_y = b, 0, b[:line_address].rindex(addr) @curcontext.view_x += (width/2 / @zoom - width/2) @curcontext.view_y += (height/2 / @zoom - height/2) @zoom = 1.0 update_caret elsif can_update_context @curcontext = Graph.new 'testic' @curcontext.root_addrs = dasm_find_roots(addr) @want_focus_addr = addr gui_update else return end true end
# File metasm/gui/dasm_graph.rb, line 1633 def focus_xy(x, y) # dont move during a click return if @mousemove_origin # ensure the caret stays onscreen if not view_x @curcontext.view_x = x - width/5/@zoom redraw elsif @caret_box and @caret_box.w < width*27/30/@zoom # keep @caret_box full if possible if view_x + width/20/@zoom > @caret_box.x @curcontext.view_x = @caret_box.x-width/20/@zoom elsif view_x + width*9/10/@zoom < @caret_box.x+@caret_box.w @curcontext.view_x = @caret_box.x+@caret_box.w-width*9/10/@zoom end elsif view_x + width/20/@zoom > x @curcontext.view_x = x-width/20/@zoom redraw elsif view_x + width*9/10/@zoom < x @curcontext.view_x = x-width*9/10/@zoom redraw end if not view_y @curcontext.view_y = y - height/5/@zoom redraw elsif @caret_box and @caret_box.h < height*27/30/@zoom if view_y + height/20/@zoom > @caret_box.y @curcontext.view_y = @caret_box.y-height/20/@zoom elsif view_y + height*9/10/@zoom < @caret_box.y+@caret_box.h @curcontext.view_y = @caret_box.y+@caret_box.h-height*9/10/@zoom end elsif view_y + height/20/@zoom > y @curcontext.view_y = y-height/20/@zoom redraw elsif view_y + height*9/10/@zoom < y @curcontext.view_y = y-height*9/10/@zoom redraw end end
# File metasm/gui/dasm_graph.rb, line 1600 def get_cursor_pos [current_address, @caret_x] end
# File metasm/gui/dasm_graph.rb, line 1137 def gui_update @want_update_graph = true redraw end
# File metasm/gui/dasm_graph.rb, line 1544 def hide_non_ascendants(list) reach = {} todo = list.dup while b = todo.pop next if reach[b] reach[b] = true b.from.each { |bb| todo << bb if bb.y <= b.h+b.y } end @curcontext.box.delete_if { |bb| !reach[bb] } @curcontext.box.each { |bb| bb.from.delete_if { |bbb| !reach[bbb] } bb.to.delete_if { |bbb| !reach[bbb] } } redraw end
# File metasm/gui/dasm_graph.rb, line 1523 def hide_non_descendants(list) reach = {} todo = list.dup while b = todo.pop next if reach[b] reach[b] = true b.to.each { |bb| todo << bb if bb.y+bb.h >= b.y } end @curcontext.box.delete_if { |bb| !reach[bb] } @curcontext.box.each { |bb| bb.from.delete_if { |bbb| !reach[bbb] } bb.to.delete_if { |bbb| !reach[bbb] } } redraw end
# File metasm/gui/dasm_graph.rb, line 750 def initialize_widget(dasm, parent_widget) @dasm = dasm @parent_widget = parent_widget @show_addresses = false @caret_box = nil @selected_boxes = [] @shown_boxes = [] @mousemove_origin = @mousemove_origin_ctrl = nil @curcontext = Graph.new(nil) @want_focus_addr = nil @margin = 8 @zoom = 1.0 @default_color_association = ColorTheme.merge :hlbox_bg => :palegrey, :box_bg => :white, :arrow_hl => :red, :arrow_cond => :darkgreen, :arrow_uncond => :darkblue, :arrow_direct => :darkred, :box_bg_shadow => :black, :background => :paleblue # @othergraphs = ? (to keep user-specified formatting) end
# File metasm/gui/dasm_graph.rb, line 1326 def keypress(key) case key when :left if @caret_box if @caret_x > 0 @caret_x -= 1 update_caret elsif b = @curcontext.box.sort_by { |b_| -b_.x }.find { |b_| b_.x < @caret_box.x and b_.y < @caret_box.y+@caret_y*@font_height and b_.y+b_.h > @caret_box.y+(@caret_y+1)*@font_height } @caret_x = (b.w/@font_width).to_i @caret_y += ((@caret_box.y-b.y)/@font_height).to_i @caret_box = b update_caret redraw else @curcontext.view_x -= 20/@zoom redraw end else @curcontext.view_x -= 20/@zoom redraw end when :up if @caret_box if @caret_y > 0 @caret_y -= 1 update_caret elsif b = @curcontext.box.sort_by { |b_| -b_.y }.find { |b_| b_.y < @caret_box.y and b_.x < @caret_box.x+@caret_x*@font_width and b_.x+b_.w > @caret_box.x+(@caret_x+1)*@font_width } @caret_x += ((@caret_box.x-b.x)/@font_width).to_i @caret_y = b[:line_address].length-1 @caret_box = b update_caret redraw else @curcontext.view_y -= 20/@zoom redraw end else @curcontext.view_y -= 20/@zoom redraw end when :right if @caret_box if @caret_x <= @caret_box[:line_text_col].map { |s| s.map { |ss, cc| ss }.join.length }.max @caret_x += 1 update_caret elsif b = @curcontext.box.sort_by { |b_| b_.x }.find { |b_| b_.x > @caret_box.x and b_.y < @caret_box.y+@caret_y*@font_height and b_.y+b_.h > @caret_box.y+(@caret_y+1)*@font_height } @caret_x = 0 @caret_y += ((@caret_box.y-b.y)/@font_height).to_i @caret_box = b update_caret redraw else @curcontext.view_x += 20/@zoom redraw end else @curcontext.view_x += 20/@zoom redraw end when :down if @caret_box if @caret_y < @caret_box[:line_address].length-1 @caret_y += 1 update_caret elsif b = @curcontext.box.sort_by { |b_| b_.y }.find { |b_| b_.y > @caret_box.y and b_.x < @caret_box.x+@caret_x*@font_width and b_.x+b_.w > @caret_box.x+(@caret_x+1)*@font_width } @caret_x += ((@caret_box.x-b.x)/@font_width).to_i @caret_y = 0 @caret_box = b update_caret redraw else @curcontext.view_y += 20/@zoom redraw end else @curcontext.view_y += 20/@zoom redraw end when :pgup if @caret_box @caret_y -= (height/4/@zoom/@font_height).to_i @caret_y = 0 if @caret_y < 0 update_caret(false) else @curcontext.view_y -= height/4/@zoom redraw end when :pgdown if @caret_box @caret_y += (height/4/@zoom/@font_height).to_i @caret_y = [@caret_box[:line_address].length-1, @caret_y].min update_caret(false) else @curcontext.view_y += height/4/@zoom redraw end when :home if @caret_box @caret_x = 0 update_caret(false) else @curcontext.view_x = @curcontext.box.map { |b_| b_.x }.min-10 @curcontext.view_y = @curcontext.box.map { |b_| b_.y }.min-10 redraw end when :end if @caret_box @caret_x = @caret_box[:line_text_col][@caret_y].to_a.map { |ss, cc| ss }.join.length update_caret(false) else @curcontext.view_x = [@curcontext.box.map { |b_| b_.x+b_.w }.max-width/@zoom+10, @curcontext.box.map { |b_| b_.x }.min-10].max @curcontext.view_y = [@curcontext.box.map { |b_| b_.y+b_.h }.max-height/@zoom+10, @curcontext.box.map { |b_| b_.y }.min-10].max redraw end when :delete @selected_boxes.each { |b_| @curcontext.box.delete b_ b_.from.each { |bb| bb.to.delete b_ } b_.to.each { |bb| bb.from.delete b_ } } redraw when :popupmenu if @caret_box cx = (@caret_box.x - view_x + 1 + @caret_x*@font_width)*@zoom cy = (@caret_box.y - view_y + 1 + @caret_y*@font_height)*@zoom rightclick(cx, cy) end when a t0 = Time.now puts 'autoarrange' @curcontext.auto_arrange_boxes redraw puts 'autoarrange done %.02f' % (Time.now - t0) when u gui_update when R load __FILE__ when I # create arbitrary boxes/links if @selected_boxes.empty? @fakebox ||= 0 b = @curcontext.new_box "id_#@fakebox", :addresses => [], :line_address => [], :line_text_col => [[[" blublu #@fakebox", :text]]] b.w = @font_width * 15 b.h = @font_height * 2 b.x = rand(200) - 100 b.y = rand(200) - 100 @fakebox += 1 else b1, *bl = @selected_boxes bl = [b1] if bl.empty? # loop bl.each { |b2| if b1.to.include? b2 b1.to.delete b2 b2.from.delete b1 else b1.to << b2 b2.from << b1 end } end redraw when 1 # (numeric) zoom to 1:1 if @zoom == 1.0 zoom_all else @curcontext.view_x += (width/2 / @zoom - width/2) @curcontext.view_y += (height/2 / @zoom - height/2) @zoom = 1.0 end redraw when :insert # split curbox at @caret_y if @caret_box and a = @caret_box[:line_address][@caret_y] and @dasm.decoded[a] @dasm.split_block(a) @curcontext.keep_split ||= [] @curcontext.keep_split |= [a] gui_update focus_addr a end else return false end true end
# File metasm/gui/dasm_graph.rb, line 1305 def keypress_ctrl(key) case key when F @parent_widget.inputbox('text to search in curview (regex)', :text => @hl_word) { |pat| re = /#{pat}/ list = [['addr', 'instr']] @curcontext.box.each { |b| b[:line_text_col].zip(b[:line_address]) { |l, a| str = l.map { |s, c| s }.join list << [Expression[a], str] if str =~ re } } @parent_widget.list_bghilight("search result for /#{pat}/i", list) { |i| @parent_widget.focus_addr i[0] } } when +; mouse_wheel_ctrl(:up, width/2, height/2) when -; mouse_wheel_ctrl(:down, width/2, height/2) else return false end true end
# File metasm/gui/dasm_graph.rb, line 1174 def load_dot(dota) @want_update_graph = false @curcontext.clear boxes = {} new_box = lambda { |text| b = @curcontext.new_box(text, :line_text_col => [[[text, :text]]]) b.w = (text.length+1) * @font_width b.h = @font_height b } dota.scan(/^.*$/) { |l| a = l.strip.chomp(';').split(/->/).map { |s| s.strip.delete '"' } next if not id = a.shift b0 = boxes[id] ||= new_box[id] while id = a.shift b1 = boxes[id] ||= new_box[id] b0.to |= [b1] b1.from |= [b0] b0 = b1 end } redraw rescue Interrupt puts "dot_len #{boxes.length}" end
# File metasm/gui/dasm_graph.rb, line 1170 def load_dotfile(path) load_dot(File.read(path)) end
# File metasm/gui/dasm_graph.rb, line 809 def mouse_wheel(dir, x, y) case dir when :up; @curcontext.view_y -= height/4 / @zoom when :down; @curcontext.view_y += height/4 / @zoom end redraw end
# File metasm/gui/dasm_graph.rb, line 785 def mouse_wheel_ctrl(dir, x, y) case dir when :up if @zoom < 100 # zoom in oldzoom = @zoom @zoom *= 1.1 @zoom = 1.0 if (@zoom-1.0).abs < 0.05 @curcontext.view_x += (x / oldzoom - x / @zoom) @curcontext.view_y += (y / oldzoom - y / @zoom) end when :down if @zoom > 1.0/1000 # zoom out oldzoom = @zoom @zoom /= 1.1 @zoom = 1.0 if (@zoom-1.0).abs < 0.05 @curcontext.view_x += (x / oldzoom - x / @zoom) @curcontext.view_y += (y / oldzoom - y / @zoom) end end redraw end
# File metasm/gui/dasm_graph.rb, line 817 def mousemove(x, y) return if not @mousemove_origin dx = (x - @mousemove_origin[0])/@zoom dy = (y - @mousemove_origin[1])/@zoom @mousemove_origin = [x, y] if @selected_boxes.empty? @curcontext.view_x -= dx ; @curcontext.view_y -= dy else @selected_boxes.each { |b| b.x += dx ; b.y += dy } end redraw end
# File metasm/gui/dasm_graph.rb, line 831 def mouserelease(x, y) mousemove(x, y) @mousemove_origin = nil if @mousemove_origin_ctrl x1 = view_x + @mousemove_origin_ctrl[0]/@zoom x2 = x1 + (x - @mousemove_origin_ctrl[0])/@zoom x1, x2 = x2, x1 if x1 > x2 y1 = view_y + @mousemove_origin_ctrl[1]/@zoom y2 = y1 + (y - @mousemove_origin_ctrl[1])/@zoom y1, y2 = y2, y1 if y1 > y2 @selected_boxes |= @curcontext.box.find_all { |b| b.x >= x1 and b.x + b.w <= x2 and b.y >= y1 and b.y + b.h <= y2 } redraw @mousemove_origin_ctrl = nil end end
# File metasm/gui/dasm_graph.rb, line 983 def paint update_graph if @want_update_graph if @want_focus_addr and @curcontext.box.find { |b_| b_[:line_address].index(@want_focus_addr) } focus_addr(@want_focus_addr, false) @want_focus_addr = nil #zoom_all end @curcontext.box.each { |b| # reorder arrows so that endings do not overlap b.to = b.to.sort_by { |bt| bt.x+bt.w/2 } b.from = b.from.sort_by { |bt| bt.x+bt.w/2 } } # arrows drawn first to stay under the boxes # XXX precalc ? @curcontext.box.each { |b| b.to.each { |bt| paint_arrow(b, bt) } } @shown_boxes = [] w_w = width w_h = height @curcontext.box.each { |b| next if b.x >= view_x+w_w/@zoom or b.y >= view_y+w_h/@zoom or b.x+b.w <= view_x or b.y+b.h <= view_y @shown_boxes << b paint_box(b) } end
# File metasm/gui/dasm_graph.rb, line 1024 def paint_arrow(b1, b2) x1 = x1o = b1.x+b1.w/2-view_x y1 = b1.y+b1.h-view_y x2 = x2o = b2.x+b2.w/2-view_x y2 = b2.y-1-view_y margin = @margin x1 += (-(b1.to.length-1)/2 + b1.to.index(b2)) * margin/2 x2 += (-(b2.from.length-1)/2 + b2.from.index(b1)) * margin/2 return if (y1+margin < 0 and y2 < 0) or (y1 > height/@zoom and y2-margin > height/@zoom) # just clip on y margin, x1, y1, x2, y2, b1w, b2w, x1o, x2o = [margin, x1, y1, x2, y2, b1.w, b2.w, x1o, x2o].map { |v| v*@zoom } # straighten vertical arrows if possible if y2 > y1 and (x1-x2).abs <= margin if b1.to.length == 1 x1 = x2 elsif b2.from.length == 1 x2 = x1 end end set_color_arrow(b1, b2) if margin > 1 # draw arrow tip draw_line(x1, y1, x1, y1+margin) draw_line(x2, y2-margin+1, x2, y2) draw_line(x2-margin/2, y2-margin/2, x2, y2) draw_line(x2+margin/2, y2-margin/2, x2, y2) y1 += margin y2 -= margin-1 end if y2 > y1 - b1.h*@zoom - 2*margin+1 # straight arrow draw_line(x1, y1, x2, y2) if x1 != y1 or x2 != y2 else # arrow goes up: navigate around b2 x = (x1 <= x2 ? [x1o-b1w/2-margin, x2o-b2w/2-margin].min : [x1o+b1w/2+margin, x2o+b2w/2+margin].max) draw_line(x1, y1, x, y1) draw_line(x, y1, x, y2) draw_line(x, y2, x2, y2) draw_line(x1, y1+1, x, y1+1) # double draw_line(x+1, y1, x+1, y2) draw_line(x, y2+1, x2, y2+1) end end
# File metasm/gui/dasm_graph.rb, line 1084 def paint_box(b) set_color_boxshadow(b) draw_rectangle((b.x-view_x+3)*@zoom, (b.y-view_y+4)*@zoom, b.w*@zoom, b.h*@zoom) set_color_box(b) draw_rectangle((b.x-view_x)*@zoom, (b.y-view_y+1)*@zoom, b.w*@zoom, b.h*@zoom) # current text position x = (b.x - view_x + 1)*@zoom y = (b.y - view_y + 1)*@zoom w_w = (b.x - view_x + b.w - @font_width)*@zoom w_h = (b.y - view_y + b.h - @font_height)*@zoom w_h = height if w_h > height if @parent_widget and @parent_widget.bg_color_callback ly = 0 b[:line_address].each { |a| if c = @parent_widget.bg_color_callback[a] draw_rectangle_color(c, (b.x-view_x)*@zoom, (1+b.y-view_y+ly*@font_height)*@zoom, b.w*@zoom, (@font_height*@zoom).ceil) end ly += 1 } end if @caret_box == b draw_rectangle_color(:cursorline_bg, (b.x-view_x)*@zoom, (1+b.y-view_y+@caret_y*@font_height)*@zoom, b.w*@zoom, @font_height*@zoom) end return if @zoom < 0.99 or @zoom > 1.1 # TODO dynamic font size ? # renders a string at current cursor position with a color # must not include newline render = lambda { |str, color| next if y >= w_h+2 or x >= w_w draw_string_hl(color, x, y, str) x += str.length * @font_width } yoff = @font_height * @zoom b[:line_text_col].each { |list| list.each { |s, c| render[s, c] } if y >= -yoff x = (b.x - view_x + 1)*@zoom y += yoff break if y > w_h+2 } if b == @caret_box and focus? cx = (b.x - view_x + 1 + @caret_x*@font_width)*@zoom cy = (b.y - view_y + 1 + @caret_y*@font_height)*@zoom draw_line_color(:caret, cx, cy, cx, cy+(@font_height-1)*@zoom) end end
# File metasm/gui/dasm_graph.rb, line 775 def resized(w, h) redraw end
if the target is a call to a subfunction, open a new window with the graph of this function (popup)
# File metasm/gui/dasm_graph.rb, line 893 def rightclick(x, y) if b = find_box_xy(x, y) and @zoom >= 0.90 and @zoom <= 1.1 click(x, y) @mousemove_origin = nil m = new_menu setup_contextmenu(b, m) if @parent_widget.respond_to?(:extend_contextmenu) @parent_widget.extend_contextmenu(self, m, @caret_box[:line_address][@caret_y]) end popupmenu(m, x, y) end end
# File metasm/gui/dasm_graph.rb, line 1012 def set_color_arrow(b1, b2) if b1 == @caret_box or b2 == @caret_box draw_color :arrow_hl elsif b1.to.length == 1 draw_color :arrow_uncond elsif b1.direct_to == b2.id draw_color :arrow_direct else draw_color :arrow_cond end end
# File metasm/gui/dasm_graph.rb, line 1076 def set_color_box(b) if @selected_boxes.include? b draw_color :hlbox_bg else draw_color :box_bg end end
# File metasm/gui/dasm_graph.rb, line 1072 def set_color_boxshadow(b) draw_color :box_bg_shadow end
# File metasm/gui/dasm_graph.rb, line 1593 def set_cursor_pos(p) addr, x = p focus_addr(addr) @caret_x = x update_caret end
hint that the caret moved redraw, change the hilighted word
# File metasm/gui/dasm_graph.rb, line 1676 def update_caret(update_hlword = true) return if not b = @caret_box or not @caret_x or not l = @caret_box[:line_text_col][@caret_y] if update_hlword l = l.map { |s, c| s }.join @parent_widget.focus_changed_callback[] if @parent_widget and @parent_widget.focus_changed_callback and @oldcaret_y != @caret_y update_hl_word(l, @caret_x) end focus_xy(b.x + @caret_x*@font_width, b.y + @caret_y*@font_height) redraw end
rebuild the code flow graph from @curcontext.roots recalc the boxes w/h
# File metasm/gui/dasm_graph.rb, line 1146 def update_graph @want_update_graph = false ctx = @curcontext boxcnt = ctx.box.length arrcnt = ctx.box.inject(0) { |s, b| s + b.to.length + b.from.length } ctx.clear build_ctx(ctx) ctx.auto_arrange_boxes return if ctx != @curcontext if boxcnt != ctx.box.length or arrcnt != ctx.box.inject(0) { |s, b| s + b.to.length + b.from.length } zoom_all elsif @caret_box # update @caret_box with a box at the same place bx = @caret_box.x + @caret_box.w/2 by = @caret_box.y + @caret_box.h/2 @caret_box = ctx.box.find { |cb| cb.x < bx and cb.x+cb.w > bx and cb.y < by and cb.y+cb.h > by } end end
# File metasm/gui/dasm_graph.rb, line 770 def view_x; @curcontext.view_x; end
# File metasm/gui/dasm_graph.rb, line 771 def view_x=(vx); @curcontext.view_x = vx; end
# File metasm/gui/dasm_graph.rb, line 772 def view_y; @curcontext.view_y; end
# File metasm/gui/dasm_graph.rb, line 773 def view_y=(vy); @curcontext.view_y = vy; end
update the zoom & view_xy to show the whole graph in the window
# File metasm/gui/dasm_graph.rb, line 969 def zoom_all minx, miny, maxx, maxy = @curcontext.boundingbox minx -= @margin miny -= @margin maxx += @margin maxy += @margin @zoom = [width.to_f/(maxx-minx), height.to_f/(maxy-miny)].min @zoom = 1.0 if @zoom > 1.0 or (@zoom-1.0).abs < 0.1 @curcontext.view_x = minx + (maxx-minx-width/@zoom)/2 @curcontext.view_y = miny + (maxy-miny-height/@zoom)/2 redraw end