mirror of
https://github.com/cemu-project/idapython.git
synced 2024-11-24 18:16:55 +01:00
78c79f85b9
What's new: - Proper multi-threaded support - Better PyObject reference counting with ref_t and newref_t helper classes - Improved the pywraps/deployment script - Added IDAViewWrapper class and example - Added idc.GetDisasmEx() - Added idc.AddSegEx() - Added idc.GetLocalTinfo() - Added idc.ApplyType() - Updated type information implementation - Introduced the idaapi.require() - see http://www.hexblog.com/?p=749 - set REMOVE_CWD_SYS_PATH=1 by default in python.cfg (remove current directory from the import search path). Various bugfixes: - fixed various memory leaks - asklong/askaddr/asksel (and corresponding idc.py functions) were returning results truncated to 32 bits in IDA64 - fix wrong documentation for idc.SizeOf - GetFloat/GetDouble functions did not take into account endianness of the processor - idaapi.NO_PROCESS was not defined, and was causing GetProcessPid() to fail - idc.py: insert escape characters to string parameter when call Eval() - idc.SaveFile/savefile were always overwriting an existing file instead of writing only the new data - PluginForm.Close() wasn't passing its arguments to the delegate function, resulting in an error.
1009 lines
32 KiB
OpenEdge ABL
1009 lines
32 KiB
OpenEdge ABL
|
|
%{
|
|
//<code(py_view_base)>
|
|
|
|
//#define PYGDBG_ENABLED
|
|
#ifdef PYGDBG_ENABLED
|
|
#define PYGLOG(...) msg(__VA_ARGS__)
|
|
#else
|
|
#define PYGLOG(...)
|
|
#endif
|
|
|
|
//-------------------------------------------------------------------------
|
|
class py_customidamemo_t;
|
|
class lookup_info_t
|
|
{
|
|
public:
|
|
void add(TForm *form, TCustomControl *view, py_customidamemo_t *py_view)
|
|
{
|
|
QASSERT(0, form != NULL && view != NULL && py_view != NULL
|
|
&& !find_by_form(NULL, NULL, form)
|
|
&& !find_by_view(NULL, NULL, view)
|
|
&& !find_by_py_view(NULL, NULL, py_view));
|
|
entry_t &e = entries.push_back();
|
|
e.form = form;
|
|
e.view = view;
|
|
e.py_view = py_view;
|
|
}
|
|
|
|
#define FIND_BY__BODY(crit, res1, res2) \
|
|
{ \
|
|
for ( entries_t::const_iterator it = entries.begin(); it != entries.end(); ++it ) \
|
|
{ \
|
|
const entry_t &e = *it; \
|
|
if ( e.crit == crit ) \
|
|
{ \
|
|
if ( out_##res1 != NULL ) \
|
|
*out_##res1 = e.res1; \
|
|
if ( out_##res2 != NULL ) \
|
|
*out_##res2 = e.res2; \
|
|
return true; \
|
|
} \
|
|
} \
|
|
return false; \
|
|
}
|
|
bool find_by_form(TCustomControl **out_view, py_customidamemo_t **out_py_view, const TForm *form) const FIND_BY__BODY(form, view, py_view);
|
|
bool find_by_view(TForm **out_form, py_customidamemo_t **out_py_view, const TCustomControl *view) const FIND_BY__BODY(view, form, py_view);
|
|
bool find_by_py_view(TForm **out_form, TCustomControl **out_view, const py_customidamemo_t *py_view) const FIND_BY__BODY(py_view, view, form);
|
|
#undef FIND_BY__BODY
|
|
|
|
bool del_by_py_view(const py_customidamemo_t *py_view)
|
|
{
|
|
for ( entries_t::iterator it = entries.begin(); it != entries.end(); ++it )
|
|
{
|
|
if ( it->py_view == py_view )
|
|
{
|
|
entries.erase(it);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private:
|
|
struct entry_t
|
|
{
|
|
TForm *form;
|
|
TCustomControl *view;
|
|
py_customidamemo_t *py_view;
|
|
};
|
|
typedef qvector<entry_t> entries_t;
|
|
entries_t entries;
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
template <typename T>
|
|
T *view_extract_this(PyObject *self)
|
|
{
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
ref_t py_this(PyW_TryGetAttrString(self, S_M_THIS));
|
|
if ( py_this == NULL || !PyCObject_Check(py_this.o) )
|
|
return NULL;
|
|
return (T*) PyCObject_AsVoidPtr(py_this.o);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
class py_customidamemo_t
|
|
{
|
|
void convert_node_info(
|
|
node_info_t *out,
|
|
uint32 *out_flags,
|
|
ref_t py_nodeinfo)
|
|
{
|
|
if ( out_flags != NULL )
|
|
*out_flags = 0;
|
|
#define COPY_PROP(checker, converter, pname, flag) \
|
|
do \
|
|
{ \
|
|
newref_t pname(PyObject_GetAttrString(py_nodeinfo.o, #pname)); \
|
|
if ( pname != NULL && checker(pname.o) ) \
|
|
{ \
|
|
out->pname = converter(pname.o); \
|
|
if ( out_flags != NULL ) \
|
|
*out_flags |= flag; \
|
|
} \
|
|
} while ( false )
|
|
#define COPY_ULONG_PROP(pname, flag) COPY_PROP(PyNumber_Check, PyLong_AsUnsignedLong, pname, flag)
|
|
#define COPY_STRING_PROP(pname, flag) COPY_PROP(PyString_Check, PyString_AsString, pname, flag)
|
|
COPY_ULONG_PROP(bg_color, NIF_BG_COLOR);
|
|
COPY_ULONG_PROP(frame_color, NIF_FRAME_COLOR);
|
|
COPY_ULONG_PROP(ea, NIF_EA);
|
|
COPY_STRING_PROP(text, NIF_TEXT);
|
|
#undef COPY_STRING_PROP
|
|
#undef COPY_ULONG_PROP
|
|
#undef COPY_PROP
|
|
}
|
|
|
|
enum
|
|
{
|
|
GRBASE_HAVE_VIEW_ACTIVATED = 0x001,
|
|
GRBASE_HAVE_VIEW_DEACTIVATED = 0x002,
|
|
GRBASE_HAVE_KEYDOWN = 0x004,
|
|
GRBASE_HAVE_POPUP = 0x008,
|
|
GRBASE_HAVE_VIEW_CLICK = 0x010,
|
|
GRBASE_HAVE_VIEW_DBLCLICK = 0x020,
|
|
GRBASE_HAVE_VIEW_CURPOS = 0x040,
|
|
GRBASE_HAVE_CLOSE = 0x080,
|
|
GRBASE_HAVE_VIEW_SWITCHED = 0x100,
|
|
GRBASE_HAVE_VIEW_MOUSE_OVER = 0x200,
|
|
};
|
|
|
|
static void ensure_view_callbacks_installed();
|
|
int cb_flags;
|
|
|
|
protected:
|
|
ref_t self;
|
|
TCustomControl *view;
|
|
// This is called after having modified the
|
|
// node properties in the IDB. In case an
|
|
// implementation is performing some caching,
|
|
// this is a chance to update that cache.
|
|
// If 'ni' is NULL, then the node info was deleted.
|
|
virtual void node_info_modified(
|
|
int /*n*/,
|
|
const node_info_t * /*ni*/,
|
|
uint32 /*flags*/) {}
|
|
|
|
struct callback_id_t
|
|
{
|
|
qstring name;
|
|
int have;
|
|
};
|
|
struct callbacks_ids_t : public qvector<callback_id_t>
|
|
{
|
|
void add(const char *_n, int _h)
|
|
{
|
|
callback_id_t &o = push_back();
|
|
o.name = _n;
|
|
o.have = _h;
|
|
}
|
|
};
|
|
callbacks_ids_t cbids;
|
|
|
|
bool collect_pyobject_callbacks(PyObject *self);
|
|
virtual void collect_class_callbacks_ids(callbacks_ids_t *out);
|
|
|
|
// Bi-directionally bind/unbind the Python object and this controller.
|
|
bool bind(PyObject *_self, TCustomControl *view);
|
|
void unbind();
|
|
|
|
static lookup_info_t lookup_info;
|
|
|
|
public:
|
|
py_customidamemo_t();
|
|
virtual ~py_customidamemo_t();
|
|
virtual void refresh()
|
|
{
|
|
refresh_viewer(view);
|
|
}
|
|
void set_node_info(PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags);
|
|
void set_nodes_infos(PyObject *dict);
|
|
PyObject *get_node_info(PyObject *py_node_idx);
|
|
void del_nodes_infos(PyObject *py_nodes);
|
|
PyObject *get_current_renderer_type();
|
|
void set_current_renderer_type(PyObject *py_rto);
|
|
PyObject *create_groups(PyObject *groups_infos);
|
|
PyObject *delete_groups(PyObject *groups, PyObject *new_current);
|
|
PyObject *set_groups_visibility(PyObject *groups, PyObject *expand, PyObject *new_current);
|
|
|
|
// View events
|
|
void on_view_activated();
|
|
void on_view_deactivated();
|
|
void on_view_keydown(int key, int state);
|
|
void on_view_popup();
|
|
void on_view_click(const view_mouse_event_t *event);
|
|
void on_view_dblclick(const view_mouse_event_t *event);
|
|
void on_view_curpos();
|
|
void on_view_close();
|
|
void on_view_switched(tcc_renderer_type_t rt);
|
|
void on_view_mouse_over(const view_mouse_event_t *event);
|
|
inline bool has_callback(int flag) { return (cb_flags & flag) != 0; }
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
py_customidamemo_t::py_customidamemo_t()
|
|
: self(newref_t(NULL)),
|
|
view(NULL)
|
|
{
|
|
PYGLOG("%p: py_customidamemo_t()\n", this);
|
|
ensure_view_callbacks_installed();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
py_customidamemo_t::~py_customidamemo_t()
|
|
{
|
|
PYGLOG("%p: ~py_customidamemo_t()\n", this);
|
|
unbind();
|
|
lookup_info.del_by_py_view(this);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::ensure_view_callbacks_installed()
|
|
{
|
|
static bool installed = false;
|
|
if ( !installed )
|
|
{
|
|
struct ida_local lambda_t
|
|
{
|
|
static int idaapi callback(void * /*ud*/, int code, va_list va)
|
|
{
|
|
py_customidamemo_t *py_view;
|
|
if ( lookup_info.find_by_view(NULL, &py_view, va_arg(va, TCustomControl *)) )
|
|
{
|
|
PYW_GIL_GET;
|
|
switch ( code )
|
|
{
|
|
case view_activated:
|
|
py_view->on_view_activated();
|
|
break;
|
|
case view_deactivated:
|
|
py_view->on_view_deactivated();
|
|
break;
|
|
case view_keydown:
|
|
{
|
|
int key = va_arg(va, int);
|
|
int state = va_arg(va, int);
|
|
py_view->on_view_keydown(key, state);
|
|
}
|
|
break;
|
|
case view_popup:
|
|
py_view->on_view_popup();
|
|
break;
|
|
case view_click:
|
|
case view_dblclick:
|
|
{
|
|
const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*);
|
|
if ( code == view_click )
|
|
py_view->on_view_click(event);
|
|
else
|
|
py_view->on_view_dblclick(event);
|
|
}
|
|
break;
|
|
case view_curpos:
|
|
py_view->on_view_curpos();
|
|
break;
|
|
case view_close:
|
|
py_view->on_view_close();
|
|
delete py_view;
|
|
break;
|
|
case view_switched:
|
|
{
|
|
tcc_renderer_type_t rt = (tcc_renderer_type_t) va_arg(va, int);
|
|
py_view->on_view_switched(rt);
|
|
}
|
|
break;
|
|
case view_mouse_over:
|
|
{
|
|
const view_mouse_event_t *event = va_arg(va, view_mouse_event_t*);
|
|
py_view->on_view_mouse_over(event);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
};
|
|
hook_to_notification_point(HT_VIEW, lambda_t::callback, NULL);
|
|
installed = true;
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::set_node_info(
|
|
PyObject *py_node_idx,
|
|
PyObject *py_node_info,
|
|
PyObject *py_flags)
|
|
{
|
|
if ( !PyNumber_Check(py_node_idx) || !PyNumber_Check(py_flags) )
|
|
return;
|
|
borref_t py_idx(py_node_idx);
|
|
borref_t py_ni(py_node_info);
|
|
borref_t py_fl(py_flags);
|
|
node_info_t ni;
|
|
convert_node_info(&ni, NULL, py_ni);
|
|
int idx = PyInt_AsLong(py_idx.o);
|
|
uint32 flgs = PyLong_AsLong(py_fl.o);
|
|
viewer_set_node_info(view, idx, ni, flgs);
|
|
node_info_modified(idx, &ni, flgs);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::set_nodes_infos(PyObject *dict)
|
|
{
|
|
if ( !PyDict_Check(dict) )
|
|
return;
|
|
Py_ssize_t pos = 0;
|
|
PyObject *o_key, *o_value;
|
|
while ( PyDict_Next(dict, &pos, &o_key, &o_value) )
|
|
{
|
|
borref_t key(o_key);
|
|
borref_t value(o_value);
|
|
if ( !PyNumber_Check(key.o) )
|
|
continue;
|
|
uint32 flags;
|
|
node_info_t ni;
|
|
convert_node_info(&ni, &flags, value);
|
|
int idx = PyInt_AsLong(key.o);
|
|
viewer_set_node_info(view, idx, ni, flags);
|
|
node_info_modified(idx, &ni, flags);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *py_customidamemo_t::get_node_info(PyObject *py_node_idx)
|
|
{
|
|
if ( !PyNumber_Check(py_node_idx) )
|
|
Py_RETURN_NONE;
|
|
node_info_t ni;
|
|
if ( !viewer_get_node_info(view, &ni, PyInt_AsLong(py_node_idx)) )
|
|
Py_RETURN_NONE;
|
|
return Py_BuildValue("(kkks)", ni.bg_color, ni.frame_color, ni.ea, ni.text.c_str());
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::del_nodes_infos(PyObject *py_nodes)
|
|
{
|
|
if ( !PySequence_Check(py_nodes) )
|
|
return;
|
|
Py_ssize_t sz = PySequence_Size(py_nodes);
|
|
for ( Py_ssize_t i = 0; i < sz; ++i )
|
|
{
|
|
newref_t item(PySequence_GetItem(py_nodes, i));
|
|
if ( !PyNumber_Check(item.o) )
|
|
continue;
|
|
int idx = PyInt_AsLong(item.o);
|
|
viewer_del_node_info(view, idx);
|
|
node_info_modified(idx, NULL, 0);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *py_customidamemo_t::get_current_renderer_type()
|
|
{
|
|
tcc_renderer_type_t rt = get_view_renderer_type(view);
|
|
return PyLong_FromLong(long(rt));
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::set_current_renderer_type(PyObject *py_rto)
|
|
{
|
|
tcc_renderer_type_t rt = TCCRT_INVALID;
|
|
borref_t py_rt(py_rto);
|
|
if ( PyNumber_Check(py_rt.o) )
|
|
{
|
|
rt = tcc_renderer_type_t(PyLong_AsLong(py_rt.o));
|
|
set_view_renderer_type(view, rt);
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *py_customidamemo_t::create_groups(PyObject *_groups_infos)
|
|
{
|
|
if ( !PySequence_Check(_groups_infos) )
|
|
Py_RETURN_NONE;
|
|
borref_t groups_infos(_groups_infos);
|
|
groups_crinfos_t gis;
|
|
Py_ssize_t sz = PySequence_Size(groups_infos.o);
|
|
for ( Py_ssize_t i = 0; i < sz; ++i )
|
|
{
|
|
newref_t item(PySequence_GetItem(groups_infos.o, i));
|
|
if ( !PyDict_Check(item.o) )
|
|
continue;
|
|
borref_t nodes(PyDict_GetItemString(item.o, "nodes"));
|
|
if ( nodes.o == NULL || !PySequence_Check(nodes.o) )
|
|
continue;
|
|
borref_t text(PyDict_GetItemString(item.o, "text"));
|
|
if ( text.o == NULL || !PyString_Check(text.o) )
|
|
continue;
|
|
group_crinfo_t gi;
|
|
Py_ssize_t nodes_cnt = PySequence_Size(nodes.o);
|
|
for ( Py_ssize_t k = 0; k < nodes_cnt; ++k )
|
|
{
|
|
newref_t node(PySequence_GetItem(nodes.o, k));
|
|
if ( PyInt_Check(node.o) )
|
|
gi.nodes.insert(PyInt_AsLong(node.o));
|
|
}
|
|
if ( !gi.nodes.empty() )
|
|
{
|
|
gi.text = PyString_AsString(text.o);
|
|
gis.push_back(gi);
|
|
}
|
|
}
|
|
intset_t groups;
|
|
if ( gis.empty() || !viewer_create_groups(view, &groups, gis) || groups.empty() )
|
|
Py_RETURN_NONE;
|
|
|
|
PyObject *py_groups = PyList_New(0);
|
|
for ( intset_t::const_iterator it = groups.begin(); it != groups.end(); ++it )
|
|
PyList_Append(py_groups, PyInt_FromLong(long(*it)));
|
|
return py_groups;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
static void pynodes_to_idanodes(intset_t *idanodes, ref_t pynodes)
|
|
{
|
|
Py_ssize_t sz = PySequence_Size(pynodes.o);
|
|
for ( Py_ssize_t i = 0; i < sz; ++i )
|
|
{
|
|
newref_t item(PySequence_GetItem(pynodes.o, i));
|
|
if ( !PyInt_Check(item.o) )
|
|
continue;
|
|
idanodes->insert(PyInt_AsLong(item.o));
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *py_customidamemo_t::delete_groups(PyObject *_groups, PyObject *_new_current)
|
|
{
|
|
if ( !PySequence_Check(_groups) || !PyNumber_Check(_new_current) )
|
|
Py_RETURN_NONE;
|
|
borref_t groups(_groups);
|
|
borref_t new_current(_new_current);
|
|
intset_t ida_groups;
|
|
pynodes_to_idanodes(&ida_groups, groups);
|
|
if ( ida_groups.empty() )
|
|
Py_RETURN_NONE;
|
|
if ( viewer_delete_groups(view, ida_groups, int(PyInt_AsLong(new_current.o))) )
|
|
Py_RETURN_TRUE;
|
|
else
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *py_customidamemo_t::set_groups_visibility(PyObject *_groups, PyObject *_expand, PyObject *_new_current)
|
|
{
|
|
if ( !PySequence_Check(_groups)
|
|
|| !PyBool_Check(_expand)
|
|
|| !PyNumber_Check(_new_current) )
|
|
Py_RETURN_NONE;
|
|
borref_t groups(_groups);
|
|
borref_t expand(_expand);
|
|
borref_t new_current(_new_current);
|
|
intset_t ida_groups;
|
|
pynodes_to_idanodes(&ida_groups, groups);
|
|
if ( ida_groups.empty() )
|
|
Py_RETURN_NONE;
|
|
if ( viewer_set_groups_visibility(view, ida_groups, expand.o == Py_True, int(PyInt_AsLong(new_current.o))) )
|
|
Py_RETURN_TRUE;
|
|
else
|
|
Py_RETURN_FALSE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool py_customidamemo_t::bind(PyObject *_self, TCustomControl *view)
|
|
{
|
|
if ( this->self != NULL || this->view != NULL )
|
|
return false;
|
|
PYGLOG("%p: py_customidamemo_t::bind(_self=%p, view=%p)\n", this, _self, view);
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
|
|
newref_t py_cobj(PyCObject_FromVoidPtr(this, NULL));
|
|
PyObject_SetAttrString(_self, S_M_THIS, py_cobj.o);
|
|
|
|
this->self = borref_t(_self);
|
|
this->view = view;
|
|
return true;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::unbind()
|
|
{
|
|
if ( self == NULL )
|
|
return;
|
|
PYGLOG("%p: py_customidamemo_t::unbind(); self.o=%p, view=%p\n", this, self.o, view);
|
|
PYW_GIL_CHECK_LOCKED_SCOPE();
|
|
newref_t py_cobj(PyCObject_FromVoidPtr(NULL, NULL));
|
|
PyObject_SetAttrString(self.o, S_M_THIS, py_cobj.o);
|
|
self = newref_t(NULL);
|
|
view = NULL;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::collect_class_callbacks_ids(callbacks_ids_t *out)
|
|
{
|
|
out->add(S_ON_VIEW_ACTIVATED, GRBASE_HAVE_VIEW_ACTIVATED);
|
|
out->add(S_ON_VIEW_DEACTIVATED, GRBASE_HAVE_VIEW_DEACTIVATED);
|
|
out->add(S_ON_VIEW_KEYDOWN, GRBASE_HAVE_KEYDOWN);
|
|
out->add(S_ON_POPUP, GRBASE_HAVE_POPUP);
|
|
out->add(S_ON_VIEW_CLICK, GRBASE_HAVE_VIEW_CLICK);
|
|
out->add(S_ON_VIEW_DBLCLICK, GRBASE_HAVE_VIEW_DBLCLICK);
|
|
out->add(S_ON_VIEW_CURPOS, GRBASE_HAVE_VIEW_CURPOS);
|
|
out->add(S_ON_CLOSE, GRBASE_HAVE_CLOSE);
|
|
out->add(S_ON_VIEW_SWITCHED, GRBASE_HAVE_VIEW_SWITCHED);
|
|
out->add(S_ON_VIEW_MOUSE_OVER, GRBASE_HAVE_VIEW_MOUSE_OVER);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool py_customidamemo_t::collect_pyobject_callbacks(PyObject *o)
|
|
{
|
|
callbacks_ids_t cbids;
|
|
collect_class_callbacks_ids(&cbids);
|
|
cb_flags = 0;
|
|
for ( callbacks_ids_t::const_iterator it = cbids.begin(); it != cbids.end(); ++it )
|
|
{
|
|
const callback_id_t &cbid = *it;
|
|
ref_t attr(PyW_TryGetAttrString(o, cbid.name.c_str()));
|
|
int have = cbid.have;
|
|
// Mandatory fields not present?
|
|
if ( (attr == NULL && have <= 0 )
|
|
// Mandatory callback fields present but not callable?
|
|
|| (attr != NULL && have >= 0 && PyCallable_Check(attr.o) == 0))
|
|
{
|
|
return false;
|
|
}
|
|
if ( have > 0 && attr != NULL )
|
|
cb_flags |= have;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
#define CHK_EVT(flag_needed) \
|
|
if ( self == NULL || !has_callback(flag_needed) ) \
|
|
return; \
|
|
PYW_GIL_CHECK_LOCKED_SCOPE()
|
|
|
|
#ifdef PYGDBG_ENABLED
|
|
#define CHK_RES() PYGLOG("%s: return code: %p\n", __FUNCTION__, result.o)
|
|
#else
|
|
#define CHK_RES()
|
|
#endif
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_activated()
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_ACTIVATED);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_ACTIVATED,
|
|
NULL));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_deactivated()
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_DEACTIVATED);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_DEACTIVATED,
|
|
NULL));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_keydown(int key, int state)
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_KEYDOWN);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_KEYDOWN,
|
|
"ii",
|
|
key, state));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_popup()
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_POPUP);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_POPUP,
|
|
NULL));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_click(const view_mouse_event_t *event)
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_CLICK);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_CLICK,
|
|
"iii",
|
|
event->x, event->y, event->state));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_dblclick(const view_mouse_event_t *event)
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_DBLCLICK);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_DBLCLICK,
|
|
"iii",
|
|
event->x, event->y, event->state));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_curpos()
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_CURPOS);
|
|
newref_t result(
|
|
PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_CURPOS,
|
|
NULL));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_close()
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_CLOSE);
|
|
newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_CLOSE, NULL));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_switched(tcc_renderer_type_t rt)
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_SWITCHED);
|
|
newref_t result(PyObject_CallMethod(self.o, (char *)S_ON_VIEW_SWITCHED, "i", int(rt)));
|
|
CHK_RES();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void py_customidamemo_t::on_view_mouse_over(const view_mouse_event_t *event)
|
|
{
|
|
CHK_EVT(GRBASE_HAVE_VIEW_MOUSE_OVER);
|
|
if ( event->rtype == TCCRT_GRAPH || event->rtype == TCCRT_PROXIMITY )
|
|
{
|
|
const selection_item_t *item = event->location.item;
|
|
int icode;
|
|
ref_t tuple;
|
|
if ( item != NULL )
|
|
{
|
|
if ( item->is_node )
|
|
{
|
|
icode = 1;
|
|
tuple = newref_t(Py_BuildValue("(i)", item->node));
|
|
}
|
|
else
|
|
{
|
|
icode = 2;
|
|
tuple = newref_t(Py_BuildValue("(ii)", item->elp.e.src, item->elp.e.dst));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
icode = 0;
|
|
tuple = newref_t(Py_BuildValue("()"));
|
|
}
|
|
newref_t result(PyObject_CallMethod(
|
|
self.o,
|
|
(char *)S_ON_VIEW_MOUSE_OVER,
|
|
"iiiiO",
|
|
event->x, event->y, event->state, icode, tuple.o));
|
|
CHK_RES();
|
|
}
|
|
}
|
|
|
|
|
|
#undef CHK_RES
|
|
#undef CHK_EVT
|
|
|
|
//-------------------------------------------------------------------------
|
|
//-------------------------------------------------------------------------
|
|
|
|
#define GET_THIS() py_customidamemo_t *_this = view_extract_this<py_customidamemo_t>(self)
|
|
#define CHK_THIS() \
|
|
GET_THIS(); \
|
|
if ( _this == NULL ) \
|
|
return
|
|
#define CHK_THIS_OR_NONE() \
|
|
GET_THIS(); \
|
|
if ( _this == NULL ) \
|
|
Py_RETURN_NONE
|
|
|
|
//-------------------------------------------------------------------------
|
|
void pygc_refresh(PyObject *self)
|
|
{
|
|
CHK_THIS();
|
|
_this->refresh();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags)
|
|
{
|
|
CHK_THIS();
|
|
_this->set_node_info(py_node_idx, py_node_info, py_flags);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void pygc_set_nodes_infos(PyObject *self, PyObject *values)
|
|
{
|
|
CHK_THIS();
|
|
_this->set_nodes_infos(values);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx)
|
|
{
|
|
GET_THIS();
|
|
if ( _this != NULL )
|
|
return _this->get_node_info(py_node_idx);
|
|
else
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes)
|
|
{
|
|
CHK_THIS();
|
|
_this->del_nodes_infos(py_nodes);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *pygc_get_current_renderer_type(PyObject *self)
|
|
{
|
|
GET_THIS();
|
|
if ( _this != NULL )
|
|
return _this->get_current_renderer_type();
|
|
else
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt)
|
|
{
|
|
CHK_THIS();
|
|
_this->set_current_renderer_type(py_rt);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos)
|
|
{
|
|
CHK_THIS_OR_NONE();
|
|
return _this->create_groups(groups_infos);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current)
|
|
{
|
|
CHK_THIS_OR_NONE();
|
|
return _this->delete_groups(groups, new_current);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current)
|
|
{
|
|
CHK_THIS_OR_NONE();
|
|
return _this->set_groups_visibility(groups, expand, new_current);
|
|
}
|
|
|
|
#undef CHK_THIS_OR_NONE
|
|
#undef CHK_THIS
|
|
#undef GET_THIS
|
|
|
|
//-------------------------------------------------------------------------
|
|
lookup_info_t py_customidamemo_t::lookup_info;
|
|
//</code(py_view_base)>
|
|
|
|
%}
|
|
|
|
%inline %{
|
|
//<inline(py_view_base)>
|
|
void pygc_refresh(PyObject *self);
|
|
void pygc_set_node_info(PyObject *self, PyObject *py_node_idx, PyObject *py_node_info, PyObject *py_flags);
|
|
void pygc_set_nodes_infos(PyObject *self, PyObject *values);
|
|
PyObject *pygc_get_node_info(PyObject *self, PyObject *py_node_idx);
|
|
void pygc_del_nodes_infos(PyObject *self, PyObject *py_nodes);
|
|
PyObject *pygc_get_current_renderer_type(PyObject *self);
|
|
void pygc_set_current_renderer_type(PyObject *self, PyObject *py_rt);
|
|
PyObject *pygc_create_groups(PyObject *self, PyObject *groups_infos);
|
|
PyObject *pygc_delete_groups(PyObject *self, PyObject *groups, PyObject *new_current);
|
|
PyObject *pygc_set_groups_visibility(PyObject *self, PyObject *groups, PyObject *expand, PyObject *new_current);
|
|
//</inline(py_view_base)>
|
|
%}
|
|
|
|
%pythoncode %{
|
|
#<pycode(py_view_base)>
|
|
class CustomIDAMemo(object):
|
|
def Refresh(self):
|
|
"""
|
|
Refreshes the graph. This causes the OnRefresh() to be called
|
|
"""
|
|
_idaapi.pygc_refresh(self)
|
|
|
|
def GetCurrentRendererType(self):
|
|
return _idaapi.pygc_get_current_renderer_type(self)
|
|
|
|
def SetCurrentRendererType(self, rtype):
|
|
"""
|
|
Set the current view's renderer.
|
|
|
|
@param rtype: The renderer type. Should be one of the idaapi.TCCRT_* values.
|
|
"""
|
|
_idaapi.pygc_set_current_renderer_type(self, rtype)
|
|
|
|
def SetNodeInfo(self, node_index, node_info, flags):
|
|
"""
|
|
Set the properties for the given node.
|
|
|
|
Example usage (set second nodes's bg color to red):
|
|
inst = ...
|
|
p = idaapi.node_info_t()
|
|
p.bg_color = 0x00ff0000
|
|
inst.SetNodeInfo(1, p, idaapi.NIF_BG_COLOR)
|
|
|
|
@param node_index: The node index.
|
|
@param node_info: An idaapi.node_info_t instance.
|
|
@param flags: An OR'ed value of NIF_* values.
|
|
"""
|
|
_idaapi.pygc_set_node_info(self, node_index, node_info, flags)
|
|
|
|
def SetNodesInfos(self, values):
|
|
"""
|
|
Set the properties for the given nodes.
|
|
|
|
Example usage (set first three nodes's bg color to purple):
|
|
inst = ...
|
|
p = idaapi.node_info_t()
|
|
p.bg_color = 0x00ff00ff
|
|
inst.SetNodesInfos({0 : p, 1 : p, 2 : p})
|
|
|
|
@param values: A dictionary of 'int -> node_info_t' objects.
|
|
"""
|
|
_idaapi.pygc_set_nodes_infos(self, values)
|
|
|
|
def GetNodeInfo(self, node):
|
|
"""
|
|
Get the properties for the given node.
|
|
|
|
@param node: The index of the node.
|
|
@return: A tuple (bg_color, frame_color, ea, text), or None.
|
|
"""
|
|
return _idaapi.pygc_get_node_info(self, node)
|
|
|
|
def DelNodesInfos(self, *nodes):
|
|
"""
|
|
Delete the properties for the given node(s).
|
|
|
|
@param nodes: A list of node IDs
|
|
"""
|
|
return _idaapi.pygc_del_nodes_infos(self, nodes)
|
|
|
|
def CreateGroups(self, groups_infos):
|
|
"""
|
|
Send a request to modify the graph by creating a
|
|
(set of) group(s), and perform an animation.
|
|
|
|
Each object in the 'groups_infos' list must be of the format:
|
|
{
|
|
"nodes" : [<int>, <int>, <int>, ...] # The list of nodes to group
|
|
"text" : <string> # The synthetic text for that group
|
|
}
|
|
|
|
@param groups_infos: A list of objects that describe those groups.
|
|
@return: A [<int>, <int>, ...] list of group nodes, or None (failure).
|
|
"""
|
|
return _idaapi.pygc_create_groups(self, groups_infos)
|
|
|
|
def DeleteGroups(self, groups, new_current = -1):
|
|
"""
|
|
Send a request to delete the specified groups in the graph,
|
|
and perform an animation.
|
|
|
|
@param groups: A list of group node numbers.
|
|
@param new_current: A node to focus on after the groups have been deleted
|
|
@return: True on success, False otherwise.
|
|
"""
|
|
return _idaapi.pygc_delete_groups(self, groups, new_current)
|
|
|
|
def SetGroupsVisibility(self, groups, expand, new_current = -1):
|
|
"""
|
|
Send a request to expand/collapse the specified groups in the graph,
|
|
and perform an animation.
|
|
|
|
@param groups: A list of group node numbers.
|
|
@param expand: True to expand the group, False otherwise.
|
|
@param new_current: A node to focus on after the groups have been expanded/collapsed.
|
|
@return: True on success, False otherwise.
|
|
"""
|
|
return _idaapi.pygc_set_groups_visibility(self, groups, expand, new_current)
|
|
|
|
#</pycode(py_view_base)>
|
|
%}
|
|
|
|
%{
|
|
//<code(py_idaview)>
|
|
class py_idaview_t : public py_customidamemo_t
|
|
{
|
|
typedef py_customidamemo_t inherited;
|
|
|
|
public:
|
|
static bool Bind(PyObject *self);
|
|
};
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool py_idaview_t::Bind(PyObject *self)
|
|
{
|
|
// Already a py_idaview_t associated to this object?
|
|
py_idaview_t *_this = view_extract_this<py_idaview_t>(self);
|
|
if ( _this != NULL )
|
|
return false;
|
|
|
|
qstring title;
|
|
if ( !PyW_GetStringAttr(self, S_M_TITLE, &title) )
|
|
return false;
|
|
|
|
// Get the IDAView associated to this TForm
|
|
TForm *tform = find_tform(title.c_str());
|
|
if ( tform == NULL )
|
|
return false;
|
|
TCustomControl *v = get_tform_idaview(tform);
|
|
if ( v == NULL )
|
|
return false;
|
|
|
|
// Get unique py_idaview_t associated to that tform
|
|
py_idaview_t *py_view;
|
|
TCustomControl *found_view;
|
|
if ( lookup_info.find_by_form(&found_view, (py_customidamemo_t**) &py_view, tform) )
|
|
{
|
|
// If we have a py_idaview_t for that form, ensure it has
|
|
// the expected view.
|
|
QASSERT(30451, found_view == v);
|
|
}
|
|
else
|
|
{
|
|
py_view = new py_idaview_t();
|
|
lookup_info.add(tform, v, py_view);
|
|
}
|
|
|
|
// Finally, bind:
|
|
// py_idaview_t <=> IDAViewWrapper
|
|
// py_idaview_t => TCustomControl
|
|
bool ok = py_view->bind(self, v);
|
|
if ( ok )
|
|
{
|
|
ok = py_view->collect_pyobject_callbacks(self);
|
|
if ( !ok )
|
|
delete py_view;
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool pyidag_bind(PyObject *self)
|
|
{
|
|
return py_idaview_t::Bind(self);
|
|
}
|
|
|
|
//</code(py_idaview)>
|
|
%}
|
|
|
|
%inline %{
|
|
//<inline(py_idaview)>
|
|
bool pyidag_bind(PyObject *self);
|
|
//</inline(py_idaview)>
|
|
%}
|
|
|
|
%pythoncode %{
|
|
#<pycode(py_idaview)>
|
|
class IDAViewWrapper(CustomIDAMemo):
|
|
"""This class wraps access to native IDA views. See kernwin.hpp file"""
|
|
def __init__(self, title):
|
|
"""
|
|
Constructs the IDAViewWrapper object around the view
|
|
whose title is 'title'.
|
|
|
|
@param title: The title of the existing IDA view. E.g., 'IDA View-A'
|
|
"""
|
|
self._title = title
|
|
|
|
def Bind(self):
|
|
return _idaapi.pyidag_bind(self)
|
|
#</pycode(py_idaview)>
|
|
%}
|