#include #include #include #include #include #include #include #include #include "procfs.h" #include "memview.h" static int screen_width = 640; static int screen_height = 480; static double scale = 5; static double pos_x = 1024; static double pos_y = 0; static int mouse_x = 0; static int mouse_y = 0; SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; pid_t pid; int mem_fd; struct viewer *viewer; static void add_scale(double amount) { pos_x += mouse_x / scale; pos_y += mouse_y / scale; scale += amount; if (scale > 50) scale = 50; else if (scale < 0.5) scale = 0.5; pos_x -= mouse_x / scale; pos_y -= mouse_y / scale; } static void world_pos(double *x, double *y) { *x = *x / scale + floor(pos_x); *y = *y / scale + floor(pos_y); } static void goto_addr(uintptr_t addr) { int x, y; to_pos(addr, &x, &y); pos_x = x; pos_y = y; pos_x -= (double) screen_width / 2 * scale; pos_y -= (double) screen_height / 2 * scale; } static size_t current_map = 0; static void next_map() { struct procfs_map *maps; int n = procfs_maps(pid, &maps); if (n < 1) return; for (int i = 0; i < n; i++) { current_map = (current_map + 1) % n; if (maps[current_map].prot & PROT_READ) break; } goto_addr(maps[current_map].base); free(maps); } static void prev_map() { struct procfs_map *maps; int n = procfs_maps(pid, &maps); if (n < 1) return; for (int i = 0; i < n; i++) { current_map = current_map > 1 ? current_map - 1 : n - 1; if (maps[current_map].prot & PROT_READ) break; } goto_addr(maps[current_map].base); free(maps); } static void report_error() { fprintf(stderr, "SDL Error: %s\n", SDL_GetError()); exit(-1); } static void init_sdl(const char *title) { if (SDL_Init(SDL_INIT_VIDEO) < 0) report_error(); window = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); if (window == NULL) report_error(); renderer = SDL_CreateRenderer(window, -1, 0); if (renderer == NULL) report_error(); } static void deinit_sdl() { SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); } static bool held(SDL_Keycode k) { const uint8_t *state = SDL_GetKeyboardState(NULL); return state[SDL_GetScancodeFromKey(k)]; } static void handle_events() { bool refresh = false; SDL_Event e; while (SDL_PollEvent(&e)) { switch (e.type) { case SDL_QUIT: exit(0); break; case SDL_WINDOWEVENT: switch (e.window.event) { case SDL_WINDOWEVENT_RESIZED: screen_width = e.window.data1; screen_height = e.window.data2; SDL_SetWindowSize(window, screen_width, screen_height); refresh = true; break; case SDL_WINDOWEVENT_EXPOSED: refresh = true; break; default: break; } break; case SDL_MOUSEMOTION:; uint32_t state = e.motion.state; if (state & SDL_BUTTON_MMASK || (state & SDL_BUTTON_RMASK && held(SDLK_LSHIFT))) { pos_x -= (double) e.motion.xrel / scale; pos_y -= (double) e.motion.yrel / scale; refresh = true; } else if (state & (SDL_BUTTON_LMASK | SDL_BUTTON_RMASK)) { double x1 = e.motion.x - e.motion.xrel; double y1 = e.motion.y - e.motion.yrel; double x2 = e.motion.x; double y2 = e.motion.y; world_pos(&x1, &y1); world_pos(&x2, &y2); pencil(viewer, x1, y1, x2, y2, !(state & SDL_BUTTON_RMASK)); refresh = true; } mouse_x = e.motion.x; mouse_y = e.motion.y; break; case SDL_MOUSEBUTTONDOWN:; double x = e.button.x; double y = e.button.y; world_pos(&x, &y); if (e.button.button == SDL_BUTTON_LEFT) pencil(viewer, x, y, x, y, true); else if (e.button.button == SDL_BUTTON_RIGHT) pencil(viewer, x, y, x, y, false); break; case SDL_MOUSEWHEEL: if (e.wheel.y == 0) break; add_scale((double) e.wheel.y / 2); refresh = true; break; case SDL_KEYDOWN: switch (e.key.keysym.sym) { case SDLK_RIGHT: next_map(); refresh = true; break; case SDLK_LEFT: prev_map(); refresh = true; break; case SDLK_SPACE:; static bool stopped = false; kill(pid, stopped ? SIGSTOP : SIGCONT); stopped = !stopped; break; default: break; } default: break; } } if (render_pages(viewer, pos_x, pos_y, screen_width, screen_height, scale, refresh)) SDL_RenderPresent(renderer); } void cleanup() { destroy_viewer(viewer); deinit_sdl(); } int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "todo: show usage\n"); return -1; } pid = atoi(argv[1]); if (pid == 0) { fprintf(stderr, "todo: show usage\n"); return -1; } mem_fd = procfs_open(pid); if (mem_fd == -1) perror("error attaching to process"); char title[1024]; snprintf(title, 1024, "%s [%d]", argv[0], pid); init_sdl(title); viewer = create_viewer(mem_fd, renderer); if (!viewer) abort(); atexit(cleanup); prev_map(); int last_time = 0; int current_time = 0; while (1) { handle_events(); last_time = current_time; current_time = SDL_GetTicks(); int elapsed = current_time - last_time; if (elapsed < 1000 / 30) SDL_Delay(1000 / 30 - elapsed); } }