diff --git a/build.py b/build.py index a88ce0d..2616310 100644 --- a/build.py +++ b/build.py @@ -80,6 +80,7 @@ BINDIST_MANIFEST = [ "examples/ex_debug_names.py", "examples/ex_graph.py", "examples/ex_dbg.py", + "examples/ex_custview.py", "examples/ex_imports.py" ] @@ -171,7 +172,7 @@ class BuilderBase: self.linker_out_string(outfile)) for objectfile in objects: - cmdstring = "%s %s" % (cmdstring, objectfile + self.object_extension) + cmdstring = "%s %s" % (cmdstring, objectfile + self.object_extension) for libpath in libpaths: cmdstring = "%s %s%s" % (cmdstring, self.libpath_delimiter, libpath) for library in libraries: @@ -192,7 +193,7 @@ class BuilderBase: macrostring += '%s%s ' % (argument_delimiter, item) return macrostring - + class GCCBuilder(BuilderBase): """ Generic GCC compiler class """ diff --git a/examples/ex_custview.py b/examples/ex_custview.py new file mode 100644 index 0000000..2e9ea58 --- /dev/null +++ b/examples/ex_custview.py @@ -0,0 +1,166 @@ +# ----------------------------------------------------------------------- +# This is an example illustrating how to use customview in Python +# (c) Hex-Rays +# +import idaapi +import idc +from idaapi import simplecustview_t +# + +# ----------------------------------------------------------------------- +class mycv_t(simplecustview_t): + def Create(self, sn=None): + # Form the title + title = "Simple custom view test" + if sn: + title += " %d" % sn + # Create the customview + if not simplecustview_t.Create(self, title): + return False + id = self.AddPopupMenu("Hello") + for i in xrange(0, 100): + self.AddLine("Line %d" % i) + return True + + def OnClick(self, shift): + """ + User clicked in the view + @param shift: Shift flag + @return Boolean. True if you handled the event + """ + print "OnClick, shift=%d" % shift + return True + + def OnDblClick(self, shift): + """ + User dbl-clicked in the view + @param shift: Shift flag + @return Boolean. True if you handled the event + """ + print "OnDblClick, shift=%d" % shift + return True + + def OnCursorPosChanged(self): + """ + Cursor position changed. + @return Nothing + """ + print "OnCurposChanged" + + def OnClose(self): + """ + The view is closing. Use this event to cleanup. + @return Nothing + """ + print "OnClose " + self.title + + def OnKeydown(self, vkey, shift): + """ + User pressed a key + @param vkey: Virtual key code + @param shift: Shift flag + @return Boolean. True if you handled the event + """ + print "OnKeydown, vk=%d shift=%d" % (vkey, shift) + # ESCAPE? + if vkey == 27: + self.Close() + elif vkey == 46: + n = self.GetLineNo() + self.DelLine(n) + self.Refresh() + print "Deleted line %d" % n + # Goto? + elif vkey == ord('G'): + v = idc.AskLong(self.GetLineNo(), "Where to go?") + if v: + self.Jump(v, 0, 5) + elif vkey == ord('R'): + print "refreshing...." + self.Refresh() + elif vkey == ord('C'): + print "refreshing current line..." + self.RefreshCurrent() + elif vkey == ord('A'): + s = idc.AskStr("NewLine%d" % self.Count(), "Append new line") + self.AddLine(s) + self.Refresh() + elif vkey == ord('X'): + print "Clearing all lines" + self.ClearLines() + self.Refresh() + elif vkey == ord('I'): + n = self.GetLineNo() + s = idc.AskStr("InsertedLine%d" % n, "Insert new line") + self.InsertLine(n, s) + self.Refresh() + elif vkey == ord('E'): + l = self.GetCurrentLine(notags=1) + if not l: + return False + n = self.GetLineNo() + print "curline=<%s>" % l + l = l + idaapi.COLSTR("*", idaapi.SCOLOR_VOIDOP) + self.EditLine(n, l) + self.RefreshCurrent() + print "Edited line %d" % n + else: + return False + return True + + def OnPopup(self): + """ + Context menu popup is about to be shown. Create items dynamically if you wish + @return Boolean. True if you handled the event + """ + print "OnPopup" + + def OnHint(self, lineno): + """ + Hint requested for the given line number. + @param lineno: The line number (zero based) + @return: + - string: a string containing the hint + - None: if no hint available + """ + return "OnHint, line=%d" % lineno + + def OnPopupMenu(self, menu_id): + """ + A context (or popup) menu item was executed. + @param menu_id: ID previously registered with add_popup_menu() + @return: Boolean + """ + print "OnPopupMenu, menu_id=%d" % menu_id + return True + +# ----------------------------------------------------------------------- +try: + # created already? + mycv + print "Already created, will close it..." + mycv.Close() + del mycv +except: + pass + +def show_win(): + x = mycv_t() + if not x.Create(): + print "Failed to create!" + return None + x.Show() + return x +mycv = show_win() +if not mycv: + del mycv + +def make_many(n): + for i in xrange(1, n+1): + t = mycv_t() + if not t.Create(i): + break + t.Show() + return i + +# \ No newline at end of file diff --git a/swig/kernwin.i b/swig/kernwin.i index 5e47153..902afd5 100644 --- a/swig/kernwin.i +++ b/swig/kernwin.i @@ -74,6 +74,762 @@ def askseg(defval, format): %apply unsigned long *OUTPUT { ea_t *ea1, ea_t *ea2 }; %{ + +// + +//--------------------------------------------------------------------------- +// Base class for all custview place_t providers +class custview_data_t +{ +public: + virtual void *get_ud() = 0; + virtual place_t *get_min() = 0; + virtual place_t *get_max() = 0; +}; + +//--------------------------------------------------------------------------- +class cvdata_simpleline_t: public custview_data_t +{ +private: + strvec_t lines; + simpleline_place_t pl_min, pl_max; +public: + void *get_ud() + { + return &lines; + } + place_t *get_min() + { + return &pl_min; + } + place_t *get_max() + { + return &pl_max; + } + strvec_t &get_lines() + { + return lines; + } + void set_minmax(size_t start=size_t(-1), size_t end=size_t(-1)) + { + if ( start == size_t(-1) && end == size_t(-1) ) + { + end = lines.size(); + pl_min.n = 0; + pl_max.n = end == 0 ? 0 : end - 1; + } + else + { + pl_min.n = start; + pl_max.n = end; + } + } + bool set_line(size_t nline, simpleline_t &sl) + { + if ( nline >= lines.size() ) + return false; + lines[nline] = sl; + return true; + } + bool del_line(size_t nline) + { + if ( nline >= lines.size() ) + return false; + lines.erase(lines.begin()+nline); + return true; + } + void add_line(simpleline_t &line) + { + lines.push_back(line); + } + void add_line(const char *str) + { + lines.push_back(simpleline_t(str)); + } + bool insert_line(size_t nline, simpleline_t &line) + { + if ( nline >= lines.size() ) + return false; + lines.insert(lines.begin()+nline, line); + return true; + } + bool patch_line(size_t nline, size_t offs, int value) + { + if ( nline >= lines.size() ) + return false; + qstring &L = lines[nline].line; + L[offs] = (uchar) value & 0xFF; + return true; + } + const size_t to_lineno(place_t *pl) const + { + return ((simpleline_place_t *)pl)->n; + } + bool curline(place_t *pl, size_t *n) + { + if ( pl == NULL ) + return false; + *n = to_lineno(pl); + return true; + } + simpleline_t *get_line(size_t nline) + { + return nline >= lines.size() ? NULL : &lines[nline]; + } + simpleline_t *get_line(place_t *pl) + { + return pl == NULL ? NULL : get_line(((simpleline_place_t *)pl)->n); + } + const size_t count() const + { + return lines.size(); + } + void clear_lines() + { + lines.clear(); + set_minmax(); + } +}; + +//--------------------------------------------------------------------------- +class customview_t +{ +protected: + qstring _title; + TForm *_form; + TCustomControl *_cv; + custview_data_t *_data; + int _features; + enum + { + HAVE_HINT = 0x0001, + HAVE_KEYDOWN = 0x0002, + HAVE_POPUP = 0x0004, + HAVE_DBLCLICK = 0x0008, + HAVE_CURPOS = 0x0010, + HAVE_CLICK = 0x0020, + HAVE_CLOSE = 0x0040 + }; +private: + struct pyw_popupctx_t + { + size_t menu_id; + customview_t *cv; + pyw_popupctx_t(): menu_id(0), cv(NULL) { } + pyw_popupctx_t(size_t mid, customview_t *v): menu_id(mid), cv(v) { } + }; + typedef std::map pyw_popupmap_t; + static pyw_popupmap_t _global_popup_map; + static size_t _global_popup_id; + qstring _curline; + intvec_t _installed_popups; + + static bool idaapi s_popup_cb(void *ud) + { + customview_t *_this = (customview_t *)ud; + return _this->on_popup(); + } + + static bool idaapi s_popup_menu_cb(void *ud) + { + size_t mid = (size_t)ud; + pyw_popupmap_t::iterator it = _global_popup_map.find(mid); + if ( it == _global_popup_map.end() ) + return false; + return it->second.cv->on_popup_menu(it->second.menu_id); + } + + static bool idaapi s_cv_keydown(TCustomControl * /*cv*/, int vk_key, int shift, void *ud) + { + customview_t *_this = (customview_t *)ud; + return _this->on_keydown(vk_key, shift); + } + // The popup menu is being constructed + static void idaapi s_cv_popup(TCustomControl * /*cv*/, void *ud) + { + customview_t *_this = (customview_t *)ud; + _this->on_popup(); + } + // The user clicked + static bool idaapi s_cv_click(TCustomControl *cv, int shift, void *ud) + { + customview_t *_this = (customview_t *)ud; + return _this->on_click(shift); + } + // The user double clicked + static bool idaapi s_cv_dblclick(TCustomControl * /*cv*/, int shift, void *ud) + { + customview_t *_this = (customview_t *)ud; + return _this->on_dblclick(shift); + } + // Cursor position has been changed + static void idaapi s_cv_curpos(TCustomControl * /*cv*/, void *ud) + { + customview_t *_this = (customview_t *)ud; + _this->on_curpos_changed(); + } + + //-------------------------------------------------------------------------- + static int idaapi s_ui_cb(void *ud, int code, va_list va) + { + customview_t *_this = (customview_t *)ud; + switch ( code ) + { + case ui_get_custom_viewer_hint: + { + TCustomControl *viewer = va_arg(va, TCustomControl *); + place_t *place = va_arg(va, place_t *); + int *important_lines = va_arg(va, int *); + qstring &hint = *va_arg(va, qstring *); + return ((_this->_features & HAVE_HINT) == 0 || place == NULL || _this->_cv != viewer) ? 0 : (_this->on_hint(place, important_lines, hint) ? 1 : 0); + } + case ui_tform_invisible: + { + TForm *form = va_arg(va, TForm *); + if ( _this->_form != form ) + break; + unhook_from_notification_point(HT_UI, s_ui_cb, _this); + _this->on_close(); + _this->on_post_close(); + } + break; + } + return 0; + } + + void on_post_close() + { + init_vars(); + clear_popup_menu(); + } + +public: + // All the overridable callbacks + // OnClick + virtual bool on_click(int /*shift*/) { return false; } + // OnDblClick + virtual bool on_dblclick(int /*shift*/) { return false; } + // OnCurorPositionChanged + virtual void on_curpos_changed() { } + // OnHostFormClose + virtual void on_close() { } + // OnKeyDown + virtual bool on_keydown(int /*vk_key*/, int /*shift*/) { return false; } + // OnPopupShow + virtual bool on_popup() { return false; } + // OnHint + virtual bool on_hint(place_t * /*place*/, int * /*important_lines*/, qstring &/*hint*/) { return false; } + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) { return false; } + + void init_vars() + { + _data = NULL; + _features = 0; + _curline.clear(); + _cv = NULL; + _form = NULL; + } + + customview_t() + { + init_vars(); + } + + ~customview_t() + { + } + + void close() + { + if ( _form != NULL ) + close_tform(_form, FORM_SAVE); + } + + bool set_range( + const place_t *minplace = NULL, + const place_t *maxplace = NULL) + { + if ( _cv == NULL ) + return false; + set_custom_viewer_range( + _cv, + minplace == NULL ? _data->get_min() : minplace, + maxplace == NULL ? _data->get_max() : maxplace); + return true; + } + + place_t *get_place( + bool mouse = false, + int *x = 0, + int *y = 0) + { + return _cv == NULL ? NULL : get_custom_viewer_place(_cv, mouse, x, y); + } + + //-------------------------------------------------------------------------- + bool refresh() + { + if ( _cv == NULL ) + return false; + refresh_custom_viewer(_cv); + return true; + } + + //-------------------------------------------------------------------------- + bool refresh_current(bool mouse = false) + { + int x, y; + place_t *pl = get_place(mouse, &x, &y); + if ( pl == NULL ) + return false; + return jumpto(pl, x, y); + } + + //-------------------------------------------------------------------------- + const char *get_current_line(bool mouse, bool notags) + { + const char *r = get_custom_viewer_curline(_cv, mouse); + if ( r == NULL || !notags ) + return r; + size_t sz = strlen(r); + if ( sz == 0 ) + return r; + _curline.resize(sz + 5, '\0'); + tag_remove(r, &_curline[0], sz + 1); + return _curline.c_str(); + } + + //-------------------------------------------------------------------------- + bool is_focused() + { + return get_current_viewer() == _cv; + } + + //-------------------------------------------------------------------------- + bool jumpto(place_t *place, int x, int y) + { + return ::jumpto(_cv, place, x, y); + } + + //-------------------------------------------------------------------------- + void clear_popup_menu() + { + if ( _cv != NULL ) + set_custom_viewer_popup_menu(_cv, NULL); + + for (intvec_t::iterator it=_installed_popups.begin(), it_end=_installed_popups.end(); + it != it_end; + ++it) + { + _global_popup_map.erase(*it); + } + _installed_popups.clear(); + } + + //-------------------------------------------------------------------------- + size_t add_popup_menu( + const char *title, + const char *hotkey) + { + size_t menu_id = _global_popup_id + 1; + // Overlap / already exists? + if (_cv == NULL || // No custview? + menu_id == 0 || // Overlap? + _global_popup_map.find(menu_id) != _global_popup_map.end()) // Already exists? + { + return 0; + } + add_custom_viewer_popup_item(_cv, title, hotkey, s_popup_menu_cb, (void *)menu_id); + + // Save global association + _global_popup_map[menu_id] = pyw_popupctx_t(menu_id, this); + _global_popup_id = menu_id; + + // Remember what menu IDs are set with this form + _installed_popups.push_back(menu_id); + return menu_id; + } + + //-------------------------------------------------------------------------- + bool create(const char *title, int features, custview_data_t *data) + { + // Already created? (in the instance) + if ( _form != NULL ) + return true; + + // Already created? (in IDA windows list) + HWND hwnd(NULL); + TForm *form = create_tform(title, &hwnd); + if ( hwnd == NULL ) + return false; + + _title = title; + _data = data; + _form = form; + _features = features; + + // Create the viewer + _cv = create_custom_viewer( + title, + (TWinControl *)_form, + _data->get_min(), + _data->get_max(), + _data->get_min(), + 0, + _data->get_ud()); + + // Set user-data + set_custom_viewer_handler(_cv, CVH_USERDATA, (void *)this); + + // + // Set other optional callbacks + // + if ( (features & HAVE_KEYDOWN) != 0 ) + set_custom_viewer_handler(_cv, CVH_KEYDOWN, (void *)s_cv_keydown); + + if ( (features & HAVE_POPUP) != 0 ) + set_custom_viewer_handler(_cv, CVH_POPUP, (void *)s_cv_popup); + + if ( (features & HAVE_DBLCLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_DBLCLICK, (void *)s_cv_dblclick); + + if ( (features & HAVE_CURPOS) != 0 ) + set_custom_viewer_handler(_cv, CVH_CURPOS, (void *)s_cv_curpos); + + if ( (features & HAVE_CLICK) != 0 ) + set_custom_viewer_handler(_cv, CVH_CLICK, (void *)s_cv_click); + + // Hook to UI notifications (for TForm close event) + hook_to_notification_point(HT_UI, s_ui_cb, this); + + return true; + } + + //-------------------------------------------------------------------------- + bool show() + { + if ( _form == NULL ) + return false; + open_tform(_form, FORM_TAB|FORM_MENU|FORM_RESTORE); + return true; + } +}; + +customview_t::pyw_popupmap_t customview_t::_global_popup_map; +size_t customview_t::_global_popup_id = 0; +//--------------------------------------------------------------------------- +class py_simplecustview_t: public customview_t +{ +private: + cvdata_simpleline_t data; + PyObject *py_self, *py_this, *py_last_link; + int features; + + // Convert a tuple (String, [color, [bgcolor]]) to a simpleline_t + static bool py_to_simpleline(PyObject *py, simpleline_t &sl) + { + if ( PyString_Check(py) ) + { + sl.line = PyString_AsString(py); + return true; + } + Py_ssize_t sz; + if ( !PyTuple_Check(py) || (sz = PyTuple_Size(py)) <= 0 ) + return false; + PyObject *py_val = PyTuple_GetItem(py, 0); + if ( !PyString_Check(py_val) ) + return false; + sl.line = PyString_AsString(py_val); + + if ( (sz > 1) && (py_val = PyTuple_GetItem(py, 1)) && PyLong_Check(py_val) ) + sl.color = color_t(PyLong_AsUnsignedLong(py_val)); + + if ( (sz > 2) && (py_val = PyTuple_GetItem(py, 2)) && PyLong_Check(py_val) ) + sl.bgcolor = PyLong_AsUnsignedLong(py_val); + return true; + } + + // + // Callbacks + // + virtual bool on_click(int shift) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLICK, "i", shift); + PyShowErr(S_ON_CLICK); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + // OnDblClick + virtual bool on_dblclick(int shift) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_DBL_CLICK, "i", shift); + PyShowErr(S_ON_DBL_CLICK); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + // OnCurorPositionChanged + virtual void on_curpos_changed() + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CURSOR_POS_CHANGED, NULL); + PyShowErr(S_ON_CURSOR_POS_CHANGED); + Py_XDECREF(py_result); + } + + // OnHostFormClose + virtual void on_close() + { + // Call the close method if it is there and the object is still bound + if ( (features & HAVE_CLOSE) != 0 && py_self != NULL ) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_CLOSE, NULL); + PyShowErr(S_ON_CLOSE); + Py_XDECREF(py_result); + + // Cleanup + Py_DECREF(py_self); + py_self = NULL; + } + } + + // OnKeyDown + virtual bool on_keydown(int vk_key, int shift) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_KEYDOWN, "ii", vk_key, shift); + PyShowErr(S_ON_KEYDOWN); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + // OnPopupShow + virtual bool on_popup() + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_POPUP, NULL); + PyShowErr(S_ON_POPUP); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + // OnHint + virtual bool on_hint(place_t *place, int *important_lines, qstring &hint) + { + size_t ln = data.to_lineno(place); + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_HINT, PY_FMT64, pyul_t(ln)); + PyShowErr(S_ON_HINT); + bool ok = py_result != NULL && PyString_Check(py_result); + if ( ok ) + { + if ( important_lines != NULL ) + *important_lines = 0; + hint = PyString_AsString(py_result); + } + Py_XDECREF(py_result); + return ok; + } + + // OnPopupMenuClick + virtual bool on_popup_menu(size_t menu_id) + { + PyObject *py_result = PyObject_CallMethod(py_self, (char *)S_ON_POPUP_MENU, PY_FMT64, pyul_t(menu_id)); + PyShowErr(S_ON_POPUP_MENU); + bool ok = py_result != NULL && PyObject_IsTrue(py_result); + Py_XDECREF(py_result); + return ok; + } + + void refresh_range() + { + data.set_minmax(); + set_range(); + } + +public: + py_simplecustview_t() + { + py_this = py_self = py_last_link = NULL; + } + ~py_simplecustview_t() + { + } + // Edits an existing line + bool edit_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + return data.set_line(nline, sl); + } + + // Low level: patches a line string directly + bool patch_line(size_t nline, size_t offs, int value) + { + return data.patch_line(nline, offs, value); + } + + // Insert a line + bool insert_line(size_t nline, PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + return data.insert_line(nline, sl); + } + + // Adds a line tuple + bool add_line(PyObject *py_sl) + { + simpleline_t sl; + if ( !py_to_simpleline(py_sl, sl) ) + return false; + data.add_line(sl); + refresh_range(); + return true; + } + + bool del_line(size_t nline) + { + bool ok = data.del_line(nline); + if ( ok ) + refresh_range(); + return ok; + } + + // Gets the position and returns a tuple (lineno, x, y) + PyObject *get_pos(bool mouse) + { + place_t *pl; + int x, y; + pl = get_place(mouse, &x, &y); + if ( pl == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(" PY_FMT64 "ii)", pyul_t(data.to_lineno(pl)), x, y); + } + + // Returns the line tuple + PyObject *get_line(size_t nline) + { + simpleline_t *r = data.get_line(nline); + if ( r == NULL ) + Py_RETURN_NONE; + return Py_BuildValue("(sII)", r->line.c_str(), (unsigned int)r->color, (unsigned int)r->bgcolor); + } + + // Returns the count of lines + const size_t count() const + { + return data.count(); + } + + // Clears lines + void clear() + { + data.clear_lines(); + refresh_range(); + } + + bool jumpto(size_t ln, int x, int y) + { + return customview_t::jumpto(&simpleline_place_t(ln), x, y); + } + + // Initializes and links the Python object to this class + bool init(PyObject *py_link, const char *title) + { + // Already created? + if ( _form != NULL ) + return true; + + // Probe callbacks + features = 0; + static struct + { + const char *cb_name; + int feature; + } const cbtable[] = + { + {S_ON_CLICK, HAVE_CLICK}, + {S_ON_CLOSE, HAVE_CLOSE}, + {S_ON_HINT, HAVE_HINT}, + {S_ON_KEYDOWN, HAVE_KEYDOWN}, + {S_ON_POPUP, HAVE_POPUP}, + {S_ON_DBL_CLICK, HAVE_DBLCLICK}, + {S_ON_CURSOR_POS_CHANGED, HAVE_CURPOS} + }; + for ( size_t i=0; i + bool idaapi py_menu_item_callback(void *userdata) { PyObject *func, *args, *result; @@ -106,6 +862,174 @@ bool idaapi py_menu_item_callback(void *userdata) %rename (add_menu_item) wrap_add_menu_item; %inline %{ +// + +// +// Pywraps Simple Custom View functions +// +PyObject *pyscv_init(PyObject *py_link, const char *title) +{ + py_simplecustview_t *_this = new py_simplecustview_t(); + bool ok = _this->init(py_link, title); + if ( !ok ) + { + delete _this; + Py_RETURN_NONE; + } + return _this->get_pythis(); +} +#define DECL_THIS py_simplecustview_t *_this = py_simplecustview_t::get_this(py_this) + +bool pyscv_refresh(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh(); +} + +bool pyscv_delete(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + _this->close(); + delete _this; + return true; +} + +bool pyscv_refresh_current(PyObject *py_this, bool mouse) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->refresh_current(mouse); +} + +PyObject *pyscv_get_current_line(PyObject *py_this, bool mouse, bool notags) +{ + DECL_THIS; + const char *line; + if ( _this == NULL || (line = _this->get_current_line(mouse, notags)) == NULL ) + Py_RETURN_NONE; + return PyString_FromString(line); +} + +bool pyscv_is_focused(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->is_focused(); +} + +void pyscv_clear_popup_menu(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear_popup_menu(); +} + +size_t pyscv_add_popup_menu(PyObject *py_this, const char *title, const char *hotkey) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->add_popup_menu(title, hotkey); +} + +size_t pyscv_count(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? 0 : _this->count(); +} + +bool pyscv_show(PyObject *py_this) +{ + DECL_THIS; + return _this == NULL ? false : _this->show(); +} + +void pyscv_close(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->close(); +} + +bool pyscv_jumpto(PyObject *py_this, size_t ln, int x, int y) +{ + DECL_THIS; + if ( _this == NULL ) + return false; + return _this->jumpto(ln, x, y); +} + +// Returns the line tuple +PyObject *pyscv_get_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + if ( _this == NULL ) + Py_RETURN_NONE; + return _this->get_line(nline); +} + +// Gets the position and returns a tuple (lineno, x, y) +PyObject *pyscv_get_pos(PyObject *py_this, bool mouse) +{ + DECL_THIS; + if ( _this == NULL ) + Py_RETURN_NONE; + return _this->get_pos(mouse); +} + +PyObject *pyscv_clear_lines(PyObject *py_this) +{ + DECL_THIS; + if ( _this != NULL ) + _this->clear(); + Py_RETURN_NONE; +} + +// Adds a line tuple +bool pyscv_add_line(PyObject *py_this, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->add_line(py_sl); +} + +bool pyscv_insert_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->insert_line(nline, py_sl); +} + +bool pyscv_patch_line(PyObject *py_this, size_t nline, size_t offs, int value) +{ + DECL_THIS; + return _this == NULL ? false : _this->patch_line(nline, offs, value); +} + +bool pyscv_del_line(PyObject *py_this, size_t nline) +{ + DECL_THIS; + return _this == NULL ? false : _this->del_line(nline); +} + +PyObject *pyscv_get_selection(PyObject *py_this) +{ + DECL_THIS; + if ( _this == NULL ) + Py_RETURN_NONE; + return _this->py_get_selection(); +} + +// Edits an existing line +bool pyscv_edit_line(PyObject *py_this, size_t nline, PyObject *py_sl) +{ + DECL_THIS; + return _this == NULL ? false : _this->edit_line(nline, py_sl); +} +#undef DECL_THIS +// // #ifdef CH_ATTRS @@ -173,7 +1097,7 @@ static pychoose2_to_choose2_map_t choosers; py_choose2_t *choose2_find_instance(PyObject *self) { pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - if (it == choosers.end()) + if ( it == choosers.end() ) return NULL; return it->second; } @@ -186,7 +1110,7 @@ void choose2_add_instance(PyObject *self, py_choose2_t *c2) void choose2_del_instance(PyObject *self) { pychoose2_to_choose2_map_t::iterator it = choosers.find(self); - if (it != choosers.end()) + if ( it != choosers.end() ) choosers.erase(it); } @@ -194,8 +1118,8 @@ void choose2_del_instance(PyObject *self) class py_choose2_t { private: - enum - { + enum + { CHOOSE2_HAVE_DEL = 0x0001, CHOOSE2_HAVE_INS = 0x0002, CHOOSE2_HAVE_UPDATE = 0x0004, @@ -274,29 +1198,29 @@ private: //------------------------------------------------------------------------ void on_get_line(int lineno, char * const *line_arr) { - if (lineno == 0) + if ( lineno == 0 ) { - for (size_t i=0;i=0;i--) + for ( int i=ncols-1; i>=0; i-- ) line_arr[i][0] = '\0'; // Call Python PyObject *list = PyObject_CallMethod(self, (char *)S_ON_GET_LINE, "i", lineno - 1); - if (list == NULL) + if ( list == NULL ) return; - for (int i=ncols-1;i>=0;i--) + for ( int i=ncols-1; i>=0; i-- ) { PyObject *item = PyList_GetItem(list, Py_ssize_t(i)); - if (item == NULL) + if ( item == NULL ) continue; const char *str = PyString_AsString(item); - if (str != NULL) + if ( str != NULL ) qstrncpy(line_arr[i], str, MAXSTR); } Py_DECREF(list); @@ -305,7 +1229,7 @@ private: size_t on_get_size() { PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_SIZE, NULL); - if (pyres == NULL) + if ( pyres == NULL ) return 0; size_t res = PyInt_AsLong(pyres); Py_DECREF(pyres); @@ -334,7 +1258,7 @@ private: int on_delete_line(int lineno) { PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_DELETE_LINE, "i", lineno - 1); - if (pyres == NULL) + if ( pyres == NULL ) return lineno; size_t res = PyInt_AsLong(pyres); Py_DECREF(pyres); @@ -344,7 +1268,7 @@ private: int on_refresh(int lineno) { PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_REFRESH, "i", lineno - 1); - if (pyres == NULL) + if ( pyres == NULL ) return lineno; size_t res = PyInt_AsLong(pyres); Py_DECREF(pyres); @@ -372,7 +1296,7 @@ private: int on_command(int cmd_id, int lineno) { PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_COMMAND, "ii", lineno - 1, cmd_id); - if (pyres==NULL) + if ( pyres==NULL ) return lineno; size_t res = PyInt_AsLong(pyres); Py_XDECREF(pyres); @@ -389,15 +1313,15 @@ private: void on_get_line_attr(int lineno, chooser_item_attrs_t *attr) { PyObject *pyres = PyObject_CallMethod(self, (char *)S_ON_GET_LINE_ATTR, "i", lineno - 1); - if (pyres == NULL) + if ( pyres == NULL ) return; - if (PyList_Check(pyres)) + if ( PyList_Check(pyres) ) { PyObject *item; - if ((item = PyList_GetItem(pyres, 0)) != NULL) + if ( (item = PyList_GetItem(pyres, 0)) != NULL ) attr->color = PyInt_AsLong(item); - if ((item = PyList_GetItem(pyres, 1)) != NULL) + if ( (item = PyList_GetItem(pyres, 1)) != NULL ) attr->flags = PyInt_AsLong(item); } Py_XDECREF(pyres); @@ -424,7 +1348,7 @@ public: bool activate() { TForm *frm = find_tform(title.c_str()); - if (frm == NULL) + if ( frm == NULL ) return false; switchto_tform(frm, true); return true; @@ -470,10 +1394,10 @@ public: int add_command(const char *caption, int flags=0, int menu_index=-1, int icon=-1) { - if (menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS) + if ( menu_cb_idx >= MAX_CHOOSER_MENU_COMMANDS ) return -1; bool ret = add_chooser_command(title.c_str(), caption, menu_cbs[menu_cb_idx], menu_index, icon, flags); - if (!ret) + if ( !ret ) return -1; return menu_cb_idx++; } @@ -503,7 +1427,7 @@ public: // get cols caption and widthes intvec_t widths; cols.qclear(); - for (int i=0;ichoose2(flags, ncols, &widths[0], title.c_str(), deflt, popup_names, icon, pts[0], pts[1], pts[2], pts[3]); // Clear temporary popup_names - if (popup_names != NULL) + if ( popup_names != NULL ) { - for (int i=0;iactivate(); return 1; @@ -679,7 +1603,7 @@ int choose2_show(PyObject *self) void choose2_close(PyObject *self) { py_choose2_t *c2 = choose2_find_instance(self); - if (c2 != NULL) + if ( c2 != NULL ) c2->close(); } @@ -687,7 +1611,7 @@ void choose2_close(PyObject *self) void choose2_refresh(PyObject *self) { py_choose2_t *c2 = choose2_find_instance(self); - if (c2 != NULL) + if ( c2 != NULL ) c2->refresh(); } @@ -695,7 +1619,7 @@ void choose2_refresh(PyObject *self) void choose2_activate(PyObject *self) { py_choose2_t *c2 = choose2_find_instance(self); - if (c2 != NULL) + if ( c2 != NULL ) c2->activate(); } @@ -703,7 +1627,7 @@ void choose2_activate(PyObject *self) int choose2_add_command(PyObject *self, const char *caption, int flags=0, int menu_index=-1, int icon=-1) { py_choose2_t *c2 = choose2_find_instance(self); - if (c2 != NULL) + if ( c2 != NULL ) return c2->add_command(caption, flags, menu_index, icon); else return -2; @@ -714,7 +1638,7 @@ int choose2_add_command(PyObject *self, const char *caption, int flags=0, int me PyObject *choose2_find(const char *title) { py_choose2_t *c2 = py_choose2_t::find_chooser(title); - if (c2 == NULL) + if ( c2 == NULL ) return NULL; return c2->get_self(); } @@ -874,6 +1798,218 @@ class Choose: """ return _idaapi.choose_choose(self, self.flags, self.x0, self.y0, self.x1, self.y1, self.width) +# +class simplecustview_t(object): + + def __init__(self): + self.this = None + + def __del__(self): + """Destructor. It also frees the associated C++ object""" + try: + _idaapi.pyscv_delete(self.this) + except: + pass + + @staticmethod + def make_sl_arg(line, fgcolor=None, bgcolor=None): + return line if (fgcolor is None and bgcolor is None) else (line, fgcolor, bgcolor) + + def Create(self, title): + """ + Creates the custom view. This should be the first method called after instantiation + + @param title: The title of the view + @return: Boolean whether it succeeds or fails. It may fail if a window with the same title is already open. + In this case better close existing windows + """ + self.title = title + self.this = _idaapi.pyscv_init(self, title) + return True if self.this else False + + def Close(self): + """ + Destroys the view. + One has to call Create() afterwards. + Show() can be called and it will call Create() internally. + @return: Boolean + """ + return _idaapi.pyscv_close(self.this) + + def Show(self): + """ + Shows an already created view. It the view was close, then it will call Create() for you + @return: Boolean + """ + return _idaapi.pyscv_show(self.this) + + def Refresh(self): + return _idaapi.pyscv_refresh(self.this) + + def RefreshCurrent(self, mouse = 0): + """Refreshes the current line only""" + return _idaapi.pyscv_refresh_current(self.this, mouse) + + def Count(self): + """Returns the number of lines in the view""" + return _idaapi.pyscv_count(self.this) + + def GetSelection(self): + """ + Returns the selected area or None + @return: + - tuple(x1, y1, x2, y2) + - None if no selection + """ + return _idaapi.pyscv_get_selection(self.this) + + def ClearLines(self): + """Clears all the lines""" + _idaapi.pyscv_clear_lines(self.this) + + def AddLine(self, line, fgcolor=None, bgcolor=None): + """ + Adds a colored line to the view + @return: Boolean + """ + return _idaapi.pyscv_add_line(self.this, self.make_sl_arg(line, fgcolor, bgcolor)) + + def InsertLine(self, lineno, line, fgcolor=None, bgcolor=None): + """Inserts a line in the given position""" + return _idaapi.pyscv_insert_line(self.this, lineno, self.make_sl_arg(line, fgcolor, bgcolor)) + + def EditLine(self, lineno, line, fgcolor=None, bgcolor=None): + """Edits an existing line""" + return _idaapi.pyscv_edit_line(self.this, lineno, self.make_sl_arg(line, fgcolor, bgcolor)) + + def PatchLine(self, lineno, offs, value): + """Patches an existing line character at the given offset. This is a low level function. You must know what you're doing""" + return _idaapi.pyscv_patch_line(self.this, lineno, offs, value) + + def DelLine(self, lineno): + return _idaapi.pyscv_del_line(self.this, lineno) + + def GetLine(self, lineno): + return _idaapi.pyscv_get_line(self.this, lineno) + + def GetCurrentLine(self, mouse = 0, notags = 0): + """ + Returns the current line. + @param mouse: Current line at mouse pos + @param notags: If True then tag_remove() will be called before returning the line + @return: Returns the current line (colored or uncolored) + """ + return _idaapi.pyscv_get_current_line(self.this, mouse, notags) + + def GetPos(self, mouse = 0): + """ + Returns the current cursor or mouse position. + @param mouse: return mouse position + @return: Returns a tuple (lineno, x, y) + """ + return _idaapi.pyscv_get_pos(self.this, mouse) + + def GetLineNo(self, mouse = 0): + """Calls GetPos() and returns the current line number only or None on failure""" + r = self.GetPos(mouse) + return None if not r else r[0] + + def Jump(self, lineno, x=0, y=0): + return _idaapi.pyscv_jumpto(self.this, lineno, x, y) + + def AddPopupMenu(self, title, hotkey=""): + """ + Adds a popup menu item + @param title: The name of the menu item + @param hotkey: Hotkey of the item or just empty + @return: Returns the + """ + return _idaapi.pyscv_add_popup_menu(self.this, title, hotkey) + + def ClearPopupMenu(self): + """ + Clears all previously installed popup menu items. + Use this function if you're generating menu items on the fly (in the OnPopup() callback), + and before adding new items + """ + _idaapi.pyscv_clear_popup_menu(self.this) + + def IsFocused(self): + """Returns True if the current view is the focused view""" + return _idaapi.pyscv_is_focused(self.this) + + # Here are all the supported events + # Uncomment any event to enable +# def OnClick(self, shift): +# """ +# User clicked in the view +# @param shift: Shift flag +# @return Boolean. True if you handled the event +# """ +# print "OnClick, shift=%d" % shift +# return True +# +# def OnDblClick(self, shift): +# """ +# User dbl-clicked in the view +# @param shift: Shift flag +# @return Boolean. True if you handled the event +# """ +# print "OnDblClick, shift=%d" % shift +# return True +# +# def OnCursorPosChanged(self): +# """ +# Cursor position changed. +# @return Nothing +# """ +# print "OnCurposChanged" +# +# def OnClose(self): +# """ +# The view is closing. Use this event to cleanup. +# @return Nothing +# """ +# print "OnClose" +# +# def OnKeydown(self, vkey, shift): +# """ +# User pressed a key +# @param vkey: Virtual key code +# @param shift: Shift flag +# @return Boolean. True if you handled the event +# """ +# print "OnKeydown, vk=%d shift=%d" % (vkey, shift) +# return False +# +# def OnPopup(self): +# """ +# Context menu popup is about to be shown. Create items dynamically if you wish +# @return Boolean. True if you handled the event +# """ +# print "OnPopup" +# +# def OnHint(self, lineno): +# """ +# Hint requested for the given line number. +# @param lineno: The line number (zero based) +# @return: +# - string: a string containing the hint +# - None: if no hint available +# """ +# return "OnHint, line=%d" % lineno +# +# def OnPopupMenu(self, menu_id): +# """ +# A context (or popup) menu item was executed. +# @param menu_id: ID previously registered with add_popup_menu() +# @return: Boolean +# """ +# print "OnPopupMenu, menu_id=" % menu_id +# return True + +# + # class Choose2: """Choose2 wrapper class""" diff --git a/swig/lines.i b/swig/lines.i index 6643f82..25feafe 100644 --- a/swig/lines.i +++ b/swig/lines.i @@ -1,8 +1,7 @@ -// Convert this for ver 4.8 tag_remove() -%cstring_output_maxstr_none(char *buf, int bufsize); - // FIXME: These should be fixed +%ignore requires_color_esc; %ignore tag_on; +%ignore tag_remove; %ignore tag_off; %ignore tag_addchr; %ignore tag_addstr; @@ -60,4 +59,128 @@ %include "lines.hpp" -%clear(char *buf, int bufsize); +%rename (generate_disassembly) py_generate_disassembly; +%rename (tag_remove) py_tag_remove; +%rename (tag_addr) py_tag_addr; +%rename (tag_skipcodes) py_tag_skipcodes; +%rename (tag_skipcode) py_tag_skipcode; +%rename (tag_advance) py_tag_advance; +%rename (generate_disassembly) py_generate_disassembly; + +%inline +{ +// +//------------------------------------------------------------------------- +PyObject *py_tag_remove(const char *instr) +{ + size_t sz = strlen(instr); + char *buf = new char[sz + 5]; + if ( buf == NULL ) + Py_RETURN_NONE; + ssize_t r = tag_remove(instr, buf, sz); + PyObject *res; + if ( r < 0 ) + { + Py_INCREF(Py_None); + res = Py_None; + } + else + { + res = PyString_FromString(buf); + } + delete [] buf; + return res; +} + +//------------------------------------------------------------------------- +PyObject *py_tag_addr(ea_t ea) +{ + char buf[100]; + tag_addr(buf, buf + sizeof(buf), ea); + return PyString_FromString(buf); +} + +//------------------------------------------------------------------------- +int py_tag_skipcode(const char *line) +{ + return tag_skipcode(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_skipcodes(const char *line) +{ + return tag_skipcodes(line)-line; +} + +//------------------------------------------------------------------------- +int py_tag_advance(const char *line, int cnt) +{ + return tag_advance(line, cnt)-line; +} + +//------------------------------------------------------------------------- +PyObject *py_generate_disassembly(ea_t ea, int max_lines, bool as_stack, bool notags) +{ + if ( max_lines <= 0 ) + Py_RETURN_NONE; + + qstring qbuf; + char **lines = new char *[max_lines]; + int lnnum; + int nlines = generate_disassembly(ea, lines, max_lines, &lnnum, as_stack); + + PyObject *py_tuple = PyTuple_New(nlines); + for ( int i=0; i + +} + +%pythoncode %{ +# + +# ---------------- Color escape sequence defitions ------------------------- +COLOR_ADDR_SIZE = 16 if _idaapi.BADADDR == 0xFFFFFFFFFFFFFFFFL else 8 +SCOLOR_FG_MAX = '\x28' # Max color number +SCOLOR_OPND1 = chr(cvar.COLOR_ADDR+1) # Instruction operand 1 +SCOLOR_OPND2 = chr(cvar.COLOR_ADDR+2) # Instruction operand 2 +SCOLOR_OPND3 = chr(cvar.COLOR_ADDR+3) # Instruction operand 3 +SCOLOR_OPND4 = chr(cvar.COLOR_ADDR+4) # Instruction operand 4 +SCOLOR_OPND5 = chr(cvar.COLOR_ADDR+5) # Instruction operand 5 +SCOLOR_OPND6 = chr(cvar.COLOR_ADDR+6) # Instruction operand 6 +SCOLOR_UTF8 = chr(cvar.COLOR_ADDR+10) # Following text is UTF-8 encoded + +# ---------------- Line prefix colors -------------------------------------- +PALETTE_SIZE = (cvar.COLOR_FG_MAX+_idaapi.COLOR_BG_MAX) + +def requires_color_esc(c): + """ + Checks if the given character requires escaping + @param c: character (string of one char) + @return: Boolean + """ + t = ord(c[0]) + return c >= COLOR_ON and c <= COLOR_INV + +def COLSTR(str,tag): + return SCOLOR_ON + tag + str + SCOLOR_OFF + tag + +# + +%} \ No newline at end of file