| // Viewer & Rive |
| #include "viewer/viewer.hpp" |
| #include "viewer/viewer_content.hpp" |
| #include "viewer/viewer_host.hpp" |
| #include "rive/shapes/paint/color.hpp" |
| |
| // Graphics and UI abstraction |
| #include "sokol_app.h" |
| #include "sokol_gfx.h" |
| #include "sokol_glue.h" |
| #include "imgui.h" |
| #include "util/sokol_imgui.h" |
| |
| // Std lib |
| #include <stdio.h> |
| #include <memory> |
| #include <chrono> |
| |
| std::unique_ptr<ViewerHost> g_Host = ViewerHost::Make(); |
| std::unique_ptr<ViewerContent> g_Content = ViewerContent::TrimPath(""); |
| static struct |
| { |
| sg_pass_action pass_action; |
| } state; |
| |
| void displayStats(); |
| |
| static const int backgroundColor = rive::colorARGB(255, 22, 22, 22); |
| static std::chrono::time_point<std::chrono::high_resolution_clock> lastTime; |
| static const float billion = 1000000000.0; |
| |
| static void init(void) |
| { |
| sg_desc descriptor = { |
| .context = sapp_sgcontext(), |
| .buffer_pool_size = 1024, |
| .pipeline_pool_size = 1024, |
| }; |
| sg_setup(&descriptor); |
| simgui_desc_t imguiDescriptor = { |
| .write_alpha_channel = true, |
| }; |
| simgui_setup(&imguiDescriptor); |
| |
| // If the host doesn't overwrite this (in init()), Sokol is in full control, |
| // so let's clear to our bg color. |
| state.pass_action = (sg_pass_action){ |
| .colors[0] = |
| { |
| .action = SG_ACTION_CLEAR, |
| .value = |
| { |
| rive::colorRed(backgroundColor) / 255.0f, |
| rive::colorGreen(backgroundColor) / 255.0f, |
| rive::colorBlue(backgroundColor) / 255.0f, |
| rive::colorOpacity(backgroundColor), |
| }, |
| }, |
| .stencil = |
| { |
| .action = SG_ACTION_CLEAR, |
| }, |
| }; |
| |
| lastTime = std::chrono::high_resolution_clock::now(); |
| |
| if (!g_Host->init(&state.pass_action, sapp_width(), sapp_height())) |
| { |
| fprintf(stderr, "failed to initialize host\n"); |
| sapp_quit(); |
| } |
| } |
| |
| static void frame(void) |
| { |
| |
| auto newTime = std::chrono::high_resolution_clock::now(); |
| auto dur = |
| std::chrono::duration_cast<std::chrono::nanoseconds>(newTime - lastTime).count() / billion; |
| lastTime = newTime; |
| |
| g_Host->beforeDefaultPass(g_Content.get(), dur); |
| |
| sg_begin_default_pass(&state.pass_action, sapp_width(), sapp_height()); |
| |
| g_Host->afterDefaultPass(g_Content.get(), dur); |
| |
| simgui_frame_desc_t imguiDesc = { |
| .width = sapp_width(), |
| .height = sapp_height(), |
| .delta_time = sapp_frame_duration(), |
| .dpi_scale = sapp_dpi_scale(), |
| }; |
| simgui_new_frame(&imguiDesc); |
| |
| displayStats(); |
| |
| if (g_Content) |
| { |
| g_Content->handleImgui(); |
| } |
| simgui_render(); |
| |
| sg_end_pass(); |
| sg_commit(); |
| } |
| |
| static void cleanup(void) |
| { |
| g_Content = nullptr; |
| g_Host = nullptr; |
| |
| simgui_shutdown(); |
| sg_shutdown(); |
| } |
| |
| static void event(const sapp_event* ev) |
| { |
| simgui_handle_event(ev); |
| |
| switch (ev->type) |
| { |
| case SAPP_EVENTTYPE_RESIZED: |
| if (g_Content) |
| { |
| g_Content->handleResize(ev->framebuffer_width, ev->framebuffer_height); |
| } |
| g_Host->handleResize(ev->framebuffer_width, ev->framebuffer_height); |
| break; |
| case SAPP_EVENTTYPE_FILES_DROPPED: |
| { |
| // Do this to make sure the graphics is bound. |
| bindGraphicsContext(); |
| |
| // get the number of files and their paths like this: |
| const int numDroppedFiles = sapp_get_num_dropped_files(); |
| if (numDroppedFiles != 0) |
| { |
| const char* filename = sapp_get_dropped_file_path(numDroppedFiles - 1); |
| auto newContent = ViewerContent::findHandler(filename); |
| if (newContent) |
| { |
| g_Content = std::move(newContent); |
| g_Content->handleResize(ev->framebuffer_width, ev->framebuffer_height); |
| } |
| else |
| { |
| fprintf(stderr, "No handler found for %s\n", filename); |
| } |
| } |
| break; |
| } |
| case SAPP_EVENTTYPE_MOUSE_DOWN: |
| case SAPP_EVENTTYPE_TOUCHES_BEGAN: |
| if (g_Content) |
| { |
| g_Content->handlePointerDown(ev->mouse_x, ev->mouse_y); |
| } |
| break; |
| case SAPP_EVENTTYPE_MOUSE_UP: |
| case SAPP_EVENTTYPE_TOUCHES_ENDED: |
| if (g_Content) |
| { |
| g_Content->handlePointerUp(ev->mouse_x, ev->mouse_y); |
| break; |
| } |
| case SAPP_EVENTTYPE_MOUSE_MOVE: |
| case SAPP_EVENTTYPE_TOUCHES_MOVED: |
| if (g_Content) |
| { |
| g_Content->handlePointerMove(ev->mouse_x, ev->mouse_y); |
| } |
| break; |
| case SAPP_EVENTTYPE_KEY_UP: |
| switch (ev->key_code) |
| { |
| case SAPP_KEYCODE_ESCAPE: |
| sapp_quit(); |
| break; |
| case SAPP_KEYCODE_T: |
| g_Content = ViewerContent::Text(".svg"); |
| break; |
| case SAPP_KEYCODE_P: |
| g_Content = ViewerContent::TextPath(""); |
| break; |
| default: |
| break; |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| |
| sapp_desc sokol_main(int argc, char* argv[]) |
| { |
| (void)argc; |
| (void)argv; |
| |
| return (sapp_desc) |
| { |
| .init_cb = init, .frame_cb = frame, .cleanup_cb = cleanup, .event_cb = event, |
| .enable_dragndrop = true, .high_dpi = true, |
| .window_title = "Rive Viewer " |
| #if defined(SOKOL_GLCORE33) |
| "(OpenGL 3.3)", |
| #elif defined(SOKOL_GLES2) |
| "(OpenGL ES 2)", |
| #elif defined(SOKOL_GLES3) |
| "(OpenGL ES 3)", |
| #elif defined(SOKOL_D3D11) |
| "(D3D11)", |
| #elif defined(SOKOL_METAL) |
| "(Metal)", |
| #elif defined(SOKOL_WGPU) |
| "(WebGPU)", |
| #endif |
| .width = 800, .height = 600, .icon.sokol_default = true, .gl_force_gles2 = true, |
| }; |
| } |