metasm 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BUGS +11 -0
- data/CREDITS +17 -0
- data/README +270 -0
- data/TODO +114 -0
- data/doc/code_organisation.txt +146 -0
- data/doc/const_missing.txt +16 -0
- data/doc/core_classes.txt +75 -0
- data/doc/feature_list.txt +53 -0
- data/doc/index.txt +59 -0
- data/doc/install_notes.txt +170 -0
- data/doc/style.css +3 -0
- data/doc/use_cases.txt +18 -0
- data/lib/metasm.rb +80 -0
- data/lib/metasm/arm.rb +12 -0
- data/lib/metasm/arm/debug.rb +39 -0
- data/lib/metasm/arm/decode.rb +167 -0
- data/lib/metasm/arm/encode.rb +77 -0
- data/lib/metasm/arm/main.rb +75 -0
- data/lib/metasm/arm/opcodes.rb +177 -0
- data/lib/metasm/arm/parse.rb +130 -0
- data/lib/metasm/arm/render.rb +55 -0
- data/lib/metasm/compile_c.rb +1457 -0
- data/lib/metasm/dalvik.rb +8 -0
- data/lib/metasm/dalvik/decode.rb +196 -0
- data/lib/metasm/dalvik/main.rb +60 -0
- data/lib/metasm/dalvik/opcodes.rb +366 -0
- data/lib/metasm/decode.rb +213 -0
- data/lib/metasm/decompile.rb +2659 -0
- data/lib/metasm/disassemble.rb +2068 -0
- data/lib/metasm/disassemble_api.rb +1280 -0
- data/lib/metasm/dynldr.rb +1329 -0
- data/lib/metasm/encode.rb +333 -0
- data/lib/metasm/exe_format/a_out.rb +194 -0
- data/lib/metasm/exe_format/autoexe.rb +82 -0
- data/lib/metasm/exe_format/bflt.rb +189 -0
- data/lib/metasm/exe_format/coff.rb +455 -0
- data/lib/metasm/exe_format/coff_decode.rb +901 -0
- data/lib/metasm/exe_format/coff_encode.rb +1078 -0
- data/lib/metasm/exe_format/dex.rb +457 -0
- data/lib/metasm/exe_format/dol.rb +145 -0
- data/lib/metasm/exe_format/elf.rb +923 -0
- data/lib/metasm/exe_format/elf_decode.rb +979 -0
- data/lib/metasm/exe_format/elf_encode.rb +1375 -0
- data/lib/metasm/exe_format/macho.rb +827 -0
- data/lib/metasm/exe_format/main.rb +228 -0
- data/lib/metasm/exe_format/mz.rb +164 -0
- data/lib/metasm/exe_format/nds.rb +172 -0
- data/lib/metasm/exe_format/pe.rb +437 -0
- data/lib/metasm/exe_format/serialstruct.rb +246 -0
- data/lib/metasm/exe_format/shellcode.rb +114 -0
- data/lib/metasm/exe_format/xcoff.rb +167 -0
- data/lib/metasm/gui.rb +23 -0
- data/lib/metasm/gui/cstruct.rb +373 -0
- data/lib/metasm/gui/dasm_coverage.rb +199 -0
- data/lib/metasm/gui/dasm_decomp.rb +369 -0
- data/lib/metasm/gui/dasm_funcgraph.rb +103 -0
- data/lib/metasm/gui/dasm_graph.rb +1354 -0
- data/lib/metasm/gui/dasm_hex.rb +543 -0
- data/lib/metasm/gui/dasm_listing.rb +599 -0
- data/lib/metasm/gui/dasm_main.rb +906 -0
- data/lib/metasm/gui/dasm_opcodes.rb +291 -0
- data/lib/metasm/gui/debug.rb +1228 -0
- data/lib/metasm/gui/gtk.rb +884 -0
- data/lib/metasm/gui/qt.rb +495 -0
- data/lib/metasm/gui/win32.rb +3004 -0
- data/lib/metasm/gui/x11.rb +621 -0
- data/lib/metasm/ia32.rb +14 -0
- data/lib/metasm/ia32/compile_c.rb +1523 -0
- data/lib/metasm/ia32/debug.rb +193 -0
- data/lib/metasm/ia32/decode.rb +1167 -0
- data/lib/metasm/ia32/decompile.rb +564 -0
- data/lib/metasm/ia32/encode.rb +314 -0
- data/lib/metasm/ia32/main.rb +233 -0
- data/lib/metasm/ia32/opcodes.rb +872 -0
- data/lib/metasm/ia32/parse.rb +327 -0
- data/lib/metasm/ia32/render.rb +91 -0
- data/lib/metasm/main.rb +1193 -0
- data/lib/metasm/mips.rb +11 -0
- data/lib/metasm/mips/compile_c.rb +7 -0
- data/lib/metasm/mips/decode.rb +253 -0
- data/lib/metasm/mips/encode.rb +51 -0
- data/lib/metasm/mips/main.rb +72 -0
- data/lib/metasm/mips/opcodes.rb +443 -0
- data/lib/metasm/mips/parse.rb +51 -0
- data/lib/metasm/mips/render.rb +43 -0
- data/lib/metasm/os/gnu_exports.rb +270 -0
- data/lib/metasm/os/linux.rb +1112 -0
- data/lib/metasm/os/main.rb +1686 -0
- data/lib/metasm/os/remote.rb +527 -0
- data/lib/metasm/os/windows.rb +2027 -0
- data/lib/metasm/os/windows_exports.rb +745 -0
- data/lib/metasm/parse.rb +876 -0
- data/lib/metasm/parse_c.rb +3938 -0
- data/lib/metasm/pic16c/decode.rb +42 -0
- data/lib/metasm/pic16c/main.rb +17 -0
- data/lib/metasm/pic16c/opcodes.rb +68 -0
- data/lib/metasm/ppc.rb +11 -0
- data/lib/metasm/ppc/decode.rb +264 -0
- data/lib/metasm/ppc/decompile.rb +251 -0
- data/lib/metasm/ppc/encode.rb +51 -0
- data/lib/metasm/ppc/main.rb +129 -0
- data/lib/metasm/ppc/opcodes.rb +410 -0
- data/lib/metasm/ppc/parse.rb +52 -0
- data/lib/metasm/preprocessor.rb +1277 -0
- data/lib/metasm/render.rb +130 -0
- data/lib/metasm/sh4.rb +8 -0
- data/lib/metasm/sh4/decode.rb +336 -0
- data/lib/metasm/sh4/main.rb +292 -0
- data/lib/metasm/sh4/opcodes.rb +381 -0
- data/lib/metasm/x86_64.rb +12 -0
- data/lib/metasm/x86_64/compile_c.rb +1025 -0
- data/lib/metasm/x86_64/debug.rb +59 -0
- data/lib/metasm/x86_64/decode.rb +268 -0
- data/lib/metasm/x86_64/encode.rb +264 -0
- data/lib/metasm/x86_64/main.rb +135 -0
- data/lib/metasm/x86_64/opcodes.rb +118 -0
- data/lib/metasm/x86_64/parse.rb +68 -0
- data/misc/bottleneck.rb +61 -0
- data/misc/cheader-findpppath.rb +58 -0
- data/misc/hexdiff.rb +74 -0
- data/misc/hexdump.rb +55 -0
- data/misc/metasm-all.rb +13 -0
- data/misc/objdiff.rb +47 -0
- data/misc/objscan.rb +40 -0
- data/misc/pdfparse.rb +661 -0
- data/misc/ppc_pdf2oplist.rb +192 -0
- data/misc/tcp_proxy_hex.rb +84 -0
- data/misc/txt2html.rb +440 -0
- data/samples/a.out.rb +31 -0
- data/samples/asmsyntax.rb +77 -0
- data/samples/bindiff.rb +555 -0
- data/samples/compilation-steps.rb +49 -0
- data/samples/cparser_makestackoffset.rb +55 -0
- data/samples/dasm-backtrack.rb +38 -0
- data/samples/dasmnavig.rb +318 -0
- data/samples/dbg-apihook.rb +228 -0
- data/samples/dbghelp.rb +143 -0
- data/samples/disassemble-gui.rb +102 -0
- data/samples/disassemble.rb +133 -0
- data/samples/dump_upx.rb +95 -0
- data/samples/dynamic_ruby.rb +1929 -0
- data/samples/elf_list_needed.rb +46 -0
- data/samples/elf_listexports.rb +33 -0
- data/samples/elfencode.rb +25 -0
- data/samples/exeencode.rb +128 -0
- data/samples/factorize-headers-elfimports.rb +77 -0
- data/samples/factorize-headers-peimports.rb +109 -0
- data/samples/factorize-headers.rb +43 -0
- data/samples/gdbclient.rb +583 -0
- data/samples/generate_libsigs.rb +102 -0
- data/samples/hotfix_gtk_dbg.rb +59 -0
- data/samples/install_win_env.rb +78 -0
- data/samples/lindebug.rb +924 -0
- data/samples/linux_injectsyscall.rb +95 -0
- data/samples/machoencode.rb +31 -0
- data/samples/metasm-shell.rb +91 -0
- data/samples/pe-hook.rb +69 -0
- data/samples/pe-ia32-cpuid.rb +203 -0
- data/samples/pe-mips.rb +35 -0
- data/samples/pe-shutdown.rb +78 -0
- data/samples/pe-testrelocs.rb +51 -0
- data/samples/pe-testrsrc.rb +24 -0
- data/samples/pe_listexports.rb +31 -0
- data/samples/peencode.rb +19 -0
- data/samples/peldr.rb +494 -0
- data/samples/preprocess-flatten.rb +19 -0
- data/samples/r0trace.rb +308 -0
- data/samples/rubstop.rb +399 -0
- data/samples/scan_pt_gnu_stack.rb +54 -0
- data/samples/scanpeexports.rb +62 -0
- data/samples/shellcode-c.rb +40 -0
- data/samples/shellcode-dynlink.rb +146 -0
- data/samples/source.asm +34 -0
- data/samples/struct_offset.rb +47 -0
- data/samples/testpe.rb +32 -0
- data/samples/testraw.rb +45 -0
- data/samples/win32genloader.rb +132 -0
- data/samples/win32hooker-advanced.rb +169 -0
- data/samples/win32hooker.rb +96 -0
- data/samples/win32livedasm.rb +33 -0
- data/samples/win32remotescan.rb +133 -0
- data/samples/wintrace.rb +92 -0
- data/tests/all.rb +8 -0
- data/tests/dasm.rb +39 -0
- data/tests/dynldr.rb +35 -0
- data/tests/encodeddata.rb +132 -0
- data/tests/ia32.rb +82 -0
- data/tests/mips.rb +116 -0
- data/tests/parse_c.rb +239 -0
- data/tests/preprocessor.rb +269 -0
- data/tests/x86_64.rb +62 -0
- 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
|
+
|