diff options
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 391 |
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 |
