// assets.h is auto-generated by prepare-shaders.py#include "assets.h" #define GLFW_INCLUDE_VULKAN#include <GLFW/glfw3.h> #include <stdio.h>#include <stdlib.h>#include <string.h> #include "mash.h" //#define DEFAULT_FONT_PATH "content/RobotoMono-Regular.ttf"#define DEFAULT_FONT_PATH "content/Monaco_Regular.ttf" static Vulkan vk; static uint32_t cursor_color = 0; static Font_Handle font_face = nullptr;static Font_Render font_render = {0}; static File file = {0};static Grid grid = {0};static Formatter formatter = {0}; static Input_State input_state = {0}; static bool was_vertical_movement = false; static bool needs_resubmit = true; const char **get_required_instance_extensions(uint32_t *n_inst_exts) { return glfwGetRequiredInstanceExtensions(n_inst_exts);} VkResult create_window_surface(VkInstance& instance, void *window, VkSurfaceKHR *surface) { return glfwCreateWindowSurface(instance, (GLFWwindow*)window, nullptr, surface);} void get_thumb_position(Grid *g, int64_t file_size, int& y, int& h) { int64_t grid_length = g->end_grid_offset - g->grid_offset; double pos = (double)g->grid_offset / (double)(file_size - grid_length); h = (double)vk.wnd_height * THUMB_FRAC; y = (int)(pos * (double)(vk.wnd_height - h) + 0.5);} int64_t get_file_offset_from_thumb(Grid *g, int64_t file_size) { int64_t grid_length = g->end_grid_offset - g->grid_offset; double total_length = file_size - grid_length; double len_per_pixel = total_length / ((double)vk.wnd_height * (1.0 - THUMB_FRAC)); double y = input_state.y - input_state.thumb_inner_pos; return (int64_t)(y * len_per_pixel);} int upload_glyphsets(Font_Handle fh, Font_Render *renders, int n_renders) { if (!vk.glyphset_pool.size) { vk.glyphset_pool = vk.allocate_gpu_memory(GLYPHSET_POOL_SIZE); if (!vk.glyphset_pool.size) return __LINE__; } renders[0].buf = vk.glyphset_pool.staging_area; make_font_render(fh, renders[0]); return vk.push_to_gpu(vk.glyphset_pool, 0, renders[0].total_size);} int render_and_upload_views(View *views, int n_views, Font_Render *renders) { if (!vk.grids_pool.size) { vk.grids_pool = vk.allocate_gpu_memory(GRIDS_POOL_SIZE); if (!vk.grids_pool.size) return __LINE__; } Cell *cells = (Cell*)vk.grids_pool.staging_area; View& v = views[0]; v.grid->render_into(v.file, cells, v.formatter, input_state, vk.wnd_width, vk.wnd_height); int res = vk.push_to_gpu(vk.grids_pool, 0, v.grid->rows * v.grid->cols * sizeof(Cell)); if (res != 0) return __LINE__; if (!vk.view_params || n_views > vk.view_param_cap) { int cap = VIEW_PARAMS_INITIAL_CAP; while (cap < n_views) cap *= 2; auto vps = new View_Params[cap]; if (vk.view_params) delete[] vk.view_params; vk.view_params = vps; vk.view_param_cap = cap; } vk.n_view_params = n_views; for (int i = 0; i < vk.n_view_params; i++) { Font_Render *r = &renders[views[i].font_render_idx]; int thumb_y, thumb_h; get_thumb_position(v.grid, v.file->total_size, thumb_y, thumb_h); uint32_t thumb_color = v.formatter->inactive_thumb_color; if (input_state.thumb_flags & 1) thumb_color = v.formatter->active_thumb_color; else if (input_state.thumb_flags & 2) thumb_color = v.formatter->hovered_thumb_color; vk.view_params[i] = { .view_origin = {0, 0}, .view_size = {(uint32_t)vk.wnd_width, (uint32_t)vk.wnd_height}, .cell_size = {(uint32_t)r->glyph_w, (uint32_t)r->glyph_h}, .thumb_pos = {(uint32_t)thumb_y, (uint32_t)thumb_h}, .cursor = {v.grid->rel_caret_col + v.grid->last_line_num_gap, v.grid->rel_caret_row}, .thumb_color = thumb_color, .cursor_color = cursor_color, .columns = (uint32_t)v.grid->cols, .grid_cell_offset = 0, .glyphset_byte_offset = 0, .glyph_overlap_w = (uint32_t)r->overlap_w, .glyph_full_w = (uint32_t)r->glyph_img_w, }; } return 0;} int start_app(GLFWwindow *window) { auto resize_grid = [](Grid& g) { g.rows = (vk.wnd_height + font_render.glyph_h - 1) / font_render.glyph_h; g.cols = (vk.wnd_width + font_render.glyph_w - 1) / font_render.glyph_w; }; resize_grid(grid); View view = { .grid = &grid, .file = &file, .formatter = &formatter, .font_render_idx = 0 }; int res = upload_glyphsets(font_face, &font_render, 1); if (res != 0) return res; res = render_and_upload_views(&view, 1, &font_render); if (res != 0) return res; res = vk.create_descriptor_set(); if (res != 0) return res; res = vk.construct_pipeline(); if (res != 0) return res; needs_resubmit = true; while (!glfwWindowShouldClose(window)) { glfwWaitEventsTimeout(0.5); int w, h; glfwGetFramebufferSize(window, &w, &h); if (w != vk.wnd_width || h != vk.wnd_height) { vk.recreate_swapchain(w, h); needs_resubmit = true; } if (needs_resubmit) { resize_grid(grid); res = render_and_upload_views(&view, 1, &font_render); if (res != 0) return res; res = vk.update_command_buffers(); if (res != 0) return res; needs_resubmit = false; input_state.advance(); } res = vk.render(); } return res;} // TODO: Get font_render from font_renders[get_current_view()->font_render_idx] or something// also Get grid from get_current_view()->grid or something // This function **doesn't** get called from a different thread, so we can let it access globalsstatic void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) { bool is_action = true; bool vertical = false; int dir = 0; bool shift_held = glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS; input_state.mod_flags = shift_held ? 1 : 0; if (action == GLFW_PRESS || action == GLFW_REPEAT) { if (key == GLFW_KEY_UP) { vertical = true; dir = -1; } else if (key == GLFW_KEY_DOWN) { vertical = true; dir = 1; } else if (key == GLFW_KEY_LEFT) { dir = -1; } else if (key == GLFW_KEY_RIGHT) { dir = 1; } else if (key == GLFW_KEY_PAGE_UP) { grid.adjust_offsets(&file, -grid.rows, 0); is_action = false; } else if (key == GLFW_KEY_PAGE_DOWN) { grid.adjust_offsets(&file, grid.rows, 0); is_action = false; } else is_action = false; } else is_action = false; if (dir != 0) { if (vertical) { if (!was_vertical_movement) { grid.target_column = grid.rel_caret_col; was_vertical_movement = true; } grid.move_cursor_vertically(&file, dir, grid.target_column); } else { int64_t cur = grid.primary_cursor + (int64_t)dir; if (cur < 0) cur = 0; if (cur > file.total_size) cur = file.total_size; grid.primary_cursor = cur; } } if (is_action) { grid.primary_cursor = grid.jump_to_offset(&file, grid.primary_cursor, JUMP_FLAG_AFFECT_COLUMN); if (!vertical || dir == 0) was_vertical_movement = false; if (!shift_held) grid.secondary_cursor = grid.primary_cursor; } needs_resubmit = true;} static void scroll_callback(GLFWwindow *window, double xoffset, double yoffset) { if (xoffset == 0.0 && yoffset == 0.0) return; int64_t move_down = 0; int64_t move_right = 0; double dx, dy; if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT_SHIFT) == GLFW_PRESS) { dx = yoffset; dy = xoffset; } else { dx = xoffset; dy = yoffset; } if (dy > 0.0) move_down = -1; else if (dy < 0.0) move_down = 1; if (dx > 0.0) move_right = -1; else if (dx < 0.0) move_right = 1; if (move_down != 0 || move_right != 0) grid.adjust_offsets(&file, move_down, move_right); needs_resubmit = true;} static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { bool left_pressed = action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_LEFT; bool right_pressed = action == GLFW_PRESS && button == GLFW_MOUSE_BUTTON_RIGHT; input_state.left_flags = (input_state.left_flags & ~1) | (left_pressed & 1); input_state.right_flags = (input_state.right_flags & ~1) | (right_pressed & 1); if (!left_pressed) input_state.thumb_flags &= ~1; if ((input_state.left_flags & 3) == 1 && input_state.x >= vk.wnd_width - THUMB_WIDTH) { int thumb_y, thumb_h; get_thumb_position(&grid, file.total_size, thumb_y, thumb_h); int pos = input_state.y - thumb_y; bool should_jump = true; if (input_state.y < thumb_y) { pos = 0; } else if (input_state.y >= thumb_y + thumb_h) { pos = thumb_h; } else { should_jump = false; } input_state.thumb_inner_pos = pos; input_state.thumb_flags |= 1; if (should_jump) { int64_t offset = get_file_offset_from_thumb(&grid, file.total_size); grid.jump_to_offset(&file, offset, JUMP_FLAG_TOP); } } was_vertical_movement = false; needs_resubmit = true;} static void cursor_callback(GLFWwindow *window, double xpos, double ypos) { input_state.x = (int)xpos; input_state.y = (int)ypos; input_state.column = input_state.x / font_render.glyph_w; input_state.row = input_state.y / font_render.glyph_h; if (input_state.thumb_flags & 1) { int64_t offset = get_file_offset_from_thumb(&grid, file.total_size); grid.jump_to_offset(&file, offset, JUMP_FLAG_TOP); } int was_hovered = input_state.thumb_flags & 2; if (input_state.x >= vk.wnd_width - THUMB_WIDTH) input_state.thumb_flags |= 2; else input_state.thumb_flags &= ~2; if ((input_state.left_flags & 3) || was_hovered != (input_state.thumb_flags & 2)) needs_resubmit = true;} int main(int argc, char **argv) { const char *file_name = "vulkan.cpp"; if (argc > 1) file_name = argv[1]; atexit([](){ft_quit();}); font_face = load_font_face(DEFAULT_FONT_PATH); if (!font_face) return 1; // TODO: Use system DPI font_render = size_up_font_render(font_face, 10, 96, 96); formatter.modes[0].fore_color_idx = 1; formatter.modes[0].glyphset = 0; // italic formatter.colors[0] = 0x080808ff; formatter.colors[1] = 0xf0f0f0ff; formatter.colors[2] = 0x202020ff; formatter.colors[3] = 0xb0b0b0ff; formatter.colors[4] = 0x141414ff; formatter.active_thumb_color = 0x808080ff; formatter.hovered_thumb_color = 0x303030ff; formatter.inactive_thumb_color = 0x181818ff; cursor_color = 0xf0f0f0ff; grid.spaces_per_tab = 4; VkShaderModuleCreateInfo vertex_buf = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(vertex_spv_data), .pCode = (uint32_t*)vertex_spv_data }; VkShaderModuleCreateInfo fragment_buf = { .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, .codeSize = sizeof(fragment_spv_data), .pCode = (uint32_t*)fragment_spv_data }; if (file.open(file_name) < 0) return 2; glfwInit(); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); GLFWmonitor *monitor = glfwGetPrimaryMonitor(); const GLFWvidmode *vid_mode = glfwGetVideoMode(monitor); //int width = vid_mode->width, height = vid_mode->height; int width = 800, height = 600; glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); GLFWwindow *window = glfwCreateWindow(width, height, "Mash", nullptr, nullptr); if (!window) { fprintf(stderr, "Failed to create GLFW window\n"); glfwTerminate(); return 3; } glfwSetKeyCallback(window, key_callback); glfwSetScrollCallback(window, scroll_callback); glfwSetMouseButtonCallback(window, mouse_button_callback); glfwSetCursorPosCallback(window, cursor_callback); vk.glfw_monitor = (void*)monitor; vk.glfw_window = (void*)window; int res = init_vulkan(vk, vertex_buf, fragment_buf, width, height); if (res == 0) res = start_app(window); file.close(); vk.close(); glfwDestroyWindow(window); glfwTerminate(); return res;}