From fd73bd96a2eabf8abb12e0f55f6b1fd7f552ec8f Mon Sep 17 00:00:00 2001 From: Helco Date: Fri, 7 Dec 2018 15:43:35 +0100 Subject: [PATCH] PCMockup: Migrate to imgui (#39) * cmake: Add cimgui and glad dependencies * pcmockup: Add imgui WindowContainer * pcmockup: Add imgui ImageWindow * pcmockup: Migrate PebbleWindow to imgui * pcmockup: Migrate debugWindow to imgui * pcmockup: add initial menubar * pcmockup: Improve ImageWindow content alignment * pcmockup: add extra include for travis * pcmockup: work around missing header guard * cmake: Update cimgui and ignore warnings * PCMockup: Add Window module * pcmockup: Migrate ImageWindow and consumers to Window * pcmockup: Readd debug dragging * pcmockup: Readd debug zooming * pcmockup: fix slight misalignment * pcmockup: fix logical merge conflict * pcmockup: replace dragging with key controls because imgui really does not like other people knowing what it does * pcmockup: reintroduce dragging with MMB * pcmockup: tweaks --- .gitmodules | 6 ++ CMakeLists.txt | 2 + cmake/cimgui.cmake | 49 ++++++++++ cmake/glad.cmake | 14 +++ external/cimgui | 1 + external/glad | 1 + pcmockup/CMakeLists.txt | 11 ++- pcmockup/cimgui.include.h | 12 +++ pcmockup/debugwindow.c | 164 ++++++++++++++++++-------------- pcmockup/debugwindowset.c | 19 +++- pcmockup/imagewindow.c | 150 ++++++++++++++++++++++++++++++ pcmockup/pcmockup.c | 122 ++++++++++++------------ pcmockup/pcmockup.h | 27 ++++-- pcmockup/pebblewindow.c | 109 +++++++++++----------- pcmockup/window.c | 169 +++++++++++++++++++++++++++++++++ pcmockup/window.h | 58 ++++++++++++ pcmockup/window_internal.h | 11 +++ pcmockup/windowcontainer.c | 185 +++++++++++++++++++++++++++++++++++++ renderer/CMakeLists.txt | 3 + 19 files changed, 918 insertions(+), 195 deletions(-) create mode 100644 cmake/cimgui.cmake create mode 100644 cmake/glad.cmake create mode 160000 external/cimgui create mode 160000 external/glad create mode 100644 pcmockup/cimgui.include.h create mode 100644 pcmockup/imagewindow.c create mode 100644 pcmockup/window.c create mode 100644 pcmockup/window.h create mode 100644 pcmockup/window_internal.h create mode 100644 pcmockup/windowcontainer.c diff --git a/.gitmodules b/.gitmodules index ad1bdcf..7b7510e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,9 @@ [submodule "external/stb"] path = external/stb url = https://github.com/nothings/stb.git +[submodule "external/cimgui"] + path = external/cimgui + url = https://github.com/cimgui/cimgui.git +[submodule "external/glad"] + path = external/glad + url = https://github.com/Dav1dde/glad.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 927f21d..8e6b940 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -51,6 +51,8 @@ include(CTest) include(GoogleTest) include("${CMAKE_SOURCE_DIR}/cmake/GTest.cmake") include("${CMAKE_SOURCE_DIR}/cmake/stb.cmake") +include("${CMAKE_SOURCE_DIR}/cmake/glad.cmake") +include("${CMAKE_SOURCE_DIR}/cmake/cimgui.cmake") if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "MSVC") message(FATAL_ERROR " Microsoft C Compiler is not supported, please use GCC or Clang") diff --git a/cmake/cimgui.cmake b/cmake/cimgui.cmake new file mode 100644 index 0000000..5a1c748 --- /dev/null +++ b/cmake/cimgui.cmake @@ -0,0 +1,49 @@ +# Configures cimgui for use in PebbleOfDoom + +set(CIMGUI_DIR "${CMAKE_SOURCE_DIR}/external/cimgui/") +set(IMGUI_DIR "${CIMGUI_DIR}/imgui/") +if (NOT EXISTS "${CIMGUI_DIR}/cimgui.h") + message(FATAL_ERROR "Could not find cimgui, did you clone the submodules?") +endif() +if (NOT EXISTS "${IMGUI_DIR}/imgui.h") + message(FATAL_ERROR "Could not find imgui, did you clone the submodules *recursively*?") +endif() + +set(sources_cimgui + ${CIMGUI_DIR}/cimgui.h + ${CIMGUI_DIR}/cimgui.cpp + + ${IMGUI_DIR}/imgui.h + ${IMGUI_DIR}/imgui.cpp + ${IMGUI_DIR}/imgui_draw.cpp + ${IMGUI_DIR}/imgui_demo.cpp + ${IMGUI_DIR}/imgui_widgets.cpp + + ${IMGUI_DIR}/examples/imgui_impl_sdl.h + ${IMGUI_DIR}/examples/imgui_impl_sdl.cpp + ${IMGUI_DIR}/examples/imgui_impl_opengl3.h + ${IMGUI_DIR}/examples/imgui_impl_opengl3.cpp +) + +add_library(cimgui ${sources_cimgui}) +target_include_directories(cimgui + PRIVATE ${IMGUI_DIR} + PRIVATE ${SDL2_INCLUDE_DIR} + INTERFACE ${CIMGUI_DIR} + INTERFACE ${IMGUI_DIR}/examples +) +target_link_libraries(cimgui + PUBLIC ${SDL2_LIBRARY} + PUBLIC glad +) +target_compile_definitions(cimgui + PRIVATE "-DIMGUI_IMPL_API=extern \"C\"" + PRIVATE IMGUI_IMPL_OPENGL_LOADER_GLAD + INTERFACE CIMGUI_DEFINE_ENUMS_AND_STRUCTS +) +target_compile_options(cimgui PRIVATE + "-Wno-pragma-pack" + "-Wno-return-type-c-linkage" + "-Wno-unused-const-variable" + "-Wno-macro-redefined" +) diff --git a/cmake/glad.cmake b/cmake/glad.cmake new file mode 100644 index 0000000..1e5ec89 --- /dev/null +++ b/cmake/glad.cmake @@ -0,0 +1,14 @@ +# Configures glad for use in PebbleOfDoom + +set(GLAD_DIR "${CMAKE_SOURCE_DIR}/external/glad") +if (NOT EXISTS "${GLAD_DIR}/CMakeLists.txt") + message(FATAL_ERROR "Could not find glad, did you clone the submodules?") +endif() + +set(GLAD_PROFILE "core" CACHE STRING "" FORCE) +set(GLAD_API "gl=3.0" CACHE STRING "" FORCE) +set(GLAD_GENERATOR "c" CACHE STRING "" FORCE) +set(GLAD_EXTENSIONS "" CACHE STRING "" FORCE) +set(GLAD_SPEC "gl" CACHE STRING "" FORCE) +option(GLAD_NO_LOADER "" ON) +add_subdirectory(${GLAD_DIR}) diff --git a/external/cimgui b/external/cimgui new file mode 160000 index 0000000..204f282 --- /dev/null +++ b/external/cimgui @@ -0,0 +1 @@ +Subproject commit 204f2828bb81857ffa4b9e2dbc360eabbb7cbd25 diff --git a/external/glad b/external/glad new file mode 160000 index 0000000..ea33861 --- /dev/null +++ b/external/glad @@ -0,0 +1 @@ +Subproject commit ea33861340d2407a4f5939f9c3166a97a5069ef1 diff --git a/pcmockup/CMakeLists.txt b/pcmockup/CMakeLists.txt index e753ffc..cd242a3 100644 --- a/pcmockup/CMakeLists.txt +++ b/pcmockup/CMakeLists.txt @@ -6,6 +6,13 @@ set(sources_pcmockup pebble.h sdl.include.h stb.include.h + cimgui.include.h + + window.h + window_internal.h + window.c + windowcontainer.c + pcmockup.h pcmockup.c pebblewindow.c @@ -15,13 +22,15 @@ set(sources_pcmockup safeframebuffer.c rendererinterface.c texturemanager.c + windowcontainer.c + imagewindow.c ) assign_source_group(${sources_pcmockup}) add_executable(pcmockup ${sources_pcmockup}) target_link_libraries(pcmockup ${SDL2_LIBRARY} - ${STB_LIBRARY} + stb podrenderer ) target_include_directories(pcmockup diff --git a/pcmockup/cimgui.include.h b/pcmockup/cimgui.include.h new file mode 100644 index 0000000..8a09090 --- /dev/null +++ b/pcmockup/cimgui.include.h @@ -0,0 +1,12 @@ +#ifndef CIMGUI_INCLUDE_H +#define CIMGUI_INCLUDE_H + +#pragma GCC diagnostic push +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Wpragma-pack" +#pragma GCC diagnostic ignored "-Wmacro-redefined" +#endif +#include +#pragma GCC diagnostic pop + +#endif diff --git a/pcmockup/debugwindow.c b/pcmockup/debugwindow.c index 1211bd5..2884c56 100644 --- a/pcmockup/debugwindow.c +++ b/pcmockup/debugwindow.c @@ -1,42 +1,67 @@ #include "pcmockup.h" #include "renderer.h" +#include "platform.h" #include struct DebugWindow { - SDL_Window* window; + ImageWindow* window; SDL_Renderer* renderer; - SDL_Texture* texture; - SDL_Rect texturePos; + SDL_Surface* surface; Renderer* podRenderer; const DebugView* view; xz_t position, offset; real_t zoom; }; -DebugWindow* debugWindow_init(SDL_Rect bounds, const DebugView* view, Renderer* podRenderer) +SDL_Surface* createSDLSurface(int w, int h, Uint32 format) +{ + SDL_PixelFormat* formatInfo = SDL_AllocFormat(format); + if (formatInfo == NULL) + return NULL; + SDL_Surface* surface = SDL_CreateRGBSurface( + SDL_SWSURFACE, w, h, formatInfo->BitsPerPixel, + formatInfo->Rmask, formatInfo->Gmask, formatInfo->Bmask, formatInfo->Amask + ); + SDL_FreeFormat(formatInfo); + return surface; +} + +void debugWindow_onDrag(Window* window, int button, ImVec2 delta, void* userdata); +void debugWindow_onKeyDown(Window* window, SDL_Keysym sym, void* userdata); + +DebugWindow* debugWindow_init(WindowContainer* parent, SDL_Rect bounds, const DebugView* view, Renderer* podRenderer) { DebugWindow* me = (DebugWindow*)malloc(sizeof(DebugWindow)); if (me == NULL) return NULL; memset(me, 0, sizeof(DebugWindow)); - me->window = SDL_CreateWindow(view->name, - bounds.x, bounds.y, - bounds.w, bounds.h, - SDL_WINDOW_RESIZABLE); + GRect b = { { bounds.x, bounds.y }, { bounds.w, bounds.h} }; + me->window = imageWindow_init(parent, view->name, b, false); if (me->window == NULL) { - fprintf(stderr, "SDL_CreateWindow: %s\n", SDL_GetError()); + debugWindow_free(me); + return NULL; + } + window_setDragCallback(imageWindow_asWindow(me->window), debugWindow_onDrag, me); + window_setKeyCallbacks(imageWindow_asWindow(me->window), (WindowKeyCallbacks) { + .down = debugWindow_onKeyDown, + .userdata = me + }); + + me->surface = createSDLSurface(bounds.w, bounds.h, SDL_PIXELFORMAT_ABGR8888); + if (me->surface == NULL) + { + fprintf(stderr, "createSDLSurface: %s\n", SDL_GetError()); debugWindow_free(me); return NULL; } - me->renderer = SDL_CreateRenderer(me->window, -1, SDL_RENDERER_TARGETTEXTURE); + me->renderer = SDL_CreateSoftwareRenderer(me->surface); if (me->renderer == NULL) { - fprintf(stderr, "SDL_CreateRenderer: %s\n", SDL_GetError()); debugWindow_free(me); return NULL; } @@ -53,56 +78,26 @@ void debugWindow_free(DebugWindow* me) { if (me == NULL) return; - if (me->texture != NULL) - SDL_DestroyTexture(me->texture); + if (me->window != NULL) + imageWindow_free(me->window); + if (me->surface != NULL) + SDL_FreeSurface(me->surface); if (me->renderer != NULL) SDL_DestroyRenderer(me->renderer); - if (me->window != NULL) - SDL_DestroyWindow(me->window); free(me); } void debugWindow_startUpdate(DebugWindow* me) { - SDL_Rect src; - int textureW = -1, textureH = -1; - SDL_GetWindowSize(me->window, &src.w, &src.h); - if (me->texture != NULL) - SDL_QueryTexture(me->texture, NULL, NULL, &textureW, &textureH); - me->texturePos = findBestFit(src, 1.0f); - - if (me->texturePos.w != textureW || me->texturePos.h != textureH) - { - if (me->texture != NULL) - SDL_DestroyTexture(me->texture); - me->texture = SDL_CreateTexture(me->renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_TARGET, - me->texturePos.w, me->texturePos.h); - if (me->texture == NULL) - { - fprintf(stderr, "SDL_CreateTexture: %s\n", SDL_GetError()); - exit(-1); - } - } - - SDL_SetRenderTarget(me->renderer, me->texture); + float scale = real_to_float(me->zoom); SDL_SetRenderDrawColor(me->renderer, 0, 0, 0, 255); SDL_RenderClear(me->renderer); - - float scale = real_to_float(me->zoom); SDL_RenderSetScale(me->renderer, scale, scale); } void debugWindow_endUpdate(DebugWindow* me) { - SDL_RenderSetScale(me->renderer, 1.0f, 1.0f); - SDL_SetRenderTarget(me->renderer, NULL); - SDL_SetRenderDrawColor(me->renderer, 255, 0, 255, 255); - SDL_RenderClear(me->renderer); - SDL_SetRenderDrawColor(me->renderer, 255, 255, 255, 255); - SDL_RenderCopy(me->renderer, me->texture, NULL, &me->texturePos); - SDL_RenderPresent(me->renderer); + imageWindow_setImageData(me->window, GSize(me->surface->w, me->surface->h), (SDL_Color*)me->surface->pixels); } void debugWindow_update(DebugWindow* me) @@ -112,32 +107,61 @@ void debugWindow_update(DebugWindow* me) debugWindow_endUpdate(me); } -void debugWindow_handleEvent(DebugWindow* me, const SDL_Event* ev) +void debugWindow_updateOffset(DebugWindow* me) { - Uint32 windowID = SDL_GetWindowID(me->window); - if (ev->type == SDL_MOUSEMOTION && ev->motion.windowID == windowID && (ev->motion.state & SDL_BUTTON_LMASK) > 0) - { - xz_t move = xz(real_from_int(ev->motion.xrel), real_from_int(ev->motion.yrel)); - move = xz_invScale(move, me->zoom); - me->position = xz_sub(me->position, move); - } - if (ev->type == SDL_MOUSEWHEEL && ev->wheel.windowID == windowID) - { - real_t zoom = real_from_int(ev->wheel.y); - zoom = real_mul(real_div(zoom, real_from_int(10)), me->zoom); - me->zoom = real_add(me->zoom, zoom); - } - if (ev->type == SDL_KEYDOWN && ev->key.windowID == windowID && ev->key.keysym.sym == SDLK_r) - { - me->position = xz(real_zero, real_zero); - } - - // update render offset - int textureW, textureH; - SDL_QueryTexture(me->texture, NULL, NULL, &textureW, &textureH); - xz_t halfSize = xz(real_from_int(textureW / 2), real_from_int(textureH / 2)); + xz_t halfSize = xz(real_from_int(me->surface->w / 2), real_from_int(me->surface->h / 2)); me->offset = xz_sub( xz_invScale(halfSize, me->zoom), me->position ); } + +void debugWindow_onDrag(Window* window, int button, ImVec2 delta, void* userdata) +{ + UNUSED(window); + if (button != 2) // middle mouse button + return; + DebugWindow* me = (DebugWindow*)userdata; + xz_t move = xz_invScale((xz_t) { real_from_int(delta.x), real_from_int(delta.y) }, me->zoom); + me->position = xz_sub(me->position, move); + debugWindow_updateOffset(me); +} + +void debugWindow_onKeyDown(Window* window, SDL_Keysym sym, void* userdata) +{ + UNUSED(window); + static const float zoomSpeed = 0.1f; + static const float moveSpeed = 8.0f; + real_t zoomDelta = real_zero; + xz_t moveDelta = xz_zero; + switch (sym.sym) + { + case (SDLK_PLUS): + case (SDLK_KP_PLUS): zoomDelta = real_from_float(zoomSpeed); break; + case (SDLK_MINUS): + case (SDLK_KP_MINUS): zoomDelta = real_from_float(-zoomSpeed); break; + case (SDLK_w): + case (SDLK_UP): moveDelta = xz(real_zero, real_from_float(-moveSpeed)); break; + case (SDLK_s): + case (SDLK_DOWN): moveDelta = xz(real_zero, real_from_float(moveSpeed)); break; + case (SDLK_a): + case (SDLK_LEFT): moveDelta = xz(real_from_float(-moveSpeed), real_zero); break; + case (SDLK_d): + case (SDLK_RIGHT): moveDelta = xz(real_from_float(moveSpeed), real_zero); break; + default: return; + } + DebugWindow* me = (DebugWindow*)userdata; + me->position = xz_sub(me->position, xz_invScale(moveDelta, me->zoom)); + me->zoom = real_add(me->zoom, real_mul(me->zoom, zoomDelta)); + debugWindow_updateOffset(me); +} + +const DebugView* debugWindow_getDebugView(const DebugWindow* me) +{ + return me->view; +} + +ImageWindow* debugWindow_asImageWindow(DebugWindow* me) +{ + return me->window; +} diff --git a/pcmockup/debugwindowset.c b/pcmockup/debugwindowset.c index 6ce4bb0..57e7e33 100644 --- a/pcmockup/debugwindowset.c +++ b/pcmockup/debugwindowset.c @@ -1,4 +1,5 @@ #include "pcmockup.h" +#include "cimgui.include.h" #define WINDOW_GAP 64 @@ -9,7 +10,7 @@ struct DebugWindowSet Renderer* renderer; }; -DebugWindowSet* debugWindowSet_init(const WindowGrid* grid, Renderer* renderer) +DebugWindowSet* debugWindowSet_init(WindowContainer* parent, const WindowGrid* grid, Renderer* renderer) { DebugWindowSet* me = (DebugWindowSet*)malloc(sizeof(DebugWindowSet)); if (me == NULL) @@ -31,6 +32,7 @@ DebugWindowSet* debugWindowSet_init(const WindowGrid* grid, Renderer* renderer) for (int i = 0; i < me->count; i++) { me->windows[i] = debugWindow_init( + parent, windowGrid_getSingleBounds(grid, -1 - i), &renderer_getDebugViews(renderer)[i], renderer @@ -73,8 +75,19 @@ void debugWindowSet_update(DebugWindowSet* me) #endif } -void debugWindowSet_handleEvent(DebugWindowSet* me, const SDL_Event* ev) +void debugWindowSet_updateMenubar(DebugWindowSet* me) { +#ifdef DEBUG_WINDOWS + if (!igBeginMenu("Debug windows", true)) + return; for (int i = 0; i < me->count; i++) - debugWindow_handleEvent(me->windows[i], ev); + { + ImageWindow* window = debugWindow_asImageWindow(me->windows[i]); + const DebugView* view = debugWindow_getDebugView(me->windows[i]); + bool isOpen = imageWindow_isOpen(window); + igMenuItemBoolPtr(view->name, NULL, &isOpen, true); + imageWindow_toggle(window, isOpen); + } + igEndMenu(); +#endif } diff --git a/pcmockup/imagewindow.c b/pcmockup/imagewindow.c new file mode 100644 index 0000000..570014d --- /dev/null +++ b/pcmockup/imagewindow.c @@ -0,0 +1,150 @@ +#include "pcmockup.h" +#include +#include "cimgui.include.h" +#include +#include "platform.h" + +struct ImageWindow +{ + Window* window; + GLuint textureID; + GSize imageSize; + float aspect; + bool isEssential; + float toolbarHeight; +}; + +const Uint32 imageWindow_SDLPixelFormat = SDL_PIXELFORMAT_ABGR8888; + +void imageWindow_beforeUpdate(Window* me, void* userdata); +void imageWindow_contentUpdate(Window* me, void* userdata); + +ImageWindow* imageWindow_init(WindowContainer* parent, const char* title, GRect initialBounds, bool_t isEssential) +{ + ImageWindow* me = (ImageWindow*)malloc(sizeof(ImageWindow)); + if (me == NULL) + return NULL; + memset(me, 0, sizeof(ImageWindow)); + + me->window = windowContainer_newWindow(parent, title); + if (me->window == NULL) + { + fprintf(stderr, "Could not allocate new window\n"); + imageWindow_free(me); + return NULL; + } + window_setInitialBounds(me->window, initialBounds); + window_setOpenState(me->window, isEssential ? WindowOpenState_Unclosable : WindowOpenState_Open); + window_setFlags(me->window, + ImGuiWindowFlags_NoScrollbar | + ImGuiWindowFlags_NoScrollWithMouse | + ImGuiWindowFlags_NoCollapse); + window_setUpdateCallbacks(me->window, (WindowUpdateCallbacks) { + .before = imageWindow_beforeUpdate, + .content = imageWindow_contentUpdate, + .userdata = me + }); + + glGenTextures(1, &me->textureID); + if (me->textureID == 0) + { + fprintf(stderr, "glGenTextures: %d\n", glGetError()); + imageWindow_free(me); + return NULL; + } + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, me->textureID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + const SDL_Color black = { 255, 255, 255, 255 }; + imageWindow_setImageData(me, GSize(1, 1), &black); + + me->isEssential = isEssential; + me->aspect = (float)initialBounds.size.w / initialBounds.size.h; + return me; +} + +void imageWindow_free(ImageWindow* me) +{ + if (me == NULL) + return; + if (me->textureID != 0) + glDeleteTextures(1, &me->textureID); + free(me); +} + +void imageWindow_setImageData(ImageWindow* me, GSize size, const SDL_Color* data) +{ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, me->textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.w, size.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + me->imageSize = size; + me->aspect = (float)size.w / (float)size.h; +} + +void imageWindow_constrainWindowSize(ImGuiSizeCallbackData* data) +{ + ImageWindow* me = (ImageWindow*)data->UserData; + data->DesiredSize.y -= me->toolbarHeight; + + ImVec2 byWidth = { + floorf(data->DesiredSize.x), + floorf(data->DesiredSize.x / me->aspect + me->toolbarHeight) + }; + float byWidthArea = byWidth.x * byWidth.y; + ImVec2 byHeight = { + floorf(data->DesiredSize.y * me->aspect), + floorf(data->DesiredSize.y + me->toolbarHeight) + }; + float byHeightArea = byHeight.x * byHeight.y; + + data->DesiredSize = byWidthArea > byHeightArea ? byWidth : byHeight; +} + +void imageWindow_beforeUpdate(Window* window, void* userdata) +{ + UNUSED(window); + ImageWindow* me = (ImageWindow*)userdata; + const ImVec2 + zero = { 0, 0 }, + minSize = { (float)me->imageSize.w, (float)me->imageSize.h }, + maxSize = { FLT_MAX, FLT_MAX }; + igSetNextWindowSizeConstraints(minSize, maxSize, imageWindow_constrainWindowSize, me); + igPushStyleVarVec2(ImGuiStyleVar_WindowPadding, zero); // space between image and window border +} + +void imageWindow_contentUpdate(Window* window, void* userdata) +{ + ImageWindow* me = (ImageWindow*)userdata; + const GSize windowSize = window_getBounds(window).size; + const ImVec2 + zero = { 0, 0 }, + one = { 1, 1 }, + size = { (float)windowSize.w, (float)windowSize.h - me->toolbarHeight }; + const ImVec4 + tintColor = { 1, 1, 1, 1 }, + borderColor = { 0, 0, 0, 0 }; + ImVec2 toolbarSize; + + igGetItemRectSize_nonUDT(&toolbarSize); + me->toolbarHeight = toolbarSize.y; + igImageButton((ImTextureID)(intptr_t)me->textureID, size, zero, one, 0, borderColor, tintColor); + igPopStyleVar(1); +} + +void imageWindow_toggle(ImageWindow* me, bool_t isOpen) +{ + window_setOpenState(me->window, isOpen ? WindowOpenState_Open : WindowOpenState_Closed); +} + +bool_t imageWindow_isOpen(ImageWindow* me) +{ + return window_getOpenState(me->window) == WindowOpenState_Open; +} + +Window* imageWindow_asWindow(ImageWindow* me) +{ + return me->window; +} diff --git a/pcmockup/pcmockup.c b/pcmockup/pcmockup.c index 018b4df..1a94929 100644 --- a/pcmockup/pcmockup.c +++ b/pcmockup/pcmockup.c @@ -1,8 +1,10 @@ #include +#include "cimgui.include.h" #include "pcmockup.h" #include "renderer.h" #include "platform.h" +static const GSize WINDOW_START_SIZE = { 1024, 768 }; static const int MAX_FRAMERATE = 60; struct PCMockup @@ -12,6 +14,7 @@ struct PCMockup TextureManager* textureManager; PebbleWindow *pebbleWindow; DebugWindowSet *debugWindowSet; + WindowContainer* windowContainer; bool_t isRunning; }; @@ -53,15 +56,22 @@ PCMockup *pcmockup_init() return NULL; } - SDL_DisplayMode displayMode; - SDL_GetCurrentDisplayMode(0, &displayMode); + me->windowContainer = windowContainer_init(WINDOW_START_SIZE); + if (me->windowContainer == NULL) + { + pcmockup_free(me); + return NULL; + } + WindowGrid windowGrid; windowGrid.windowCount = 1 + renderer_getDebugCount(me->renderer); - windowGrid.totalSize = GSize(displayMode.w, displayMode.h); + windowGrid.totalSize = WINDOW_START_SIZE; me->pebbleWindow = pebbleWindow_init( + me->windowContainer, windowGrid_getSingleBounds(&windowGrid, 0), - GSize(RENDERER_WIDTH, RENDERER_HEIGHT) + GSize(RENDERER_WIDTH, RENDERER_HEIGHT), + me->renderer ); if (me->pebbleWindow == NULL) { @@ -70,6 +80,7 @@ PCMockup *pcmockup_init() } me->debugWindowSet = debugWindowSet_init( + me->windowContainer, &windowGrid, me->renderer ); @@ -87,6 +98,8 @@ void pcmockup_free(PCMockup *me) { if (me == NULL) return; + if (me->windowContainer != NULL) + windowContainer_free(me->windowContainer); if (me->debugWindowSet != NULL) debugWindowSet_free(me->debugWindowSet); if (me->pebbleWindow != NULL) @@ -100,13 +113,32 @@ void pcmockup_free(PCMockup *me) free(me); } +void pcmockup_updateMainMenubar(PCMockup* me) +{ + if (!igBeginMainMenuBar()) + return; + + if (igBeginMenu("PCMockup", true)) + { + if (igMenuItemBool("Exit", NULL, false, true)) + me->isRunning = false; + igEndMenu(); + } + debugWindowSet_updateMenubar(me->debugWindowSet); + + igEndMainMenuBar(); +} + void pcmockup_update(PCMockup *me) { + windowContainer_startUpdate(me->windowContainer); GColor *framebuffer = pebbleWindow_getPebbleFramebuffer(me->pebbleWindow); pebbleWindow_startUpdate(me->pebbleWindow); renderer_render(me->renderer, framebuffer); pebbleWindow_endUpdate(me->pebbleWindow); debugWindowSet_update(me->debugWindowSet); + pcmockup_updateMainMenubar(me); + windowContainer_endUpdate(me->windowContainer); SDL_Event event; while (SDL_PollEvent(&event)) @@ -120,61 +152,10 @@ void pcmockup_update(PCMockup *me) case (SDLK_ESCAPE): { me->isRunning = 0; - } - break; - case (SDLK_w): - { - renderer_moveForward(me->renderer); - } - break; - case (SDLK_s): - { - renderer_moveBackwards(me->renderer); - } - break; - case (SDLK_a): - { - renderer_moveLeft(me->renderer); - } - break; - case (SDLK_d): - { - renderer_moveRight(me->renderer); - } - break; - case (SDLK_RIGHT): - { - renderer_rotateRight(me->renderer); - } - break; - case (SDLK_LEFT): - { - renderer_rotateLeft(me->renderer); - } - break; - case (SDLK_UP): - { - renderer_moveUp(me->renderer); - } - break; - case (SDLK_DOWN): - { - renderer_moveDown(me->renderer); - } - break; - case (SDLK_SPACE): - { - Location playerLocation; - playerLocation.angle = real_degToRad(real_from_int(0)); - playerLocation.height = real_zero; - playerLocation.position = xz(real_from_int(20), real_from_int(20)); - - renderer_moveTo(me->renderer, playerLocation); - } - break; + }break; } } - debugWindowSet_handleEvent(me->debugWindowSet, &event); + windowContainer_handleEvent(me->windowContainer, &event); } } @@ -202,6 +183,11 @@ int main(int argc, char *argv[]) fprintf(stderr, "SDL_Init: %s\n", SDL_GetError()); return -1; } + if (SDL_GL_LoadLibrary(NULL) < 0) + { + fprintf(stderr, "SDL_GL_LoadLibrary: %s\n", SDL_GetError()); + return -1; + } PCMockup* pcmockup = pcmockup_init(); if (pcmockup == NULL) return -1; @@ -243,3 +229,25 @@ SDL_Rect padRect(SDL_Rect rect, GSize amount) rect.h - amount.h }; } + +Uint32 getWindowIDByEvent(const SDL_Event* ev) +{ + switch (ev->type) + { + case (SDL_KEYDOWN): + case (SDL_KEYUP): + return ev->key.windowID; + case (SDL_MOUSEBUTTONDOWN): + case (SDL_MOUSEBUTTONUP): + return ev->button.windowID; + case (SDL_MOUSEMOTION): + return ev->motion.windowID; + case (SDL_MOUSEWHEEL): + return ev->wheel.windowID; + case (SDL_WINDOWEVENT): + return ev->window.windowID; + case (SDL_TEXTINPUT): + return ev->text.windowID; + } + return UINT32_MAX; +} diff --git a/pcmockup/pcmockup.h b/pcmockup/pcmockup.h index fbd844c..9781c06 100644 --- a/pcmockup/pcmockup.h +++ b/pcmockup/pcmockup.h @@ -3,10 +3,20 @@ #include "sdl.include.h" #include "pebble.h" #include "renderer.h" +#include "window.h" SDL_Rect findBestFit(SDL_Rect target, float aspect); SDL_Rect padRect(SDL_Rect rect, GSize amount); +extern const Uint32 imageWindow_SDLPixelFormat; +typedef struct ImageWindow ImageWindow; +ImageWindow* imageWindow_init(WindowContainer* parent, const char* title, GRect initialBounds, bool_t isEssential); +void imageWindow_free(ImageWindow* me); +void imageWindow_setImageData(ImageWindow* me, GSize size, const SDL_Color* data); +void imageWindow_toggle(ImageWindow* me, bool_t isOpen); +bool_t imageWindow_isOpen(ImageWindow* me); +Window* imageWindow_asWindow(ImageWindow* me); + typedef struct WindowGrid { int windowCount; @@ -23,24 +33,25 @@ void safeFramebuffer_prepare(SafeFramebuffer* me); void safeFramebuffer_check(SafeFramebuffer* me); typedef struct PebbleWindow PebbleWindow; -PebbleWindow* pebbleWindow_init(SDL_Rect initialBounds, GSize pebbleSize); +PebbleWindow* pebbleWindow_init(WindowContainer* parent, SDL_Rect initialBounds, GSize pebbleSize, Renderer* renderer); void pebbleWindow_free(PebbleWindow* window); -void pebbleWindow_startUpdate(PebbleWindow* window); -void pebbleWindow_endUpdate(PebbleWindow* window); -SDL_Rect pebbleWindow_getBounds(PebbleWindow* window); +void pebbleWindow_startUpdate(PebbleWindow* me); +void pebbleWindow_endUpdate(PebbleWindow* me); GColor* pebbleWindow_getPebbleFramebuffer(PebbleWindow* window); +ImageWindow* pebbleWindow_asImageWindow(PebbleWindow* window); typedef struct DebugWindow DebugWindow; -DebugWindow* debugWindow_init(SDL_Rect bounds, const DebugView* debugView, Renderer* renderer); +DebugWindow* debugWindow_init(WindowContainer* parent, SDL_Rect bounds, const DebugView* debugView, Renderer* renderer); void debugWindow_free(DebugWindow* window); void debugWindow_update(DebugWindow* window); -void debugWindow_handleEvent(DebugWindow* window, const SDL_Event* ev); +const DebugView* debugWindow_getDebugView(const DebugWindow* window); +ImageWindow* debugWindow_asImageWindow(DebugWindow* window); typedef struct DebugWindowSet DebugWindowSet; -DebugWindowSet* debugWindowSet_init(const WindowGrid* grid, Renderer* renderer); +DebugWindowSet* debugWindowSet_init(WindowContainer* parent, const WindowGrid* grid, Renderer* renderer); void debugWindowSet_free(DebugWindowSet* set); void debugWindowSet_update(DebugWindowSet* set); -void debugWindowSet_handleEvent(DebugWindowSet* set, const SDL_Event* ev); +void debugWindowSet_updateMenubar(DebugWindowSet* me); typedef struct PCMockup PCMockup; PCMockup* pcmockup_init(); diff --git a/pcmockup/pebblewindow.c b/pcmockup/pebblewindow.c index 98d8be7..05e40e0 100644 --- a/pcmockup/pebblewindow.c +++ b/pcmockup/pebblewindow.c @@ -1,4 +1,5 @@ #include "pcmockup.h" +#include "platform.h" #include #include @@ -6,52 +7,44 @@ struct PebbleWindow { - SDL_Window* window; - SDL_Renderer* renderer; - SDL_Texture* pebbleTexture; + ImageWindow* window; + SDL_Color* textureData; SDL_PixelFormat* texturePixelFormat; SafeFramebuffer* framebuffer; + Renderer* renderer; GSize pebbleSize; }; -PebbleWindow* pebbleWindow_init(SDL_Rect initialBounds, GSize pebbleSize) +void pebbleWindow_onKeyDown(Window* window, SDL_Keysym sym, void* userdata); + +PebbleWindow* pebbleWindow_init(WindowContainer* parent, SDL_Rect initialBounds, GSize pebbleSize, Renderer* renderer) { PebbleWindow* me = (PebbleWindow*)malloc(sizeof(PebbleWindow)); if (me == NULL) return NULL; memset(me, 0, sizeof(PebbleWindow)); - me->window = SDL_CreateWindow("PebbleOfDoom - PCMockup", - initialBounds.x, initialBounds.y, - initialBounds.w, initialBounds.h, - SDL_WINDOW_RESIZABLE); + GRect b = { { initialBounds.x, initialBounds.y }, { initialBounds.w, initialBounds.h } }; + me->window = imageWindow_init(parent, "Pebble screen", b, true); if (me->window == NULL) { - fprintf(stderr, "SDL_CreateWindow: %s\n", SDL_GetError()); - pebbleWindow_free(me); - return NULL; - } - - me->renderer = SDL_CreateRenderer(me->window, -1, 0); - if (me->renderer == NULL) - { - fprintf(stderr, "SDL_CreateRenderer: %s\n", SDL_GetError()); pebbleWindow_free(me); return NULL; } + window_setKeyCallbacks(imageWindow_asWindow(me->window), (WindowKeyCallbacks) { + .down = pebbleWindow_onKeyDown, + .userdata = me + }); - me->pebbleTexture = SDL_CreateTexture(me->renderer, - SDL_PIXELFORMAT_ARGB8888, - SDL_TEXTUREACCESS_STREAMING, - pebbleSize.w, pebbleSize.h); - if (me->pebbleTexture == NULL) + me->textureData = (SDL_Color*)malloc(sizeof(SDL_Color) * pebbleSize.w * pebbleSize.h); + if (me->textureData == NULL) { - fprintf(stderr, "SDL_CreateTexture: %s\n", SDL_GetError()); + fprintf(stderr, "Could not allocate pebble texture!\n"); pebbleWindow_free(me); return NULL; } - me->texturePixelFormat = SDL_AllocFormat(SDL_PIXELFORMAT_ARGB8888); + me->texturePixelFormat = SDL_AllocFormat(imageWindow_SDLPixelFormat); if (me->texturePixelFormat == NULL) { fprintf(stderr, "SDL_AllocFormat: %s\n", SDL_GetError()); @@ -67,7 +60,7 @@ PebbleWindow* pebbleWindow_init(SDL_Rect initialBounds, GSize pebbleSize) } me->pebbleSize = pebbleSize; - + me->renderer = renderer; return me; } @@ -75,27 +68,17 @@ void pebbleWindow_free(PebbleWindow* me) { if (me == NULL) return; + if (me->window != NULL) + imageWindow_free(me->window); + if (me->textureData != NULL) + free(me->textureData); if (me->framebuffer != NULL) safeFramebuffer_free(me->framebuffer); if (me->texturePixelFormat != NULL) SDL_FreeFormat(me->texturePixelFormat); - if (me->pebbleTexture != NULL) - SDL_DestroyTexture(me->pebbleTexture); - if (me->renderer != NULL) - SDL_DestroyRenderer(me->renderer); - if (me->window != NULL) - SDL_DestroyWindow(me->window); free(me); } -static SDL_Rect prv_pebbleWindow_fitPebbleScreen(const PebbleWindow* me) -{ - SDL_Rect src; - SDL_GetWindowSize(me->window, &src.w, &src.h); - const float pebbleAspect = (float)me->pebbleSize.w / me->pebbleSize.h; - return findBestFit(src, pebbleAspect); -} - static inline SDL_Color prv_convertGColorTo32Bit(GColor pebbleColor) { SDL_Color color; @@ -109,9 +92,7 @@ static inline SDL_Color prv_convertGColorTo32Bit(GColor pebbleColor) static void prv_pebbleWindow_convertPebbleToTexture(PebbleWindow* me) { const GColor* pebblePixels = pebbleWindow_getPebbleFramebuffer(me); - char* texPixels; - int texPitch; - SDL_LockTexture(me->pebbleTexture, NULL, (void**)&texPixels, &texPitch); + SDL_Color* texPixels = me->textureData; uint32_t* itTexPixel; const GColor* itPebblePixel; @@ -129,10 +110,8 @@ static void prv_pebbleWindow_convertPebbleToTexture(PebbleWindow* me) } // Advance to next line - texPixels += texPitch; + texPixels += me->pebbleSize.w; } - - SDL_UnlockTexture(me->pebbleTexture); } void pebbleWindow_startUpdate(PebbleWindow* me) @@ -144,23 +123,41 @@ void pebbleWindow_endUpdate(PebbleWindow* me) { safeFramebuffer_check(me->framebuffer); prv_pebbleWindow_convertPebbleToTexture(me); - - SDL_SetRenderDrawColor(me->renderer, 255, 0, 255, 255); - SDL_RenderClear(me->renderer); - const SDL_Rect dst = prv_pebbleWindow_fitPebbleScreen(me); - SDL_RenderCopy(me->renderer, me->pebbleTexture, NULL, &dst); - SDL_RenderPresent(me->renderer); + imageWindow_setImageData(me->window, me->pebbleSize, me->textureData); } -SDL_Rect pebbleWindow_getBounds(PebbleWindow* me) +void pebbleWindow_onKeyDown(Window* window, SDL_Keysym sym, void* userdata) { - SDL_Rect bounds; - SDL_GetWindowPosition(me->window, &bounds.x, &bounds.y); - SDL_GetWindowSize(me->window, &bounds.w, &bounds.h); - return bounds; + UNUSED(window); + PebbleWindow* me = (PebbleWindow*)userdata; + switch(sym.sym) + { + case (SDLK_w): renderer_moveForward(me->renderer); break; + case (SDLK_s): renderer_moveBackwards(me->renderer); break; + case (SDLK_a): renderer_moveLeft(me->renderer); break; + case (SDLK_d): renderer_moveRight(me->renderer); break; + case (SDLK_UP): renderer_moveUp(me->renderer); break; + case (SDLK_DOWN): renderer_moveDown(me->renderer); break; + case (SDLK_LEFT): renderer_rotateLeft(me->renderer); break; + case (SDLK_RIGHT): renderer_rotateRight(me->renderer); break; + case(SDLK_SPACE): + { + Location playerLocation; + playerLocation.angle = real_degToRad(real_from_int(0)); + playerLocation.height = real_zero; + playerLocation.position = xz(real_from_int(20), real_from_int(20)); + + renderer_moveTo(me->renderer, playerLocation); + }break; + } } GColor* pebbleWindow_getPebbleFramebuffer(PebbleWindow* window) { return safeFramebuffer_getScreenBuffer(window->framebuffer); } + +ImageWindow* pebbleWindow_asImageWindow(PebbleWindow* me) +{ + return me->window; +} diff --git a/pcmockup/window.c b/pcmockup/window.c new file mode 100644 index 0000000..74c345a --- /dev/null +++ b/pcmockup/window.c @@ -0,0 +1,169 @@ +#define _CRT_NONSTDC_NO_DEPRECATE +#include "window_internal.h" + +#define MOUSE_BUTTON_COUNT 5 // even imgui uses this "magic" number +#define DEFAULT_MOUSE_THRESHOLD -1.0f + +struct Window +{ + char* title; + ImVec2 currentPos, currentSize; + ImVec2 initialPos, initialSize; + bool isFocused; + ImGuiWindowFlags flags; + WindowOpenState openState; + ImVec2 lastDragDelta[MOUSE_BUTTON_COUNT]; + + void* onDragUserdata; + WindowUpdateCallbacks onUpdate; + WindowDragCallback onDrag; + WindowKeyCallbacks onKey; +}; + +Window* window_init() +{ + Window* me = (Window*)malloc(sizeof(Window)); + if (me == NULL) + return NULL; + memset(me, 0, sizeof(Window)); + + me->initialPos = me->currentPos = (ImVec2) { -1, -1 }; + me->initialSize = me->currentSize = (ImVec2) { -1, -1 }; + window_setTitle(me, ""); + return me; +} + +void window_free(Window* me) +{ + if (me == NULL) + return; + if (me->title != NULL) + free(me->title); + free(me); +} + +void window_update(Window* me) +{ + if (me->openState == WindowOpenState_Closed) + return; + if (me->onUpdate.before != NULL) + me->onUpdate.before(me, me->onUpdate.userdata); + + bool isOpen = (me->openState == WindowOpenState_Open); + bool* isOpenPtr = me->openState == WindowOpenState_Unclosable ? NULL : &isOpen; + if (me->initialPos.x >= 0 && me->initialPos.y >= 0) + igSetNextWindowPos(me->initialPos, ImGuiCond_Once, (ImVec2) { 0, 0 }); + if (me->initialSize.x > 0 && me->initialSize.y > 0) + igSetNextWindowSize(me->initialSize, ImGuiCond_Once); + igBegin(me->title, isOpenPtr, me->flags); + igGetCursorScreenPos_nonUDT(&me->currentPos); // handles toolbar height + igGetWindowSize_nonUDT(&me->currentSize); + me->isFocused = igIsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); + + if (me->onUpdate.content != NULL) + me->onUpdate.content(me, me->onUpdate.userdata); + + igEnd(); + if (isOpenPtr != NULL) + me->openState = isOpen ? WindowOpenState_Open : WindowOpenState_Closed; + + if (me->onUpdate.after != NULL) + me->onUpdate.after(me, me->onUpdate.userdata); +} + +static bool window_shouldHandleDrag(const Window* me, int button) +{ + if (!igIsMouseDragging(button, DEFAULT_MOUSE_THRESHOLD) || me->onDrag == NULL) + return false; + const ImVec2 dragSource = igGetIO()->MouseClickedPos[button]; + return + dragSource.x >= me->currentPos.x && dragSource.x < me->currentPos.x + me->currentSize.x && + dragSource.y >= me->currentPos.y && dragSource.y < me->currentPos.y + me->currentSize.y; +} + +void window_handleDragEvent(Window* me) +{ + for (int button = 0; button < MOUSE_BUTTON_COUNT; button++) + { + if (!window_shouldHandleDrag(me, button)) + { + me->lastDragDelta[button] = (ImVec2) { 0, 0 }; + continue; + } + ImVec2 lastDelta = me->lastDragDelta[button], delta; + igGetMouseDragDelta_nonUDT(&delta, button, DEFAULT_MOUSE_THRESHOLD); + + ImVec2 deltaDelta = { delta.x - lastDelta.x, delta.y - lastDelta.y }; + me->onDrag(me, button, deltaDelta, me->onDragUserdata); + me->lastDragDelta[button] = delta; + } +} + +void window_handleKeyEvent(Window* me, SDL_Keysym sym, bool isDown) +{ + if (isDown && me->onKey.down != NULL) + me->onKey.down(me, sym, me->onKey.userdata); + if (!isDown && me->onKey.up != NULL) + me->onKey.up(me, sym, me->onKey.userdata); +} + +GRect window_getBounds(const Window* me) +{ + return (GRect) { + (GPoint) { (int)me->currentPos.x, (int)me->currentPos.y }, + (GSize) { (int)me->currentSize.x, (int)me->currentSize.y } + }; +} + +WindowOpenState window_getOpenState(const Window* me) +{ + return me->openState; +} + +bool window_isFocused(const Window* me) +{ + return me->isFocused; +} + +void window_setTitle(Window* me, const char* title) +{ + if (me->title != NULL) + free(me->title); + me->title = strdup(title); +} + +void window_setFlags(Window* me, ImGuiWindowFlags flags) +{ + me->flags = flags; +} + +void window_setOpenState(Window* me, WindowOpenState state) +{ + me->openState = state; +} + +void window_setInitialBounds(Window* me, GRect bounds) +{ + me->initialPos = (ImVec2) { (float)bounds.origin.x, (float)bounds.origin.y }; + me->initialSize = (ImVec2) { (float)bounds.size.w, (float)bounds.size.h }; + if (me->initialPos.x < 0 && me->initialPos.y < 0) + me->currentPos = me->initialPos; + if (me->initialSize.x < 0 && me->initialSize.y < 0) + me->currentSize = me->initialSize; +} + +void window_setUpdateCallbacks(Window* me, WindowUpdateCallbacks callbacks) +{ + me->onUpdate = callbacks; +} + +void window_setDragCallback(Window* me, WindowDragCallback callback, void* userdata) +{ + me->onDrag = callback; + me->onDragUserdata = userdata; +} + +void window_setKeyCallbacks(Window* me, WindowKeyCallbacks callbacks) +{ + me->onKey = callbacks; +} diff --git a/pcmockup/window.h b/pcmockup/window.h new file mode 100644 index 0000000..19af4fb --- /dev/null +++ b/pcmockup/window.h @@ -0,0 +1,58 @@ +#ifndef WINDOW_H +#define WINDOW_H +#include +#include +#include +#include "cimgui.include.h" +#include "sdl.include.h" + +typedef struct Window Window; +typedef void (*WindowUpdateCallback)(Window* window, void* userdata); +typedef struct WindowUpdateCallbacks +{ + WindowUpdateCallback + before, + content, + after; + void* userdata; +} WindowUpdateCallbacks; +typedef void (*WindowDragCallback)(Window* window, int mouseKey, ImVec2 delta, void* userdata); +typedef void (*WindowKeyCallback)(Window* window, SDL_Keysym sym, void* userdata); +typedef struct WindowKeyCallbacks +{ + WindowKeyCallback + down, + up; + void* userdata; +} WindowKeyCallbacks; + +typedef enum WindowOpenState +{ + WindowOpenState_Open, + WindowOpenState_Closed, + WindowOpenState_Unclosable +} WindowOpenState; + +Uint32 getWindowIDByEvent(const SDL_Event* ev); + +GRect window_getBounds(const Window* window); +bool window_isFocused(const Window* window); +WindowOpenState window_getOpenState(const Window* window); +void window_setTitle(Window* window, const char* title); +void window_setFlags(Window* window, ImGuiWindowFlags flags); +void window_setOpenState(Window* window, WindowOpenState state); +void window_setInitialBounds(Window* window, GRect bounds); +void window_setUpdateCallbacks(Window* window, WindowUpdateCallbacks callbacks); +void window_setDragCallback(Window* window, WindowDragCallback callback, void* userdata); +void window_setKeyCallbacks(Window* window, WindowKeyCallbacks callbacks); + +typedef struct WindowContainer WindowContainer; +WindowContainer* windowContainer_init(GSize windowSize); +void windowContainer_free(WindowContainer* me); +void windowContainer_startUpdate(WindowContainer* me); +void windowContainer_endUpdate(WindowContainer* me); +Window* windowContainer_newWindow(WindowContainer* me, const char* title); +Window* windowContainer_getFocusedWindow(WindowContainer* me); +void windowContainer_handleEvent(WindowContainer* me, const SDL_Event* ev); + +#endif diff --git a/pcmockup/window_internal.h b/pcmockup/window_internal.h new file mode 100644 index 0000000..fd11c46 --- /dev/null +++ b/pcmockup/window_internal.h @@ -0,0 +1,11 @@ +#ifndef WINDOW_INTERNAL_H +#define WINDOW_INTERNAL_H +#include "window.h" + +Window* window_init(); +void window_free(Window* window); +void window_update(Window* window); +void window_handleDragEvent(Window* window); +void window_handleKeyEvent(Window* window, SDL_Keysym sym, bool isDown); + +#endif diff --git a/pcmockup/windowcontainer.c b/pcmockup/windowcontainer.c new file mode 100644 index 0000000..21013e8 --- /dev/null +++ b/pcmockup/windowcontainer.c @@ -0,0 +1,185 @@ +#include "window_internal.h" +#include +#include +#include "cimgui.include.h" +#include "sdl.include.h" + +#define IMGUI_IMPL_API +#include + +// glsl_version is default-defined in the header file, so we have to +// declare them ourself without this default value +extern int ImGui_ImplOpenGL3_Init(const char* glsl_version); +extern void ImGui_ImplOpenGL3_Shutdown(); +extern void ImGui_ImplOpenGL3_NewFrame(); +extern void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); + +#define WINDOW_CONTAINER_CHUNK 16 + +struct WindowContainer +{ + SDL_Window* window; + SDL_GLContext glContext; + ImGuiContext* imGuiContext; + Window** windows; + Window* focusedWindow; + int windowCount, windowCapacity; +}; + +WindowContainer* windowContainer_init(GSize windowSize) +{ + WindowContainer* me = (WindowContainer*)malloc(sizeof(WindowContainer)); + if (me == NULL) + return NULL; + memset(me, 0, sizeof(WindowContainer)); + + me->window = SDL_CreateWindow("PebbleOfDoom", + SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + windowSize.w, windowSize.h, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL); + if (me->window == NULL) + { + fprintf(stderr, "SDL_CreateWindow: %s\n", SDL_GetError()); + windowContainer_free(me); + return NULL; + } + + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); + SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetSwapInterval(0); + me->glContext = SDL_GL_CreateContext(me->window); + if (me->glContext == NULL) + { + fprintf(stderr, "SDL_GL_CreateContext: %s\n", SDL_GetError()); + windowContainer_free(me); + return NULL; + } + + SDL_GL_MakeCurrent(me->window, me->glContext); + if (!gladLoadGLLoader(SDL_GL_GetProcAddress)) + { + fprintf(stderr, "gladLoadGLLoader: %s\n", SDL_GetError()); + windowContainer_free(me); + return NULL; + } + + me->imGuiContext = igCreateContext(NULL); + if (me->imGuiContext == NULL) + { + fprintf(stderr, "igCreateContext: failure\n"); + windowContainer_free(me); + return NULL; + } + ImGuiIO* io = igGetIO(); + io->IniFilename = NULL; + + if (!ImGui_ImplOpenGL3_Init(NULL)) + { + fprintf(stderr, "ImGui_ImplOpenGL3_Init: failure\n"); + windowContainer_free(me); + return NULL; + } + + if (!ImGui_ImplSDL2_InitForOpenGL(me->window, me->glContext)) + { + fprintf(stderr, "ImGui_ImplSDL2_InitForOpenGL: failure\n"); + windowContainer_free(me); + return NULL; + } + + glClearColor(0.45f, 0.55f, 0.60f, 1.00f); + return me; +} + +void windowContainer_free(WindowContainer* me) +{ + if (me == NULL) + return; + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplSDL2_Shutdown(); + if (me->imGuiContext != NULL) + igDestroyContext(me->imGuiContext); + if (me->glContext) + SDL_GL_DeleteContext(me->glContext); + if (me->window) + SDL_DestroyWindow(me->window); + if (me->windows != NULL) + { + for (int i = 0; i < me->windowCount; i++) + window_free(me->windows[i]); + free(me->windows); + } + free(me); +} + +void windowContainer_startUpdate(WindowContainer* me) +{ + bool open = true; + SDL_GL_MakeCurrent(me->window, me->glContext); + igSetCurrentContext(me->imGuiContext); + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplSDL2_NewFrame(me->window); + igNewFrame(); + igShowDemoWindow(&open); + + me->focusedWindow = NULL; + for (int i = 0; i < me->windowCount; i++) + { + window_update(me->windows[i]); + if (window_isFocused(me->windows[i])) + me->focusedWindow = me->windows[i]; + } +} + +void windowContainer_endUpdate(WindowContainer* me) +{ + SDL_GL_MakeCurrent(me->window, me->glContext); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + igRender(); + ImGui_ImplOpenGL3_RenderDrawData(igGetDrawData()); + glFlush(); + SDL_GL_SwapWindow(me->window); +} + +Window* windowContainer_newWindow(WindowContainer* me, const char* title) +{ + if (me->windowCount == me->windowCapacity) + { + me->windowCapacity += WINDOW_CONTAINER_CHUNK; + me->windows = (Window**)realloc(me->windows, sizeof(Window*) * me->windowCapacity); + assert(me->windows != NULL); + } + Window* newWindow = window_init(); + window_setTitle(newWindow, title); + me->windows[me->windowCount++] = newWindow; + return newWindow; +} + +Window* windowContainer_getFocusedWindow(WindowContainer* me) +{ + return me->focusedWindow; +} + +void windowContainer_handleEvent(WindowContainer* me, const SDL_Event* ev) +{ + // the ImGui SDL2 implementation does not filter the events by window ID + Uint32 windowID = SDL_GetWindowID(me->window); + if (getWindowIDByEvent(ev) != windowID) + return; + ImGui_ImplSDL2_ProcessEvent((SDL_Event*)ev); + // TODO: Remove const-removing-cast as soon as (https://github.com/ocornut/imgui/issues/2187) is resolved! + + if (me->focusedWindow == NULL) + return; + const ImGuiIO* io = igGetIO(); + if ((ev->type == SDL_KEYDOWN || ev->type == SDL_KEYUP) && !io->WantCaptureKeyboard) + window_handleKeyEvent(me->focusedWindow, ev->key.keysym, (ev->type == SDL_KEYDOWN)); + window_handleDragEvent(me->focusedWindow); +} diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt index e7e9c93..671f5cf 100644 --- a/renderer/CMakeLists.txt +++ b/renderer/CMakeLists.txt @@ -33,4 +33,7 @@ target_compile_definitions(podrenderer PUBLIC REAL_USE_FLOAT PUBLIC DEBUG_WINDOWS ) +target_link_libraries(podrenderer + PUBLIC cimgui +) enable_warnings(podrenderer)