metasm 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+