radare2/libr/core/panels.c
2018-08-29 18:34:38 +02:00

2669 lines
76 KiB
C

/* Copyright radare2 2014-2018 - Author: pancake, vane11ope */
// pls move the typedefs into roons and rename it -> RConsPanel
#include <r_core.h>
#define PANEL_NUM_LIMIT 256
#define PANEL_MENU_LIMIT 10
#define PANEL_TITLE_SYMBOLS "Symbols"
#define PANEL_TITLE_STACK "Stack"
#define PANEL_TITLE_LOCALS "Locals"
#define PANEL_TITLE_STACKREFS "StackRefs"
#define PANEL_TITLE_REGISTERS "Registers"
#define PANEL_TITLE_REGISTERREFS "RegisterRefs"
#define PANEL_TITLE_DISASSEMBLY "Disassembly"
#define PANEL_TITLE_PSEUDO "Pseudo"
#define PANEL_TITLE_NEWFILES "New files"
#define PANEL_TITLE_INFO "Info"
#define PANEL_TITLE_DATABASE "Database"
#define PANEL_TITLE_HEXDUMP "Hexdump"
#define PANEL_TITLE_FUNCTIONS "Functions"
#define PANEL_TITLE_COMMENTS "Comments"
#define PANEL_TITLE_ENTROPY "Entropy"
#define PANEL_TITLE_DRX "DRX"
#define PANEL_TITLE_SECTIONS "Sections"
#define PANEL_TITLE_STRINGS "Strings"
#define PANEL_TITLE_MAPS "Maps"
#define PANEL_TITLE_MODULES "Modules"
#define PANEL_TITLE_BACKTRACE "Backtrace"
#define PANEL_TITLE_BREAKPOINTS "Breakpoints"
#define PANEL_TITLE_IMPORTS "Imports"
#define PANEL_TITLE_CLIPBOARD "Clipboard"
#define PANEL_TITLE_FCNINFO "FcnInfo"
#define PANEL_TITLE_GRAPH "Graph"
#define PANEL_CMD_SYMBOLS "isq"
#define PANEL_CMD_STACK "px 256@r:SP"
#define PANEL_CMD_STACKREFS "pxr 256@r:SP"
#define PANEL_CMD_LOCALS "afvd"
#define PANEL_CMD_REGISTERS "dr="
#define PANEL_CMD_REGISTERREFS "drr"
#define PANEL_CMD_DISASSEMBLY "pd $r"
#define PANEL_CMD_PSEUDO "pdc"
#define PANEL_CMD_GRAPH "agf"
#define PANEL_CMD_INFO "i"
#define PANEL_CMD_DATABASE "k ***"
#define PANEL_CMD_HEXDUMP "px 512"
#define PANEL_CMD_FUNCTIONS "afl"
#define PANEL_CMD_COMMENTS "CC"
#define PANEL_CMD_ENTROPY "p=e"
#define PANEL_CMD_DRX "drx"
#define PANEL_CMD_SECTIONS "iSq"
#define PANEL_CMD_STRINGS "izq"
#define PANEL_CMD_MAPS "dm"
#define PANEL_CMD_MODULES "dmm"
#define PANEL_CMD_BACKTRACE "dbt"
#define PANEL_CMD_BREAKPOINTS "db"
#define PANEL_CMD_IMPORTS "iiq"
#define PANEL_CMD_CLIPBOARD "yx"
#define PANEL_CMD_FCNINFO "afi"
#define PANEL_CONFIG_PAGE 10
#define PANEL_CONFIG_SIDEPANEL_W 60
#define PANEL_CONFIG_RESIZE_W 4
#define PANEL_CONFIG_RESIZE_H 2
static const int layoutMaxCount = 2;
enum {
LAYOUT_DEFAULT = 0,
LAYOUT_BALANCE = 1
};
static const char *menus[] = {
"File", "Edit", "View", "Tools", "Search", "Debug", "Analyze", "Help",
NULL
};
static const int menuNum = ((int)sizeof (menus) / (int)sizeof (const char*)) - 1;
static const char *menus_File[] = {
"New", "Open", "ReOpen", "Close", "Sections", "Strings", "Symbols", "Imports", "Info", "Database", "Quit",
NULL
};
static const char *menus_ReOpen[] = {
"In RW", "In Debugger",
NULL
};
static const char *menus_Edit[] = {
"Copy", "Paste", "Clipboard", "Write String", "Write Hex", "Write Value", "Assemble", "Fill", "io.cache",
NULL
};
static const char *menus_View[] = {
"Hexdump", "Disassembly", "Graph", "FcnInfo", "Functions", "Comments", "Entropy", "Colors",
"Stack", "StackRefs", "Pseudo",
NULL
};
static const char *menus_Tools[] = {
"Assembler", "Calculator", "R2 Shell", "System Shell",
NULL
};
static const char *menus_Search[] = {
"String", "ROP", "Code", "Hexpairs",
NULL
};
static const char *menus_Debug[] = {
"Registers", "RegisterRefs", "DRX", "Breakpoints",
"Watchpoints", "Maps", "Modules",
"Backtrace", "Locals",
"Continue", "Cont until.",
"Step", "Step Over",
"Reload",
NULL
};
static const char *menus_Analyze[] = {
"Function", "Symbols", "Program", "BasicBlocks", "Calls", "References",
NULL
};
static const char *menus_Help[] = {
"Fortune", "Commands", "2048", "License", ".", "About",
NULL
};
static void layoutMenu(RPanel *panel);
static void layoutSubMenu(RPanels *panels, int w);
static void layoutDefault(RPanels *panels);
static void layoutBalance(RPanels *panels);
static void layoutSidePanel(RCore *core, const char *title, const char *cmd);
static void changePanelNum(RPanels *panels, int now, int after);
static void splitPanelVertical(RCore *core);
static void splitPanelHorizontal(RCore *core);
static void panelPrint(RCore *core, RConsCanvas *can, RPanel *panel, int color);
static void panelAllClear(RPanels *panels);
static void addPanelFrame(RCore* core, RPanels* panels, const char *title, const char *cmd);
static RPanel createMenuPanel(int x, int y, char *title);
static bool checkFunc(RCore *core);
static void activateCursor(RCore *core);
static void cursorLeft(RCore *core);
static void cursorRight(RCore *core);
static void cursorDown(RCore *core);
static void cursorUp(RCore *core);
static int cursorThreshold(RPanel* panel);
static void delPanel(RPanels *panels, int delPanelNum);
static void delCurPanel(RPanels *panels);
static void delInvalidPanels(RPanels *panels);
static void dismantlePanel(RPanels *panels);
static void doPanelsRefresh(RCore *core);
static void doPanelsRefreshOneShot(RCore *core);
static void handleZoomMode(RCore *core, const int key);
static bool handleCursorMode(RCore *core, const int key);
static void handleUpKey(RCore *core);
static void handleDownKey(RCore *core);
static void handleLeftKey(RCore *core);
static void handleRightKey(RCore *core);
static void resizePanelLeft(RPanels *panels);
static void resizePanelRight(RPanels *panels);
static void resizePanelUp(RPanels *panels);
static void resizePanelDown(RPanels *panels);
static void handleTabKey(RCore *core, bool shift);
static bool init(RCore *core, RPanels *panels, int w, int h);
static bool initPanels(RCore *core, RPanels *panels);
static void freePanel(RPanel *panel);
static void panelBreakpoint(RCore *core);
static void panelContinue(RCore *core);
static void panelPrompt(const char *prompt, char *buf, int len);
static void panelSingleStepIn(RCore *core);
static void panelSingleStepOver(RCore *core);
static void setRefreshAll(RPanels *panels);
static void setCursor(RCore *core, bool cur);
static void savePanelPos(RPanel* panel);
static void restorePanelPos(RPanel* panel);
static void replaceCmd(RPanels* panels, char *title, char *cmd);
static void handleMenu(RCore *core, const int key, int *exit);
static void onMenu(RCore *core, const char *menu, int *exit);
static void changeMenu(RPanels *panels, const char **dstMenu);
static void switchMode(RPanels *panels);
static void maximizePanelSize(RPanels *panels);
static void insertValue(RCore *core);
static RConsCanvas *createNewCanvas(RCore *core, int w, int h);
static void panelPrint(RCore *core, RConsCanvas *can, RPanel *panel, int color) {
if (!can || !panel|| !panel->refresh) {
return;
}
if (can->w <= panel->pos.x || can->h <= panel->pos.y) {
return;
}
int w = R_MIN (can->w - panel->pos.x, panel->pos.w);
int h = R_MIN (can->h - panel->pos.y, panel->pos.h);
panel->refresh = false;
char *text;
char title[128];
int delta_x, delta_y, graph_pad = 0;
delta_x = panel->sx;
delta_y = panel->sy;
r_cons_canvas_fill (can, panel->pos.x, panel->pos.y, w, h, ' ');
if (panel->type == PANEL_TYPE_MENU) {
(void) r_cons_canvas_gotoxy (can, panel->pos.x + 2, panel->pos.y + 2);
text = r_str_ansi_crop (panel->title,
delta_x, delta_y, w + 5, h - delta_y);
if (text) {
r_cons_canvas_write (can, text);
free (text);
} else {
r_cons_canvas_write (can, panel->title);
}
} else {
if (color) {
snprintf (title, sizeof (title) - 1,
"%s[x] %s"Color_RESET, core->cons->pal.graph_box2, panel->title);
} else {
snprintf (title, sizeof (title) - 1,
" %s ", panel->title);
}
if (r_cons_canvas_gotoxy (can, panel->pos.x + 1, panel->pos.y + 1)) {
r_cons_canvas_write (can, title);
}
(void) r_cons_canvas_gotoxy (can, panel->pos.x + 2, panel->pos.y + 2);
char *cmdStr;
if (!strcmp (panel->cmd, PANEL_CMD_DISASSEMBLY)) {
core->offset = panel->addr;
r_core_seek (core, panel->addr, 1);
r_core_block_read (core);
cmdStr = r_core_cmd_str (core, panel->cmd);
} else if (!strcmp (panel->cmd, PANEL_CMD_STACK)) {
const int delta = r_config_get_i (core->config, "stack.delta");
const char sign = (delta < 0)? '+': '-';
const int absdelta = R_ABS (delta);
cmdStr = r_core_cmd_strf (core, "%s%c%d", PANEL_CMD_STACK, sign, absdelta);
} else if (!strcmp (panel->cmd, PANEL_CMD_GRAPH)) {
if (panel->cmdStrCache) {
cmdStr = panel->cmdStrCache;
} else {
cmdStr = r_core_cmd_str (core, panel->cmd);
panel->cmdStrCache = cmdStr;
}
graph_pad = 1;
core->cons->event_resize = NULL; // avoid running old event with new data
core->cons->event_data = core;
core->cons->event_resize = (RConsEvent) doPanelsRefreshOneShot;
} else if (!strcmp (panel->cmd, PANEL_CMD_PSEUDO)) {
if (panel->cmdStrCache) {
cmdStr = panel->cmdStrCache;
} else {
cmdStr = r_core_cmd_str (core, panel->cmd);
panel->cmdStrCache = cmdStr;
}
} else {
cmdStr = r_core_cmd_str (core, panel->cmd);
}
if (delta_y < 0) {
delta_y = 0;
}
if (delta_x < 0) {
char *white = (char*)r_str_pad (' ', 128);
int idx = -delta_x;
if (idx >= sizeof (white)) {
idx = sizeof (white) - 1;
}
white[idx] = 0;
text = r_str_ansi_crop (cmdStr,
0, delta_y + graph_pad, w + delta_x - 3, h - 2 + delta_y);
char *newText = r_str_prefix_all (text, white);
if (newText) {
free (text);
text = newText;
}
} else {
text = r_str_ansi_crop (cmdStr,
delta_x, delta_y + graph_pad, w + delta_x - 3, h - 2 + delta_y);
}
if (text) {
r_cons_canvas_write (can, text);
free (text);
} else {
r_cons_canvas_write (can, panel->title);
}
if (!panel->cmdStrCache) {
free (cmdStr);
}
}
if (color) {
r_cons_canvas_box (can, panel->pos.x, panel->pos.y, w, h, core->cons->pal.graph_box2);
} else {
r_cons_canvas_box (can, panel->pos.x, panel->pos.y, w, h, core->cons->pal.graph_box);
}
}
static void panelAllClear(RPanels *panels) {
if (!panels) {
return;
}
int i;
RPanel *panel = NULL;
for (i = 0; i < panels->n_panels; i++) {
panel = &panels->panel[i];
r_cons_canvas_fill (panels->can, panel->pos.x, panel->pos.y, panel->pos.w, panel->pos.h, ' ');
}
r_cons_canvas_print (panels->can);
r_cons_flush ();
}
static void layoutMenu(RPanel *panel) {
panel->pos.w = r_str_bounds (panel->title, &panel->pos.h);
panel->pos.h += 4;
}
static void layoutSubMenu(RPanels *panels, int w) {
int i, j;
RPanel panel;
int x = panels->menuIndexStack[0] * 6;
int y = 1;
const char **currentMenu;
int currentMenuIndex;
for (i = 0; i < panels->menuStackDepth; i++) {
if (i == 0) {
continue;
}
currentMenu = panels->menuStack[i];
currentMenuIndex = panels->menuIndexStack[i];
RStrBuf *tit = r_strbuf_new (NULL);
if (!tit) {
return;
}
for (j = 0; currentMenu[j]; j++) {
if (currentMenuIndex == j) {
r_strbuf_append (tit, "> ");
} else {
r_strbuf_append (tit, " ");
}
r_strbuf_append (tit, currentMenu[j]);
r_strbuf_append (tit, " \n");
}
panel = createMenuPanel (x, y, r_strbuf_drain (tit));
layoutMenu (&panel);
panels->menuPanel[i] = panel;
x += panel.pos.w - 1;
y = panels->menuIndexStack[i] + 2;
}
RStrBuf *tit = r_strbuf_new (NULL);
if (!tit) {
return;
}
for (i = 0; panels->currentMenu[i]; i++) {
if (panels->currentMenuIndex == i) {
r_strbuf_append (tit, "> ");
} else {
r_strbuf_append (tit, " ");
}
r_strbuf_append (tit, panels->currentMenu[i]);
r_strbuf_append (tit, " \n");
}
panel = createMenuPanel (x, y, r_strbuf_drain (tit));
layoutMenu (&panel);
panels->menuPanel[panels->menuStackDepth] = panel;
}
R_API void r_core_panels_layout (RPanels *panels) {
panels->can->sx = 0;
panels->can->sy = 0;
switch (panels->layout) {
case LAYOUT_DEFAULT:
layoutDefault (panels);
break;
case LAYOUT_BALANCE:
layoutBalance (panels);
break;
}
}
static void layoutDefault(RPanels *panels) {
int h, w = r_cons_get_size (&h);
int ph = (h - 1) / (panels->n_panels - 1);
int i;
int colpos = w - panels->columnWidth;
RPanel *panel = panels->panel;
panel[0].pos.x = 0;
panel[0].pos.y = 1;
if (panels->n_panels > 1) {
panel[0].pos.w = colpos + 1;
} else {
panel[0].pos.w = w;
}
panel[0].pos.h = h - 1;
for (i = 1; i < panels->n_panels; i++) {
panel[i].pos.x = colpos;
panel[i].pos.y = 2 + (ph * (i - 1));
panel[i].pos.w = w - colpos;
if (panel[i].pos.w < 0) {
panel[i].pos.w = 0;
}
if ((i + 1) == panels->n_panels) {
panel[i].pos.h = h - panel[i].pos.y;
} else {
panel[i].pos.h = ph;
}
panel[i].pos.y--;
panel[i].pos.h++;
}
}
static void layoutBalance(RPanels *panels) {
int h, w = r_cons_get_size (&h);
int i, ii;
int panelNum = panels->n_panels;
int leftCol = panelNum / 2;
int rightCol = panelNum - leftCol;
int pw = w / 2;
RPanel *panel = panels->panel;
for (i = 0; i < leftCol; i++) {
panel[i].pos.x = 0;
panel[i].pos.y = 1 + i * (h / leftCol - 1);
panel[i].pos.w = pw + 2;
panel[i].pos.h = h / leftCol;
if (i == leftCol - 1) {
panel[i].pos.h = h - panel[i].pos.y;
} else {
panel[i].pos.h = h / leftCol;
}
}
for (i = 0; i < rightCol; i++) {
ii = i + leftCol;
panel[ii].pos.x = pw + 1;
panel[ii].pos.y = 1 + i * (h / rightCol - 1);
panel[ii].pos.w = pw - 1;
if (i == rightCol - 1) {
panel[ii].pos.h = h - panel[ii].pos.y;
} else {
panel[ii].pos.h = h / rightCol;
}
}
}
static void layoutSidePanel(RCore *core, const char *title, const char *cmd) {
RPanels *panels = core->panels;
RPanel *panel = panels->panel;
int h;
r_cons_get_size (&h);
int i;
(void)r_cons_get_size (&h);
for (i = 0; i < panels->n_panels; i++) {
RPanel *p = &panel[i];
if (p->pos.x == 0) {
if (p->pos.w >= PANEL_CONFIG_SIDEPANEL_W) {
p->pos.x += PANEL_CONFIG_SIDEPANEL_W - 1;
p->pos.w -= PANEL_CONFIG_SIDEPANEL_W - 1;
}
}
}
addPanelFrame (core, panels, title, cmd);
changePanelNum (panels, panels->n_panels - 1, 0);
panel[0].pos.x = 0;
panel[0].pos.y = 1;
panel[0].pos.w = PANEL_CONFIG_SIDEPANEL_W;
panel[0].pos.h = h - 1;
panels->curnode = 0;
setRefreshAll (panels);
}
static void splitPanelVertical(RCore *core) {
RPanels *panels = core->panels;
RPanel *panel = panels->panel;
const int curnode = panels->curnode;
const int owidth = panel[curnode].pos.w;
addPanelFrame (core, panels, panel[curnode].title, panel[curnode].cmd);
changePanelNum (panels, panels->n_panels - 1, panels->curnode + 1);
panel[curnode].pos.w = owidth / 2 + 1;
panel[curnode + 1].pos.x = panel[curnode].pos.x + panel[curnode].pos.w - 1;
panel[curnode + 1].pos.y = panel[curnode].pos.y;
panel[curnode + 1].pos.w = owidth - panel[curnode].pos.w + 1;
panel[curnode + 1].pos.h = panel[curnode].pos.h;
setRefreshAll (panels);
}
static void splitPanelHorizontal(RCore *core) {
RPanels *panels = core->panels;
RPanel *panel = panels->panel;
const int curnode = panels->curnode;
const int oheight = panel[curnode].pos.h;
panel[curnode].curpos = 0;
addPanelFrame (core, panels, panel[curnode].title, panel[curnode].cmd);
changePanelNum (panels, panels->n_panels - 1, panels->curnode + 1);
panel[curnode].pos.h = oheight / 2 + 1;
panel[curnode + 1].pos.x = panel[curnode].pos.x;
panel[curnode + 1].pos.y = panel[curnode].pos.y + panel[curnode].pos.h - 1;
panel[curnode + 1].pos.w = panel[curnode].pos.w;
panel[curnode + 1].pos.h = oheight - panel[curnode].pos.h + 1;
setRefreshAll (panels);
}
R_API void r_core_panels_layout_refresh(RCore *core) {
delInvalidPanels (core->panels);
r_core_panels_check_stackbase (core);
r_core_panels_refresh (core);
}
static void changePanelNum(RPanels *panels, int now, int after) {
RPanel *panel = panels->panel;
const int n_panels = panels->n_panels;
int i;
RPanel tmpPanel = panel[now];
for (i = n_panels - 1; i > after; i--) {
panel[i] = panel[i - 1];
}
panel[after] = tmpPanel;
}
static void setCursor(RCore *core, bool cur) {
core->print->cur_enabled = cur;
if (cur) {
core->print->cur = core->panels->panel[core->panels->curnode].curpos;
} else {
core->panels->panel[core->panels->curnode].curpos = core->print->cur;
}
core->print->col = core->print->cur_enabled ? 1: 0;
}
static void cursorRight(RCore *core) {
if (!strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_STACK) && core->print->cur >= 15) {
return;
}
if (!strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_REGISTERS)
|| !strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_STACK)) {
core->print->cur++;
core->panels->panel[core->panels->curnode].addr++;
} else {
core->print->cur++;
RPanel *curPanel = &core->panels->panel[core->panels->curnode];
int threshold = cursorThreshold (curPanel);
int row = r_print_row_at_off (core->print, core->print->cur);
if (row >= threshold) {
core->offset = core->panels->panel[core->panels->curnode].addr;
RAsmOp op;
ut32 next_roff = r_print_rowoff (core->print, row + 1);
int sz = r_asm_disassemble (core->assembler, &op,
core->block + next_roff, 32);
if (sz < 1) {
sz = 1;
}
r_core_seek_delta (core, sz);
core->panels->panel[core->panels->curnode].addr = core->offset;
r_core_block_read (core);
core->print->cur = R_MAX (core->print->cur - sz, 0);
}
}
}
static void activateCursor(RCore *core) {
RPanels *panels = core->panels;
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_STACK)
|| !strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_REGISTERS)
|| !strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
setCursor (core, !core->print->cur_enabled);
panels->panel[panels->curnode].refresh = true;
}
}
static void cursorLeft(RCore *core) {
if (!strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_REGISTERS)
|| !strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_STACK)) {
if (core->print->cur > 0) {
core->print->cur--;
core->panels->panel[core->panels->curnode].addr--;
}
} else {
core->print->cur--;
int row = r_print_row_at_off (core->print, core->print->cur);
if (row < 0) {
int cols = core->print->cols;
ut64 prevoff = core->offset;
core->offset = core->panels->panel[core->panels->curnode].addr;
r_core_visual_disasm_up (core, &cols);
r_core_seek_delta (core, -cols);
core->panels->panel[core->panels->curnode].addr = core->offset;
core->print->cur = prevoff - core->offset - 1;
}
}
}
static void cursorUp(RCore *core) {
RPrint *p = core->print;
ut32 roff;
int row;
if (p->row_offsets) {
row = r_print_row_at_off (p, p->cur);
roff = r_print_rowoff (p, row);
if (roff == UT32_MAX) {
p->cur--;
return;
}
if (row > 0) {
ut32 prev_roff;
int delta, prev_sz;
prev_roff = r_print_rowoff (p, row - 1);
delta = p->cur - roff;
prev_sz = roff - prev_roff;
int res = R_MIN (delta, prev_sz - 1);
ut64 cur = prev_roff + res;
if (cur == p->cur) {
if (p->cur > 0) {
p->cur--;
}
} else {
p->cur = prev_roff + delta;
}
} else {
int cols = core->print->cols;
ut64 prevoff = core->offset;
r_core_visual_disasm_up (core, &cols);
r_core_seek_delta (core, -cols);
core->panels->panel[core->panels->curnode].addr = core->offset;
core->print->cur = R_MIN (prevoff - core->offset - 1, core->print->cur);
}
} else {
p->cur -= p->cols;
}
}
static void cursorDown(RCore *core) {
RPanel *curPanel = &core->panels->panel[core->panels->curnode];
int threshold = cursorThreshold (curPanel);
RPrint *p = core->print;
ut32 roff, next_roff;
int row, sz, delta;
RAsmOp op;
if (p->row_offsets) {
row = r_print_row_at_off (p, p->cur);
roff = r_print_rowoff (p, row);
if (roff == -1) {
p->cur++;
return;
}
next_roff = r_print_rowoff (p, row + 1);
if (next_roff == -1) {
p->cur++;
return;
}
sz = r_asm_disassemble (core->assembler, &op,
core->block + next_roff, 32);
if (sz < 1) {
sz = 1;
}
delta = p->cur - roff;
p->cur = next_roff + R_MIN (delta, sz - 1);
row = r_print_row_at_off (p, p->cur);
if (row >= threshold) {
r_core_seek_delta (core, sz);
p->cur = R_MAX (p->cur - sz, 0);
}
} else {
p->cur += R_MAX (1, p->cols);
}
}
static int cursorThreshold(RPanel* panel) {
int threshold = (panel->pos.h - 4) / 2;
if (threshold < 10) {
threshold = 1;
}
return threshold;
}
static void handleUpKey(RCore *core) {
RPanels *panels = core->panels;
r_cons_switchbuf (false);
if (panels->curnode == panels->menu_pos) {
if (panels->menuStackDepth == 0) {
return;
} else if (panels->menuStackDepth == 1) {
if (!panels->currentMenuIndex) {
panels->menuStackDepth = 0;
panels->currentMenuIndex = panels->menuIndexStack[0];
panels->currentMenu = panels->menuStack[0];
setRefreshAll (panels);
} else {
panels->currentMenuIndex--;
}
} else {
if (panels->currentMenuIndex) {
panels->currentMenuIndex--;
}
}
} else {
panels->panel[panels->curnode].refresh = true;
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
core->offset = panels->panel[panels->curnode].addr;
if (core->print->cur_enabled) {
cursorUp (core);
panels->panel[panels->curnode].addr = core->offset;
} else {
int cols = core->print->cols;
r_core_visual_disasm_up (core, &cols);
r_core_seek_delta (core, -cols);
panels->panel[panels->curnode].addr = core->offset;
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_STACK)) {
int width = r_config_get_i (core->config, "hex.cols");
if (width < 1) {
width = 16;
}
r_config_set_i (core->config, "stack.delta",
r_config_get_i (core->config, "stack.delta") + width);
panels->panel[panels->curnode].addr -= width;
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_REGISTERS)) {
if (core->print->cur_enabled) {
int cur = core->print->cur;
int cols = core->dbg->regcols;
cols = cols > 0 ? cols : 3;
cur -= cols;
if (cur >= 0) {
core->print->cur = cur;
}
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_GRAPH)) {
if (panels->panel[panels->curnode].sy > 0) {
panels->panel[panels->curnode].sy -= r_config_get_i (core->config, "graph.scroll");
}
} else {
if (panels->panel[panels->curnode].sy > 0) {
panels->panel[panels->curnode].sy--;
}
}
}
}
static void handleDownKey(RCore *core) {
RPanels *panels = core->panels;
r_cons_switchbuf (false);
if (panels->curnode == panels->menu_pos) {
if (!panels->menuStackDepth) {
int exit = 0;
onMenu (core, panels->currentMenu[panels->currentMenuIndex], &exit);
} else {
if (panels->currentMenu[panels->currentMenuIndex + 1]) {
panels->currentMenuIndex++;
}
}
} else {
panels->panel[panels->curnode].refresh = true;
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
core->offset = panels->panel[panels->curnode].addr;
if (core->print->cur_enabled) {
cursorDown (core);
r_core_block_read (core);
panels->panel[panels->curnode].addr = core->offset;
} else {
RAsmOp op;
int cols = core->print->cols;
r_core_visual_disasm_down (core, &op, &cols);
r_core_seek (core, core->offset + cols, 1);
r_core_block_read (core);
panels->panel[panels->curnode].addr = core->offset;
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_STACK)) {
int width = r_config_get_i (core->config, "hex.cols");
if (width < 1) {
width = 16;
}
r_config_set_i (core->config, "stack.delta",
r_config_get_i (core->config, "stack.delta") - width);
panels->panel[panels->curnode].addr += width;
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_REGISTERS)) {
if (core->print->cur_enabled) {
const int cols = core->dbg->regcols;
core->print->cur += cols > 0 ? cols : 3;
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_GRAPH)) {
panels->panel[panels->curnode].sy += r_config_get_i (core->config, "graph.scroll");
} else {
panels->panel[panels->curnode].sy++;
}
}
}
static void handleLeftKey(RCore *core) {
RPanels *panels = core->panels;
r_cons_switchbuf (false);
if (panels->curnode == panels->menu_pos) {
if (!panels->menuStackDepth) {
if (panels->currentMenuIndex) {
panels->currentMenuIndex--;
} else {
panels->currentMenuIndex = menuNum - 1;
}
} else if (panels->menuStackDepth > 0) {
panels->menuStackDepth = 0;
panels->currentMenu = panels->menuStack[0];
if (panels->menuIndexStack[0]) {
panels->currentMenuIndex = panels->menuIndexStack[0] - 1;
} else {
panels->currentMenuIndex = menuNum - 1;
}
setRefreshAll (panels);
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_GRAPH)) {
if (panels->panel[panels->curnode].sx > 0) {
panels->panel[panels->curnode].sx -= r_config_get_i (core->config, "graph.scroll");
panels->panel[panels->curnode].refresh = true;
}
} else if (core->print->cur_enabled
&& (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_REGISTERS)
|| !strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_STACK)
|| !strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY))) {
cursorLeft (core);
panels->panel[panels->curnode].refresh = true;
} else {
if (panels->panel[panels->curnode].sx > 0) {
panels->panel[panels->curnode].sx--;
panels->panel[panels->curnode].refresh = true;
}
}
}
static void handleRightKey(RCore *core) {
RPanels *panels = core->panels;
r_cons_switchbuf (false);
if (panels->curnode == panels->menu_pos) {
if (!panels->menuStackDepth) {
panels->currentMenuIndex++;
panels->currentMenuIndex %= menuNum;
} else if (panels->menuStackDepth > 0) {
panels->menuStackDepth = 0;
panels->currentMenu = panels->menuStack[0];
panels->currentMenuIndex = panels->menuIndexStack[0] + 1;
panels->currentMenuIndex %= menuNum;
setRefreshAll (panels);
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_GRAPH)) {
panels->panel[panels->curnode].sx += r_config_get_i (core->config, "graph.scroll");
panels->panel[panels->curnode].refresh = true;
} else if (core->print->cur_enabled
&& (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_REGISTERS)
|| !strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_STACK)
|| !strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY))) {
cursorRight (core);
panels->panel[panels->curnode].refresh = true;
} else {
panels->panel[panels->curnode].sx++;
panels->panel[panels->curnode].refresh = true;
}
}
static void handleZoomMode(RCore *core, const int key) {
RPanels *panels = core->panels;
RPanel *panel = panels->panel;
switch (key) {
case 'Q':
case 'q':
case 0x0d:
switchMode (panels);
break;
case 'h':
handleLeftKey (core);
break;
case 'j':
handleDownKey (core);
break;
case 'k':
handleUpKey (core);
break;
case 'l':
handleRightKey (core);
break;
case 'c':
activateCursor (core);
break;
case 9:
restorePanelPos (&panel[panels->curnode]);
handleTabKey (core, false);
panels->curnode = R_MAX (panels->curnode, 0);
savePanelPos (&panel[panels->curnode]);
maximizePanelSize (panels);
break;
case 'Z':
restorePanelPos (&panel[panels->curnode]);
handleTabKey (core, true);
if (panels->curnode == panels->menu_pos) {
panels->curnode = panels->n_panels - 1;
}
savePanelPos (&panel[panels->curnode]);
maximizePanelSize (panels);
break;
}
}
static bool handleCursorMode(RCore *core, const int key) {
const RPanels *panels = core->panels;
if (!core->print->cur_enabled) {
return false;
}
if (core->print->cur_enabled) {
switch (key) {
case 'h':
handleLeftKey (core);
break;
case 'j':
handleDownKey (core);
break;
case 'k':
handleUpKey (core);
break;
case 'l':
handleRightKey (core);
break;
case 'Q':
case 'q':
setCursor (core, !core->print->cur_enabled);
panels->panel[panels->curnode].refresh = true;
break;
case 'i':
insertValue (core);
break;
case '*':
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
r_core_cmdf (core, "dr PC=0x%08"PFMT64x, core->offset + core->print->cur);
panels->panel[panels->curnode].addr = core->offset + core->print->cur;
panels->panel[panels->curnode].refresh = true;
}
break;
}
}
return true;
}
static void resizePanelLeft(RPanels *panels) {
RPanel *panel = panels->panel;
RPanel *curPanel = &panel[panels->curnode];
int i, cx0, cx1, cy0, cy1, tx0, tx1, ty0, ty1, cur1 = 0, cur2 = 0;
bool isResized = false;
cx0 = curPanel->pos.x;
cx1 = curPanel->pos.x + curPanel->pos.w - 1;
cy0 = curPanel->pos.y;
cy1 = curPanel->pos.y + curPanel->pos.h - 1;
RPanel **targets1 = malloc (sizeof (RPanel *) * panels->n_panels);
RPanel **targets2 = malloc (sizeof (RPanel *) * panels->n_panels);
if (cx0 > 0) {
if (cx1 - PANEL_CONFIG_RESIZE_W <= 0) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx1 == cx0 && ty0 == cy0 && ty1 == cy1) {
isResized = true;
p->pos.w -= PANEL_CONFIG_RESIZE_W;
p->refresh = true;
break;
} else if (tx1 == cx0) {
targets1[cur1++] = p;
} else if (tx0 == cx0) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.w -= PANEL_CONFIG_RESIZE_W;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.x -= PANEL_CONFIG_RESIZE_W;
targets2[i]->pos.w += PANEL_CONFIG_RESIZE_W;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.x -= PANEL_CONFIG_RESIZE_W;
curPanel->pos.w += PANEL_CONFIG_RESIZE_W;
} else {
if (cx1 - PANEL_CONFIG_RESIZE_W <= cx0) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx0 == cx1 && ty0 == cy0 && ty1 == cy1) {
isResized = true;
p->pos.x -= PANEL_CONFIG_RESIZE_W;
p->pos.w += PANEL_CONFIG_RESIZE_W;
p->refresh = true;
break;
} else if (tx0 == cx1) {
targets1[cur1++] = p;
} else if (tx1 == cx1) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.x -= PANEL_CONFIG_RESIZE_W;
targets1[i]->pos.w += PANEL_CONFIG_RESIZE_W;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.w -= PANEL_CONFIG_RESIZE_W;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.w -= PANEL_CONFIG_RESIZE_W;
}
beach:
free (targets1);
free (targets2);
}
static void resizePanelRight(RPanels *panels) {
RPanel *panel = panels->panel;
RPanel *curPanel = &panel[panels->curnode];
int i, tx0, tx1, ty0, ty1, cur1 = 0, cur2 = 0;
bool isResized = false;
int cx0 = curPanel->pos.x;
int cx1 = curPanel->pos.x + curPanel->pos.w - 1;
int cy0 = curPanel->pos.y;
int cy1 = curPanel->pos.y + curPanel->pos.h - 1;
RPanel **targets1 = malloc (sizeof (RPanel *) * panels->n_panels);
RPanel **targets2 = malloc (sizeof (RPanel *) * panels->n_panels);
if (!targets1 || !targets2) {
goto beach;
}
if (cx0 > 0) {
if (cx0 + PANEL_CONFIG_RESIZE_W >= cx1) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx1 == cx0 && ty0 == cy0 && ty1 == cy1) {
isResized = true;
p->pos.w += PANEL_CONFIG_RESIZE_W;
p->refresh = true;
break;
} else if (tx1 == cx0) {
targets1[cur1++] = p;
} else if (tx0 == cx0) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.w += PANEL_CONFIG_RESIZE_W;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.x += PANEL_CONFIG_RESIZE_W;
targets2[i]->pos.w -= PANEL_CONFIG_RESIZE_W;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.x += PANEL_CONFIG_RESIZE_W;
curPanel->pos.w -= PANEL_CONFIG_RESIZE_W;
} else {
if (cx1 + PANEL_CONFIG_RESIZE_W >= panels->can->w - 1) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx0 == cx1 && ty0 == cy0 && ty1 == cy1) {
if (cx1 + PANEL_CONFIG_RESIZE_W < tx1) {
isResized = true;
p->pos.x += PANEL_CONFIG_RESIZE_W;
p->pos.w -= PANEL_CONFIG_RESIZE_W;
p->refresh = true;
}
} else if (tx0 == cx1) {
targets1[cur1++] = p;
} else if (tx1 == cx1) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.x += PANEL_CONFIG_RESIZE_W;
targets1[i]->pos.w -= PANEL_CONFIG_RESIZE_W;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.w += PANEL_CONFIG_RESIZE_W;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.w += PANEL_CONFIG_RESIZE_W;
}
beach:
free (targets1);
free (targets2);
}
static void resizePanelUp(RPanels *panels) {
RPanel *panel = panels->panel;
RPanel *curPanel = &panel[panels->curnode];
int i, tx0, tx1, ty0, ty1, cur1 = 0, cur2 = 0;
bool isResized = false;
int cx0 = curPanel->pos.x;
int cx1 = curPanel->pos.x + curPanel->pos.w - 1;
int cy0 = curPanel->pos.y;
int cy1 = curPanel->pos.y + curPanel->pos.h - 1;
RPanel **targets1 = malloc (sizeof (RPanel *) * panels->n_panels);
RPanel **targets2 = malloc (sizeof (RPanel *) * panels->n_panels);
if (!targets1 || !targets2) {
goto beach;
}
if (cy0 > 1) {
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx0 == cx0 && tx1 == cx1 && ty1 == cy0) {
if (cy0 - PANEL_CONFIG_RESIZE_H <= ty0) {
goto beach;
}
isResized = true;
p->pos.h -= PANEL_CONFIG_RESIZE_H;
p->refresh = true;
break;
} else if (ty1 == cy0) {
targets1[cur1++] = p;
} else if (ty0 == cy0) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.h -= PANEL_CONFIG_RESIZE_H;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.y -= PANEL_CONFIG_RESIZE_H;
targets2[i]->pos.h += PANEL_CONFIG_RESIZE_H;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.y -= PANEL_CONFIG_RESIZE_H;
curPanel->pos.h += PANEL_CONFIG_RESIZE_H;
} else {
if (cy1 - PANEL_CONFIG_RESIZE_H <= 1) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx0 == cx0 && tx1 == cx1 && ty0 == cy1) {
isResized = true;
p->pos.y -= PANEL_CONFIG_RESIZE_H;
p->pos.h += PANEL_CONFIG_RESIZE_H;
p->refresh = true;
} else if (ty0 == cy1) {
targets1[cur1++] = p;
} else if (ty1 == cy1) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.y -= PANEL_CONFIG_RESIZE_H;
targets1[i]->pos.h += PANEL_CONFIG_RESIZE_H;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.h -= PANEL_CONFIG_RESIZE_H;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.h -= PANEL_CONFIG_RESIZE_H;
}
beach:
free (targets1);
free (targets2);
}
static void resizePanelDown(RPanels *panels) {
RPanel *panel = panels->panel;
RPanel *curPanel = &panel[panels->curnode];
int i, tx0, tx1, ty0, ty1, cur1 = 0, cur2 = 0;
bool isResized = false;
int cx0 = curPanel->pos.x;
int cx1 = curPanel->pos.x + curPanel->pos.w - 1;
int cy0 = curPanel->pos.y;
int cy1 = curPanel->pos.y + curPanel->pos.h - 1;
RPanel **targets1 = malloc (sizeof (RPanel *) * panels->n_panels);
RPanel **targets2 = malloc (sizeof (RPanel *) * panels->n_panels);
if (!targets1 || !targets2) {
goto beach;
}
if (cy0 > 1) {
if (cy0 + PANEL_CONFIG_RESIZE_H >= cy1) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx0 == cx0 && tx1 == cx1 && ty1 == cy0) {
isResized = true;
p->pos.h += PANEL_CONFIG_RESIZE_H;
p->refresh = true;
break;
} else if (ty1 == cy0) {
targets1[cur1++] = p;
} else if (ty0 == cy0) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.h += PANEL_CONFIG_RESIZE_H;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.y += PANEL_CONFIG_RESIZE_H;
targets2[i]->pos.h -= PANEL_CONFIG_RESIZE_H;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.y += PANEL_CONFIG_RESIZE_H;
curPanel->pos.h -= PANEL_CONFIG_RESIZE_H;
} else {
if (cy1 + PANEL_CONFIG_RESIZE_H >= panels->can->h - 1) {
goto beach;
}
for (i = 0; i < panels->n_panels; i++) {
if (i == panels->curnode) {
continue;
}
RPanel *p = &panel[i];
tx0 = p->pos.x;
tx1 = p->pos.x + p->pos.w - 1;
ty0 = p->pos.y;
ty1 = p->pos.y + p->pos.h - 1;
if (tx0 == cx0 && tx1 == cx1 && ty0 == cy1) {
if (cy1 + PANEL_CONFIG_RESIZE_H >= ty1) {
goto beach;
}
isResized = true;
p->pos.y += PANEL_CONFIG_RESIZE_H;
p->pos.h -= PANEL_CONFIG_RESIZE_H;
p->refresh = true;
} else if (ty0 == cy1) {
targets1[cur1++] = p;
} else if (ty1 == cy1) {
targets2[cur2++] = p;
}
}
if (!isResized) {
for (i = 0; i < cur1; i++) {
targets1[i]->pos.y += PANEL_CONFIG_RESIZE_H;
targets1[i]->pos.h -= PANEL_CONFIG_RESIZE_H;
targets1[i]->refresh = true;
}
for (i = 0; i < cur2; i++) {
targets2[i]->pos.h += PANEL_CONFIG_RESIZE_H;
targets2[i]->refresh = true;
}
}
curPanel->refresh = true;
curPanel->pos.h += PANEL_CONFIG_RESIZE_H;
}
beach:
free (targets1);
free (targets2);
}
static void delPanel(RPanels *panels, int delPanelNum) {
int i;
for (i = delPanelNum; i < (panels->n_panels - 1); i++) {
panels->panel[i] = panels->panel[i + 1];
}
panels->panel[i].title = 0;
panels->n_panels--;
if (panels->curnode >= panels->n_panels) {
panels->curnode = panels->n_panels - 1;
}
}
static void delCurPanel(RPanels *panels) {
dismantlePanel (panels);
if (panels->curnode >= 0 && panels->n_panels > 2) {
delPanel (panels, panels->curnode);
}
}
static void delInvalidPanels(RPanels *panels) {
int i;
for (i = 1; i < panels->n_panels; i++) {
RPanel *panel = &panels->panel[i];
if (panel->pos.w < 2) {
delPanel (panels, i);
delInvalidPanels (panels);
break;
}
if (panel->pos.h < 2) {
delPanel (panels, i);
delInvalidPanels (panels);
break;
}
}
}
static void dismantlePanel(RPanels *panels) {
RPanel *justLeftPanel = NULL, *justRightPanel = NULL, *justUpPanel = NULL, *justDownPanel = NULL;
RPanel *tmpPanel = NULL;
bool leftUpValid = false, leftDownValid = false, rightUpValid = false, rightDownValid = false,
upLeftValid = false, upRightValid = false, downLeftValid = false, downRightValid = false;
int left[PANEL_NUM_LIMIT], right[PANEL_NUM_LIMIT], up[PANEL_NUM_LIMIT], down[PANEL_NUM_LIMIT];
memset (left, -1, sizeof (left));
memset (right, -1, sizeof (right));
memset (up, -1, sizeof (up));
memset (down, -1, sizeof (down));
int i, ox, oy, ow, oh;
ox = panels->panel[panels->curnode].pos.x;
oy = panels->panel[panels->curnode].pos.y;
ow = panels->panel[panels->curnode].pos.w;
oh = panels->panel[panels->curnode].pos.h;
for (i = 0; i < panels->n_panels; i++) {
tmpPanel = &panels->panel[i];
if (tmpPanel->pos.x + tmpPanel->pos.w - 1 == ox) {
left[i] = 1;
if (oy == tmpPanel->pos.y) {
leftUpValid = true;
if (oh == tmpPanel->pos.h) {
justLeftPanel = tmpPanel;
break;
}
}
if (oy + oh == tmpPanel->pos.y + tmpPanel->pos.h) {
leftDownValid = true;
}
}
if (tmpPanel->pos.x == ox + ow - 1) {
right[i] = 1;
if (oy == tmpPanel->pos.y) {
rightUpValid = true;
if (oh == tmpPanel->pos.h) {
rightDownValid = true;
justRightPanel = tmpPanel;
}
}
if (oy + oh == tmpPanel->pos.y + tmpPanel->pos.h) {
rightDownValid = true;
}
}
if (tmpPanel->pos.y + tmpPanel->pos.h - 1 == oy) {
up[i] = 1;
if (ox == tmpPanel->pos.x) {
upLeftValid = true;
if (ow == tmpPanel->pos.w) {
upRightValid = true;
justUpPanel = tmpPanel;
}
}
if (ox + ow == tmpPanel->pos.x + tmpPanel->pos.w) {
upRightValid = true;
}
}
if (tmpPanel->pos.y == oy + oh - 1) {
down[i] = 1;
if (ox == tmpPanel->pos.x) {
downLeftValid = true;
if (ow == tmpPanel->pos.w) {
downRightValid = true;
justDownPanel = tmpPanel;
}
}
if (ox + ow == tmpPanel->pos.x + tmpPanel->pos.w) {
downRightValid = true;
}
}
}
if (justLeftPanel) {
justLeftPanel->pos.w += ox + ow - (justLeftPanel->pos.x + justLeftPanel->pos.w);
} else if (justRightPanel) {
justRightPanel->pos.w = justRightPanel->pos.x + justRightPanel->pos.w - ox;
justRightPanel->pos.x = ox;
} else if (justUpPanel) {
justUpPanel->pos.h += oy + oh - (justUpPanel->pos.y + justUpPanel->pos.h);
} else if (justDownPanel) {
justDownPanel->pos.h = oh + justDownPanel->pos.y + justDownPanel->pos.h - (oy + oh);
justDownPanel->pos.y = oy;
} else if (leftUpValid && leftDownValid) {
for (i = 0; i < panels->n_panels; i++) {
if (left[i] != -1) {
tmpPanel = &panels->panel[i];
tmpPanel->pos.w += ox + ow - (tmpPanel->pos.x + tmpPanel->pos.w);
}
}
} else if (rightUpValid && rightDownValid) {
for (i = 0; i < panels->n_panels; i++) {
if (right[i] != -1) {
tmpPanel = &panels->panel[i];
tmpPanel->pos.w = tmpPanel->pos.x + tmpPanel->pos.w - ox;
tmpPanel->pos.x = ox;
}
}
} else if (upLeftValid && upRightValid) {
for (i = 0; i < panels->n_panels; i++) {
if (up[i] != -1) {
tmpPanel = &panels->panel[i];
tmpPanel->pos.h += oy + oh - (tmpPanel->pos.y + tmpPanel->pos.h);
}
}
} else if (downLeftValid && downRightValid) {
for (i = 0; i < panels->n_panels; i++) {
if (down[i] != -1) {
tmpPanel = &panels->panel[i];
tmpPanel->pos.h = oh + tmpPanel->pos.y + tmpPanel->pos.h - (oy + oh);
tmpPanel->pos.y = oy;
}
}
}
}
static void replaceCmd(RPanels* panels, char *title, char *cmd) {
freePanel (&panels->panel[panels->curnode]);
panels->panel[panels->curnode].title = strdup (title);
panels->panel[panels->curnode].cmd = r_str_newf (cmd);
panels->panel[panels->curnode].cmdStrCache = NULL;
setRefreshAll (panels);
}
static bool checkFunc(RCore *core) {
RAnalFunction *fun = r_anal_get_fcn_in (core->anal, core->offset, R_ANAL_FCN_TYPE_NULL);
if (!fun) {
r_cons_message ("Not in a function. Type 'df' to define it here");
return false;
}
if (r_list_empty (fun->bbs)) {
r_cons_message ("No basic blocks in this function. You may want to use 'afb+'.");
return false;
}
return true;
}
static void setRefreshAll(RPanels *panels) {
int i;
for (i = 0; i < panels->n_panels; i++) {
panels->panel[i].refresh = true;
}
}
static RConsCanvas *createNewCanvas(RCore *core, int w, int h) {
RConsCanvas *can = r_cons_canvas_new (w, h);
if (!can) {
eprintf ("Cannot create RCons.canvas context\n");
return false;
}
r_cons_canvas_fill (can, 0, 0, w, h, ' ');
can->linemode = r_config_get_i (core->config, "graph.linemode");
can->color = r_config_get_i (core->config, "scr.color");
return can;
}
static void addPanelFrame(RCore *core, RPanels* panels, const char *title, const char *cmd) {
RPanel* panel = panels->panel;
const int n_panels = panels->n_panels;
if (title) {
panel[n_panels].title = strdup (title);
panel[n_panels].cmd = r_str_newf (cmd);
} else {
panel[n_panels].title = r_core_cmd_str (core, cmd);
panel[n_panels].cmd = NULL;
}
panel[n_panels].type = PANEL_TYPE_FRAME;
panel[n_panels].refresh = true;
panel[n_panels].curpos = 0;
panel[n_panels].cmdStrCache = NULL;
if (!strcmp (panel[n_panels].cmd, PANEL_CMD_DISASSEMBLY)) {
panel[n_panels].addr = core->offset;
}
if (!strcmp (panel[n_panels].cmd, PANEL_CMD_STACK)) {
const char *sp = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
const ut64 stackbase = r_reg_getv (core->anal->reg, sp);
panel[n_panels].baseAddr = stackbase;
panel[n_panels].addr = stackbase - r_config_get_i (core->config, "stack.delta");
}
panels->n_panels++;
}
static RPanel createMenuPanel(int x, int y, char *title) {
RPanel panel = {{0}};
panel.pos.x = x;
panel.pos.y = y;
panel.title = title;
panel.refresh = true;
panel.type = PANEL_TYPE_MENU;
return panel;
}
static bool initPanels(RCore *core, RPanels *panels) {
panels->panel = calloc (sizeof (RPanel), PANEL_NUM_LIMIT);
if (!panels->panel) {
return false;
}
panels->n_panels = 0;
addPanelFrame (core, panels, PANEL_TITLE_DISASSEMBLY, PANEL_CMD_DISASSEMBLY);
addPanelFrame (core, panels, PANEL_TITLE_SYMBOLS, PANEL_CMD_SYMBOLS);
//addPanelFrame (core, panels, PANEL_TITLE_STACK, PANEL_CMD_STACK);
addPanelFrame (core, panels, PANEL_TITLE_STACKREFS, PANEL_CMD_STACKREFS);
addPanelFrame (core, panels, PANEL_TITLE_REGISTERS, PANEL_CMD_REGISTERS);
addPanelFrame (core, panels, PANEL_TITLE_REGISTERREFS, PANEL_CMD_REGISTERREFS);
panels->curnode = 0;
return true;
}
static void freePanel(RPanel *panel) {
free (panel->title);
free (panel->cmd);
free (panel->cmdStrCache);
}
// damn singletons.. there should be only one screen and therefor
// only one visual instance of the graph view. refactoring this
// into a struct makes the code to reference pointers unnecesarily
// we can look for a non-global solution here in the future if
// necessary
R_API void r_core_panels_refresh(RCore *core) {
RPanels *panels = core->panels;
if (!panels) {
return;
}
RPanel *panel = panels->panel;
if (!panel) {
return;
}
RConsCanvas *can = panels->can;
if (!can) {
return;
}
if (panels->curnode > panels->menu_pos) {
panels->menuStackDepth = 0;
panels->currentMenu = NULL;
panels->currentMenuIndex = 0;
}
char title[1024];
char str[1024];
int i, h, w = r_cons_get_size (&h);
r_cons_gotoxy (0, 0);
if (panels->isResizing) {
panels->isResizing = false;
if (!r_cons_canvas_resize (can, w, h)) {
return;
}
setRefreshAll (panels);
}
for (i = 0; i < panels->n_panels; i++) {
if (i != panels->curnode) {
panelPrint (core, can, &panel[i], 0);
}
}
if (panels->curnode > panels->menu_pos) {
panelPrint (core, can, &panel[panels->curnode], 1);
}
if (panels->curnode == panels->menu_pos && panels->menuStackDepth > 0) {
layoutSubMenu (panels, w);
for (i = 0; i <= panels->menuStackDepth; i++) {
panelPrint (core, can, &panels->menuPanel[i], 1);
}
}
(void) r_cons_canvas_gotoxy (can, -can->sx, -can->sy);
r_cons_canvas_fill (can, -can->sx, -can->sy, panel->pos.w, 1, ' ');
title[0] = 0;
if (panels->curnode == panels->menu_pos) {
strcpy (title, "> ");
}
const char *color = panels->curnode == panels->menu_pos ? core->cons->pal.graph_box : core->cons->pal.graph_box2;
if (panels->isZoom) {
snprintf (str, sizeof (title) - 1, "%s Zoom Mode: Press Enter or q to quit"Color_RESET, color);
strcat (title, str);
} else {
for (i = 0; menus[i]; i++) {
if (panels->curnode == panels->menu_pos) {
if (panels->menuStackDepth > 0) {
if (i == panels->menuIndexStack[0]) {
snprintf (str, sizeof (title) - 1, "%s[%s]"Color_RESET, color, menus[i]);
} else {
snprintf (str, sizeof (title) - 1, "%s %s "Color_RESET, color, menus[i]);
}
} else {
if (i == panels->currentMenuIndex) {
snprintf (str, sizeof (title) - 1, "%s[%s]"Color_RESET, color, menus[i]);
} else {
snprintf (str, sizeof (title) - 1, "%s %s "Color_RESET, color, menus[i]);
}
}
} else {
snprintf (str, sizeof (title) - 1, "%s %s "Color_RESET, color, menus[i]);
}
strcat (title, str);
}
}
if (panels->curnode == panels->menu_pos) {
r_cons_canvas_write (can, Color_BLUE);
r_cons_canvas_write (can, title);
r_cons_canvas_write (can, Color_RESET);
} else {
r_cons_canvas_write (can, Color_RESET);
r_cons_canvas_write (can, title);
}
snprintf (title, sizeof (title) - 1,
"[0x%08"PFMT64x "]", core->offset);
(void) r_cons_canvas_gotoxy (can, -can->sx + w - strlen (title), -can->sy);
r_cons_canvas_write (can, title);
r_cons_canvas_print (can);
r_cons_flush ();
}
static void doPanelsRefresh(RCore *core) {
if (!core->panels) {
return;
}
core->panels->isResizing = true;
panelAllClear (core->panels);
r_core_panels_refresh (core);
}
static void doPanelsRefreshOneShot(RCore *core) {
r_core_task_enqueue_oneshot (core, (RCoreTaskOneShot) doPanelsRefresh, core);
}
static void panelSingleStepIn(RCore *core) {
if (r_config_get_i (core->config, "cfg.debug")) {
r_core_cmd (core, "ds", 0);
r_core_cmd (core, ".dr*", 0);
} else {
r_core_cmd (core, "aes", 0);
r_core_cmd (core, ".ar*", 0);
}
if (!strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
core->panels->panel[core->panels->curnode].addr = core->offset;
}
}
static void panelSingleStepOver(RCore *core) {
bool io_cache = r_config_get_i (core->config, "io.cache");
r_config_set_i (core->config, "io.cache", false);
if (r_config_get_i (core->config, "cfg.debug")) {
r_core_cmd (core, "dso", 0);
r_core_cmd (core, ".dr*", 0);
} else {
r_core_cmd (core, "aeso", 0);
r_core_cmd (core, ".ar*", 0);
}
r_config_set_i (core->config, "io.cache", io_cache);
if (!strcmp (core->panels->panel[core->panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
core->panels->panel[core->panels->curnode].addr = core->offset;
}
}
static void panelBreakpoint(RCore *core) {
r_core_cmd (core, "dbs $$", 0);
}
static void panelContinue(RCore *core) {
r_core_cmd (core, "dc", 0);
}
R_API void r_core_panels_check_stackbase(RCore *core) {
if (!core || !core->panels) {
return;
}
int i;
const char *sp = r_reg_get_name (core->anal->reg, R_REG_NAME_SP);
const ut64 stackbase = r_reg_getv (core->anal->reg, sp);
RPanels *panels = core->panels;
for (i = 1; i < panels->n_panels; i++) {
RPanel *panel = &panels->panel[i];
if (!strcmp (panel->cmd, PANEL_CMD_STACK) && panel->baseAddr != stackbase) {
panel->baseAddr = stackbase;
panel->addr = stackbase - r_config_get_i (core->config, "stack.delta") + core->print->cur;
panel->refresh = true;
}
}
}
static void panelPrompt(const char *prompt, char *buf, int len) {
r_line_set_prompt (prompt);
*buf = 0;
r_cons_fgets (buf, len, 0, NULL);
}
static bool init (RCore *core, RPanels *panels, int w, int h) {
panels->panel = NULL;
panels->n_panels = 0;
panels->columnWidth = 80;
panels->layout = 0;
panels->menu_pos = -1;
panels->menuStack = calloc (sizeof (char **), PANEL_MENU_LIMIT);
panels->menuIndexStack = calloc (sizeof (int), PANEL_MENU_LIMIT);
panels->menuStackDepth = 0;
panels->currentMenu = NULL;
panels->currentMenuIndex = 0;
panels->menuPanel = calloc (sizeof (RPanel), PANEL_MENU_LIMIT);
panels->callgraph = 0;
panels->isResizing = false;
panels->isZoom = false;
panels->can = createNewCanvas (core, w, h);
if (w < 140) {
panels->columnWidth = w / 3;
}
return true;
}
R_API int file_history_up(RLine *line) {
RCore *core = line->user;
RList *files = r_id_storage_list (core->io->files);
int num_files = r_list_length (files);
if (line->file_hist_index >= num_files || line->file_hist_index < 0) {
return false;
}
line->file_hist_index++;
RIODesc *desc = r_list_get_n (files, num_files - line->file_hist_index);
strncpy (line->buffer.data, desc->name, R_LINE_BUFSIZE - 1);
line->buffer.index = line->buffer.length = strlen (line->buffer.data);
r_list_free (files);
return true;
}
R_API int file_history_down(RLine *line) {
RCore *core = line->user;
RList *files = r_id_storage_list (core->io->files);
int num_files = r_list_length (files);
if (line->file_hist_index <= 0 || line->file_hist_index > num_files) {
return false;
}
line->file_hist_index--;
if (line->file_hist_index <= 0) {
line->buffer.data[0] = '\0';
line->buffer.index = line->buffer.length = 0;
return false;
}
RIODesc *desc = r_list_get_n (files, num_files - line->file_hist_index);
strncpy (line->buffer.data, desc->name, R_LINE_BUFSIZE - 1);
line->buffer.index = line->buffer.length = strlen (line->buffer.data);
r_list_free (files);
return true;
}
static void handleMenu(RCore *core, const int key, int *exit) {
RPanels *panels = core->panels;
switch (key) {
case 'h':
handleLeftKey (core);
break;
case 'j':
handleDownKey (core);
break;
case 'k':
handleUpKey (core);
break;
case 'l':
handleRightKey (core);
break;
case 'q':
case -1:
if (panels->menuStackDepth == 0) {
*exit = 1;
return;
}
panels->menuStackDepth--;
panels->currentMenu = panels->menuStack[panels->menuStackDepth];
panels->currentMenuIndex = panels->menuIndexStack[panels->menuStackDepth];
setRefreshAll (core->panels);
break;
case ' ':
case '\r':
case '\n':
onMenu (core, panels->currentMenu[panels->currentMenuIndex], exit);
break;
case 9:
handleTabKey (core, false);
break;
case 'Z':
handleTabKey (core, true);
}
}
static void onMenu(RCore *core, const char *menu, int *exit) {
RPanels *panels = core->panels;
if (!strcmp (menu, "File")) {
changeMenu (panels, menus_File);
} else if (!strcmp (menu, "Edit")) {
changeMenu (panels, menus_Edit);
} else if (!strcmp (menu, "View")) {
changeMenu (panels, menus_View);
} else if (!strcmp (menu, "Tools")) {
changeMenu (panels, menus_Tools);
} else if (!strcmp (menu, "Search")) {
changeMenu (panels, menus_Search);
} else if (!strcmp (menu, "Debug")) {
changeMenu (panels, menus_Debug);
} else if (!strcmp (menu, "Analyze")) {
changeMenu (panels, menus_Analyze);
} else if (!strcmp (menu, "Help")) {
changeMenu (panels, menus_Help);
} else if (!strcmp (menu, "New")) {
addPanelFrame (core, panels, PANEL_TITLE_NEWFILES, "o");
} else if (!strcmp (menu, "Open")) {
r_cons_enable_mouse (false);
core->cons->line->file_prompt = true;
r_line_set_hist_callback (core->cons->line, &file_history_up, &file_history_down);
char *res = r_cons_input ("open file: ");
if (res) {
if (*res) {
r_core_cmdf (core, "o %s", res);
}
free (res);
}
core->cons->line->file_prompt = false;
r_line_set_hist_callback (core->cons->line, &cmd_history_up, &cmd_history_down);
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "RegisterRefs")) {
layoutSidePanel (core, PANEL_TITLE_REGISTERREFS, PANEL_CMD_REGISTERREFS);
} else if (!strcmp (menu, "Registers")) {
layoutSidePanel (core, PANEL_TITLE_REGISTERS, PANEL_CMD_REGISTERS);
} else if (!strcmp (menu, "Info")) {
layoutSidePanel (core, PANEL_TITLE_INFO, PANEL_CMD_INFO);
} else if (!strcmp (menu, "Database")) {
layoutSidePanel (core, PANEL_TITLE_DATABASE, PANEL_CMD_DATABASE);
} else if (!strcmp (menu, "About")) {
char *s = r_core_cmd_str (core, "?V");
r_cons_message (s);
free (s);
} else if (!strcmp (menu, "Hexdump")) {
layoutSidePanel (core, PANEL_TITLE_HEXDUMP, PANEL_CMD_HEXDUMP);
} else if (!strcmp (menu, "Disassembly")) {
layoutSidePanel (core, PANEL_TITLE_DISASSEMBLY, PANEL_CMD_DISASSEMBLY);
} else if (!strcmp (menu, "Functions")) {
layoutSidePanel (core, PANEL_TITLE_FUNCTIONS, PANEL_CMD_FUNCTIONS);
} else if (!strcmp (menu, "Comments")) {
layoutSidePanel (core, PANEL_TITLE_COMMENTS, PANEL_CMD_COMMENTS);
} else if (!strcmp (menu, "Entropy")) {
layoutSidePanel (core, PANEL_TITLE_ENTROPY, PANEL_CMD_ENTROPY);
} else if (!strcmp (menu, "Pseudo")) {
layoutSidePanel (core, PANEL_TITLE_PSEUDO, PANEL_CMD_PSEUDO);
} else if (!strcmp (menu, "Symbols")) {
r_core_cmdf (core, "aa");
} else if (!strcmp (menu, "BasicBlocks")) {
r_core_cmdf (core, "aab");
} else if (!strcmp (menu, "Function")) {
r_core_cmdf (core, "af");
} else if (!strcmp (menu, "DRX")) {
layoutSidePanel (core, PANEL_TITLE_DRX, PANEL_CMD_DRX);
} else if (!strcmp (menu, "Program")) {
r_core_cmdf (core, "aaa");
} else if (!strcmp (menu, "Calls")) {
r_core_cmdf (core, "aac");
} else if (!strcmp (menu, "ROP")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("rop grep: ");
if (res) {
r_core_cmdf (core, "\"/R %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "String")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("search string: ");
if (res) {
r_core_cmdf (core, "\"/ %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Hexpairs")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("search hexpairs: ");
if (res) {
r_core_cmdf (core, "\"/x %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Code")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("search code: ");
if (res) {
r_core_cmdf (core, "\"/c %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Copy")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("How many bytes? ");
if (res) {
r_core_cmdf (core, "\"y %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Write String")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("insert string: ");
if (res) {
r_core_cmdf (core, "\"w %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Write Value")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("insert number: ");
if (res) {
r_core_cmdf (core, "\"wv %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Write Hex")) {
r_cons_enable_mouse (false);
char *res = r_cons_input ("insert hexpairs: ");
if (res) {
r_core_cmdf (core, "\"wx %s\"", res);
free (res);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Calculator")) {
r_cons_enable_mouse (false);
for (;;) {
char *s = r_cons_input ("> ");
if (!s || !*s) {
free (s);
break;
}
r_core_cmdf (core, "? %s", s);
r_cons_flush ();
free (s);
}
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "Assemble")) {
r_core_visual_asm (core, core->offset);
} else if (!strcmp (menu, "Sections")) {
layoutSidePanel (core, PANEL_TITLE_SECTIONS, PANEL_CMD_SECTIONS);
} else if (!strcmp (menu, "Close")) {
r_core_cmd0 (core, "o-*");
} else if (!strcmp (menu, "Strings")) {
layoutSidePanel (core, PANEL_TITLE_STRINGS, PANEL_CMD_STRINGS);
} else if (!strcmp (menu, "Maps")) {
layoutSidePanel (core, PANEL_TITLE_MAPS, PANEL_CMD_MAPS);
} else if (!strcmp (menu, "Modules")) {
layoutSidePanel (core, PANEL_TITLE_MODULES, PANEL_CMD_MODULES);
} else if (!strcmp (menu, "Backtrace")) {
layoutSidePanel (core, PANEL_TITLE_BACKTRACE, PANEL_CMD_BACKTRACE);
} else if (!strcmp (menu, "Locals")) {
layoutSidePanel (core, PANEL_TITLE_LOCALS, PANEL_CMD_LOCALS);
} else if (!strcmp (menu, "Step")) {
r_core_cmd (core, "ds", 0);
r_cons_flush ();
} else if (!strcmp (menu, "ReOpen")) {
panels->menuStack[panels->menuStackDepth] = panels->currentMenu;
panels->menuIndexStack[panels->menuStackDepth] = panels->currentMenuIndex;
panels->menuStackDepth++;
panels->currentMenu = menus_ReOpen;
panels->currentMenuIndex = 0;
} else if (!strcmp (menu, "In RW")) {
r_core_cmd (core, "oo+", 0);
r_cons_flush ();
} else if (!strcmp (menu, "In Debugger")) {
r_core_cmd (core, "oo", 0);
r_cons_flush ();
} else if (!strcmp (menu, "Reload")) {
r_core_cmd (core, "ood", 0);
r_cons_flush ();
} else if (!strcmp (menu, "Step Over")) {
r_core_cmd (core, "dso", 0);
r_cons_flush ();
} else if (!strcmp (menu, "StackRefs")) {
layoutSidePanel (core, PANEL_TITLE_STACKREFS, PANEL_CMD_STACKREFS);
} else if (!strcmp (menu, "Stack")) {
layoutSidePanel (core, PANEL_TITLE_STACK, PANEL_CMD_STACK);
} else if (!strcmp (menu, "Continue")) {
r_core_cmd (core, "dc", 0);
r_cons_flush ();
} else if (!strcmp (menu, "Breakpoints")) {
layoutSidePanel (core, PANEL_TITLE_BREAKPOINTS, PANEL_CMD_BREAKPOINTS);
} else if (!strcmp (menu, "Symbols")) {
layoutSidePanel (core, PANEL_TITLE_SYMBOLS, PANEL_CMD_SYMBOLS);
} else if (!strcmp (menu, "Imports")) {
layoutSidePanel (core, PANEL_TITLE_IMPORTS, PANEL_CMD_IMPORTS);
} else if (!strcmp (menu, "Paste")) {
r_core_cmd0 (core, "yy");
} else if (!strcmp (menu, "Clipboard")) {
layoutSidePanel (core, PANEL_TITLE_CLIPBOARD, PANEL_CMD_CLIPBOARD);
} else if (!strcmp (menu, "io.cache")) {
r_core_cmd0 (core, "e!io.cache");
} else if (!strcmp (menu, "Fill")) {
r_cons_enable_mouse (false);
char *s = r_cons_input ("Fill with: ");
r_core_cmdf (core, "wow %s", s);
free (s);
r_cons_enable_mouse (true);
} else if (!strcmp (menu, "References")) {
r_core_cmdf (core, "aar");
} else if (!strcmp (menu, "FcnInfo")) {
layoutSidePanel (core, PANEL_TITLE_FCNINFO, PANEL_CMD_FCNINFO);
} else if (!strcmp (menu, "Graph")) {
r_core_visual_graph (core, NULL, NULL, true);
// addPanelFrame ("Graph", "agf");
} else if (!strcmp (menu, "System Shell")) {
r_cons_set_raw (0);
r_cons_flush ();
r_sys_cmd ("$SHELL");
} else if (!strcmp (menu, "R2 Shell")) {
core->vmode = false;
r_core_visual_prompt_input (core);
core->vmode = true;
} else if (!strcmp (menu, "2048")) {
r_cons_2048 (panels->can->color);
} else if (!strcmp (menu, "License")) {
r_cons_message ("Copyright 2006-2016 - pancake - LGPL");
} else if (!strcmp (menu, "Fortune")) {
char *s = r_core_cmd_str (core, "fo");
r_cons_message (s);
free (s);
} else if (!strcmp (menu, "Commands")) {
r_core_cmd0 (core, "?;?@?;?$?;???");
r_cons_any_key (NULL);
} else if (!strcmp (menu, "Colors")) {
r_core_cmd0 (core, "e!scr.color");
} else if (!strcmp (menu, "Quit")) {
*exit = 1;
return;
}
doPanelsRefresh (core);
}
static void changeMenu(RPanels *panels, const char **dstMenu) {
panels->menuStack[panels->menuStackDepth] = panels->currentMenu;
panels->menuIndexStack[panels->menuStackDepth] = panels->currentMenuIndex;
panels->menuStackDepth++;
panels->currentMenu = dstMenu;
panels->currentMenuIndex = 0;
}
static void handleTabKey(RCore *core, bool shift) {
RPanels *panels = core->panels;
r_cons_switchbuf (false);
if (panels->curnode != panels->menu_pos) {
panels->panel[panels->curnode].refresh = true;
}
if (!shift) {
panels->curnode++;
if (panels->curnode == panels->n_panels) {
panels->curnode = panels->menu_pos;
}
} else {
if (panels->curnode > panels->menu_pos) {
panels->curnode--;
} else {
panels->curnode = panels->n_panels - 1;
}
}
if (panels->curnode == panels->menu_pos) {
panels->currentMenu = menus;
panels->currentMenuIndex = 0;
} else {
panels->panel[panels->curnode].refresh = true;
}
}
static void savePanelPos(RPanel* panel) {
panel->prevPos.x = panel->pos.x;
panel->prevPos.y = panel->pos.y;
panel->prevPos.w = panel->pos.w;
panel->prevPos.h = panel->pos.h;
}
static void restorePanelPos(RPanel* panel) {
panel->pos.x = panel->prevPos.x;
panel->pos.y = panel->prevPos.y;
panel->pos.w = panel->prevPos.w;
panel->pos.h = panel->prevPos.h;
}
static void maximizePanelSize(RPanels *panels) {
RPanel *panel = &panels->panel[panels->curnode];
panel->pos.x = 0;
panel->pos.y = 1;
panel->pos.w = panels->can->w;
panel->pos.h = panels->can->h - 1;
panel->refresh = true;
}
static void switchMode(RPanels *panels) {
panels->isZoom = !panels->isZoom;
RPanel *curPanel = &panels->panel[panels->curnode];
if (panels->isZoom) {
savePanelPos (curPanel);
maximizePanelSize (panels);
} else {
restorePanelPos (curPanel);
setRefreshAll (panels);
}
}
static void insertValue(RCore *core) {
RPanels *panels = core->panels;
char buf[128];
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_STACK)) {
const char *prompt = "insert hex: ";
panelPrompt (prompt, buf, sizeof (buf));
r_core_cmdf (core, "wx %s @ 0x%08" PFMT64x, buf, panels->panel[panels->curnode].addr);
panels->panel[panels->curnode].refresh = true;
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_REGISTERS)) {
const char *creg = core->dbg->creg;
if (creg) {
const char *prompt = "new-reg-value> ";
panelPrompt (prompt, buf, sizeof (buf));
r_core_cmdf (core, "dr %s = %s", creg, buf);
panels->panel[panels->curnode].refresh = true;
}
} else if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
const char *prompt = "insert hex: ";
panelPrompt (prompt, buf, sizeof (buf));
r_core_cmdf (core, "wx %s @ 0x%08" PFMT64x, buf, core->offset + core->print->cur);
panels->panel[panels->curnode].refresh = true;
}
}
R_API RPanels *r_core_panels_new(RCore *core) { int w, h;
RPanels *panels = R_NEW0 (RPanels);
if (!panels) {
return NULL;
}
w = r_cons_get_size (&h);
if (!init (core, panels, w, h)) {
free (panels);
return NULL;
}
return panels;
}
R_API void r_core_panels_free(RPanels *panels) {
int i;
r_cons_switchbuf (true);
if (panels) {
for (i = 0; i < panels->n_panels; i++) {
freePanel (&panels->panel[i]);
}
free (panels->panel);
if (panels->can) {
r_cons_canvas_free (panels->can);
}
free (panels);
}
}
R_API int r_core_visual_panels(RCore *core, RPanels *panels) {
int i, okey, key, wheel;
if (!panels) {
panels = r_core_panels_new (core);
if (!panels) {
r_core_panels_free (panels);
return false;
}
}
if (!initPanels (core, panels)) {
r_core_panels_free (panels);
return false;
}
r_cons_switchbuf (false);
int originCursor = core->print->cur;
core->print->cur = 0;
core->print->cur_enabled = false;
core->print->col = 0;
bool originVmode = core->vmode;
core->vmode = true;
r_core_panels_layout (panels);
repeat:
core->panels = panels;
core->cons->event_resize = NULL; // avoid running old event with new data
core->cons->event_data = core;
core->cons->event_resize = (RConsEvent) doPanelsRefreshOneShot;
r_core_panels_layout_refresh (core);
wheel = r_config_get_i (core->config, "scr.wheel");
if (wheel) {
r_cons_enable_mouse (true);
}
okey = r_cons_readchar ();
key = r_cons_arrow_to_hjkl (okey);
r_cons_switchbuf (true);
if (panels->curnode == panels->menu_pos) {
int exit = 0;
handleMenu (core, key, &exit);
if (exit) {
goto exit;
}
goto repeat;
}
if (handleCursorMode (core, key)) {
goto repeat;
}
if (panels->isZoom) {
handleZoomMode (core, key);
goto repeat;
}
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)
&& '0' < key && key <= '9') {
ut8 ch = key;
r_core_visual_jump (core, ch);
panels->panel[panels->curnode].addr = core->offset;
panels->panel[panels->curnode].refresh = true;
goto repeat;
}
const char *cmd;
RConsCanvas *can = panels->can;
switch (key) {
case 'u':
r_core_cmd0 (core, "s-");
break;
case 'U':
r_core_cmd0 (core, "s+");
break;
case 'n':
{
r_cons_enable_mouse (false);
char *res = r_cons_input ("New panel with command: ");
if (res) {
if (*res) {
addPanelFrame (core, panels, res, res);
// do not refresh stuff
}
free (res);
}
r_cons_enable_mouse (true);
}
break;
case 'N':
{
r_cons_enable_mouse (false);
char *res = r_cons_input ("New panel with command: ");
if (res) {
if (*res) {
addPanelFrame (core, panels, NULL, res);
}
free (res);
}
r_cons_enable_mouse (true);
}
break;
case 'p':
r_core_cmd0 (core, "sp");
break;
case 'P':
r_core_cmd0 (core, "sn");
break;
case '.':
if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY)) {
if (r_config_get_i (core->config, "cfg.debug")) {
r_core_cmd0 (core, "sr PC");
} else {
r_core_cmd0 (core, "s entry0; px");
}
panels->panel[panels->curnode].addr = core->offset;
panels->panel[panels->curnode].refresh = true;
}
break;
case '?':
r_cons_clear00 ();
r_cons_printf ("Visual Ascii Art Panels:\n"
" ? - show this help\n"
" ! - run r2048 game\n"
" . - seek to PC or entrypoint\n"
" : - run r2 command in prompt\n"
" _ - start the hud input mode\n"
" | - split the current panel vertically\n"
" - - split the current panel horizontally\n"
" * - show pseudo code/r2dec in the current panel\n"
" [1-9] - follow jmp/call identified by shortcut (like ;[1])\n"
" <> - scroll panels vertically by page\n"
" b - browse symbols, flags, configurations, classes, ...\n"
" c - toggle cursor\n"
" C - toggle color\n"
" d - define in the current address. Same as Vd\n"
" D - show disassembly in the current panel\n"
" e - change title and command of current panel\n"
" g - show graph in the current panel\n"
" hjkl - move around (left-down-up-right)\n"
" JK - resize panels vertically\n"
" HL - resize panels horizontally\n"
" i - insert hex\n"
" m - move to the menu\n"
" M - open new custom frame\n"
" nN - create new panel with given command\n"
" o - go/seek to given offset\n"
" pP - seek to next or previous scr.nkey\n"
" q - quit, back to visual mode\n"
" r - toggle jmphints/leahints\n"
" sS - step in / step over\n"
" uU - undo / redo seek\n"
" V - go to the graph mode\n"
" w - change the current layout of the panels\n"
" X - close current panel\n"
);
r_cons_flush ();
r_cons_any_key (NULL);
break;
case 'b':
r_core_visual_browse (core);
break;
case 'o':
//if (!strcmp (panels->panel[panels->curnode].cmd, PANEL_CMD_DISASSEMBLY))
r_core_visual_showcursor (core, true);
r_core_visual_offset (core);
r_core_visual_showcursor (core, false);
panels->panel[panels->curnode].addr = core->offset;
panels->panel[panels->curnode].refresh = true;
break;
case 's':
panelSingleStepIn (core);
setRefreshAll (panels);
break;
case 'S':
panelSingleStepOver (core);
setRefreshAll (panels);
break;
case ':':
core->vmode = false;
r_core_visual_prompt_input (core);
core->vmode = true;
// FIX: Issue with visual mode instruction highlighter
// not updating after 'ds' or 'dcu' commands.
r_core_cmd0 (core, ".dr*");
setRefreshAll (panels);
break;
case 'c':
activateCursor (core);
break;
case 'C':
can->color = !can->color;
// r_config_toggle (core->config, "scr.color");
// refresh graph
setRefreshAll (panels);
break;
case 'r':
r_core_cmd0 (core, "e!asm.jmphints");
r_core_cmd0 (core, "e!asm.leahints");
setRefreshAll (panels);
break;
case 'R':
if (r_config_get_i (core->config, "scr.randpal")) {
r_core_cmd0 (core, "ecr");
} else {
r_core_cmd0 (core, "ecn");
}
doPanelsRefresh (core);
break;
case 'A':
r_core_visual_asm (core, core->offset);
break;
case 'd':
r_core_visual_define (core, "");
setRefreshAll (panels);
break;
case 'D':
replaceCmd (panels, PANEL_TITLE_DISASSEMBLY, PANEL_CMD_DISASSEMBLY);
break;
case 'j':
handleDownKey (core);
break;
case 'k':
handleUpKey (core);
break;
case '<':
for (i = 0; i < PANEL_CONFIG_PAGE; i++) {
handleUpKey (core);
}
break;
case '>':
for (i = 0; i < PANEL_CONFIG_PAGE; i++) {
handleDownKey (core);
}
break;
case '_':
r_core_visual_hud (core);
break;
case 'X':
delCurPanel (panels);
setRefreshAll (panels);
break;
case 9: // TAB
handleTabKey (core, false);
break;
case 'Z': // SHIFT-TAB
handleTabKey (core, true);
break;
case 'M':
{
r_cons_enable_mouse (false);
char *name = r_cons_input ("Name: ");
char *cmd = r_cons_input ("Command: ");
if (name && *name && cmd && *cmd) {
addPanelFrame (core, panels, name, cmd);
}
free (name);
free (cmd);
r_cons_enable_mouse (true);
}
break;
case 'e':
{
r_cons_enable_mouse (false);
char *new_name = r_cons_input ("New name: ");
char *new_cmd = r_cons_input ("New command: ");
if (new_name && *new_name && new_cmd && *new_cmd) {
replaceCmd (panels, new_name, new_cmd);
}
free (new_name);
free (new_cmd);
r_cons_enable_mouse (true);
}
break;
case 'm':
panels->curnode = panels->menu_pos;
panels->currentMenu = menus;
panels->currentMenuIndex = 0;
break;
case 'H':
r_cons_switchbuf (false);
resizePanelLeft (panels);
break;
case 'L':
r_cons_switchbuf (false);
resizePanelRight (panels);
break;
case 'J':
r_cons_switchbuf (false);
resizePanelDown(panels);
break;
case 'K':
r_cons_switchbuf (false);
resizePanelUp (panels);
break;
case 'g':
if (checkFunc (core)) {
replaceCmd (panels, PANEL_TITLE_GRAPH, PANEL_CMD_GRAPH);
}
break;
case 'h':
handleLeftKey (core);
break;
case 'l':
handleRightKey (core);
break;
case 'V':
if (r_config_get_i (core->config, "graph.web")) {
r_core_cmd0 (core, "agv $$");
} else {
if (checkFunc (core)) {
int ocolor = r_config_get_i (core->config, "scr.color");
r_core_visual_graph (core, NULL, NULL, true);
r_config_set_i (core->config, "scr.color", ocolor);
core->panels->can = createNewCanvas (core, core->panels->can->w, core->panels->can->h);
setRefreshAll (panels);
}
}
break;
case ']':
r_config_set_i (core->config, "hex.cols", r_config_get_i (core->config, "hex.cols") + 1);
break;
case '[':
r_config_set_i (core->config, "hex.cols", r_config_get_i (core->config, "hex.cols") - 1);
break;
case 'w':
panels->layout++;
if (panels->layout >= layoutMaxCount) {
panels->layout = 0;
}
r_core_panels_layout (panels);
setRefreshAll (panels);
break;
case 0x0d:
switchMode (panels);
break;
case '|':
splitPanelVertical (core);
break;
case '-':
splitPanelHorizontal (core);
break;
case '*':
if (checkFunc (core)) {
replaceCmd (panels, PANEL_TITLE_PSEUDO, PANEL_CMD_PSEUDO);
core->panels->can = createNewCanvas (core, core->panels->can->w, core->panels->can->h);
}
break;
case R_CONS_KEY_F1:
cmd = r_config_get (core->config, "key.f1");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F2:
cmd = r_config_get (core->config, "key.f2");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
} else {
panelBreakpoint (core);
}
break;
case R_CONS_KEY_F3:
cmd = r_config_get (core->config, "key.f3");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F4:
cmd = r_config_get (core->config, "key.f4");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F5:
cmd = r_config_get (core->config, "key.f5");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F6:
cmd = r_config_get (core->config, "key.f6");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F7:
cmd = r_config_get (core->config, "key.f7");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
} else {
panelSingleStepIn (core);
setRefreshAll (panels);
}
break;
case R_CONS_KEY_F8:
cmd = r_config_get (core->config, "key.f8");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
} else {
panelSingleStepOver (core);
setRefreshAll (panels);
}
break;
case R_CONS_KEY_F9:
cmd = r_config_get (core->config, "key.f9");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
} else {
panelContinue (core);
}
break;
case R_CONS_KEY_F10:
cmd = r_config_get (core->config, "key.f10");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F11:
cmd = r_config_get (core->config, "key.f11");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case R_CONS_KEY_F12:
cmd = r_config_get (core->config, "key.f12");
if (cmd && *cmd) {
key = r_core_cmd0 (core, cmd);
}
break;
case '!':
r_cons_2048 (core->panels->can->color);
break;
case 'q':
case -1: // EOF
goto exit;
#if 0
case 27: // ESC
if (r_cons_readchar () == 91) {
if (r_cons_readchar () == 90) {}
}
break;
#endif
default:
// eprintf ("Key %d\n", key);
// sleep (1);
break;
}
goto repeat;
exit:
core->cons->event_resize = NULL;
core->cons->event_data = NULL;
core->print->cur = originCursor;
core->print->cur_enabled = false;
core->print->col = 0;
core->vmode = originVmode;
r_core_panels_free (panels);
core->panels = NULL;
return true;
}