metasm 1.0.0

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 (192) hide show
  1. data/BUGS +11 -0
  2. data/CREDITS +17 -0
  3. data/README +270 -0
  4. data/TODO +114 -0
  5. data/doc/code_organisation.txt +146 -0
  6. data/doc/const_missing.txt +16 -0
  7. data/doc/core_classes.txt +75 -0
  8. data/doc/feature_list.txt +53 -0
  9. data/doc/index.txt +59 -0
  10. data/doc/install_notes.txt +170 -0
  11. data/doc/style.css +3 -0
  12. data/doc/use_cases.txt +18 -0
  13. data/lib/metasm.rb +80 -0
  14. data/lib/metasm/arm.rb +12 -0
  15. data/lib/metasm/arm/debug.rb +39 -0
  16. data/lib/metasm/arm/decode.rb +167 -0
  17. data/lib/metasm/arm/encode.rb +77 -0
  18. data/lib/metasm/arm/main.rb +75 -0
  19. data/lib/metasm/arm/opcodes.rb +177 -0
  20. data/lib/metasm/arm/parse.rb +130 -0
  21. data/lib/metasm/arm/render.rb +55 -0
  22. data/lib/metasm/compile_c.rb +1457 -0
  23. data/lib/metasm/dalvik.rb +8 -0
  24. data/lib/metasm/dalvik/decode.rb +196 -0
  25. data/lib/metasm/dalvik/main.rb +60 -0
  26. data/lib/metasm/dalvik/opcodes.rb +366 -0
  27. data/lib/metasm/decode.rb +213 -0
  28. data/lib/metasm/decompile.rb +2659 -0
  29. data/lib/metasm/disassemble.rb +2068 -0
  30. data/lib/metasm/disassemble_api.rb +1280 -0
  31. data/lib/metasm/dynldr.rb +1329 -0
  32. data/lib/metasm/encode.rb +333 -0
  33. data/lib/metasm/exe_format/a_out.rb +194 -0
  34. data/lib/metasm/exe_format/autoexe.rb +82 -0
  35. data/lib/metasm/exe_format/bflt.rb +189 -0
  36. data/lib/metasm/exe_format/coff.rb +455 -0
  37. data/lib/metasm/exe_format/coff_decode.rb +901 -0
  38. data/lib/metasm/exe_format/coff_encode.rb +1078 -0
  39. data/lib/metasm/exe_format/dex.rb +457 -0
  40. data/lib/metasm/exe_format/dol.rb +145 -0
  41. data/lib/metasm/exe_format/elf.rb +923 -0
  42. data/lib/metasm/exe_format/elf_decode.rb +979 -0
  43. data/lib/metasm/exe_format/elf_encode.rb +1375 -0
  44. data/lib/metasm/exe_format/macho.rb +827 -0
  45. data/lib/metasm/exe_format/main.rb +228 -0
  46. data/lib/metasm/exe_format/mz.rb +164 -0
  47. data/lib/metasm/exe_format/nds.rb +172 -0
  48. data/lib/metasm/exe_format/pe.rb +437 -0
  49. data/lib/metasm/exe_format/serialstruct.rb +246 -0
  50. data/lib/metasm/exe_format/shellcode.rb +114 -0
  51. data/lib/metasm/exe_format/xcoff.rb +167 -0
  52. data/lib/metasm/gui.rb +23 -0
  53. data/lib/metasm/gui/cstruct.rb +373 -0
  54. data/lib/metasm/gui/dasm_coverage.rb +199 -0
  55. data/lib/metasm/gui/dasm_decomp.rb +369 -0
  56. data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
  57. data/lib/metasm/gui/dasm_graph.rb +1354 -0
  58. data/lib/metasm/gui/dasm_hex.rb +543 -0
  59. data/lib/metasm/gui/dasm_listing.rb +599 -0
  60. data/lib/metasm/gui/dasm_main.rb +906 -0
  61. data/lib/metasm/gui/dasm_opcodes.rb +291 -0
  62. data/lib/metasm/gui/debug.rb +1228 -0
  63. data/lib/metasm/gui/gtk.rb +884 -0
  64. data/lib/metasm/gui/qt.rb +495 -0
  65. data/lib/metasm/gui/win32.rb +3004 -0
  66. data/lib/metasm/gui/x11.rb +621 -0
  67. data/lib/metasm/ia32.rb +14 -0
  68. data/lib/metasm/ia32/compile_c.rb +1523 -0
  69. data/lib/metasm/ia32/debug.rb +193 -0
  70. data/lib/metasm/ia32/decode.rb +1167 -0
  71. data/lib/metasm/ia32/decompile.rb +564 -0
  72. data/lib/metasm/ia32/encode.rb +314 -0
  73. data/lib/metasm/ia32/main.rb +233 -0
  74. data/lib/metasm/ia32/opcodes.rb +872 -0
  75. data/lib/metasm/ia32/parse.rb +327 -0
  76. data/lib/metasm/ia32/render.rb +91 -0
  77. data/lib/metasm/main.rb +1193 -0
  78. data/lib/metasm/mips.rb +11 -0
  79. data/lib/metasm/mips/compile_c.rb +7 -0
  80. data/lib/metasm/mips/decode.rb +253 -0
  81. data/lib/metasm/mips/encode.rb +51 -0
  82. data/lib/metasm/mips/main.rb +72 -0
  83. data/lib/metasm/mips/opcodes.rb +443 -0
  84. data/lib/metasm/mips/parse.rb +51 -0
  85. data/lib/metasm/mips/render.rb +43 -0
  86. data/lib/metasm/os/gnu_exports.rb +270 -0
  87. data/lib/metasm/os/linux.rb +1112 -0
  88. data/lib/metasm/os/main.rb +1686 -0
  89. data/lib/metasm/os/remote.rb +527 -0
  90. data/lib/metasm/os/windows.rb +2027 -0
  91. data/lib/metasm/os/windows_exports.rb +745 -0
  92. data/lib/metasm/parse.rb +876 -0
  93. data/lib/metasm/parse_c.rb +3938 -0
  94. data/lib/metasm/pic16c/decode.rb +42 -0
  95. data/lib/metasm/pic16c/main.rb +17 -0
  96. data/lib/metasm/pic16c/opcodes.rb +68 -0
  97. data/lib/metasm/ppc.rb +11 -0
  98. data/lib/metasm/ppc/decode.rb +264 -0
  99. data/lib/metasm/ppc/decompile.rb +251 -0
  100. data/lib/metasm/ppc/encode.rb +51 -0
  101. data/lib/metasm/ppc/main.rb +129 -0
  102. data/lib/metasm/ppc/opcodes.rb +410 -0
  103. data/lib/metasm/ppc/parse.rb +52 -0
  104. data/lib/metasm/preprocessor.rb +1277 -0
  105. data/lib/metasm/render.rb +130 -0
  106. data/lib/metasm/sh4.rb +8 -0
  107. data/lib/metasm/sh4/decode.rb +336 -0
  108. data/lib/metasm/sh4/main.rb +292 -0
  109. data/lib/metasm/sh4/opcodes.rb +381 -0
  110. data/lib/metasm/x86_64.rb +12 -0
  111. data/lib/metasm/x86_64/compile_c.rb +1025 -0
  112. data/lib/metasm/x86_64/debug.rb +59 -0
  113. data/lib/metasm/x86_64/decode.rb +268 -0
  114. data/lib/metasm/x86_64/encode.rb +264 -0
  115. data/lib/metasm/x86_64/main.rb +135 -0
  116. data/lib/metasm/x86_64/opcodes.rb +118 -0
  117. data/lib/metasm/x86_64/parse.rb +68 -0
  118. data/misc/bottleneck.rb +61 -0
  119. data/misc/cheader-findpppath.rb +58 -0
  120. data/misc/hexdiff.rb +74 -0
  121. data/misc/hexdump.rb +55 -0
  122. data/misc/metasm-all.rb +13 -0
  123. data/misc/objdiff.rb +47 -0
  124. data/misc/objscan.rb +40 -0
  125. data/misc/pdfparse.rb +661 -0
  126. data/misc/ppc_pdf2oplist.rb +192 -0
  127. data/misc/tcp_proxy_hex.rb +84 -0
  128. data/misc/txt2html.rb +440 -0
  129. data/samples/a.out.rb +31 -0
  130. data/samples/asmsyntax.rb +77 -0
  131. data/samples/bindiff.rb +555 -0
  132. data/samples/compilation-steps.rb +49 -0
  133. data/samples/cparser_makestackoffset.rb +55 -0
  134. data/samples/dasm-backtrack.rb +38 -0
  135. data/samples/dasmnavig.rb +318 -0
  136. data/samples/dbg-apihook.rb +228 -0
  137. data/samples/dbghelp.rb +143 -0
  138. data/samples/disassemble-gui.rb +102 -0
  139. data/samples/disassemble.rb +133 -0
  140. data/samples/dump_upx.rb +95 -0
  141. data/samples/dynamic_ruby.rb +1929 -0
  142. data/samples/elf_list_needed.rb +46 -0
  143. data/samples/elf_listexports.rb +33 -0
  144. data/samples/elfencode.rb +25 -0
  145. data/samples/exeencode.rb +128 -0
  146. data/samples/factorize-headers-elfimports.rb +77 -0
  147. data/samples/factorize-headers-peimports.rb +109 -0
  148. data/samples/factorize-headers.rb +43 -0
  149. data/samples/gdbclient.rb +583 -0
  150. data/samples/generate_libsigs.rb +102 -0
  151. data/samples/hotfix_gtk_dbg.rb +59 -0
  152. data/samples/install_win_env.rb +78 -0
  153. data/samples/lindebug.rb +924 -0
  154. data/samples/linux_injectsyscall.rb +95 -0
  155. data/samples/machoencode.rb +31 -0
  156. data/samples/metasm-shell.rb +91 -0
  157. data/samples/pe-hook.rb +69 -0
  158. data/samples/pe-ia32-cpuid.rb +203 -0
  159. data/samples/pe-mips.rb +35 -0
  160. data/samples/pe-shutdown.rb +78 -0
  161. data/samples/pe-testrelocs.rb +51 -0
  162. data/samples/pe-testrsrc.rb +24 -0
  163. data/samples/pe_listexports.rb +31 -0
  164. data/samples/peencode.rb +19 -0
  165. data/samples/peldr.rb +494 -0
  166. data/samples/preprocess-flatten.rb +19 -0
  167. data/samples/r0trace.rb +308 -0
  168. data/samples/rubstop.rb +399 -0
  169. data/samples/scan_pt_gnu_stack.rb +54 -0
  170. data/samples/scanpeexports.rb +62 -0
  171. data/samples/shellcode-c.rb +40 -0
  172. data/samples/shellcode-dynlink.rb +146 -0
  173. data/samples/source.asm +34 -0
  174. data/samples/struct_offset.rb +47 -0
  175. data/samples/testpe.rb +32 -0
  176. data/samples/testraw.rb +45 -0
  177. data/samples/win32genloader.rb +132 -0
  178. data/samples/win32hooker-advanced.rb +169 -0
  179. data/samples/win32hooker.rb +96 -0
  180. data/samples/win32livedasm.rb +33 -0
  181. data/samples/win32remotescan.rb +133 -0
  182. data/samples/wintrace.rb +92 -0
  183. data/tests/all.rb +8 -0
  184. data/tests/dasm.rb +39 -0
  185. data/tests/dynldr.rb +35 -0
  186. data/tests/encodeddata.rb +132 -0
  187. data/tests/ia32.rb +82 -0
  188. data/tests/mips.rb +116 -0
  189. data/tests/parse_c.rb +239 -0
  190. data/tests/preprocessor.rb +269 -0
  191. data/tests/x86_64.rb +62 -0
  192. metadata +255 -0
@@ -0,0 +1,495 @@
1
+ # This file is part of Metasm, the Ruby assembly manipulation suite
2
+ # Copyright (C) 2006-2009 Yoann GUILLOT
3
+ #
4
+ # Licence is LGPL, see LICENCE in the top-level directory
5
+
6
+
7
+ require 'Qt4'
8
+
9
+ module Metasm
10
+ module Gui
11
+
12
+ module Protect
13
+ @@lasterror = Time.now
14
+ def protect
15
+ yield
16
+ rescue Object
17
+ puts $!.message, $!.backtrace # also dump on stdout, for c/c
18
+ delay = Time.now-@@lasterror
19
+ sleep 1-delay if delay < 1 # msgbox flood protection
20
+ @@lasterror = Time.now
21
+ messagebox([$!.message, $!.backtrace].join("\n"), $!.class.name)
22
+ end
23
+ end
24
+
25
+ module Msgbox
26
+ include Protect
27
+
28
+ # shows a message box (non-modal)
29
+ # args: message, title/optionhash
30
+ def messagebox(text, opts={})
31
+ opts = {:title => opts} if opts.kind_of? String
32
+ mbox = Qt::MessageBox.new#(self)
33
+ mbox.text = text
34
+ mbox.window_title = opts[:title] if opts[:title]
35
+ mbox.window_modality = Qt::NonModal
36
+ mbox.show
37
+ mbox
38
+ end
39
+
40
+ # asks for user input, yields the result (unless 'cancel' clicked)
41
+ # args: prompt, :text => default text, :title => title
42
+ def inputbox(prompt, opts={})
43
+ ibox = Qt::InputDialog.new#(self)
44
+ ibox.label_text = prompt
45
+ ibox.window_title = opts[:title] if opts[:title]
46
+ ibox.text_value = opts[:text] if opts[:text]
47
+ connect(ibox, SIGNAL('TextValueSelected(v)')) { |v| protect { yield v } }
48
+ ibox.show
49
+ ibox
50
+ end
51
+
52
+ @@dialogfilefolder = nil
53
+
54
+ # asks to chose a file to open, yields filename
55
+ # args: title, :path => path
56
+ def openfile(title, opts={})
57
+ f = Qt::FileDialog.get_open_file_name(nil, title, @@dialogfilefolder)
58
+ if f and f != ''
59
+ @@dialogfilefolder = File.dirname(f)
60
+ protect { yield f }
61
+ end
62
+ f # useless, dialog is modal
63
+ end
64
+
65
+ # same as openfile, but for writing a (new) file
66
+ def savefile(title, opts={})
67
+ f = Qt::FileDialog.get_save_file_name(nil, title, @@dialogfilefolder)
68
+ if f and f != ''
69
+ @@dialogfilefolder = File.dirname(f)
70
+ protect { yield f }
71
+ end
72
+ f # useless, dialog is modal
73
+ end
74
+
75
+ # shows a window with a list of items
76
+ # the list is an array of arrays, displayed as String
77
+ # the first array is the column names
78
+ # each item clicked yields the block with the selected iterator, double-click also close the popup
79
+ # args: title, [[col0 title, col1 title...], [col0 val0, col1 val0...], [val1], [val2]...]
80
+ def listwindow(title, list, h={})
81
+ l = Qt::TreeWidget.new#(self)
82
+ l.window_title = title
83
+
84
+ cols = list.shift
85
+ #l.column_count = cols.length
86
+ l.header_labels = cols
87
+ list.each { |e|
88
+ i = Qt::TreeWidgetItem.new
89
+ e.length.times { |idx| i.set_text(idx, e[idx].to_s) }
90
+ l.add_top_level_item i
91
+ }
92
+
93
+ connect(l, SIGNAL('itemActivated(QTreeWidgetItem*,int)')) { |item, col|
94
+ next if not item.is_selected
95
+ next if not idx = l.index_of_top_level_item(item)
96
+ protect { yield(list[idx].map { |e| e.to_s }) } #if iter = treeview.selection.selected
97
+ }
98
+ connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*,int)')) { l.close }
99
+ l.resize(cols.length*120, 400)
100
+ l.show if not h[:noshow]
101
+ l
102
+ end
103
+ end
104
+
105
+ # a widget that holds many other widgets, and displays only one of them at a time
106
+ class ContainerChoiceWidget < Qt::StackedWidget
107
+ include Msgbox
108
+
109
+ attr_accessor :views, :view_indexes
110
+ def initialize(*a)
111
+ super()
112
+ @views = {}
113
+ @view_indexes = []
114
+
115
+ initialize_widget(*a)
116
+ initialize_visible if respond_to? :initialize_visible
117
+ end
118
+
119
+ def view(i)
120
+ @views[i]
121
+ end
122
+
123
+ def showview(i)
124
+ set_current_index @view_indexes.index(i)
125
+ end
126
+
127
+ def addview(name, w)
128
+ @view_indexes << name
129
+ @views[name] = w
130
+ add_widget w
131
+ end
132
+
133
+ def curview
134
+ @views[curview_index]
135
+ end
136
+
137
+ def curview_index
138
+ @view_indexes[current_index]
139
+ end
140
+ end
141
+
142
+ class ContainerVBoxWidget < Qt::VBoxLayout
143
+ def initialize(*a)
144
+ super()
145
+
146
+ signal_connect('realize') { initialize_visible } if respond_to? :initialize_visible
147
+
148
+ signal_connect('size_request') { |w, alloc| resize(*alloc) } if respond_to? :resize
149
+
150
+ self.spacing = 2
151
+
152
+ initialize_widget(*a)
153
+ end
154
+ end
155
+
156
+ class DrawableWidget < Qt::Widget
157
+ include Msgbox
158
+
159
+ attr_accessor :parent_widget, :caret_x, :caret_y, :hl_word
160
+ # this hash is used to determine the colors of the GUI elements (background, caret, ...)
161
+ # modifications to it are only useful before the widget is first rendered (IE before GUI.main)
162
+ attr_accessor :default_color_association
163
+
164
+ # keypress event keyval traduction table
165
+ # RHA no way to enumerate all Key_* constants, they are handled in Qt.const_missing
166
+ Keyboard_trad = %w[
167
+ Escape Tab Backtab Backspace Return Enter Insert Delete Pause Print SysReq Clear Home End Left Up Right
168
+ Down PageUp PageDown Shift Control Meta Alt AltGr CapsLock NumLock ScrollLock
169
+ F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15 F16 F17 F18 F19 F20 F21 F22 F23 F24 F25 F26 F27 F28 F29 F30 F31 F32 F33 F34 F35 Super_L Super_R
170
+ Menu Hyper_L Hyper_R Help Direction_L Direction_R nobreakspace exclamdown cent sterling currency yen brokenbar section diaeresis copyright
171
+ ordfeminine guillemotleft notsign hyphen registered macron degree plusminus twosuperior threesuperior acute mu paragraph periodcentered cedilla onesuperior
172
+ masculine guillemotright onequarter onehalf threequarters questiondown Agrave Aacute Acircumflex Atilde Adiaeresis Aring AE Ccedilla Egrave Eacute
173
+ Ecircumflex Ediaeresis Igrave Iacute Icircumflex Idiaeresis ETH Ntilde Ograve Oacute Ocircumflex Otilde Odiaeresis multiply Ooblique Ugrave
174
+ Uacute Ucircumflex Udiaeresis Yacute THORN ssharp division ydiaeresis Multi_key Codeinput SingleCandidate MultipleCandidate PreviousCandidate Mode_switch Kanji Muhenkan
175
+ Henkan Romaji Hiragana Katakana Hiragana_Katakana Zenkaku Hankaku Zenkaku_Hankaku Touroku Massyo Kana_Lock Kana_Shift Eisu_Shift Eisu_toggle Hangul Hangul_Start
176
+ Hangul_End Hangul_Hanja Hangul_Jamo Hangul_Romaja Hangul_Jeonja Hangul_Banja Hangul_PreHanja Hangul_PostHanja Hangul_Special Dead_Grave Dead_Acute Dead_Circumflex Dead_Tilde Dead_Macron Dead_Breve Dead_Abovedot
177
+ Dead_Diaeresis Dead_Abovering Dead_Doubleacute Dead_Caron Dead_Cedilla Dead_Ogonek Dead_Iota Dead_Voiced_Sound Dead_Semivoiced_Sound Dead_Belowdot Dead_Hook Dead_Horn Back Forward Stop Refresh
178
+ VolumeDown VolumeMute VolumeUp BassBoost BassUp BassDown TrebleUp TrebleDown MediaPlay MediaStop MediaPrevious MediaNext MediaRecord HomePage Favorites Search
179
+ Standby OpenUrl LaunchMail LaunchMedia Launch0 Launch1 Launch2 Launch3 Launch4 Launch5 Launch6 Launch7 Launch8 Launch9 LaunchA LaunchB
180
+ LaunchC LaunchD LaunchE LaunchF MediaLast unknown Call Context1 Context2 Context3 Context4 Flip Hangup No Select Yes
181
+ Execute Printer Play Sleep Zoom Cancel
182
+ ].inject({}) { |h, cst|
183
+ v = Qt.const_get("Key_#{cst}").to_i # AONETUHANOTEUHATNOHEU Qt::Enum != Fixnum
184
+ key = cst.downcase.to_sym
185
+ key = { :pageup => :pgup, :pagedown => :pgdown, :escape => :esc, :return => :enter }.fetch(key, key)
186
+ h.update v => key
187
+ }
188
+
189
+ def initialize(*a)
190
+ @parent_widget = nil
191
+
192
+ @caret_x = @caret_y = 0 # text cursor position
193
+ @oldcaret_x = @oldcaret_y = 1
194
+ @hl_word = nil
195
+
196
+ #@layout = Pango::Layout.new Gdk::Pango.context # text rendering
197
+
198
+ @color = {}
199
+ @default_color_association = {:background => :palegrey}
200
+
201
+ if a.last.kind_of? Qt::Widget
202
+ super(a.last)
203
+ else
204
+ super()
205
+ end
206
+
207
+ { :white => 'fff', :palegrey => 'ddd', :black => '000', :grey => '444',
208
+ :red => 'f00', :darkred => '800', :palered => 'fcc',
209
+ :green => '0f0', :darkgreen => '080', :palegreen => 'cfc',
210
+ :blue => '00f', :darkblue => '008', :paleblue => 'ccf',
211
+ :yellow => 'ff0', :darkyellow => '440', :paleyellow => 'ffc',
212
+ }.each { |tag, val| @color[tag] = color(val) }
213
+
214
+ initialize_widget(*a)
215
+ set_auto_fill_background true
216
+ set_color_association @default_color_association
217
+ set_focus_policy Qt::StrongFocus
218
+
219
+ initialize_visible if respond_to? :initialize_visible
220
+
221
+ set_font 'courier 10'
222
+ end
223
+
224
+ def keyPressEvent(key)
225
+ val = key.key >= 128 ? Keyboard_trad[key.key] : key.text[0].ord # must use text[0] to differenciate downcase/upcase
226
+ if key.modifiers.to_i & Qt::ControlModifier.to_i > 0 # AONETHUAAAAAAAAAAAAAAA
227
+ protect { keypress_ctrl(val) } if respond_to? :keypress_ctrl
228
+ else
229
+ protect { keypress(val) } if respond_to? :keypress
230
+ end
231
+ end
232
+
233
+ def mousePressEvent(ev)
234
+ if ev.modifiers.to_i & Qt::ControlModifier.to_i > 0
235
+ protect { click_ctrl(ev.x, ev.y) } if respond_to? :click_ctrl
236
+ else
237
+ if ev.button == Qt::LeftButton
238
+ protect { click(ev.x, ev.y) }
239
+ elsif ev.button == Qt::RightButton
240
+ protect { rightclick(ev.x, ev.y) } if respond_to? :rightclick
241
+ end
242
+ end
243
+ end
244
+
245
+ def mouseDoubleClickEvent(ev)
246
+ protect { doubleclick(ev.x, ev.y) } if respond_to? :doubleclick
247
+ end
248
+
249
+ def mouseReleaseEvent(ev)
250
+ if ev.button == Qt::LeftButton
251
+ protect { mouserelease(ev.x, ev.y) } if respond_to? :mouserelease
252
+ end
253
+ end
254
+
255
+ def mouseMoveEvent(ev)
256
+ if ev.modifiers.to_i & Qt::ControlModifier.to_i > 0
257
+ protect { mousemove_ctrl(ev.x, ev.y) } if respond_to? :mousemove_ctrl
258
+ else
259
+ protect { mousemove(ev.x, ev.y) } if respond_to? :mousemove
260
+ end
261
+ end
262
+
263
+ def wheelEvent(ev)
264
+ dir = ev.delta > 0 ? :up : :down
265
+ if ev.modifiers.to_i & Qt::ControlModifier.to_i > 0
266
+ protect { mouse_wheel_ctrl(dir, ev.x, ev.y) } if respond_to? :mouse_wheel_ctrl
267
+ else
268
+ protect { mouse_wheel(dir, ev.x, ev.y) } if respond_to? :mouse_wheel
269
+ end
270
+ end
271
+
272
+ def resizeEvent(ev)
273
+ protect { resized(ev.size.width, ev.size.height) } if respond_to? :resized
274
+ end
275
+
276
+ def grab_focus; set_focus end
277
+
278
+ def paintEvent(*a)
279
+ @painter = Qt::Painter.new(self)
280
+ protect { paint }
281
+ @painter.end
282
+ @painter = nil
283
+ end
284
+
285
+ def paint
286
+ end
287
+
288
+ def gui_update
289
+ redraw
290
+ end
291
+
292
+ # create a color from a 'rgb' description
293
+ def color(val)
294
+ @color[val] ||= Qt::Color.new(*val.unpack('CCC').map { |c| (c.chr*2).hex })
295
+ end
296
+
297
+ def set_caret_from_click(x, y)
298
+ @caret_x = (x-1).to_i / @font_width
299
+ @caret_y = y.to_i / @font_height
300
+ update_caret
301
+ end
302
+
303
+ # change the font of the widget
304
+ # arg is a Gtk Fontdescription string (eg 'courier 10')
305
+ def set_font(descr)
306
+ descr, sz = descr.split
307
+ super(Qt::Font.new(descr, sz.to_i))
308
+ @font_width = font_metrics.width('x')
309
+ @font_height = font_metrics.line_spacing
310
+ @font_descent = font_metrics.descent
311
+ gui_update
312
+ end
313
+
314
+ # change the color association
315
+ # arg is a hash function symbol => color symbol
316
+ # color must be allocated
317
+ # check #initialize/sig('realize') for initial function/color list
318
+ def set_color_association(hash)
319
+ hash.each { |k, v| @color[k] = color(v) }
320
+ #set_background_role Qt::Palette::Window(color(:background))
321
+ palette.set_color(Qt::Palette::Window, color(:background))
322
+ gui_update
323
+ end
324
+
325
+ # update @hl_word from a line & offset, return nil if unchanged
326
+ def update_hl_word(line, offset)
327
+ return if not line
328
+ word = line[0...offset].to_s[/\w*$/] << line[offset..-1].to_s[/^\w*/]
329
+ word = nil if word == ''
330
+ @hl_word = word if @hl_word != word
331
+ end
332
+
333
+ # invalidate the whole widget area
334
+ def redraw
335
+ invalidate(0, 0, 1000000, 1000000)
336
+ end
337
+
338
+ def invalidate_caret(cx, cy, x=0, y=0)
339
+ invalidate(x + cx*@font_width, y + cy*@font_height, 2, @font_height)
340
+ end
341
+
342
+ def invalidate(x, y, w, h)
343
+ update x, y, w, h
344
+ end
345
+
346
+ def resized(w, h)
347
+ redraw
348
+ end
349
+
350
+ def keypress(key)
351
+ end
352
+
353
+ def keypress_ctrl(key)
354
+ end
355
+
356
+ def draw_color(col)
357
+ @col = color(col)
358
+ @painter.set_brush Qt::Brush.new(@col)
359
+ @painter.set_pen Qt::Pen.new(@col)
360
+ end
361
+
362
+ def draw_rectangle(x, y, w, h)
363
+ @painter.fill_rect(x, y, w, h, @col)
364
+ end
365
+
366
+ def draw_rectangle_color(col, x, y, w, h)
367
+ draw_color(col)
368
+ draw_rectangle(x, y, w, h)
369
+ end
370
+
371
+ def draw_line(x, y, ex, ey)
372
+ @painter.draw_line(x, y, ex, ey)
373
+ end
374
+
375
+ def draw_line_color(col, x, y, ex, ey)
376
+ draw_color(col)
377
+ draw_line(x, y, ex, ey)
378
+ end
379
+
380
+ def draw_string(x, y, str)
381
+ @painter.draw_text(x, y-@font_descent, str)
382
+ end
383
+
384
+ def draw_string_color(col, x, y, str)
385
+ draw_color(col)
386
+ draw_string(x, y, str)
387
+ end
388
+ end
389
+
390
+ class Window < Qt::MainWindow
391
+ include Msgbox
392
+
393
+ attr_accessor :menu
394
+ def initialize(*a)
395
+ super()
396
+
397
+ #connect(self, SIGNAL(:destroy)) { destroy_window }
398
+
399
+ @menu = menu_bar
400
+
401
+ screen = Qt::Application.desktop
402
+ resize screen.width*3/4, screen.height*3/4
403
+
404
+ initialize_window(*a)
405
+ build_menu
406
+
407
+ show
408
+ end
409
+
410
+ def build_menu
411
+ end
412
+
413
+ def destroy_window
414
+ end
415
+
416
+ def widget=(w)
417
+ set_central_widget w
418
+ end
419
+
420
+ def title=(t); set_window_title(t) end
421
+ def title; window_title end
422
+
423
+ def new_menu
424
+ Qt::Menu.new
425
+ end
426
+
427
+ def addsubmenu(menu, *args)
428
+ accel = args.grep(/^\^?(\w|<\w+>)$/).first
429
+ args.delete accel if accel
430
+ check = args.delete :check
431
+ submenu = args.grep(Qt::Menu).first
432
+ args.delete submenu if submenu
433
+ if label = args.shift
434
+ label = label.capitalize if label == label.upcase # TODO icon on OPEN/CLOSE etc
435
+ label = label.gsub('_', '&')
436
+ end
437
+
438
+ if submenu
439
+ submenu.title = label
440
+ menu.add_menu submenu
441
+ return
442
+ end
443
+
444
+ if check
445
+ # TODO
446
+ #item = Gtk::CheckMenuItem.new(label)
447
+ #item.active = args.shift
448
+ item = Qt::Action.new(label, self)
449
+ menu.add_action item
450
+ elsif label
451
+ item = Qt::Action.new(label, self)
452
+ menu.add_action item
453
+ else
454
+ menu.add_separator
455
+ end
456
+ item.setShortcut accel.sub('^', 'Ctrl+')
457
+
458
+ connect(item, SIGNAL(:triggered)) { protect { yield(item) } } if block_given?
459
+
460
+ item
461
+ end
462
+ end
463
+
464
+ @app = Qt::Application.new ARGV
465
+
466
+ # start the GUI main loop
467
+ def self.main
468
+ @app.exec
469
+ end
470
+
471
+ # ends the GUI main loop
472
+ def self.main_quit
473
+ @app.quit # XXX segfault..
474
+ end
475
+
476
+ # register a proc to be run whenever the gui loop is idle
477
+ # if the proc returns nil/false, delete it
478
+ def self.idle_add
479
+ t = Qt::Timer.new
480
+ t.connect(t, SIGNAL(:timeout)) { if not yield ; t.stop ; t = nil end }
481
+ t.start
482
+ end
483
+
484
+ # run a single iteration of the main_loop
485
+ # e.g. call this from time to time when doing heavy computation, to keep the UI somewhat responsive
486
+ def self.main_iter
487
+ Qt::Application.process_events
488
+ end
489
+
490
+ end
491
+ end
492
+
493
+ require 'metasm/gui/dasm_main'
494
+ require 'metasm/gui/debug'
495
+