summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c391
1 files changed, 391 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..a806692
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,391 @@
+#include <SDL3/SDL.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include "panic.h"
+#include "curve.h"
+#include "procfs.h"
+#include "memory.h"
+
+SDL_Window *window;
+SDL_Renderer *renderer;
+
+static int window_width = 640;
+static int window_height = 480;
+
+static double scale = 1;
+static double pos_x = 1024;
+static double pos_y = 0;
+static bool brush_bit = true;
+
+static pid_t pid = -1;
+static bool is_child = false;
+static bool stopped = false;
+static page_list pages = {.fd = -1, .first = NULL};
+static procfs_map *mappings = NULL;
+static int n_mappings = 0;
+
+static double get_scale() {
+ if (scale >= 1) {
+ return SDL_round(scale);
+ }
+ return scale;
+}
+
+static void add_scale(double amount) {
+ float mx, my;
+ SDL_GetMouseState(&mx, &my);
+ double old_scale = get_scale();
+ scale += amount;
+ if (scale > 50) scale = 50;
+ else if (scale < 0.125) scale = 0.125;
+ pos_x -= mx/get_scale() - mx/old_scale;
+ pos_y -= my/get_scale() - my/old_scale;
+}
+
+static void absolute_pos(double *x, double *y) {
+ *x = *x / get_scale() + pos_x;
+ *y = *y / get_scale() + pos_y;
+}
+
+static void go_to(uintptr_t addr) {
+ int ipos_x, ipos_y;
+ to_pos(addr, &ipos_x, &ipos_y);
+ pos_x = ipos_x; pos_y = ipos_y;
+ float mx, my;
+ SDL_GetMouseState(&mx, &my);
+ pos_x -= mx / get_scale();
+ pos_y -= my / get_scale();
+}
+
+static void detach() {
+ if (pid == -1) return;
+ if (pages.fd != -1) {
+ int fd = pages.fd;
+ free_page_list(&pages);
+ close(fd);
+ }
+ if (is_child) {
+ kill(pid, SIGKILL);
+ while (true) {
+ int status;
+ pid_t w = waitpid(pid, &status, 0);
+ if (w == -1) {
+ break;
+ }
+ if (w == pid && WIFEXITED(status)) {
+ break;
+ }
+ }
+ }
+ pid = -1;
+ is_child = false;
+}
+
+static bool attach(pid_t p) {
+ detach();
+ pid = p;
+ int fd = procfs_open(pid);
+ if (fd == -1) {
+ pid = -1;
+ fprintf(stderr, "error attaching to pid %d: %s\n", p, strerror(errno));
+ return false;
+ }
+ init_page_list(&pages, fd);
+ stopped = false;
+ n_mappings = procfs_maps(pid, &mappings);
+ for (int i = 0; i < n_mappings; i++) {
+ if (strcmp("[stack]", mappings[i].name) == 0) {
+ go_to(mappings[i].max);
+ }
+ }
+ return true;
+}
+
+static bool spawn(char *const cmdline[]) {
+ detach();
+ int fildes[2];
+ if (pipe(fildes) == -1) {
+ fprintf(stderr, "error starting child: pipe: %d\n", strerror(errno));
+ return false;
+ }
+ pid_t child = fork();
+ if (child == -1) {
+ fprintf(stderr, "error starting child: %d\n", strerror(errno));
+ close(fildes[0]);
+ close(fildes[1]);
+ return false;
+ }
+ if (child == 0) {
+ close(fildes[0]);
+ if (fcntl(fildes[1], F_SETFD, FD_CLOEXEC) == -1) {
+ perror("fcntl");
+ exit(-1);
+ }
+ if (execvp(cmdline[0], cmdline) == -1) {
+ const char *err = strerror(errno);
+ write(fildes[1], err, strlen(err));
+ exit(-1);
+ }
+ }
+ close(fildes[1]);
+ char child_err[2048] = {0};
+ ssize_t n = read(fildes[0], child_err, 2047);
+ close(fildes[0]);
+ if (n == -1) {
+ fprintf(stderr,
+ "error starting child: read pipe: %d\n", strerror(errno)
+ );
+ goto err;
+ }
+ if (n != 0) {
+ fprintf(stderr, "%s: %s\n", cmdline[0], child_err);
+ }
+
+ if (attach(child)) {
+ is_child = true;
+ return true;
+ }
+err:
+ kill(child, SIGKILL);
+ while (true) {
+ int status;
+ pid_t w = waitpid(child, &status, 0);
+ if (w == -1) {
+ break;
+ }
+ if (w == child && WIFEXITED(status)) {
+ break;
+ }
+ }
+ return false;
+}
+
+static void handle_child() {
+ int status;
+ pid_t w = waitpid(pid, &status, WNOHANG);
+ if (w == -1) {
+ return;
+ }
+ if (w == pid && WIFEXITED(status)) {
+ is_child = false;
+ detach();
+ fprintf(
+ stderr, "child exited with status %d\n", WEXITSTATUS(status)
+ );
+ }
+}
+
+static bool held(SDL_Keycode k) {
+ const bool *state = SDL_GetKeyboardState(NULL);
+ return state[SDL_GetScancodeFromKey(k, NULL)];
+}
+
+static void handle_key(SDL_Keycode key) {
+ switch (key) {
+ case SDLK_SPACE:
+ if (pid == -1) break;
+ kill(pid, stopped ? SIGSTOP : SIGCONT);
+ stopped = !stopped;
+ break;
+ case SDLK_E:
+ brush_bit = !brush_bit;
+ break;
+ default:
+ }
+}
+
+static bool handle_event(SDL_Event e) {
+ switch (e.type) {
+ case SDL_EVENT_KEY_DOWN:
+ handle_key(e.key.key);
+ break;
+ case SDL_EVENT_MOUSE_MOTION:
+ SDL_MouseButtonFlags state = e.motion.state;
+ if (state & SDL_BUTTON_LMASK) {
+ pos_x -= (double) e.motion.xrel / get_scale();
+ pos_y -= (double) e.motion.yrel / get_scale();
+ } break;
+ case SDL_EVENT_MOUSE_WHEEL:
+ add_scale(e.wheel.y * get_scale() / 10);
+ break;
+ case SDL_EVENT_QUIT:
+ return false;
+ break;
+ default:
+ }
+ return true;
+}
+
+static void update() {
+ SDL_GetWindowSize(window, &window_width, &window_height);
+ if (pid != -1) {
+ n_mappings = procfs_maps(pid, &mappings);
+ if (is_child) handle_child();
+ }
+ if (held(SDLK_W)) {
+ pos_y -= 5 / get_scale();
+ }
+ if (held(SDLK_S)) {
+ pos_y += 5 / get_scale();
+ }
+ if (held(SDLK_A)) {
+ pos_x -= 5 / get_scale();
+ }
+ if (held(SDLK_D)) {
+ pos_x += 5 / get_scale();
+ }
+ float mx, my;
+ static bool mouse_held = false;
+ static double px, py;
+ SDL_MouseButtonFlags state = SDL_GetMouseState(&mx, &my);
+ if (state & SDL_BUTTON_RMASK) {
+ double x2 = mx, y2 = my;
+ absolute_pos(&x2, &y2);
+ double x1, y1;
+ if (mouse_held) {
+ x1 = px; y1 = py;
+ } else {
+ x1 = x2; y1 = y2;
+ }
+ px = x2; py = y2;
+ mouse_held = true;
+ draw_line(&pages, x1, y1, x2, y2, brush_bit);
+ } else {
+ mouse_held = false;
+ }
+}
+
+static SDL_Texture *missing_texture();
+
+static void show_pages() {
+ double s = get_scale();
+
+ int page_x = pos_x / PAGE_WIDTH;
+ int page_y = pos_y / PAGE_HEIGHT;
+
+ double offs_x = SDL_fmod(pos_x, PAGE_WIDTH) * s;
+ double offs_y = SDL_fmod(pos_y, PAGE_HEIGHT) * s;
+
+ double page_width = PAGE_WIDTH * s;
+ double page_height = PAGE_HEIGHT * s;
+
+ double view_width = window_width / page_width;
+ double view_height = window_height / page_height;
+
+ for (int draw_y = -1; draw_y <= view_height + 1; draw_y++) {
+ for (int draw_x = -1; draw_x <= view_width + 1; draw_x++) {
+ SDL_FRect src = {0, 0, PAGE_WIDTH, PAGE_HEIGHT};
+ SDL_FRect dest = {
+ draw_x * page_width - offs_x, draw_y * page_height - offs_y,
+ page_width, page_height
+ };
+ SDL_Texture *tex = NULL;
+ if (pid != -1) {
+ uintptr_t addr = zorder(page_x + draw_x, page_y + draw_y);
+ addr *= PAGE_SIZE;
+ page *p = get_page(&pages, addr);
+ tex = get_texture(p);
+ }
+ if (!tex) {
+ tex = missing_texture();
+ }
+ if (s < 0.75) {
+ SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_LINEAR);
+ } else {
+ SDL_SetTextureScaleMode(tex, SDL_SCALEMODE_NEAREST);
+ }
+ SDL_RenderTexture(renderer, tex, &src, &dest);
+ }
+ }
+ free_unused_pages(&pages);
+}
+
+static SDL_Texture *missing_texture() {
+ static SDL_Texture *tex = NULL;
+ if (!tex) {
+ tex = must(SDL_CreateTexture(renderer,
+ SDL_PIXELFORMAT_RGBA32,
+ SDL_TEXTUREACCESS_STREAMING,
+ PAGE_WIDTH, PAGE_HEIGHT
+ ));
+ uint32_t *pixels; int pitch;
+ SDL_Rect rect = {0, 0, PAGE_WIDTH, PAGE_HEIGHT};
+ must(SDL_LockTexture(tex, &rect, (void **) &pixels, &pitch));
+ for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++) {
+ if (((i / 32) + i / PAGE_WIDTH / 32) % 2) {
+ pixels[i] = 0xFF151515;
+ } else {
+ pixels[i] = 0xFF222222;
+ }
+ }
+ SDL_UnlockTexture(tex);
+ }
+ return tex;
+}
+
+static void main_loop() {
+ int last_time = 0, current_time = 0;
+ while (true) {
+ update();
+ SDL_Event e;
+ while (SDL_PollEvent(&e)) {
+ if (!handle_event(e)) {
+ return;
+ }
+ }
+ SDL_GetWindowSize(window, &window_width, &window_height);
+ SDL_RenderClear(renderer);
+ show_pages();
+ SDL_RenderPresent(renderer);
+
+ last_time = current_time;
+ current_time = SDL_GetTicks();
+ int elapsed = current_time - last_time;
+ if (elapsed < 5) SDL_Delay(5 - elapsed);
+ }
+}
+
+static void usage(const char *name) {
+ fprintf(stderr, "usage: %s -p <pid>\n", name);
+ fprintf(stderr, " %s command [arg...]\n", name);
+ exit(-1);
+}
+
+int main(int argc, char *argv[]) {
+ must(SDL_Init(SDL_INIT_VIDEO));
+ must(SDL_CreateWindowAndRenderer("core",
+ window_width, window_height, SDL_WINDOW_RESIZABLE, &window, &renderer
+ ));
+ SDL_SetRenderVSync(renderer, 1);
+
+ if (argc > 1) {
+ if (argv[1][0] == '-') {
+ if (strcmp(argv[1], "-p") == 0) {
+ if (argc < 3) {
+ usage(argv[0]);
+ }
+ pid_t arg = strtol(argv[2], NULL, 10);
+ if (arg == 0 && errno == EINVAL) {
+ usage(argv[0]);
+ }
+ attach(arg);
+ } else {
+ usage(argv[0]);
+ }
+ } else {
+ spawn(argv + 1);
+ }
+ }
+ main_loop();
+ detach();
+ SDL_DestroyRenderer(renderer);
+ SDL_DestroyWindow(window);
+ SDL_Quit();
+ return 0;
+} \ No newline at end of file