From 44d58037b70df1f564ee5717e681e7f1ffa54502 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Sun, 21 Jul 2019 15:45:50 +0100 Subject: [PATCH] t/gui: Add SDL2 based GUI target --- CMakeLists.txt | 14 ++ src/xrt/targets/CMakeLists.txt | 4 + src/xrt/targets/gui/CMakeLists.txt | 62 +++++++++ src/xrt/targets/gui/gui_common.h | 91 +++++++++++++ src/xrt/targets/gui/gui_main.c | 30 +++++ src/xrt/targets/gui/gui_sdl2.c | 202 +++++++++++++++++++++++++++++ 6 files changed, 403 insertions(+) create mode 100644 src/xrt/targets/gui/CMakeLists.txt create mode 100644 src/xrt/targets/gui/gui_common.h create mode 100644 src/xrt/targets/gui/gui_main.c create mode 100644 src/xrt/targets/gui/gui_sdl2.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b641cbee..d941a4194 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ find_package(OpenHMD) find_package(OpenCV COMPONENTS core calib3d highgui imgproc imgcodecs features2d video) find_package(Libusb1) find_package(JPEG) +find_package(SDL2) # @TODO Turn into a find_package LIBUVC file. pkg_check_modules(LIBUVC libuvc) @@ -59,6 +60,7 @@ cmake_dependent_option(BUILD_WITH_LIBUVC "Enable libuvc video driver" ON "LIBUVC cmake_dependent_option(BUILD_WITH_FFMPEG "Enable ffmpeg testing video driver" ON "FFMPEG_FOUND" OFF) cmake_dependent_option(BUILD_WITH_HIDAPI "Enable HIDAPI-based driver(s) (OSVR HDK, PSVR)" ON "HIDAPI_FOUND" OFF) cmake_dependent_option(BUILD_WITH_OPENHMD "Enable OpenHMD driver" ON "OPENHMD_FOUND" OFF) +cmake_dependent_option(BUILD_WITH_SDL2 "Enable SDL2 based test application" ON "SDL2_FOUND" OFF) ### @@ -101,6 +103,18 @@ if(BUILD_WITH_FFMPEG) add_definitions(-DXRT_HAVE_FFMPEG) endif() +if(BUILD_WITH_SDL2) + add_definitions(-DXRT_HAVE_SDL2) + + # Arch work around + if(NOT DEFINED SDL2_LIBRARIES) + set(SDL2_LIBRARIES SDL2::SDL2) + endif() + + # SDL2 based gui + set(BUILD_TARGET_GUI TRUE) +endif() + if(BUILD_WITH_OPENHMD) add_definitions(-DXRT_HAVE_OPENHMD) diff --git a/src/xrt/targets/CMakeLists.txt b/src/xrt/targets/CMakeLists.txt index 4add14db1..a64eca8d7 100644 --- a/src/xrt/targets/CMakeLists.txt +++ b/src/xrt/targets/CMakeLists.txt @@ -56,3 +56,7 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_subdirectory(common) add_subdirectory(openxr) add_subdirectory(cli) + +if(BUILD_TARGET_GUI) + add_subdirectory(gui) +endif() diff --git a/src/xrt/targets/gui/CMakeLists.txt b/src/xrt/targets/gui/CMakeLists.txt new file mode 100644 index 000000000..06b61b411 --- /dev/null +++ b/src/xrt/targets/gui/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright 2019, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 + +###### +# Create a small SDL2 based GUI for Monado. + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR}/../../targets/common + ${CMAKE_CURRENT_SOURCE_DIR}/../../auxiliary + ${CMAKE_CURRENT_SOURCE_DIR}/../../include + ${CMAKE_CURRENT_SOURCE_DIR}/../../drivers + ${CMAKE_CURRENT_SOURCE_DIR}/../../../external + ${SDL2_INCLUDE_DIRS} + ) + +set(SOURCE_FILES + gui_common.h + gui_main.c + gui_sdl2.c + ../../../external/glad/gl.h + ../../../external/glad/gl.c + ) + +add_executable(gui + ${SOURCE_FILES} + $ + $ + $ + $ + $ + ) + +set_target_properties(gui PROPERTIES + OUTPUT_NAME monado-gui + PREFIX "" + ) + +target_link_libraries(gui PRIVATE + ${LIBUSB_LIBRARIES} + ${LIBUVC_LIBRARIES} + ${UDEV_LIBRARIES} + ${SDL2_LIBRARIES} + ) + +if(BUILD_TRACKING) + target_link_libraries(gui PRIVATE + $ + ${OpenCV_LIBRARIES} + ) +endif() + +if(DRIVER_OBJECTS) + target_sources(gui PRIVATE ${DRIVER_OBJECTS}) +endif() + +if(BUILD_WITH_JPEG) + target_link_libraries(gui PRIVATE ${JPEG_LIBRARIES}) +endif() + +if(DRIVER_LIBRARIES) + target_link_libraries(gui PRIVATE ${DRIVER_LIBRARIES}) +endif() diff --git a/src/xrt/targets/gui/gui_common.h b/src/xrt/targets/gui/gui_common.h new file mode 100644 index 000000000..bdd752fa5 --- /dev/null +++ b/src/xrt/targets/gui/gui_common.h @@ -0,0 +1,91 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Common file for the Monado GUI program. + * @author Jakob Bornecrantz + * @ingroup gui + */ + +#pragma once + +#include "xrt/xrt_defines.h" +#include + +/*! + * @defgroup gui GUI Config Interface + * @ingroup xrt + * + * @brief Small GUI interface to configure Monado based on SDL2. + */ + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! + * Common struct holding state for the GUI interface. + * + * @ingroup gui + */ +struct program +{ + SDL_Window *win; + SDL_GLContext ctx; + + bool stopped; + bool initialized; + + struct + { + SDL_Surface *sf; + uint8_t *buffer; + size_t stride; + uint32_t width; + uint32_t height; + bool own_buffer; + } blit; +}; + +/*! + * Init SDL2, create and show a window and bring up any other structs needed. + * + * @ingroup gui + */ +int +gui_sdl2_init(struct program *p); + +/*! + * Loop until quit signal has been received. + * + * @ingroup gui + */ +void +gui_sdl2_loop(struct program *p); + +/*! + * Display a 24bit RGB image on the screen. + * + * @ingroup gui + */ +void +gui_sdl2_display_R8G8B8(struct program *p, + bool resize, + uint32_t width, + uint32_t height, + size_t stride, + void *data); + +/*! + * Destroy all SDL things and quit SDL. + * + * @ingroup gui + */ +void +gui_sdl2_quit(struct program *p); + + +#ifdef __cplusplus +} +#endif diff --git a/src/xrt/targets/gui/gui_main.c b/src/xrt/targets/gui/gui_main.c new file mode 100644 index 000000000..31b694a51 --- /dev/null +++ b/src/xrt/targets/gui/gui_main.c @@ -0,0 +1,30 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Main entrypoint for the Monado GUI program. + * @author Jakob Bornecrantz + * @ingroup gui + */ + +#include "gui_common.h" + + +int +main(int argc, char **argv) +{ + struct program p = {0}; + int ret; + + ret = gui_sdl2_init(&p); + if (ret != 0) { + gui_sdl2_quit(&p); + return ret; + } + + gui_sdl2_loop(&p); + + gui_sdl2_quit(&p); + + return 0; +} diff --git a/src/xrt/targets/gui/gui_sdl2.c b/src/xrt/targets/gui/gui_sdl2.c new file mode 100644 index 000000000..24e0b0826 --- /dev/null +++ b/src/xrt/targets/gui/gui_sdl2.c @@ -0,0 +1,202 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief SDL2 functions to drive the GUI. + * @author Jakob Bornecrantz + * @ingroup gui + */ + +#include "gui_common.h" +#include "glad/gl.h" + + +/* + * + * Internal static functions + * + */ + +XRT_MAYBE_UNUSED static void +create_blit_info(struct program *p, uint32_t width, uint32_t height) +{ + if (p->blit.sf != NULL) { + SDL_FreeSurface(p->blit.sf); + p->blit.sf = NULL; + + if (p->blit.own_buffer) { + free(p->blit.buffer); + } + p->blit.buffer = NULL; + } + + size_t stride = width * 4; + size_t size = stride * height; + p->blit.width = width; + p->blit.height = height; + p->blit.stride = stride; + p->blit.own_buffer = true; + + p->blit.buffer = malloc(size); + p->blit.sf = + SDL_CreateRGBSurfaceFrom(p->blit.buffer, width, height, 32, stride, + 0x0000FF, 0x00FF00, 0xFF0000, 0); +} + +static void +sdl2_handle_keydown(struct program *p, const SDL_Event *e) +{ + switch (e->key.keysym.sym) { + case SDLK_ESCAPE: p->stopped = true; break; + default: break; + } + + return; +} + + +/* + * + * "Exported" functions. + * + */ + +void +gui_sdl2_display_R8G8B8(struct program *p, + bool resize, + uint32_t width, + uint32_t height, + size_t stride, + void *data) +{ + if (p->blit.buffer != data || p->blit.width != width || + p->blit.height != height || p->blit.sf == NULL) { + + if (resize) { + SDL_SetWindowSize(p->win, width, height); + } + + p->blit.sf = + SDL_CreateRGBSurfaceFrom(data, width, height, 24, stride, + 0x0000FF, 0x00FF00, 0xFF0000, 0); + + p->blit.width = width; + p->blit.height = height; + p->blit.stride = stride; + p->blit.buffer = data; + p->blit.own_buffer = false; + } + + SDL_Surface *win_sf = SDL_GetWindowSurface(p->win); + + if (SDL_BlitSurface(p->blit.sf, NULL, win_sf, NULL) == 0) { + SDL_UpdateWindowSurface(p->win); + } +} + +void +gui_sdl2_loop(struct program *p) +{ + SDL_Event event; + while (!p->stopped) { + if (SDL_WaitEvent(&event) == 0) { + return; + } + + if (event.type == SDL_QUIT) { + return; + } + + if (event.type == SDL_KEYDOWN) { + sdl2_handle_keydown(p, &event); + } + } + + return; +} + +int +gui_sdl2_init(struct program *p) +{ + if (SDL_Init(SDL_INIT_EVERYTHING) < 0) { + return -1; + } + + const char *title = "Monado! ☺"; + int x = SDL_WINDOWPOS_UNDEFINED; + int y = SDL_WINDOWPOS_UNDEFINED; + int w = 1920; + int h = 1080; + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_CONTEXT_PROFILE_CORE); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + + SDL_WindowFlags window_flags = 0; + window_flags |= SDL_WINDOW_SHOWN; + window_flags |= SDL_WINDOW_OPENGL; + window_flags |= SDL_WINDOW_RESIZABLE; + window_flags |= SDL_WINDOW_ALLOW_HIGHDPI; +#if 0 + window_flags |= SDL_WINDOW_MAXIMIZED; +#endif + + p->initialized = true; + p->win = SDL_CreateWindow(title, x, y, w, h, window_flags); + + if (p->win == NULL) { + return -1; + } + + p->ctx = SDL_GL_CreateContext(p->win); + if (p->ctx == NULL) { + return -1; + } + + SDL_GL_MakeCurrent(p->win, p->ctx); + SDL_GL_SetSwapInterval(1); // Enable vsync + + // Setup OpenGL bindings. + bool err = gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress) == 0; + if (err) { + return -1; + } + + return 0; +} + +void +gui_sdl2_quit(struct program *p) +{ + if (!p->initialized) { + return; + } + + if (p->blit.sf != NULL) { + SDL_FreeSurface(p->blit.sf); + p->blit.sf = NULL; + + if (p->blit.own_buffer) { + free(p->blit.buffer); + } + p->blit.buffer = NULL; + } + + if (p->ctx != NULL) { + SDL_GL_DeleteContext(p->ctx); + p->ctx = NULL; + } + + if (p->win != NULL) { + SDL_DestroyWindow(p->win); + p->win = NULL; + } + + SDL_Quit(); + p->initialized = false; +}