metasm 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (235) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.hgtags +3 -0
  4. data/Gemfile +1 -0
  5. data/INSTALL +61 -0
  6. data/LICENCE +458 -0
  7. data/README +29 -21
  8. data/Rakefile +10 -0
  9. data/TODO +10 -12
  10. data/doc/code_organisation.txt +2 -0
  11. data/doc/core/DynLdr.txt +247 -0
  12. data/doc/core/ExeFormat.txt +43 -0
  13. data/doc/core/Expression.txt +220 -0
  14. data/doc/core/GNUExports.txt +27 -0
  15. data/doc/core/Ia32.txt +236 -0
  16. data/doc/core/SerialStruct.txt +108 -0
  17. data/doc/core/VirtualString.txt +145 -0
  18. data/doc/core/WindowsExports.txt +61 -0
  19. data/doc/core/index.txt +1 -0
  20. data/doc/style.css +6 -3
  21. data/doc/usage/debugger.txt +327 -0
  22. data/doc/usage/index.txt +1 -0
  23. data/doc/use_cases.txt +2 -2
  24. data/metasm.gemspec +22 -0
  25. data/{lib/metasm.rb → metasm.rb} +11 -3
  26. data/{lib/metasm → metasm}/compile_c.rb +13 -7
  27. data/metasm/cpu/arc.rb +8 -0
  28. data/metasm/cpu/arc/decode.rb +425 -0
  29. data/metasm/cpu/arc/main.rb +191 -0
  30. data/metasm/cpu/arc/opcodes.rb +588 -0
  31. data/{lib/metasm → metasm/cpu}/arm.rb +7 -5
  32. data/{lib/metasm → metasm/cpu}/arm/debug.rb +2 -2
  33. data/{lib/metasm → metasm/cpu}/arm/decode.rb +13 -12
  34. data/{lib/metasm → metasm/cpu}/arm/encode.rb +23 -8
  35. data/{lib/metasm → metasm/cpu}/arm/main.rb +0 -3
  36. data/metasm/cpu/arm/opcodes.rb +324 -0
  37. data/{lib/metasm → metasm/cpu}/arm/parse.rb +25 -13
  38. data/{lib/metasm → metasm/cpu}/arm/render.rb +2 -2
  39. data/metasm/cpu/arm64.rb +15 -0
  40. data/metasm/cpu/arm64/debug.rb +38 -0
  41. data/metasm/cpu/arm64/decode.rb +289 -0
  42. data/metasm/cpu/arm64/encode.rb +41 -0
  43. data/metasm/cpu/arm64/main.rb +105 -0
  44. data/metasm/cpu/arm64/opcodes.rb +232 -0
  45. data/metasm/cpu/arm64/parse.rb +20 -0
  46. data/metasm/cpu/arm64/render.rb +95 -0
  47. data/{lib/metasm/ppc.rb → metasm/cpu/bpf.rb} +2 -4
  48. data/metasm/cpu/bpf/decode.rb +142 -0
  49. data/metasm/cpu/bpf/main.rb +60 -0
  50. data/metasm/cpu/bpf/opcodes.rb +81 -0
  51. data/metasm/cpu/bpf/render.rb +41 -0
  52. data/metasm/cpu/cy16.rb +9 -0
  53. data/metasm/cpu/cy16/decode.rb +253 -0
  54. data/metasm/cpu/cy16/main.rb +63 -0
  55. data/metasm/cpu/cy16/opcodes.rb +78 -0
  56. data/metasm/cpu/cy16/render.rb +41 -0
  57. data/metasm/cpu/dalvik.rb +11 -0
  58. data/{lib/metasm → metasm/cpu}/dalvik/decode.rb +35 -13
  59. data/{lib/metasm → metasm/cpu}/dalvik/main.rb +51 -2
  60. data/{lib/metasm → metasm/cpu}/dalvik/opcodes.rb +19 -11
  61. data/metasm/cpu/ia32.rb +17 -0
  62. data/{lib/metasm → metasm/cpu}/ia32/compile_c.rb +5 -7
  63. data/{lib/metasm → metasm/cpu}/ia32/debug.rb +5 -5
  64. data/{lib/metasm → metasm/cpu}/ia32/decode.rb +246 -59
  65. data/{lib/metasm → metasm/cpu}/ia32/decompile.rb +7 -7
  66. data/{lib/metasm → metasm/cpu}/ia32/encode.rb +19 -13
  67. data/{lib/metasm → metasm/cpu}/ia32/main.rb +51 -8
  68. data/metasm/cpu/ia32/opcodes.rb +1424 -0
  69. data/{lib/metasm → metasm/cpu}/ia32/parse.rb +47 -16
  70. data/{lib/metasm → metasm/cpu}/ia32/render.rb +31 -4
  71. data/metasm/cpu/mips.rb +14 -0
  72. data/{lib/metasm → metasm/cpu}/mips/compile_c.rb +1 -1
  73. data/metasm/cpu/mips/debug.rb +42 -0
  74. data/{lib/metasm → metasm/cpu}/mips/decode.rb +46 -16
  75. data/{lib/metasm → metasm/cpu}/mips/encode.rb +4 -3
  76. data/{lib/metasm → metasm/cpu}/mips/main.rb +11 -4
  77. data/{lib/metasm → metasm/cpu}/mips/opcodes.rb +86 -17
  78. data/{lib/metasm → metasm/cpu}/mips/parse.rb +1 -1
  79. data/{lib/metasm → metasm/cpu}/mips/render.rb +1 -1
  80. data/{lib/metasm/dalvik.rb → metasm/cpu/msp430.rb} +1 -1
  81. data/metasm/cpu/msp430/decode.rb +247 -0
  82. data/metasm/cpu/msp430/main.rb +62 -0
  83. data/metasm/cpu/msp430/opcodes.rb +101 -0
  84. data/{lib/metasm → metasm/cpu}/pic16c/decode.rb +6 -7
  85. data/{lib/metasm → metasm/cpu}/pic16c/main.rb +0 -0
  86. data/{lib/metasm → metasm/cpu}/pic16c/opcodes.rb +1 -1
  87. data/{lib/metasm/mips.rb → metasm/cpu/ppc.rb} +4 -4
  88. data/{lib/metasm → metasm/cpu}/ppc/decode.rb +18 -12
  89. data/{lib/metasm → metasm/cpu}/ppc/decompile.rb +3 -3
  90. data/{lib/metasm → metasm/cpu}/ppc/encode.rb +2 -2
  91. data/{lib/metasm → metasm/cpu}/ppc/main.rb +17 -12
  92. data/{lib/metasm → metasm/cpu}/ppc/opcodes.rb +11 -5
  93. data/metasm/cpu/ppc/parse.rb +55 -0
  94. data/metasm/cpu/python.rb +8 -0
  95. data/metasm/cpu/python/decode.rb +136 -0
  96. data/metasm/cpu/python/main.rb +36 -0
  97. data/metasm/cpu/python/opcodes.rb +180 -0
  98. data/{lib/metasm → metasm/cpu}/sh4.rb +1 -1
  99. data/{lib/metasm → metasm/cpu}/sh4/decode.rb +48 -17
  100. data/{lib/metasm → metasm/cpu}/sh4/main.rb +13 -4
  101. data/{lib/metasm → metasm/cpu}/sh4/opcodes.rb +7 -8
  102. data/metasm/cpu/x86_64.rb +15 -0
  103. data/{lib/metasm → metasm/cpu}/x86_64/compile_c.rb +28 -17
  104. data/{lib/metasm → metasm/cpu}/x86_64/debug.rb +4 -4
  105. data/{lib/metasm → metasm/cpu}/x86_64/decode.rb +57 -15
  106. data/{lib/metasm → metasm/cpu}/x86_64/encode.rb +55 -26
  107. data/{lib/metasm → metasm/cpu}/x86_64/main.rb +14 -6
  108. data/metasm/cpu/x86_64/opcodes.rb +136 -0
  109. data/{lib/metasm → metasm/cpu}/x86_64/parse.rb +10 -2
  110. data/metasm/cpu/x86_64/render.rb +35 -0
  111. data/metasm/cpu/z80.rb +9 -0
  112. data/metasm/cpu/z80/decode.rb +313 -0
  113. data/metasm/cpu/z80/main.rb +67 -0
  114. data/metasm/cpu/z80/opcodes.rb +224 -0
  115. data/metasm/cpu/z80/render.rb +59 -0
  116. data/{lib/metasm/os/main.rb → metasm/debug.rb} +160 -401
  117. data/{lib/metasm → metasm}/decode.rb +35 -4
  118. data/{lib/metasm → metasm}/decompile.rb +15 -16
  119. data/{lib/metasm → metasm}/disassemble.rb +201 -45
  120. data/{lib/metasm → metasm}/disassemble_api.rb +651 -87
  121. data/{lib/metasm → metasm}/dynldr.rb +220 -133
  122. data/{lib/metasm → metasm}/encode.rb +10 -1
  123. data/{lib/metasm → metasm}/exe_format/a_out.rb +9 -6
  124. data/{lib/metasm → metasm}/exe_format/autoexe.rb +1 -0
  125. data/{lib/metasm → metasm}/exe_format/bflt.rb +57 -27
  126. data/{lib/metasm → metasm}/exe_format/coff.rb +11 -3
  127. data/{lib/metasm → metasm}/exe_format/coff_decode.rb +53 -20
  128. data/{lib/metasm → metasm}/exe_format/coff_encode.rb +11 -13
  129. data/{lib/metasm → metasm}/exe_format/dex.rb +13 -5
  130. data/{lib/metasm → metasm}/exe_format/dol.rb +1 -0
  131. data/{lib/metasm → metasm}/exe_format/elf.rb +93 -57
  132. data/{lib/metasm → metasm}/exe_format/elf_decode.rb +143 -34
  133. data/{lib/metasm → metasm}/exe_format/elf_encode.rb +122 -31
  134. data/metasm/exe_format/gb.rb +65 -0
  135. data/metasm/exe_format/javaclass.rb +424 -0
  136. data/{lib/metasm → metasm}/exe_format/macho.rb +204 -16
  137. data/{lib/metasm → metasm}/exe_format/main.rb +26 -3
  138. data/{lib/metasm → metasm}/exe_format/mz.rb +1 -0
  139. data/{lib/metasm → metasm}/exe_format/nds.rb +7 -4
  140. data/{lib/metasm → metasm}/exe_format/pe.rb +71 -8
  141. data/metasm/exe_format/pyc.rb +167 -0
  142. data/{lib/metasm → metasm}/exe_format/serialstruct.rb +67 -14
  143. data/{lib/metasm → metasm}/exe_format/shellcode.rb +7 -3
  144. data/metasm/exe_format/shellcode_rwx.rb +114 -0
  145. data/metasm/exe_format/swf.rb +205 -0
  146. data/{lib/metasm → metasm}/exe_format/xcoff.rb +7 -7
  147. data/metasm/exe_format/zip.rb +335 -0
  148. data/metasm/gui.rb +13 -0
  149. data/{lib/metasm → metasm}/gui/cstruct.rb +35 -41
  150. data/{lib/metasm → metasm}/gui/dasm_coverage.rb +11 -11
  151. data/{lib/metasm → metasm}/gui/dasm_decomp.rb +7 -20
  152. data/{lib/metasm → metasm}/gui/dasm_funcgraph.rb +0 -0
  153. data/metasm/gui/dasm_graph.rb +1695 -0
  154. data/{lib/metasm → metasm}/gui/dasm_hex.rb +12 -8
  155. data/{lib/metasm → metasm}/gui/dasm_listing.rb +43 -28
  156. data/{lib/metasm → metasm}/gui/dasm_main.rb +310 -53
  157. data/{lib/metasm → metasm}/gui/dasm_opcodes.rb +5 -19
  158. data/{lib/metasm → metasm}/gui/debug.rb +93 -27
  159. data/{lib/metasm → metasm}/gui/gtk.rb +162 -40
  160. data/{lib/metasm → metasm}/gui/qt.rb +12 -2
  161. data/{lib/metasm → metasm}/gui/win32.rb +179 -42
  162. data/{lib/metasm → metasm}/gui/x11.rb +59 -59
  163. data/{lib/metasm → metasm}/main.rb +389 -264
  164. data/{lib/metasm/os/remote.rb → metasm/os/gdbremote.rb} +146 -54
  165. data/{lib/metasm → metasm}/os/gnu_exports.rb +1 -1
  166. data/{lib/metasm → metasm}/os/linux.rb +628 -151
  167. data/metasm/os/main.rb +330 -0
  168. data/{lib/metasm → metasm}/os/windows.rb +132 -42
  169. data/{lib/metasm → metasm}/os/windows_exports.rb +141 -0
  170. data/{lib/metasm → metasm}/parse.rb +26 -24
  171. data/{lib/metasm → metasm}/parse_c.rb +221 -116
  172. data/{lib/metasm → metasm}/preprocessor.rb +55 -40
  173. data/{lib/metasm → metasm}/render.rb +14 -38
  174. data/misc/hexdump.rb +2 -1
  175. data/misc/lint.rb +58 -0
  176. data/misc/txt2html.rb +9 -7
  177. data/samples/bindiff.rb +3 -4
  178. data/samples/dasm-plugins/bindiff.rb +15 -0
  179. data/samples/dasm-plugins/bookmark.rb +133 -0
  180. data/samples/dasm-plugins/c_constants.rb +57 -0
  181. data/samples/dasm-plugins/colortheme_solarized.rb +125 -0
  182. data/samples/dasm-plugins/cppobj_funcall.rb +60 -0
  183. data/samples/dasm-plugins/dasm_all.rb +70 -0
  184. data/samples/dasm-plugins/demangle_cpp.rb +31 -0
  185. data/samples/dasm-plugins/deobfuscate.rb +251 -0
  186. data/samples/dasm-plugins/dump_text.rb +35 -0
  187. data/samples/dasm-plugins/export_graph_svg.rb +86 -0
  188. data/samples/dasm-plugins/findgadget.rb +75 -0
  189. data/samples/dasm-plugins/hl_opcode.rb +32 -0
  190. data/samples/dasm-plugins/hotfix_gtk_dbg.rb +19 -0
  191. data/samples/dasm-plugins/imm2off.rb +34 -0
  192. data/samples/dasm-plugins/match_libsigs.rb +93 -0
  193. data/samples/dasm-plugins/patch_file.rb +95 -0
  194. data/samples/dasm-plugins/scanfuncstart.rb +36 -0
  195. data/samples/dasm-plugins/scanxrefs.rb +26 -0
  196. data/samples/dasm-plugins/selfmodify.rb +197 -0
  197. data/samples/dasm-plugins/stringsxrefs.rb +28 -0
  198. data/samples/dasmnavig.rb +1 -1
  199. data/samples/dbg-apihook.rb +24 -9
  200. data/samples/dbg-plugins/heapscan.rb +283 -0
  201. data/samples/dbg-plugins/heapscan/compiled_heapscan_lin.c +155 -0
  202. data/samples/dbg-plugins/heapscan/compiled_heapscan_win.c +128 -0
  203. data/samples/dbg-plugins/heapscan/graphheap.rb +616 -0
  204. data/samples/dbg-plugins/heapscan/heapscan.rb +709 -0
  205. data/samples/dbg-plugins/heapscan/winheap.h +174 -0
  206. data/samples/dbg-plugins/heapscan/winheap7.h +307 -0
  207. data/samples/dbg-plugins/trace_func.rb +214 -0
  208. data/samples/disassemble-gui.rb +35 -5
  209. data/samples/disassemble.rb +31 -6
  210. data/samples/dump_upx.rb +24 -12
  211. data/samples/dynamic_ruby.rb +12 -3
  212. data/samples/exeencode.rb +6 -5
  213. data/samples/factorize-headers-peimports.rb +1 -1
  214. data/samples/lindebug.rb +175 -381
  215. data/samples/metasm-shell.rb +1 -2
  216. data/samples/peldr.rb +2 -2
  217. data/tests/all.rb +1 -1
  218. data/tests/arc.rb +26 -0
  219. data/tests/dynldr.rb +22 -4
  220. data/tests/expression.rb +55 -0
  221. data/tests/graph_layout.rb +285 -0
  222. data/tests/ia32.rb +79 -26
  223. data/tests/mips.rb +9 -2
  224. data/tests/x86_64.rb +66 -18
  225. metadata +330 -218
  226. data/lib/metasm/arm/opcodes.rb +0 -177
  227. data/lib/metasm/gui.rb +0 -23
  228. data/lib/metasm/gui/dasm_graph.rb +0 -1354
  229. data/lib/metasm/ia32.rb +0 -14
  230. data/lib/metasm/ia32/opcodes.rb +0 -873
  231. data/lib/metasm/ppc/parse.rb +0 -52
  232. data/lib/metasm/x86_64.rb +0 -12
  233. data/lib/metasm/x86_64/opcodes.rb +0 -118
  234. data/samples/gdbclient.rb +0 -583
  235. data/samples/rubstop.rb +0 -399
@@ -9,7 +9,7 @@ class AsmOpcodeWidget < DrawableWidget
9
9
  attr_accessor :dasm
10
10
  # nr of raw data bytes to display next to decoded instructions
11
11
  attr_accessor :raw_data_length
12
-
12
+
13
13
  def initialize_widget(dasm, parent_widget)
14
14
  @dasm = dasm
15
15
  @parent_widget = parent_widget
@@ -22,9 +22,7 @@ class AsmOpcodeWidget < DrawableWidget
22
22
  @view_max = @dasm.sections.map { |s, e| s + e.length }.max rescue nil
23
23
  @view_addr = @dasm.prog_binding['entrypoint'] || @view_min || 0
24
24
 
25
- @default_color_association = { :comment => :darkblue, :label => :darkgreen, :text => :black,
26
- :instruction => :black, :address => :blue, :caret => :black, :raw_data => :black,
27
- :background => :white, :cursorline_bg => :paleyellow, :hl_word => :palered }
25
+ @default_color_association = ColorTheme.merge :raw_data => :black
28
26
  end
29
27
 
30
28
  def resized(w, h)
@@ -124,19 +122,7 @@ class AsmOpcodeWidget < DrawableWidget
124
122
  # must not include newline
125
123
  render = lambda { |str, color|
126
124
  fullstr << str
127
- if @hl_word
128
- stmp = str
129
- pre_x = 0
130
- while stmp =~ /^(.*?)(\b#{Regexp.escape @hl_word}\b)/
131
- s1, s2 = $1, $2
132
- pre_x += s1.length * @font_width
133
- hl_x = s2.length * @font_width
134
- draw_rectangle_color(:hl_word, x+pre_x, y, hl_x, @font_height)
135
- pre_x += hl_x
136
- stmp = stmp[s1.length+s2.length..-1]
137
- end
138
- end
139
- draw_string_color(color, x, y, str)
125
+ draw_string_hl(color, x, y, str)
140
126
  x += str.length * @font_width
141
127
  }
142
128
 
@@ -154,7 +140,7 @@ class AsmOpcodeWidget < DrawableWidget
154
140
 
155
141
  # draw text until screen is full
156
142
  while y < height
157
- if label = invb[curaddr]
143
+ if invb[curaddr]
158
144
  nl[]
159
145
  @dasm.label_alias[curaddr].to_a.each { |name|
160
146
  render["#{name}:", :label]
@@ -251,7 +237,7 @@ class AsmOpcodeWidget < DrawableWidget
251
237
  # redraws the caret, change the hilighted word, redraw if needed
252
238
  def update_caret
253
239
  if update_hl_word(@line_text[@caret_y], @caret_x) or @caret_y != @oldcaret_y
254
- redraw
240
+ redraw
255
241
  elsif @oldcaret_x != @caret_x
256
242
  invalidate_caret(@oldcaret_x, @oldcaret_y)
257
243
  invalidate_caret(@caret_x, @caret_y)
@@ -30,7 +30,7 @@ class DbgWidget < ContainerVBoxWidget
30
30
  oldcb = @code.bg_color_callback
31
31
  @code.bg_color_callback = lambda { |a|
32
32
  if a == @dbg.pc
33
- 'f88'
33
+ :red_bg
34
34
  # TODO breakpoints & stuff
35
35
  elsif oldcb; oldcb[a]
36
36
  end
@@ -48,8 +48,8 @@ class DbgWidget < ContainerVBoxWidget
48
48
 
49
49
  pc = @dbg.resolve_expr(@watchpoint[@code])
50
50
  graph = :graph if @dbg.disassembler.function_blocks(pc).to_a.length < 100
51
- @code.focus_addr(pc, graph)
52
- @mem.focus_addr(0, :hex)
51
+ @code.focus_addr(pc, graph, true)
52
+ @mem.focus_addr(0, :hex, true)
53
53
  end
54
54
 
55
55
  def swapin_tid
@@ -98,10 +98,16 @@ class DbgWidget < ContainerVBoxWidget
98
98
 
99
99
  # TODO check_target always, incl when :stopped
100
100
  def post_dbg_run
101
+ # focus currently stopped threads
102
+ if @dbg.state == :running and tt = @dbg.tid_stuff.find { |tid, tstuff| tstuff[:state] != :running }
103
+ @dbg.tid = tt[0]
104
+ end
105
+
101
106
  want_redraw = true
102
107
  return if @idle_checking ||= nil # load only one bg proc
103
108
  @idle_checking = true
104
109
  Gui.idle_add {
110
+ protect {
105
111
  @dbg.check_target
106
112
  if @dbg.state == :running
107
113
  redraw if want_redraw # redraw once if the target is running (less flicker with singlestep)
@@ -121,6 +127,7 @@ class DbgWidget < ContainerVBoxWidget
121
127
  }
122
128
  redraw
123
129
  false
130
+ }
124
131
  }
125
132
  end
126
133
 
@@ -166,7 +173,10 @@ class DbgWidget < ContainerVBoxWidget
166
173
  l = listwindow('running processes', list,
167
174
  :noshow => true,
168
175
  :color_callback => lambda { |le| [:grey, :palegrey] if le[0] == me }
169
- ) { |e| i.text = e[0] }
176
+ ) { |e|
177
+ i.text = e[0]
178
+ i.keypress(:enter) if l.destroyed?
179
+ }
170
180
  l.x += l.width
171
181
  l.show
172
182
  false
@@ -198,10 +208,28 @@ class DbgWidget < ContainerVBoxWidget
198
208
  case f
199
209
  when /\.(c|h|cpp)$/; @dbg.disassembler.parse_c_file(f)
200
210
  when /\.map$/; @dbg.load_map(f)
201
- when /\.rb$/; @dbg.load_plugin(f)
211
+ when /\.rb$/; @dbg.load_plugin(f) ; @console.add_log "loaded plugin #{File.basename(f, '.rb')}"
202
212
  else messagebox("unsupported file extension #{f}")
203
213
  end
204
214
  end
215
+
216
+ def extend_contextmenu(tg, menu, addr=nil)
217
+ if addr
218
+ bm = tg.new_menu
219
+ bl = @dbg.all_breakpoints(addr)
220
+ if not bl.empty?
221
+ tg.addsubmenu(bm, '_clear breakpoint') { bl.each { |b| @dbg.del_bp(b) } }
222
+ end
223
+ tg.addsubmenu(bm, '_go here') { @dbg.bpx(addr, true) ; dbg_continue }
224
+ tg.addsubmenu(bm, '_bpx') { @dbg.bpx(addr) }
225
+ tg.addsubmenu(bm, 'bpm _read') { @dbg.hwbp(addr, :r, 1) }
226
+ tg.addsubmenu(bm, 'bpm _write') { @dbg.hwbp(addr, :w, 1) }
227
+ tg.addsubmenu(menu, '_bp', bm)
228
+ end
229
+ if @parent_widget.respond_to?(:extend_contextmenu)
230
+ @parent_widget.extend_contextmenu(tg, menu, addr)
231
+ end
232
+ end
205
233
  end
206
234
 
207
235
 
@@ -221,10 +249,9 @@ class DbgRegWidget < DrawableWidget
221
249
  swapin_tid
222
250
 
223
251
  @reg_pos = [] # list of x y w h vx of the reg drawing on widget, vx is x of value
224
-
225
- @default_color_association = { :label => :black, :data => :blue, :write_pending => :darkred,
226
- :changed => :darkgreen, :caret => :black, :background => :white,
227
- :inactive => :palegrey }
252
+
253
+ @default_color_association = ColorTheme.merge :label => :text, :data => :blue, :write_pending => :darkred,
254
+ :changed => :green, :caret => :text, :inactive => :palegrey
228
255
  end
229
256
 
230
257
  def swapin_tid
@@ -262,7 +289,6 @@ class DbgRegWidget < DrawableWidget
262
289
  end
263
290
 
264
291
  def paint
265
- curaddr = 0
266
292
  x = 1
267
293
  y = 0
268
294
 
@@ -402,7 +428,7 @@ class DbgRegWidget < DrawableWidget
402
428
  @write_pending[reg] = v
403
429
  rsz = 1
404
430
  end
405
-
431
+
406
432
  if rsz == 1
407
433
  @caret_reg += 1
408
434
  @caret_reg = @registers.length if @caret_reg >= @registers.length + @flags.length
@@ -472,8 +498,8 @@ class DbgConsoleWidget < DrawableWidget
472
498
 
473
499
  @dbg.set_log_proc { |l| add_log l }
474
500
 
475
- @default_color_association = { :log => :palegrey, :curline => :white, :caret => :yellow,
476
- :background => :black, :status => :black, :status_bg => '088' }
501
+ @default_color_association = ColorTheme.merge :log => :palegrey, :curline => :white, :caret => :yellow,
502
+ :background => :black, :status => :black, :status_bg => '088'
477
503
 
478
504
  init_commands
479
505
  end
@@ -513,6 +539,21 @@ class DbgConsoleWidget < DrawableWidget
513
539
  clipboard_copy(txt)
514
540
  end
515
541
 
542
+ # copy/paste word under cursor (paste when on last line)
543
+ def rightclick(x, y)
544
+ y -= height % @font_height
545
+ y = y.to_i / @font_height
546
+ hc = height / @font_height
547
+ x /= @font_width
548
+ if y >= hc - 2
549
+ keypress_ctrl ?v
550
+ else
551
+ txt = @log.reverse[@log_offset + hc - y - 3].to_s
552
+ word = txt[0...x].to_s[/\w*$/] << txt[x..-1].to_s[/^\w*/]
553
+ clipboard_copy(word)
554
+ end
555
+ end
556
+
516
557
  def mouse_wheel(dir, x, y)
517
558
  case dir
518
559
  when :up; @log_offset += 3
@@ -531,12 +572,12 @@ class DbgConsoleWidget < DrawableWidget
531
572
 
532
573
  w_w = width
533
574
 
534
- y -= @font_height
575
+ y -= @font_height
535
576
  draw_rectangle_color(:status_bg, 0, y, w_w, @font_height)
536
577
  str = "#{@dbg.pid}:#{@dbg.tid} #{@dbg.state} #{@dbg.info}"
537
578
  draw_string_color(:status, w_w-str.length*@font_width-1, y, str)
538
579
  draw_string_color(:status, 1+@font_width, y, @statusline)
539
- y -= @font_height
580
+ y -= @font_height
540
581
 
541
582
  w_w_c = w_w/@font_width
542
583
  @caret_y = y
@@ -744,7 +785,7 @@ class DbgConsoleWidget < DrawableWidget
744
785
  def cmd_dd(addr, dlen=nil, len=nil)
745
786
  if addr.kind_of? String
746
787
  s = addr.strip
747
- addr = solve_expr!(s)
788
+ addr = solve_expr!(s) || @parent_widget.mem.curaddr
748
789
  if not s.empty?
749
790
  s = s[1..-1] if s[0] == ?,
750
791
  len ||= solve_expr(s)
@@ -757,7 +798,7 @@ class DbgConsoleWidget < DrawableWidget
757
798
  le = (@dbg.cpu.endianness == :little)
758
799
  data = '' if @dbg.memory.page_invalid?(addr)
759
800
  case dlen
760
- when nil; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ').ljust(2*16+15)} #{data.tr("\0-\x1f\x7f-\xff", '.')}"
801
+ when nil; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ').ljust(2*16+15)} #{data.tr("^\x20-\x7e", '.')}"
761
802
  when 1; add_log "#{Expression[addr]} #{data.unpack('C*').map { |c| '%02X' % c }.join(' ')}"
762
803
  when 2; add_log "#{Expression[addr]} #{data.unpack(le ? 'v*' : 'n*').map { |c| '%04X' % c }.join(' ')}"
763
804
  when 4; add_log "#{Expression[addr]} #{data.unpack(le ? 'V*' : 'N*').map { |c| '%08X' % c }.join(' ')}"
@@ -767,8 +808,12 @@ class DbgConsoleWidget < DrawableWidget
767
808
  len -= 16
768
809
  end
769
810
  else
770
- @parent_widget.mem.view(:hex).data_size = dlen if dlen
771
- @parent_widget.mem.focus_addr(solve_expr(addr)) if addr and addr != ''
811
+ if dlen
812
+ @parent_widget.mem.view(:hex).data_size = dlen
813
+ @parent_widget.mem.view(:hex).resized
814
+ @parent_widget.mem.showview(:hex)
815
+ end
816
+ @parent_widget.mem.focus_addr(solve_expr(addr))
772
817
  @parent_widget.mem.gui_update
773
818
  end
774
819
  end
@@ -783,6 +828,18 @@ class DbgConsoleWidget < DrawableWidget
783
828
  new_command('dw', 'dump/focus words in data window') { |arg| cmd_dd(arg, 2) }
784
829
  new_command('dd', 'dump/focus dwords in data window') { |arg| cmd_dd(arg, 4) }
785
830
  new_command('dq', 'dump/focus qwords in data window') { |arg| cmd_dd(arg, 8) }
831
+ new_command('dc', 'focus C struct in data window: <name> <addr>') { |arg|
832
+ name, addr = arg.strip.split(/\s+/, 2)
833
+ addr = (addr ? solve_expr(addr) : @parent_widget.mem.curaddr)
834
+ @parent_widget.mem.focus_addr(addr, :cstruct, false, name)
835
+ }
836
+ new_command('dC', 'dump C struct: dC <name> <addr>') { |arg|
837
+ name, addr = arg.strip.split(/\s+/, 2)
838
+ addr = (addr ? solve_expr(addr) : @parent_widget.mem.curaddr)
839
+ if st = @dbg.disassembler.c_parser.decode_c_struct(name, @dbg.memory, addr)
840
+ add_log st.to_s.gsub("\t", ' ')
841
+ end
842
+ }
786
843
  new_command('u', 'focus code window on an address') { |arg| p.code.focus_addr(solve_expr(arg)) }
787
844
  new_command('.', 'focus code window on current address') { p.code.focus_addr(solve_expr(@dbg.register_pc.to_s)) }
788
845
  new_command('wc', 'set code window height') { |arg|
@@ -828,12 +885,14 @@ class DbgConsoleWidget < DrawableWidget
828
885
  cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
829
886
  @dbg.bpx(solve_expr(e), o, cd, &cb)
830
887
  }
831
- new_command('hwbp', 'set a hardware breakpoint') { |arg|
832
- arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
833
- e, c, a = $1, ($2 || $4), $3
888
+ new_command('hwbp', 'set a hardware breakpoint (hwbp 0x2345 w)') { |arg|
889
+ arg =~ /^(.*?)( once)?( [rwx])?(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
890
+ e, o, t, c, a = $1, $2, $3, ($4 || $6), $5
891
+ o = o ? true : false
892
+ t = (t || 'x').strip.to_sym
834
893
  cd = parse_expr(c) if c
835
894
  cb = lambda { a.split(';').each { |aaa| run_command(aaa) } } if a
836
- @dbg.hwbp(solve_expr(e), :x, 1, false, cd, &cb)
895
+ @dbg.hwbp(solve_expr(e), t, 1, o, cd, &cb)
837
896
  }
838
897
  new_command('bpm', 'set a hardware memory breakpoint: bpm r 0x4800ff 16') { |arg|
839
898
  arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
@@ -846,7 +905,7 @@ class DbgConsoleWidget < DrawableWidget
846
905
  exp = solve_expr!(e)
847
906
  len = solve_expr(e) if e != ''
848
907
  len ||= 1
849
- @dbg.hwbp(exp, mode, len, false, cd, &cb)
908
+ @dbg.bpm(exp, mode, len, false, cd, &cb)
850
909
  }
851
910
  new_command('g', 'wait until target reaches the specified address') { |arg|
852
911
  arg =~ /^(.*?)(?: if (.*?))?(?: do (.*?))?(?: if (.*?))?$/i
@@ -1038,7 +1097,7 @@ class DbgConsoleWidget < DrawableWidget
1038
1097
  add_log "#{t} #{stf[:state]} #{stf[:info]}"
1039
1098
  }
1040
1099
  }
1041
-
1100
+
1042
1101
  new_command('pid', 'select a pid') { |arg|
1043
1102
  if pid = solve_expr(arg)
1044
1103
  @dbg.pid = pid
@@ -1072,7 +1131,7 @@ class DbgConsoleWidget < DrawableWidget
1072
1131
  @dbg.ignore_newthread = false
1073
1132
  @dbg.ignore_endthread = false
1074
1133
  }
1075
- new_command('thread_event_ignore', 'ignore thread creation/termination') {
1134
+ new_command('thread_events_ignore', 'ignore thread creation/termination') {
1076
1135
  @dbg.ignore_newthread = true
1077
1136
  @dbg.ignore_endthread = true
1078
1137
  }
@@ -1101,6 +1160,12 @@ class DbgConsoleWidget < DrawableWidget
1101
1160
  @dbg.create_process(arg)
1102
1161
  }
1103
1162
 
1163
+ new_command('plugin', 'load', 'load a debugger plugin') { |arg|
1164
+ @dbg.load_plugin arg
1165
+ add_log "loaded plugin #{File.basename(arg, '.rb')}"
1166
+ }
1167
+
1168
+
1104
1169
  @dbg.ui_command_setup(self) if @dbg.respond_to? :ui_command_setup
1105
1170
  end
1106
1171
 
@@ -1122,12 +1187,13 @@ class DbgConsoleWidget < DrawableWidget
1122
1187
  end
1123
1188
 
1124
1189
  def run_command(cmd)
1190
+ cmd = cmd.sub(/^\s+/, '')
1125
1191
  cn = cmd.split.first
1126
1192
  if not @commands[cn]
1127
1193
  a = @commands.keys.find_all { |k| k[0, cn.length] == cn }
1128
1194
  cn = a.first if a.length == 1
1129
1195
  end
1130
- if pc = @commands[cn]
1196
+ if pc = @commands[cn]
1131
1197
  pc[cmd.split(/\s+/, 2)[1].to_s]
1132
1198
  else
1133
1199
  add_log 'unknown command'
@@ -145,7 +145,7 @@ end
145
145
  class DrawableWidget < Gtk::DrawingArea
146
146
  include Msgbox
147
147
 
148
- attr_accessor :parent_widget, :caret_x, :caret_y, :hl_word
148
+ attr_accessor :parent_widget, :caret_x, :caret_y, :hl_word, :hl_word_re
149
149
  # this hash is used to determine the colors of the Gui elements (background, caret, ...)
150
150
  # modifications to it are only useful before the widget is first rendered (IE before Gui.main)
151
151
  attr_accessor :default_color_association
@@ -153,7 +153,7 @@ class DrawableWidget < Gtk::DrawingArea
153
153
  # keypress event keyval traduction table
154
154
  Keyboard_trad = Gdk::Keyval.constants.grep(/^GDK_/).inject({}) { |h, cst|
155
155
  v = Gdk::Keyval.const_get(cst)
156
- key = cst.to_s.sub(/^GDK_/, '').sub(/^KP_/, '')
156
+ key = cst.to_s.sub(/^GDK_/, '').sub(/^KEY_/, '').sub(/^KP_/, '')
157
157
  if key.length == 1
158
158
  key = key[0] # ?a, ?b etc
159
159
  else
@@ -161,7 +161,7 @@ class DrawableWidget < Gtk::DrawingArea
161
161
  key = {
162
162
  :page_up => :pgup, :page_down => :pgdown, :next => :pgdown,
163
163
  :escape => :esc, :return => :enter, :l1 => :f11, :l2 => :f12,
164
- :prior => :pgup,
164
+ :prior => :pgup, :menu => :popupmenu,
165
165
 
166
166
  :space => ?\ ,
167
167
  :asciitilde => ?~, :quoteleft => ?`,
@@ -189,6 +189,15 @@ class DrawableWidget < Gtk::DrawingArea
189
189
  h.update v => key
190
190
  }
191
191
 
192
+ BasicColor = { :white => 'fff', :palegrey => 'ddd', :black => '000', :grey => '444',
193
+ :red => 'f44', :darkred => '800', :palered => 'faa',
194
+ :green => '4f4', :darkgreen => '080', :palegreen => 'afa',
195
+ :blue => '44f', :darkblue => '008', :paleblue => 'aaf',
196
+ :yellow => 'ff4', :darkyellow => '440', :paleyellow => 'ffa',
197
+ :orange => 'fc8',
198
+
199
+ }
200
+
192
201
  def initialize(*a, &b)
193
202
  @parent_widget = nil
194
203
 
@@ -226,7 +235,6 @@ class DrawableWidget < Gtk::DrawingArea
226
235
  grab_focus
227
236
  case ev.button
228
237
  when 1; protect { click(ev.x, ev.y) } if respond_to? :click
229
- when 3; protect { rightclick(ev.x, ev.y) } if respond_to? :rightclick
230
238
  end
231
239
  when Gdk::Event::Type::BUTTON2_PRESS
232
240
  case ev.button
@@ -245,8 +253,11 @@ class DrawableWidget < Gtk::DrawingArea
245
253
  } if respond_to? :mousemove
246
254
 
247
255
  signal_connect('button_release_event') { |w, ev|
248
- protect { mouserelease(ev.x, ev.y) } if ev.button == 1
249
- } if respond_to? :mouserelease
256
+ case ev.button
257
+ when 1; protect { mouserelease(ev.x, ev.y) } if respond_to? :mouserelease
258
+ when 3; protect { rightclick(ev.x, ev.y) } if respond_to? :rightclick
259
+ end
260
+ }
250
261
 
251
262
  signal_connect('scroll_event') { |w, ev|
252
263
  dir = case ev.direction
@@ -273,12 +284,7 @@ class DrawableWidget < Gtk::DrawingArea
273
284
  }
274
285
 
275
286
  signal_connect('realize') {
276
- { :white => 'fff', :palegrey => 'ddd', :black => '000', :grey => '444',
277
- :red => 'f00', :darkred => '800', :palered => 'fcc',
278
- :green => '0f0', :darkgreen => '080', :palegreen => 'cfc',
279
- :blue => '00f', :darkblue => '008', :paleblue => 'ccf',
280
- :yellow => 'ff0', :darkyellow => '440', :paleyellow => 'ffc',
281
- }.each { |tag, val|
287
+ BasicColor.each { |tag, val|
282
288
  @color[tag] = color(val)
283
289
  }
284
290
 
@@ -302,7 +308,11 @@ class DrawableWidget < Gtk::DrawingArea
302
308
  # create a color from a 'rgb' description
303
309
  def color(val)
304
310
  if not @color[val]
305
- @color[val] = Gdk::Color.new(*val.unpack('CCC').map { |c| (c.chr*4).hex })
311
+ v = case val.length
312
+ when 3; val.scan(/./).map { |c| (c*4).to_i(16) }
313
+ when 6; val.scan(/../).map { |c| (c+c).to_i(16) }
314
+ end
315
+ @color[val] = Gdk::Color.new(*v)
306
316
  window.colormap.alloc_color(@color[val], true, true)
307
317
  end
308
318
  @color[val]
@@ -325,20 +335,48 @@ class DrawableWidget < Gtk::DrawingArea
325
335
 
326
336
  # change the color association
327
337
  # arg is a hash function symbol => color symbol
328
- # color must be allocated
329
338
  # check #initialize/sig('realize') for initial function/color list
339
+ # if called before the widget is first displayed onscreen, will register a hook to re-call itself later
330
340
  def set_color_association(hash)
331
- hash.each { |k, v| @color[k] = color(v) }
332
- modify_bg Gtk::STATE_NORMAL, @color[:background]
333
- gui_update
341
+ if not realized?
342
+ sid = signal_connect('realize') {
343
+ signal_handler_disconnect(sid)
344
+ set_color_association(hash)
345
+ }
346
+ else
347
+ hord = Hash.new { |h, k| h[k] = (hash[k] ? h[hash[k]] + 1 : 0) }
348
+ hash.sort_by { |k, v| hord[k] }.each { |k, v| @color[k] = color(v) }
349
+ modify_bg Gtk::STATE_NORMAL, @color[:background]
350
+ gui_update
351
+ end
352
+ end
353
+
354
+ def new_menu
355
+ toplevel.new_menu
356
+ end
357
+ def addsubmenu(*a, &b)
358
+ toplevel.addsubmenu(*a, &b)
359
+ end
360
+ def popupmenu(m, x, y)
361
+ toplevel.popupmenu(m, (x+allocation.x).to_i, (y+allocation.y).to_i)
334
362
  end
335
363
 
336
364
  # update @hl_word from a line & offset, return nil if unchanged
337
- def update_hl_word(line, offset)
365
+ def update_hl_word(line, offset, mode=:asm)
338
366
  return if not line
339
367
  word = line[0...offset].to_s[/\w*$/] << line[offset..-1].to_s[/^\w*/]
340
368
  word = nil if word == ''
341
- @hl_word = word if @hl_word != word
369
+ if @hl_word != word
370
+ if word
371
+ if mode == :asm and defined?(@dasm) and @dasm
372
+ re = @dasm.gui_hilight_word_regexp(word)
373
+ else
374
+ re = Regexp.escape word
375
+ end
376
+ @hl_word_re = /^(.*?)(\b(?:#{re})\b)/
377
+ end
378
+ @hl_word = word
379
+ end
342
380
  end
343
381
 
344
382
  def paint
@@ -385,6 +423,20 @@ class DrawableWidget < Gtk::DrawingArea
385
423
  end
386
424
 
387
425
  def draw_rectangle(x, y, w, h)
426
+ # GTK clips coords around 0x8000
427
+ return if x > 0x7000 or y > 0x7000
428
+ if x < -0x7000
429
+ w += x + 100
430
+ x = -100
431
+ end
432
+ if y < -0x7000
433
+ h += y + 100
434
+ y = -100
435
+ end
436
+ return if w <= 0 or h <= 0
437
+ w = 0x7000 if w > 0x7000
438
+ h = 0x7000 if h > 0x7000
439
+
388
440
  @w.draw_rectangle(@gc, true, x, y, w, h)
389
441
  end
390
442
 
@@ -394,6 +446,29 @@ class DrawableWidget < Gtk::DrawingArea
394
446
  end
395
447
 
396
448
  def draw_line(x, y, ex, ey)
449
+ if x.abs > 0x7000
450
+ return if ex.abs > 0x7000 and ((ex < 0) == (x < 0))
451
+ ox = x
452
+ x = ((x > 0) ? 0x7000 : -0x7000)
453
+ y = ey+(x-ex)*(y-ey)/(ox-ex)
454
+ end
455
+ if ex.abs > 0x7000
456
+ oex = ex
457
+ ex = ((ex > 0) ? 0x7000 : -0x7000)
458
+ ey = y+(ex-x)*(ey-y)/(oex-x)
459
+ end
460
+ if y.abs > 0x7000
461
+ return if ey.abs > 0x7000 and ((ey < 0) == (y < 0))
462
+ oy = y
463
+ y = ((y > 0) ? 0x7000 : -0x7000)
464
+ x = ex+(y-ey)*(x-ex)/(oy-ey)
465
+ end
466
+ if ey.abs > 0x7000
467
+ oey = ey
468
+ ey = ((ey > 0) ? 0x7000 : -0x7000)
469
+ ex = x+(ey-y)*(ex-x)/(oey-y)
470
+ end
471
+
397
472
  @w.draw_line(@gc, x, y, ex, ey)
398
473
  end
399
474
 
@@ -403,6 +478,7 @@ class DrawableWidget < Gtk::DrawingArea
403
478
  end
404
479
 
405
480
  def draw_string(x, y, str)
481
+ return if x.abs > 0x7000 or y.abs > 0x7000
406
482
  @layout.text = str
407
483
  @w.draw_layout(@gc, x, y, @layout)
408
484
  end
@@ -412,6 +488,23 @@ class DrawableWidget < Gtk::DrawingArea
412
488
  draw_string(x, y, str)
413
489
  end
414
490
 
491
+ # same as draw_string_color + hilight @hl_word_re
492
+ def draw_string_hl(col, x, y, str)
493
+ if @hl_word
494
+ while str =~ @hl_word_re
495
+ s1, s2 = $1, $2
496
+ draw_string_color(col, x, y, s1)
497
+ x += s1.length*@font_width
498
+ hl_w = s2.length*@font_width
499
+ draw_rectangle_color(:hl_word_bg, x, y, hl_w, @font_height)
500
+ draw_string_color(:hl_word, x, y, s2)
501
+ x += hl_w
502
+ str = str[s1.length+s2.length..-1]
503
+ end
504
+ end
505
+ draw_string_color(col, x, y, str)
506
+ end
507
+
415
508
  def clipboard_copy(buf)
416
509
  clipboard = Gtk::Clipboard.get(Gdk::Selection::PRIMARY)
417
510
  clipboard.text = buf
@@ -477,15 +570,35 @@ class InputBox < Gtk::Dialog
477
570
  @textwidget = Gtk::TextView.new
478
571
  if opts[:text]
479
572
  @textwidget.buffer.text = opts[:text].to_s
480
- @textwidget.buffer.move_mark('selection_bound', @textwidget.buffer.start_iter)
481
- @textwidget.buffer.move_mark('insert', @textwidget.buffer.end_iter)
573
+ text_select_all
482
574
  end
483
575
 
576
+ @@history ||= {}
577
+ histkey = opts[:history] || str[0, 10]
578
+ @history = (@@history[histkey] ||= [])
579
+ @history_off = @history.length
580
+
484
581
  @textwidget.signal_connect('key_press_event') { |w, ev|
485
582
  key = DrawableWidget::Keyboard_trad[ev.keyval]
486
583
  case key
487
- when :escape; response(RESPONSE_REJECT) ; true
488
- when :enter; response(RESPONSE_ACCEPT) ; true
584
+ when :escape
585
+ response(RESPONSE_REJECT)
586
+ true
587
+ when :enter
588
+ @history << @textwidget.buffer.text.to_s
589
+ @history.pop if @history.last == ''
590
+ @history.pop if @history.last == @history[-2]
591
+ response(RESPONSE_ACCEPT)
592
+ true
593
+ when :up, :down
594
+ txt = @textwidget.buffer.text.to_s
595
+ if (@history_off < @history.length or @history.last != txt)
596
+ @history[@history_off] = txt
597
+ end
598
+ @history_off += (key == :up ? -1 : 1)
599
+ @history_off %= @history.length
600
+ @textwidget.buffer.text = @history[@history_off].to_s
601
+ text_select_all
489
602
  end
490
603
  }
491
604
 
@@ -502,9 +615,9 @@ class InputBox < Gtk::Dialog
502
615
  Gtk::Drag.dest_set(self,
503
616
  Gtk::Drag::DEST_DEFAULT_MOTION |
504
617
  Gtk::Drag::DEST_DEFAULT_DROP,
505
- [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
618
+ [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
506
619
  Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
507
-
620
+
508
621
  signal_connect('drag_data_received') { |w, dc, x, y, data, info, time|
509
622
  dc.targets.each { |target|
510
623
  next if target.name != 'text/plain' and target.name != 'text/uri-list'
@@ -521,6 +634,11 @@ class InputBox < Gtk::Dialog
521
634
  present
522
635
  end
523
636
 
637
+ def text_select_all
638
+ @textwidget.buffer.move_mark('selection_bound', @textwidget.buffer.start_iter)
639
+ @textwidget.buffer.move_mark('insert', @textwidget.buffer.end_iter)
640
+ end
641
+
524
642
  def text ; @textwidget.buffer.text ; end
525
643
  def text=(nt) ; @textwidget.buffer.text = nt ; end
526
644
  end
@@ -604,7 +722,7 @@ class ListWindow < Gtk::Dialog
604
722
  tvc = Gtk::TreeViewColumn.new(col, crt)
605
723
  tvc.sort_column_id = i
606
724
  tvc.set_cell_data_func(crt) { |_tvc, _crt, model, iter|
607
- _crt.text = iter[i]
725
+ _crt.text = iter[i]
608
726
  if @color_callback
609
727
  fu = (0...cols.length).map { |ii| iter[ii] }
610
728
  fg, bg = @color_callback[fu]
@@ -635,7 +753,7 @@ class ListWindow < Gtk::Dialog
635
753
 
636
754
  remove vbox
637
755
  add Gtk::ScrolledWindow.new.add(treeview)
638
- toplevel.set_default_size cols.length*120, 400
756
+ toplevel.set_default_size cols.length*240, 400
639
757
 
640
758
  show if not h[:noshow]
641
759
 
@@ -666,6 +784,7 @@ class Window < Gtk::Window
666
784
  @menubar = Gtk::MenuBar.new
667
785
  @accel_group = Gtk::AccelGroup.new
668
786
 
787
+ set_gravity Gdk::Window::GRAVITY_STATIC
669
788
  @vbox.add @menubar, 'expand' => false
670
789
  @child = nil
671
790
  s = Gdk::Screen.default
@@ -678,14 +797,13 @@ class Window < Gtk::Window
678
797
  initialize_window(*a, &b)
679
798
  build_menu
680
799
  update_menu
681
-
682
-
800
+
683
801
  Gtk::Drag.dest_set(self,
684
802
  Gtk::Drag::DEST_DEFAULT_MOTION |
685
803
  Gtk::Drag::DEST_DEFAULT_DROP,
686
- [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
804
+ [['text/plain', 0, 0], ['text/uri-list', 0, 0]],
687
805
  Gdk::DragContext::ACTION_COPY | Gdk::DragContext::ACTION_MOVE)
688
-
806
+
689
807
  signal_connect('drag_data_received') { |w, dc, x, y, data, info, time|
690
808
  dc.targets.each { |target|
691
809
  next if target.name != 'text/plain' and target.name != 'text/uri-list'
@@ -734,6 +852,13 @@ class Window < Gtk::Window
734
852
  l.grep(::Array).first if l
735
853
  end
736
854
 
855
+ def popupmenu(m, x, y)
856
+ mh = Gtk::Menu.new
857
+ m.each { |e| create_menu_item(mh, e) }
858
+ mh.show_all
859
+ mh.popup(nil, nil, 2, 0) { |_m, _x, _y, _p| [position[0]+x, position[1]+y, true] }
860
+ end
861
+
737
862
  # append stuff to a menu
738
863
  # arglist:
739
864
  # empty = menu separator
@@ -801,13 +926,10 @@ class Window < Gtk::Window
801
926
  key = accel[-1]
802
927
  if key == ?>
803
928
  key = accel[/<(.*)>/, 1]
804
- key = case key
805
- when 'enter'; Gdk::Keyval::GDK_Return
806
- when 'esc'; Gdk::Keyval::GDK_Escape
807
- when 'tab'; Gdk::Keyval::GDK_Tab
808
- when /^f(\d\d?)$/i; Gdk::Keyval.const_get("GDK_#{key.upcase}")
809
- else ??
810
- end
929
+ key = DrawableWidget::Keyboard_trad.index(case key
930
+ when 'enter', 'esc', 'tab', /^f(\d\d?)$/i; key.downcase.to_sym
931
+ else ??
932
+ end)
811
933
  end
812
934
  key = key.unpack('C')[0] if key.kind_of? String # yay rb19
813
935
  item.add_accelerator('activate', @accel_group, key, (accel[0] == ?^ ? Gdk::Window::CONTROL_MASK : 0), Gtk::ACCEL_VISIBLE)
@@ -815,7 +937,7 @@ class Window < Gtk::Window
815
937
  if action
816
938
  a = action
817
939
  if check
818
- a = lambda { item.active = action.call(item.active?) }
940
+ a = lambda { |it| it.active = action.call(it.active?) }
819
941
  end
820
942
  item.signal_connect('activate') { protect { a.call(item) } }
821
943
  end
@@ -838,7 +960,7 @@ class ToolWindow < Gtk::Dialog
838
960
  initialize_window(*a, &b)
839
961
  show_all
840
962
  end
841
-
963
+
842
964
  def widget=(w)
843
965
  remove @child if @child
844
966
  @child = w