diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt index 4318eb64b7f405717aa5e6fe7ca2b106fd6f99e9..32ea0d207018eba109d841eef8a26b784ac87f01 100644 --- a/demo/CMakeLists.txt +++ b/demo/CMakeLists.txt @@ -14,6 +14,9 @@ target_include_directories(demo ${CMAKE_CURRENT_SOURCE_DIR}/include ) +find_package(OpenGL) +target_link_libraries(demo OpenGL::EGL) + target_link_libraries(demo fggl) #target_include_directories(FgglDemo PUBLIC ${PROJECT_BINARY_DIR}) diff --git a/demo/demo/main.cpp b/demo/demo/main.cpp index 865cd9e1faa4468826c8212fefb37cb9e2d52194..ff68b31fdd6b51fe0b564846dda4b6c1df5c78b1 100644 --- a/demo/demo/main.cpp +++ b/demo/demo/main.cpp @@ -30,6 +30,8 @@ #include "fggl/gfx/compat.hpp" #include "fggl/gfx/ogl/compat.hpp" +#include "fggl/platform/platform.hpp" + #include "fggl/data/storage.hpp" #include "fggl/util/service.h" @@ -83,6 +85,9 @@ static void setup_service_locators(fggl::util::ServiceLocator& locator) { int main(int argc, const char* argv[]) { + fggl::platform::wayland::initWayland(); + fggl::platform::egl::setup(); + auto& locator = fggl::util::ServiceLocator::instance(); setup_service_locators(locator); @@ -93,7 +98,7 @@ int main(int argc, const char* argv[]) { auto& windowing = app.use<fggl::gfx::ecsGlfwModule>(locator.get<fggl::input::Input>()); // -- should not be our problem - this is a broken api - auto window = windowing.createWindow("Demo Game"); + /*auto window = windowing.createWindow("Demo Game"); window->make_graphics<fggl::gfx::OpenGL4>(); window->fullscreen( true ); app.setWindow( std::move(window) ); @@ -129,5 +134,7 @@ int main(int argc, const char* argv[]) { app.add_state<GameScene>("game"); app.add_state<demo::RollBall>("rollball"); - return app.run(argc, argv); + return app.run(argc, argv); */ + + return 0; } diff --git a/fggl/CMakeLists.txt b/fggl/CMakeLists.txt index 7305b5b474370d83bb0b468c004e0c758b9c75b0..462f4a0a9cf761e8ef6fea5059ca932bfd08e081 100644 --- a/fggl/CMakeLists.txt +++ b/fggl/CMakeLists.txt @@ -71,6 +71,8 @@ target_link_libraries(${PROJECT_NAME} PUBLIC freetype) add_subdirectory(gfx) add_subdirectory(audio) +add_subdirectory(platform/wayland) + # physics integration add_subdirectory(phys/bullet) diff --git a/fggl/platform/wayland/CMakeLists.txt b/fggl/platform/wayland/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..a6434be0736bd8e7c11a880e5b2816760761b6d3 --- /dev/null +++ b/fggl/platform/wayland/CMakeLists.txt @@ -0,0 +1,55 @@ + +find_package(wayland-client++) + +# include wayland headers +include(FindPkgConfig) +if(PKG_CONFIG_FOUND) + pkg_check_modules (WAYLAND_CLIENT REQUIRED wayland-client) + + pkg_check_modules (WAYLAND_PROTOCOLS REQUIRED wayland-protocols) + if(WAYLAND_PROTOCOLS_FOUND) + pkg_get_variable(WAYLAND_PROTOCOLS_DIR wayland-protocols pkgdatadir) + endif() + + pkg_check_modules (WAYLAND_SCANNER_TOOL REQUIRED wayland-scanner) + if(WAYLAND_SCANNER_TOOL_FOUND) + pkg_get_variable(WAYLAND_SCANNER wayland-scanner wayland_scanner) + endif() +endif() + +# generate protocol files +set(PROTOCOLS + ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml +) + +foreach(XML ${PROTOCOLS}) + get_filename_component(BASENAME ${XML} NAME_WE) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wayland-${BASENAME}-protocol.c + COMMAND wayland-scanner private-code ${XML} ${CMAKE_CURRENT_BINARY_DIR}/wayland-${BASENAME}-protocol.c + ) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wayland-${BASENAME}-protocol.h + COMMAND wayland-scanner client-header ${XML} ${CMAKE_CURRENT_BINARY_DIR}/wayland-${BASENAME}-protocol.h + ) + list(APPEND PROTOCOL_SRC + ${CMAKE_CURRENT_BINARY_DIR}/wayland-${BASENAME}-protocol.c + ) + list(APPEND PROTOCOL_HEADERS + ${CMAKE_CURRENT_BINARY_DIR}/wayland-${BASENAME}-protocol.h + ) +endforeach() + +# create a custom target for the protocol generation +set(WAYLAND_PROTOCOL_SRC ${PROTOCOL_SRC} CACHE INTERNAL "Wayland sources" FORCE) +set(WAYLAND_PROTOCOL_HEADERS ${PROTOCOL_HEADERS} CACHE INTERNAL "Wayland headers" FORCE) +add_custom_target(wayland_protocols DEPENDS ${WAYLAND_PROTOCOL_SRC} ${WAYLAND_PROTOCOL_HEADERS}) + +# let everyone know we need this to build +add_dependencies(fggl wayland_protocols) +target_include_directories(fggl PUBLIC ${CMAKE_BINARY_DIR}/exports) + +target_sources(fggl + PRIVATE + memory.cpp + ) diff --git a/fggl/platform/wayland/memory.cpp b/fggl/platform/wayland/memory.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18aea4167a4590f1e727ff2d79e2991a5ca5672b --- /dev/null +++ b/fggl/platform/wayland/memory.cpp @@ -0,0 +1,57 @@ +/** + * Code snippet derrived from the wayland book. + * the licence for the below snippet is CC0 + */ + +#define _POSIX_C_SOURCE 200112L +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <time.h> +#include <unistd.h> + +static void +randname(char *buf) +{ + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } +} + +static int +create_shm_file(void) +{ + int retries = 100; + do { + char name[] = "/wl_shm-XXXXXX"; + randname(name + sizeof(name) - 7); + --retries; + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + return -1; +} + +int +allocate_shm_file(size_t size) +{ + int fd = create_shm_file(); + if (fd < 0) + return -1; + int ret; + do { + ret = ftruncate(fd, size); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + close(fd); + return -1; + } + return fd; +} diff --git a/include/fggl/platform/egl/window.h b/include/fggl/platform/egl/window.h new file mode 100644 index 0000000000000000000000000000000000000000..2982da091f56d100d71e6d8669adb127ebe8d8e2 --- /dev/null +++ b/include/fggl/platform/egl/window.h @@ -0,0 +1,197 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 02/05/22. +// + +#ifndef FGGL_PLATFORM_EGL_WINDOW_H +#define FGGL_PLATFORM_EGL_WINDOW_H + +#include "fggl/gfx/ogl/common.hpp" +#include "fggl/platform/wayland/wayland.hpp" + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <iostream> +#include <array> +#include <string> + +namespace fggl::platform::egl { + + enum class AttrType { + ENUM, + INTEGER, + BOOLEAN, + BITMASK_GL, + BITMASK_SURF + }; + + struct Attribute { + EGLint val; + const char* name; + AttrType type = AttrType::INTEGER; + }; + + constexpr static std::array<Attribute, 33> attrs {{ + {EGL_BUFFER_SIZE, "buffer size"}, + {EGL_RED_SIZE, "red size"}, + {EGL_GREEN_SIZE, "green size"}, + {EGL_BLUE_SIZE, "blue size"}, + {EGL_LUMINANCE_SIZE, "luminance size"}, + {EGL_ALPHA_SIZE, "alpha size"}, + {EGL_ALPHA_MASK_SIZE, "alpha mask size"}, + {EGL_BIND_TO_TEXTURE_RGB, "Bind To Texture RGB", AttrType::BOOLEAN}, + {EGL_BIND_TO_TEXTURE_RGBA, "Bind To Texture RGBA", AttrType::BOOLEAN}, + {EGL_COLOR_BUFFER_TYPE, "Colour buffer Type", AttrType::ENUM}, + {EGL_CONFIG_CAVEAT, "Config Caveat", AttrType::ENUM}, + {EGL_CONFIG_ID, "Config ID"}, + {EGL_CONFORMANT, "Conformant", AttrType::BITMASK_GL}, + {EGL_DEPTH_SIZE, "Depth size"}, + {EGL_LEVEL, "level"}, + {EGL_MATCH_NATIVE_PIXMAP, "Match negative pixmap"}, + {EGL_MAX_PBUFFER_WIDTH, "Max pbuffer width"}, + {EGL_MAX_PBUFFER_HEIGHT, "Max pbuffer height"}, + {EGL_MAX_PBUFFER_PIXELS, "Max pbuffer pixels"}, + {EGL_MAX_SWAP_INTERVAL, "Max swap interval"}, + {EGL_MIN_SWAP_INTERVAL, "Min swap interval"}, + {EGL_NATIVE_RENDERABLE, "Native renderable", AttrType::BOOLEAN}, + {EGL_NATIVE_VISUAL_ID, "Native visual ID"}, + {EGL_NATIVE_VISUAL_TYPE, "Native visual type"}, + {EGL_RENDERABLE_TYPE, "Renderable type", AttrType::BITMASK_GL}, + {EGL_SAMPLE_BUFFERS, "Sample buffers"}, + {EGL_SAMPLES, "Samples"}, + {EGL_STENCIL_SIZE, "Stencil Size"}, + {EGL_SURFACE_TYPE, "Surface Type", AttrType::BITMASK_SURF}, + {EGL_TRANSPARENT_TYPE, "Transparent type"}, + {EGL_TRANSPARENT_RED_VALUE, "Transparent red"}, + {EGL_TRANSPARENT_BLUE_VALUE, "Transparent blue"}, + {EGL_TRANSPARENT_GREEN_VALUE, "Transparent green"} + }}; + + std::string getEnumValue(EGLint value) { + if ( value == EGL_RGB_BUFFER ) { + return "RGB buffer"; + } else if ( value == EGL_LUMINANCE_BUFFER ) { + return "Luminance buffer"; + } else if ( value == EGL_DONT_CARE ) { + return "Don't care"; + } else if ( value == EGL_NONE ) { + return "None"; + } else if ( value == EGL_SLOW_CONFIG ) { + return "Slow Config"; + } else if ( value == EGL_NON_CONFORMANT_CONFIG ) { + return "Non-Conformant config"; + } else if ( value == EGL_TRANSPARENT_RGB ) { + return "Transparent RGB"; + } else { + return "Unknown value"; + } + } + + std::string getBoolValue(EGLint value) { + std::string valueBool = "unknown"; + if ( value == EGL_TRUE) { + valueBool = "True"; + } else if ( value == EGL_FALSE ) { + valueBool = "False"; + } else if ( value == EGL_DONT_CARE) { + valueBool = "Don't care"; + } + return valueBool; + } + + std::string getBitmaskGL(EGLint value) { + std::string result = ""; + if ((value & EGL_OPENGL_BIT) != 0) { + result += "OpenGL 1.x or 2.x,"; + } + if ((value & EGL_OPENGL_ES_BIT) != 0) { + result += "OpenGL ES 1.x,"; + } + if ( (value & EGL_OPENGL_ES2_BIT) != 0) { + result += "OpenGL ES 2.x,"; + } + if ( (value & EGL_OPENGL_ES3_BIT) != 0) { + result += "OpenGL ES 3.x,"; + } + if ( (value & EGL_OPENVG_BIT) != 0 ) { + result += "OpenVG 1.x"; + } + return result; + } + + void printConfig(EGLDisplay& display, EGLConfig& config) { + std::cerr << "EGL Configuration" << std::endl; + for ( auto& attr : attrs ) { + EGLint value; + eglGetConfigAttrib(display, config, attr.val, &value); + if ( attr.type == AttrType::INTEGER ) { + std::cerr << "\t" << attr.name << ": " << value << std::endl; + } else if ( attr.type == AttrType::BOOLEAN ) { + std::cerr << "\t" << attr.name << ": " << getBoolValue(value) << std::endl; + } else if ( attr.type == AttrType::ENUM ) { + std::cerr << "\t" << attr.name << ": " << getEnumValue(value) << std::endl; + } else if ( attr.type == AttrType::BITMASK_GL ) { + std::cerr << "\t" << attr.name << ": " << getBitmaskGL(value) << std::endl; + } + } + } + + inline void setup() { + auto display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + auto result = eglInitialize(display, nullptr, nullptr); + if ( !result ) { + std::cerr << "something went bang when asking EGL to do it's thing" << std::endl; + return; + } + + // platform stuff + auto* extentions = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + std::cerr << "Supported extentions: " << extentions << std::endl; + + // configs + std::array<EGLConfig, 256> configs; + EGLint returnedSize; + result = eglGetConfigs( display, configs.data(), configs.size(), &returnedSize); + if ( !result ) { + std::cerr << "something went bang when asking EGL about configs" << std::endl; + return; + } + + std::cerr << "EGL reported " << returnedSize << " possible configurations" << std::endl; + for (int i = 0; i < returnedSize; i++) { + auto& config = configs[i]; + printConfig(display, config); + } + + // create an EGL context + //auto context = eglCreateContext(display, configs[0], EGL_NO_CONTEXT, nullptr); + + // create a surface + //auto surface = eglCreatePlatformWindowSurface(display, configs[0], windowPtr, NULL); + + } + + +} // namespace fggl::platform::egl + +#endif //FGGL_PLATFORM_EGL_WINDOW_H diff --git a/include/fggl/platform/platform.hpp b/include/fggl/platform/platform.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cec218cae2a972c75fb2e01766b72d17db8eda79 --- /dev/null +++ b/include/fggl/platform/platform.hpp @@ -0,0 +1,32 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 02/05/22. +// + +#ifndef FGGL_PLATFORM_PLATFORM_HPP +#define FGGL_PLATFORM_PLATFORM_HPP + +#include "fggl/platform/wayland/client.hpp" +#include "fggl/platform/wayland/wayland.hpp" +#include "fggl/platform/egl/window.h" + +#endif //FGGL_PLATFORM_PLATFORM_HPP diff --git a/include/fggl/platform/wayland/client.hpp b/include/fggl/platform/wayland/client.hpp new file mode 100644 index 0000000000000000000000000000000000000000..5c772cb61f72b1675457d76626ac7ddce819faa1 --- /dev/null +++ b/include/fggl/platform/wayland/client.hpp @@ -0,0 +1,126 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 02/05/22. +// + +#ifndef FGGL_PLATFORM_WAYLAND_CLIENT_HPP +#define FGGL_PLATFORM_WAYLAND_CLIENT_HPP + +#include <wayland-client.h> +#include "fggl/platform/wayland/wayland-xdg-shell-protocol.h" + +#include <wayland-egl.h> +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <iostream> +#include <cstring> + +#include <sys/mman.h> + +// wayland to cpp glue + +struct WaylandState { + wl_compositor* compositor; +}; + +static void registryHandleGlobal(void* userData, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) { + std::cerr << "saw item: " << interface << ", Version: " << version << std::endl; + auto* state = static_cast<WaylandState*>(userData); + + if ( strcmp(interface, wl_compositor_interface.name) == 0 ) { + state->compositor = static_cast<wl_compositor *>(wl_registry_bind(registry, name, &wl_compositor_interface, version)); + } + + if (strcmp(interface, xdg_)) + + if ( strcmp(interface, wl_shm_interface.name) == 0) { + state->shm = static_cast<wl_shm *>(wl_registry_bind(registry, name, &wl_shm_interface, version)); + } +} + +static void registeryHandleGlobalRemove(void* userData, struct wl_registry* registry, uint32_t name) { + std::cerr << "something was removed" << std::endl; +} + +static const struct wl_registry_listener registryListener{ + registryHandleGlobal, + registeryHandleGlobalRemove +}; + +namespace fggl::platform::wayland { + + void initWayland() { + auto* display = wl_display_connect(nullptr); + if ( display == nullptr ) { + std::cerr << "abort! couldn't connect to wayland display" << std::endl; + return; + } + + WaylandState wlState; + + auto* registry= wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istryListener, &wlState); + + wl_display_roundtrip(display); + wl_display_roundtrip(display); + + wl_surface* surface = wl_compositor_create_surface(wlState.compositor); + wl_egl_window* window = wl_egl_window_create(surface, 800, 600); + + + std::array<EGLConfig, 256> configs; + EGLint returnedSize; + eglGetConfigs( display, configs.data(), configs.size(), &returnedSize); + + // egl display + EGLAttrib displayAttrs[]{ + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_NONE + }; + EGLDisplay eglDisplay = eglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR, display, displayAttrs); + + // egl surface + EGLAttrib surfaceAttrs[] { + EGL_NONE + }; + EGLSurface eglSurface = eglCreatePlatformWindowSurface(eglDisplay, configs[0], window, surfaceAttrs); + + // egl openGL + EGLint contextAttrs[] { + EGL_CONTEXT_MAJOR_VERSION, 4, + EGL_CONTEXT_MINOR_VERSION, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT, + EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, + EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE, EGL_FALSE, + EGL_NONE + }; + eglBindAPI(EGL_OPENGL_API); + EGLContext context = eglCreateContext(eglDisplay, configs[0], EGL_NO_CONTEXT, contextAttrs); + eglMakeCurrent(display, surface, surface, context); + + eglSwapBuffers(eglDisplay, eglSurface); + } + +} // namespace fggl::platform::wayland + +#endif //FGGL_PLATFORM_WAYLAND_CLIENT_HPP diff --git a/include/fggl/platform/wayland/wayland.hpp b/include/fggl/platform/wayland/wayland.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7e61baead51341ca3744ba7dc4c1e82983c2ceb8 --- /dev/null +++ b/include/fggl/platform/wayland/wayland.hpp @@ -0,0 +1,53 @@ +/* + * ${license.title} + * Copyright (C) 2022 ${license.owner} + * ${license.mailto} + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// +// Created by webpigeon on 02/05/22. +// + +#ifndef FGGL_PLATFORM_WAYLAND_WAYLAND_HPP +#define FGGL_PLATFORM_WAYLAND_WAYLAND_HPP + +#include "wayland-egl.h" +#include "fggl/gfx/windowing.hpp" + +namespace fggl::platform::wayland { + + class WaylandWindow : public gfx::Window { + public: + WaylandWindow() { + m_window = wl_egl_window_create(m_surface, 800, 600); + } + + void frameStart() override; + void frameEnd() override; + + bool wantClose() const override; + math::vec2i frameSize() const override; + + private: + wl_egl_window* m_window; + wl_surface* m_surface; + }; + + +} // namespace fggl::platform::wayland + +#endif //FGGL_PLATFORM_WAYLAND_WAYLAND_HPP