diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 815a9ad5d..03a2b6b85 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,14 +38,17 @@ jobs: config: { name: x86, flags: -DCMAKE_ANDROID_ARCH_ABI=x86 -DCMAKE_SYSTEM_NAME=Android -DSFML_BUILD_TEST_SUITE=FALSE -DCMAKE_ANDROID_NDK=$GITHUB_WORKSPACE/android-ndk-r23b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 } - platform: { name: Android, os: ubuntu-latest } config: { name: armeabi-v7a, flags: -DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a -DCMAKE_SYSTEM_NAME=Android -DSFML_BUILD_TEST_SUITE=FALSE -DCMAKE_ANDROID_NDK=$GITHUB_WORKSPACE/android-ndk-r23b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 } - + - platform: { name: Linux GCC, os: ubuntu-latest } + config: { name: Static DRM, flags: -DBUILD_SHARED_LIBS=FALSE -DSFML_USE_DRM=TRUE } + - platform: { name: Linux GCC, os: ubuntu-latest } + config: { name: Shared DRM, flags: -DBUILD_SHARED_LIBS=TRUE -DSFML_USE_DRM=TRUE } steps: - name: Checkout Code uses: actions/checkout@v2 - name: Install Linux Dependencies if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install libxrandr-dev libxcursor-dev libudev-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev + run: sudo apt-get update && sudo apt-get install libxrandr-dev libxcursor-dev libudev-dev libopenal-dev libflac-dev libvorbis-dev libgl1-mesa-dev libegl1-mesa-dev libdrm-dev libgbm-dev - name: Install Android Components if: matrix.platform.name == 'Android' diff --git a/cmake/Modules/FindDRM.cmake b/cmake/Modules/FindDRM.cmake new file mode 100644 index 000000000..23b38b11a --- /dev/null +++ b/cmake/Modules/FindDRM.cmake @@ -0,0 +1,18 @@ +# +# Try to find EGL library and include path. +# Once done this will define +# +# DRM_FOUND +# DRM_INCLUDE_PATH +# DRM_LIBRARY +# + +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_DRM drm QUIET) +endif() + +find_path(DRM_INCLUDE_DIR NAMES libdrm/drm.h) +find_library(DRM_LIBRARY NAMES drm) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(DRM DEFAULT_MSG DRM_LIBRARY DRM_INCLUDE_DIR) diff --git a/cmake/Modules/FindGBM.cmake b/cmake/Modules/FindGBM.cmake new file mode 100644 index 000000000..4deb5eb63 --- /dev/null +++ b/cmake/Modules/FindGBM.cmake @@ -0,0 +1,18 @@ +# +# Try to find GBM library and include path. +# Once done this will define +# +# GBM_FOUND +# GBM_INCLUDE_PATH +# GBM_LIBRARY +# + +if(PKG_CONFIG_FOUND) + pkg_check_modules(PC_GBM gbm QUIET) +endif() + +find_path(GBM_INCLUDE_DIR NAMES gbm.h) +find_library(GBM_LIBRARY NAMES gbm) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GBM DEFAULT_MSG GBM_LIBRARY GBM_INCLUDE_DIR) diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 48a799f81..e5374384b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -31,7 +31,9 @@ if(SFML_BUILD_GRAPHICS) if(SFML_OS_WINDOWS) add_subdirectory(win32) elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD) - add_subdirectory(X11) + if(NOT SFML_USE_DRM) + add_subdirectory(X11) + endif() elseif(SFML_OS_MACOSX AND ${CMAKE_GENERATOR} MATCHES "Xcode") add_subdirectory(cocoa) endif() diff --git a/extlibs/headers/drm/drm-common.c b/extlibs/headers/drm/drm-common.c new file mode 100644 index 000000000..700914739 --- /dev/null +++ b/extlibs/headers/drm/drm-common.c @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2017 Rob Clark + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define WEAK __attribute__((weak)) + +WEAK uint64_t gbm_bo_get_modifier(struct gbm_bo *bo); +WEAK int gbm_bo_get_plane_count(struct gbm_bo *bo); +WEAK uint32_t gbm_bo_get_stride_for_plane(struct gbm_bo *bo, int plane); +WEAK uint32_t gbm_bo_get_offset(struct gbm_bo *bo, int plane); + +static void drm_fb_destroy_callback(struct gbm_bo *bo, void *data) +{ + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + struct drm_fb *fb = data; + + if (fb->fb_id) + drmModeRmFB(drm_fd, fb->fb_id); + + free(fb); +} + +struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo) +{ + int drm_fd = gbm_device_get_fd(gbm_bo_get_device(bo)); + struct drm_fb *fb = gbm_bo_get_user_data(bo); + uint32_t width, height, format, + strides[4] = {0}, handles[4] = {0}, + offsets[4] = {0}, flags = 0; + int ret = -1; + + if (fb) + return fb; + + fb = calloc(1, sizeof *fb); + fb->bo = bo; + + width = gbm_bo_get_width(bo); + height = gbm_bo_get_height(bo); + format = gbm_bo_get_format(bo); + + if (gbm_bo_get_modifier && gbm_bo_get_plane_count && + gbm_bo_get_stride_for_plane && gbm_bo_get_offset) + { + uint64_t modifiers[4] = {0}; + modifiers[0] = gbm_bo_get_modifier(bo); + const int num_planes = gbm_bo_get_plane_count(bo); + for (int i = 0; i < num_planes; i++) + { + strides[i] = gbm_bo_get_stride_for_plane(bo, i); + handles[i] = gbm_bo_get_handle(bo).u32; + offsets[i] = gbm_bo_get_offset(bo, i); + modifiers[i] = modifiers[0]; + } + + if (modifiers[0]) + { + flags = DRM_MODE_FB_MODIFIERS; + // printf("Using modifier %" PRIx64 "\n", modifiers[0]); + } + + ret = drmModeAddFB2WithModifiers(drm_fd, width, height, + format, handles, strides, offsets, + modifiers, &fb->fb_id, flags); + } + + if (ret) + { + // if (flags) + // fprintf(stderr, "Modifiers failed!\n"); + + memcpy(handles, (uint32_t [4]){gbm_bo_get_handle(bo).u32,0,0,0}, 16); + memcpy(strides, (uint32_t [4]){gbm_bo_get_stride(bo),0,0,0}, 16); + memset(offsets, 0, 16); + ret = drmModeAddFB2(drm_fd, width, height, format, + handles, strides, offsets, &fb->fb_id, 0); + } + + if (ret) + { + printf("failed to create fb: %s\n", strerror(errno)); + free(fb); + return NULL; + } + + gbm_bo_set_user_data(bo, fb, drm_fb_destroy_callback); + + return fb; +} + +static uint32_t find_crtc_for_encoder(const drmModeRes *resources, + const drmModeEncoder *encoder) +{ + + for (int i = 0; i < resources->count_crtcs; i++) + { + /* possible_crtcs is a bitmask as described here: + * https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api + */ + const uint32_t crtc_mask = 1U << i; + const uint32_t crtc_id = resources->crtcs[i]; + if (encoder->possible_crtcs & crtc_mask) + { + return crtc_id; + } + } + + /* no match found */ + return 0; +} + +static uint32_t find_crtc_for_connector(const struct drm *drm, const drmModeRes *resources, const drmModeConnector *connector) +{ + for (int i = 0; i < connector->count_encoders; i++) + { + const uint32_t encoder_id = connector->encoders[i]; + drmModeEncoder *encoder = drmModeGetEncoder(drm->fd, encoder_id); + + if (encoder) + { + const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder); + + drmModeFreeEncoder(encoder); + if (crtc_id != 0) + { + return crtc_id; + } + } + } + + /* no match found */ + return 0; +} + +static int get_resources(int fd, drmModeRes **resources) +{ + *resources = drmModeGetResources(fd); + if (*resources == NULL) + return -1; + return 0; +} + +#define MAX_DRM_DEVICES 64 + +static int find_drm_device(drmModeRes **resources) +{ + drmDevicePtr devices[MAX_DRM_DEVICES] = { NULL }; + int num_devices, fd = -1; + + num_devices = drmGetDevices2(0, devices, MAX_DRM_DEVICES); + if (num_devices < 0) + { + printf("drmGetDevices2 failed: %s\n", strerror(-num_devices)); + return -1; + } + + for (int i = 0; i < num_devices; i++) + { + drmDevicePtr device = devices[i]; + int ret; + + if (!(device->available_nodes & (1 << DRM_NODE_PRIMARY))) + continue; + /* OK, it's a primary device. If we can get the + * drmModeResources, it means it's also a + * KMS-capable device. + */ + fd = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR); + if (fd < 0) + continue; + ret = get_resources(fd, resources); + if (!ret) + break; + close(fd); + fd = -1; + } + drmFreeDevices(devices, num_devices); + + if (fd < 0) + printf("no drm device found!\n"); + return fd; +} + +int init_drm(struct drm *drm, const char *device, const char *mode_str, + unsigned int vrefresh) +{ + drmModeRes *resources; + drmModeConnector *connector = NULL; + drmModeEncoder *encoder = NULL; + int i, ret, area; + + if (device) + { + drm->fd = open(device, O_RDWR); + ret = get_resources(drm->fd, &resources); + if (ret < 0 && errno == EOPNOTSUPP) + printf("%s does not look like a modeset device\n", device); + } + else + { + drm->fd = find_drm_device(&resources); + } + + if (drm->fd < 0) + { + printf("could not open drm device\n"); + return -1; + } + + if (!resources) + { + printf("drmModeGetResources failed: %s\n", strerror(errno)); + return -1; + } + + /* find a connected connector: */ + for (i = 0; i < resources->count_connectors; i++) + { + connector = drmModeGetConnector(drm->fd, resources->connectors[i]); + if (connector->connection == DRM_MODE_CONNECTED) + { + /* it's connected, let's use this! */ + break; + } + drmModeFreeConnector(connector); + connector = NULL; + } + + if (!connector) + { + /* we could be fancy and listen for hotplug events and wait for + * a connector.. + */ + printf("no connected connector!\n"); + return -1; + } + + /* find user requested mode: */ + if (mode_str && *mode_str) + { + for (i = 0; i < connector->count_modes; i++) + { + drmModeModeInfo *current_mode = &connector->modes[i]; + + if (strcmp(current_mode->name, mode_str) == 0) + { + if (vrefresh == 0 || current_mode->vrefresh == vrefresh) + { + drm->mode = current_mode; + break; + } + } + } + if (!drm->mode) + printf("requested mode not found, using default mode!\n"); + } + + /* find preferred mode or the highest resolution mode: */ + if (!drm->mode) + { + for (i = 0, area = 0; i < connector->count_modes; i++) + { + drmModeModeInfo *current_mode = &connector->modes[i]; + + if (current_mode->type & DRM_MODE_TYPE_PREFERRED) + { + drm->mode = current_mode; + break; + } + + int current_area = current_mode->hdisplay * current_mode->vdisplay; + if (current_area > area) + { + drm->mode = current_mode; + area = current_area; + } + } + } + + if (!drm->mode) + { + printf("could not find mode!\n"); + return -1; + } + + /* find encoder: */ + for (i = 0; i < resources->count_encoders; i++) + { + encoder = drmModeGetEncoder(drm->fd, resources->encoders[i]); + if (encoder->encoder_id == connector->encoder_id) + break; + drmModeFreeEncoder(encoder); + encoder = NULL; + } + + if (encoder) + { + drm->crtc_id = encoder->crtc_id; + } + else + { + uint32_t crtc_id = find_crtc_for_connector(drm, resources, connector); + if (crtc_id == 0) + { + printf("no crtc found!\n"); + return -1; + } + + drm->crtc_id = crtc_id; + } + + drmModeFreeResources(resources); + + drm->connector_id = connector->connector_id; + + drm->saved_connector = connector; + drm->saved_encoder = encoder; + + // get original display mode so we can restore display mode after program exits + drm->original_crtc = drmModeGetCrtc(drm->fd, drm->crtc_id); + + if(getenv("SFML_DRM_DEBUG")) + { + printf("DRM Mode used: %s@%d\n", drm->mode->name, drm->mode->vrefresh); + } + + return 0; +} diff --git a/extlibs/headers/drm/drm-common.h b/extlibs/headers/drm/drm-common.h new file mode 100644 index 000000000..d388e6d0c --- /dev/null +++ b/extlibs/headers/drm/drm-common.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2017 Rob Clark + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sub license, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#ifndef _DRM_COMMON_H +#define _DRM_COMMON_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct gbm; +struct egl; + +struct plane +{ + drmModePlane *plane; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +}; + +struct crtc +{ + drmModeCrtc *crtc; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +}; + +struct connector +{ + drmModeConnector *connector; + drmModeObjectProperties *props; + drmModePropertyRes **props_info; +}; + +struct drm +{ + int fd; + + drmModeModeInfo *mode; + uint32_t crtc_id; + uint32_t connector_id; + + drmModeCrtcPtr original_crtc; + + drmModeConnectorPtr saved_connector; + drmModeEncoderPtr saved_encoder; +}; + +struct drm_fb +{ + struct gbm_bo *bo; + uint32_t fb_id; +}; + +struct drm_fb * drm_fb_get_from_bo(struct gbm_bo *bo); + +int init_drm(struct drm *drm, const char *device, const char *mode_str, unsigned int vrefresh); + +#ifdef __cplusplus +} +#endif + +#endif /* _DRM_COMMON_H */ diff --git a/src/SFML/Window/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index d37dbb8de..5d5f831e1 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -82,28 +82,44 @@ if(SFML_OS_WINDOWS) # make sure that we use the Unicode version of the Win API functions add_definitions(-DUNICODE -D_UNICODE) elseif(SFML_OS_LINUX OR SFML_OS_FREEBSD OR SFML_OS_OPENBSD OR SFML_OS_NETBSD) - set(PLATFORM_SRC - ${SRCROOT}/Unix/CursorImpl.hpp - ${SRCROOT}/Unix/CursorImpl.cpp - ${SRCROOT}/Unix/ClipboardImpl.hpp - ${SRCROOT}/Unix/ClipboardImpl.cpp - ${SRCROOT}/Unix/Display.cpp - ${SRCROOT}/Unix/Display.hpp - ${SRCROOT}/Unix/InputImpl.cpp - ${SRCROOT}/Unix/InputImpl.hpp - ${SRCROOT}/Unix/SensorImpl.cpp - ${SRCROOT}/Unix/SensorImpl.hpp - ${SRCROOT}/Unix/VideoModeImpl.cpp - ${SRCROOT}/Unix/VulkanImplX11.cpp - ${SRCROOT}/Unix/VulkanImplX11.hpp - ${SRCROOT}/Unix/WindowImplX11.cpp - ${SRCROOT}/Unix/WindowImplX11.hpp - ) - if(NOT SFML_OS_ANDROID) + if(SFML_USE_DRM) + add_definitions(-DSFML_USE_DRM) set(PLATFORM_SRC ${PLATFORM_SRC} + ${SRCROOT}/DRM/CursorImpl.hpp + ${SRCROOT}/DRM/CursorImpl.cpp + ${SRCROOT}/DRM/ClipboardImpl.hpp + ${SRCROOT}/DRM/ClipboardImpl.cpp + ${SRCROOT}/Unix/SensorImpl.cpp + ${SRCROOT}/Unix/SensorImpl.hpp + ${SRCROOT}/DRM/InputImplUDev.cpp + ${SRCROOT}/DRM/InputImplUDev.hpp + ${SRCROOT}/DRM/VideoModeImpl.cpp + ${SRCROOT}/DRM/DRMContext.cpp + ${SRCROOT}/DRM/DRMContext.hpp + ${SRCROOT}/DRM/WindowImplDRM.cpp + ${SRCROOT}/DRM/WindowImplDRM.hpp + ) + else() + set(PLATFORM_SRC + ${PLATFORM_SRC} + ${SRCROOT}/Unix/CursorImpl.hpp + ${SRCROOT}/Unix/CursorImpl.cpp + ${SRCROOT}/Unix/ClipboardImpl.hpp + ${SRCROOT}/Unix/ClipboardImpl.cpp + ${SRCROOT}/Unix/InputImpl.cpp + ${SRCROOT}/Unix/InputImpl.hpp + ${SRCROOT}/Unix/SensorImpl.cpp + ${SRCROOT}/Unix/SensorImpl.hpp + ${SRCROOT}/Unix/Display.cpp + ${SRCROOT}/Unix/Display.hpp ${SRCROOT}/Unix/GlxContext.cpp ${SRCROOT}/Unix/GlxContext.hpp + ${SRCROOT}/Unix/VideoModeImpl.cpp + ${SRCROOT}/Unix/VulkanImplX11.cpp + ${SRCROOT}/Unix/VulkanImplX11.hpp + ${SRCROOT}/Unix/WindowImplX11.cpp + ${SRCROOT}/Unix/WindowImplX11.hpp ) endif() if(SFML_OS_LINUX) @@ -236,6 +252,21 @@ endif() # define the sfml-window target sfml_add_library(Window SOURCES ${SRC} ${PLATFORM_SRC}) +# DRM libraries +if(SFML_OS_LINUX OR SFML_OS_FREEBSD OR SFML_OS_OPENBSD OR SFML_OS_NETBSD) + if(SFML_USE_DRM) + target_sources(sfml-window PRIVATE $) + sfml_find_package(DRM INCLUDE "DRM_INCLUDE_DIR" LINK "DRM_LIBRARY") + target_include_directories(sfml-window PRIVATE ${PROJECT_SOURCE_DIR}/extlibs/headers/drm ${DRM_INCLUDE_DIR}/libdrm) + sfml_find_package(GBM INCLUDE "GBM_INCLUDE_DIR" LINK "GBM_LIBRARY") + target_link_libraries(sfml-window PRIVATE drm gbm EGL) + add_library(drm-common OBJECT ${PROJECT_SOURCE_DIR}/extlibs/headers/drm/drm-common.c) + target_include_directories(drm-common PRIVATE ${PROJECT_SOURCE_DIR}/extlibs/headers/drm ${DRM_INCLUDE_DIR}/libdrm) + else() + sfml_find_package(X11 INCLUDE "X11_INCLUDE_DIR" LINK "X11_X11_LIB" "X11_Xrandr_LIB" "X11_Xcursor_LIB") + target_link_libraries(sfml-window PRIVATE X11) + endif() +endif() target_link_libraries(sfml-window PUBLIC SFML::System) # glad sources @@ -249,12 +280,6 @@ endif() # Vulkan headers target_include_directories(sfml-window SYSTEM PRIVATE "${PROJECT_SOURCE_DIR}/extlibs/headers/vulkan") -# find and setup usage for external libraries -if(SFML_OS_LINUX OR SFML_OS_FREEBSD OR SFML_OS_OPENBSD OR SFML_OS_NETBSD) - sfml_find_package(X11 INCLUDE "X11_INCLUDE_DIR" LINK "X11_X11_LIB" "X11_Xrandr_LIB" "X11_Xcursor_LIB") - target_link_libraries(sfml-window PRIVATE X11) -endif() - # CMake 3.11 and later prefer to choose GLVND, but we choose legacy OpenGL for backward compability # (unless the OpenGL_GL_PREFERENCE was explicitly set) # See CMP0072 for more details (cmake --help-policy CMP0072) @@ -268,7 +293,6 @@ if(SFML_OS_IOS) target_link_libraries(sfml-window PRIVATE GLES) elseif(SFML_OS_ANDROID) sfml_find_package(GLES INCLUDE "GLES_INCLUDE_DIR" LINK "GLES_LIBRARY") - sfml_find_package(EGL INCLUDE "EGL_INCLUDE_DIR" LINK "EGL_LIBRARY") target_link_libraries(sfml-window PRIVATE EGL) target_link_libraries(sfml-window PRIVATE GLES) diff --git a/src/SFML/Window/ClipboardImpl.hpp b/src/SFML/Window/ClipboardImpl.hpp index 1c8278724..5b4f0b8fe 100644 --- a/src/SFML/Window/ClipboardImpl.hpp +++ b/src/SFML/Window/ClipboardImpl.hpp @@ -33,7 +33,11 @@ #if defined(SFML_SYSTEM_WINDOWS) #include #elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD) || defined(SFML_SYSTEM_OPENBSD) || defined(SFML_SYSTEM_NETBSD) - #include + #if defined(SFML_USE_DRM) + #include + #else + #include + #endif #elif defined(SFML_SYSTEM_MACOS) #include #elif defined(SFML_SYSTEM_IOS) diff --git a/src/SFML/Window/CursorImpl.hpp b/src/SFML/Window/CursorImpl.hpp index dbaaa9bac..0cf31e372 100644 --- a/src/SFML/Window/CursorImpl.hpp +++ b/src/SFML/Window/CursorImpl.hpp @@ -31,25 +31,19 @@ #include #if defined(SFML_SYSTEM_WINDOWS) - #include - #elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD) || defined(SFML_SYSTEM_OPENBSD) || defined(SFML_SYSTEM_NETBSD) - - #include - + #if defined(SFML_USE_DRM) + #include + #else + #include + #endif #elif defined(SFML_SYSTEM_MACOS) - #include - #elif defined(SFML_SYSTEM_IOS) - #include - #elif defined(SFML_SYSTEM_ANDROID) - #include - #endif diff --git a/src/SFML/Window/DRM/ClipboardImpl.cpp b/src/SFML/Window/DRM/ClipboardImpl.cpp new file mode 100644 index 000000000..cd3128447 --- /dev/null +++ b/src/SFML/Window/DRM/ClipboardImpl.cpp @@ -0,0 +1,49 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +String ClipboardImpl::getString() +{ + return ""; +} + + +//////////////////////////////////////////////////////////// +void ClipboardImpl::setString(const String& /*text*/) +{ +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/DRM/ClipboardImpl.hpp b/src/SFML/Window/DRM/ClipboardImpl.hpp new file mode 100644 index 000000000..250a39b09 --- /dev/null +++ b/src/SFML/Window/DRM/ClipboardImpl.hpp @@ -0,0 +1,76 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_CLIPBOARDIMPLDRM_HPP +#define SFML_CLIPBOARDIMPLDRM_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Give access to the system clipboard +/// +//////////////////////////////////////////////////////////// +class ClipboardImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Get the content of the clipboard as string data + /// + /// This function returns the content of the clipboard + /// as a string. If the clipboard does not contain string + /// it returns an empty sf::String object. + /// + /// \return Current content of the clipboard + /// + //////////////////////////////////////////////////////////// + static String getString(); + + //////////////////////////////////////////////////////////// + /// \brief Set the content of the clipboard as string data + /// + /// This function sets the content of the clipboard as a + /// string. + /// + /// \param text sf::String object containing the data to be sent + /// to the clipboard + /// + //////////////////////////////////////////////////////////// + static void setString(const String& text); +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_CLIPBOARDIMPLDRM_HPP diff --git a/src/SFML/Window/DRM/CursorImpl.cpp b/src/SFML/Window/DRM/CursorImpl.cpp new file mode 100644 index 000000000..dbe73dcb5 --- /dev/null +++ b/src/SFML/Window/DRM/CursorImpl.cpp @@ -0,0 +1,90 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +CursorImpl::CursorImpl() +{ +} + + +//////////////////////////////////////////////////////////// +CursorImpl::~CursorImpl() +{ +} + + +//////////////////////////////////////////////////////////// +bool CursorImpl::loadFromPixels(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/) +{ + return false; +} + + +//////////////////////////////////////////////////////////// +bool CursorImpl::loadFromPixelsARGB(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/) +{ + return false; +} + + +//////////////////////////////////////////////////////////// +bool CursorImpl::loadFromPixelsMonochrome(const Uint8* /*pixels*/, Vector2u /*size*/, Vector2u /*hotspot*/) +{ + return false; +} + + +//////////////////////////////////////////////////////////// +bool CursorImpl::loadFromSystem(Cursor::Type /*type*/) +{ + return false; +} + + +//////////////////////////////////////////////////////////// +bool CursorImpl::isColorCursorSupported() +{ + return false; +} + + +//////////////////////////////////////////////////////////// +void CursorImpl::release() +{ +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/DRM/CursorImpl.hpp b/src/SFML/Window/DRM/CursorImpl.hpp new file mode 100644 index 000000000..c78d918c5 --- /dev/null +++ b/src/SFML/Window/DRM/CursorImpl.hpp @@ -0,0 +1,130 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2022 Laurent Gomila (laurent@sfml-dev.org) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_CURSORIMPLDRM_HPP +#define SFML_CURSORIMPLDRM_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include // Prevent conflict with macro None from Xlib + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Unix implementation of Cursor +/// +//////////////////////////////////////////////////////////// +class CursorImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Refer to sf::Cursor::Cursor(). + /// + //////////////////////////////////////////////////////////// + CursorImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + /// Refer to sf::Cursor::~Cursor(). + /// + //////////////////////////////////////////////////////////// + ~CursorImpl(); + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy constructor + /// + //////////////////////////////////////////////////////////// + CursorImpl(const CursorImpl&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Deleted copy assignment + /// + //////////////////////////////////////////////////////////// + CursorImpl& operator=(const CursorImpl&) = delete; + + //////////////////////////////////////////////////////////// + /// \brief Create a cursor with the provided image + /// + /// Refer to sf::Cursor::loadFromPixels(). + /// + //////////////////////////////////////////////////////////// + bool loadFromPixels(const Uint8* pixels, Vector2u size, Vector2u hotspot); + + //////////////////////////////////////////////////////////// + /// \brief Create a native system cursor + /// + /// Refer to sf::Cursor::loadFromSystem(). + /// + //////////////////////////////////////////////////////////// + bool loadFromSystem(Cursor::Type type); + +private: + + friend class WindowImplDRM; + + //////////////////////////////////////////////////////////// + /// \brief Checks if colored cursors are supported for this display. + /// + //////////////////////////////////////////////////////////// + bool isColorCursorSupported(); + + //////////////////////////////////////////////////////////// + /// \brief Create a cursor with the provided image (ARGB support) + /// + /// Refer to sf::Cursor::loadFromPixels(). + /// + //////////////////////////////////////////////////////////// + bool loadFromPixelsARGB(const Uint8* pixels, Vector2u size, Vector2u hotspot); + + //////////////////////////////////////////////////////////// + /// \brief Create a cursor with the provided image (monochrome) + /// + /// Refer to sf::Cursor::loadFromPixels(). + /// + //////////////////////////////////////////////////////////// + bool loadFromPixelsMonochrome(const Uint8* pixels, Vector2u size, Vector2u hotspot); + + //////////////////////////////////////////////////////////// + /// \brief Release the cursor, if we have loaded one. + /// + //////////////////////////////////////////////////////////// + void release(); +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_CUSROSIMPLDRM_HPP diff --git a/src/SFML/Window/DRM/DRMContext.cpp b/src/SFML/Window/DRM/DRMContext.cpp new file mode 100644 index 000000000..5980fa1c0 --- /dev/null +++ b/src/SFML/Window/DRM/DRMContext.cpp @@ -0,0 +1,552 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2020 Andrew Mickelson +// 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + bool initialized = false; + drm drmNode; + drmEventContext drmEventCtx; + pollfd pollFD; + gbm_device* gbmDevice = NULL; + int contextCount = 0; + EGLDisplay display = EGL_NO_DISPLAY; + int waitingForFlip = 0; + + static void pageFlipHandler(int fd, unsigned int frame, + unsigned int sec, unsigned int usec, void* data) + { + // suppress unused param warning + (void)fd, (void)frame, (void)sec, (void)usec; + + int* temp = static_cast(data); + *temp = 0; + } + + static bool waitForFlip(int timeout) + { + while (waitingForFlip) + { + pollFD.revents = 0; + + if (poll(&pollFD, 1, timeout) < 0) + return false; + + if (pollFD.revents & (POLLHUP | POLLERR)) + return false; + + if (pollFD.revents & POLLIN) + { + drmHandleEvent(drmNode.fd, &drmEventCtx); + } + else + { + return false; + } + } + return true; + } + + void cleanup() + { + if (!initialized) + return; + + drmModeSetCrtc(drmNode.fd, + drmNode.original_crtc->crtc_id, + drmNode.original_crtc->buffer_id, + drmNode.original_crtc->x, + drmNode.original_crtc->y, + &drmNode.connector_id, + 1, + &drmNode.original_crtc->mode); + + drmModeFreeConnector(drmNode.saved_connector); + drmModeFreeEncoder(drmNode.saved_encoder); + drmModeFreeCrtc(drmNode.original_crtc); + + eglTerminate(display); + display = EGL_NO_DISPLAY; + + gbm_device_destroy(gbmDevice); + gbmDevice = NULL; + + close(drmNode.fd); + + drmNode.fd = -1; + drmNode.mode = 0; + + std::memset(&pollFD, 0, sizeof(pollfd)); + std::memset(&drmEventCtx, 0, sizeof(drmEventContext)); + + waitingForFlip = 0; + + initialized = false; + } + + void checkInit() + { + if (initialized) + return; + + // Use environment variable "SFML_DRM_DEVICE" (or NULL if not set) + char* deviceString = std::getenv("SFML_DRM_DEVICE"); + if (deviceString && !*deviceString) + deviceString = NULL; + + // Use environment variable "SFML_DRM_MODE" (or NULL if not set) + char* modeString = std::getenv("SFML_DRM_MODE"); + + // Use environment variable "SFML_DRM_REFRESH" (or 0 if not set) + // Use in combination with mode to request specific refresh rate for the mode + // if multiple refresh rates for same mode might be supported + unsigned int refreshRate = 0; + char* refreshString = std::getenv("SFML_DRM_REFRESH"); + + if (refreshString) + refreshRate = static_cast(atoi(refreshString)); + + if (init_drm(&drmNode, + deviceString, // device + modeString, // requested mode + refreshRate) < 0) // screen refresh rate + { + sf::err() << "Error initializing DRM" << std::endl; + return; + } + + gbmDevice = gbm_create_device(drmNode.fd); + + std::atexit(cleanup); + initialized = true; + + pollFD.fd = drmNode.fd; + pollFD.events = POLLIN; + drmEventCtx.version = 2; + drmEventCtx.page_flip_handler = pageFlipHandler; + } + + + EGLDisplay getInitializedDisplay() + { + checkInit(); + + if (display == EGL_NO_DISPLAY) + { + display = eglCheck(eglGetDisplay(reinterpret_cast(gbmDevice))); + + EGLint major, minor; + eglCheck(eglInitialize(display, &major, &minor)); + +#if defined(SFML_OPENGL_ES) + if (!eglBindAPI(EGL_OPENGL_ES_API)) + { + sf::err() << "failed to bind api EGL_OPENGL_ES_API" << std::endl; + } +#else + if (!eglBindAPI(EGL_OPENGL_API)) + { + sf::err() << "failed to bind api EGL_OPENGL_API" << std::endl; + } +#endif + } + + return display; + } +} + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +DRMContext::DRMContext(DRMContext* shared) : +m_display (EGL_NO_DISPLAY), +m_context (EGL_NO_CONTEXT), +m_surface (EGL_NO_SURFACE), +m_config (NULL), +m_currentBO (NULL), +m_nextBO (NULL), +m_gbmSurface (NULL), +m_width (0), +m_height (0), +m_shown (false), +m_scanOut (false) +{ + contextCount++; + + // Get the initialized EGL display + m_display = getInitializedDisplay(); + + // Get the best EGL config matching the default video settings + m_config = getBestConfig(m_display, VideoMode::getDesktopMode().bitsPerPixel, ContextSettings()); + updateSettings(); + + // Create EGL context + createContext(shared); + + if (shared) + createSurface(shared->m_width, shared->m_height, VideoMode::getDesktopMode().bitsPerPixel, false); + else // create a surface to force the GL to initialize (seems to be required for glGetString() etc ) + createSurface(1, 1, VideoMode::getDesktopMode().bitsPerPixel, false); +} + + +//////////////////////////////////////////////////////////// +DRMContext::DRMContext(DRMContext* shared, const ContextSettings& settings, const WindowImpl& owner, unsigned int bitsPerPixel) : +m_display (EGL_NO_DISPLAY), +m_context (EGL_NO_CONTEXT), +m_surface (EGL_NO_SURFACE), +m_config (NULL), +m_currentBO (NULL), +m_nextBO (NULL), +m_gbmSurface (NULL), +m_width (0), +m_height (0), +m_shown (false), +m_scanOut (false) +{ + contextCount++; + + // Get the initialized EGL display + m_display = getInitializedDisplay(); + + // Get the best EGL config matching the requested video settings + m_config = getBestConfig(m_display, bitsPerPixel, settings); + updateSettings(); + + // Create EGL context + createContext(shared); + + Vector2u size = owner.getSize(); + createSurface(size.x, size.y, bitsPerPixel, true); +} + + +//////////////////////////////////////////////////////////// +DRMContext::DRMContext(DRMContext* shared, const ContextSettings& settings, unsigned int width, unsigned int height) : +m_display (EGL_NO_DISPLAY), +m_context (EGL_NO_CONTEXT), +m_surface (EGL_NO_SURFACE), +m_config (NULL), +m_currentBO (NULL), +m_nextBO (NULL), +m_gbmSurface (NULL), +m_width (0), +m_height (0), +m_shown (false), +m_scanOut (false) +{ + contextCount++; + + // Get the initialized EGL display + m_display = getInitializedDisplay(); + + // Get the best EGL config matching the requested video settings + m_config = getBestConfig(m_display, VideoMode::getDesktopMode().bitsPerPixel, settings); + updateSettings(); + + // Create EGL context + createContext(shared); + createSurface(width, height, VideoMode::getDesktopMode().bitsPerPixel, false); +} + + +//////////////////////////////////////////////////////////// +DRMContext::~DRMContext() +{ + // Deactivate the current context + EGLContext currentContext = eglCheck(eglGetCurrentContext()); + + if (currentContext == m_context) + { + eglCheck(eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + } + + // Destroy context + if (m_context != EGL_NO_CONTEXT) + { + eglCheck(eglDestroyContext(m_display, m_context)); + m_context = EGL_NO_CONTEXT; + } + + // Destroy surface + if (m_surface != EGL_NO_SURFACE) + { + eglCheck(eglDestroySurface(m_display, m_surface)); + m_surface = EGL_NO_SURFACE; + } + + if (m_currentBO) + gbm_surface_release_buffer(m_gbmSurface, m_currentBO); + + if (m_nextBO) + gbm_surface_release_buffer(m_gbmSurface, m_nextBO); + + if (m_gbmSurface) + gbm_surface_destroy(m_gbmSurface); + + contextCount--; + if (contextCount == 0) + cleanup(); +} + + +//////////////////////////////////////////////////////////// +bool DRMContext::makeCurrent(bool current) +{ + const EGLSurface surface = current ? m_surface : EGL_NO_SURFACE; + const EGLContext context = current ? m_context : EGL_NO_CONTEXT; + return m_surface != EGL_NO_SURFACE && eglMakeCurrent(m_display, surface, surface, context); +} + + +//////////////////////////////////////////////////////////// +void DRMContext::display() +{ + if (m_surface == EGL_NO_SURFACE) + return; + + if (!m_scanOut) + { + eglCheck(eglSwapBuffers(m_display, m_surface)); + return; + } + + // Handle display of buffer to the screen + drm_fb* fb = NULL; + + if (!waitForFlip(-1)) + return; + + if (m_currentBO) + { + gbm_surface_release_buffer(m_gbmSurface, m_currentBO); + m_currentBO = NULL; + } + + eglCheck(eglSwapBuffers(m_display, m_surface)); + + m_currentBO = m_nextBO; + + // This call must be preceeded by a single call to eglSwapBuffers() + m_nextBO = gbm_surface_lock_front_buffer(m_gbmSurface); + + if (!m_nextBO) + return; + + fb = drm_fb_get_from_bo(m_nextBO); + if (!fb) + { + err() << "Failed to get FB from buffer object" << std::endl; + return; + } + + // If first time, need to first call drmModeSetCrtc() + if (!m_shown) + { + if (drmModeSetCrtc(drmNode.fd, drmNode.crtc_id, fb->fb_id, 0, 0, + &drmNode.connector_id, 1, drmNode.mode)) + { + err() << "Failed to set mode: " << std::strerror(errno) << std::endl; + std::abort(); + } + m_shown = true; + } + + // Do page flip + if (!drmModePageFlip(drmNode.fd, drmNode.crtc_id, fb->fb_id, + DRM_MODE_PAGE_FLIP_EVENT, &waitingForFlip)) + waitingForFlip = 1; +} + + +//////////////////////////////////////////////////////////// +void DRMContext::setVerticalSyncEnabled(bool enabled) +{ + eglCheck(eglSwapInterval(m_display, enabled ? 1 : 0)); +} + + +//////////////////////////////////////////////////////////// +void DRMContext::createContext(DRMContext* shared) +{ + const EGLint contextVersion[] = + { + EGL_CONTEXT_CLIENT_VERSION, 1, + EGL_NONE + }; + + EGLContext toShared; + + if (shared) + toShared = shared->m_context; + else + toShared = EGL_NO_CONTEXT; + + if (toShared != EGL_NO_CONTEXT) + eglMakeCurrent(m_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + // Create EGL context + m_context = eglCheck(eglCreateContext(m_display, m_config, toShared, contextVersion)); + if (m_context == EGL_NO_CONTEXT) + err() << "Failed to create EGL context" << std::endl; +} + + +//////////////////////////////////////////////////////////// +void DRMContext::createSurface(unsigned int width, unsigned int height, unsigned int /*bpp*/, bool scanout) +{ + sf::Uint32 flags = GBM_BO_USE_RENDERING; + + m_scanOut = scanout; + if (m_scanOut) + flags |= GBM_BO_USE_SCANOUT; + + m_gbmSurface = gbm_surface_create( + gbmDevice, + width, + height, + GBM_FORMAT_ARGB8888, + flags); + + if (!m_gbmSurface) + { + err() << "Failed to create gbm surface." << std::endl; + return; + } + + m_width = width; + m_height = height; + + m_surface = eglCheck(eglCreateWindowSurface(m_display, m_config, reinterpret_cast(m_gbmSurface), NULL)); + + if (m_surface == EGL_NO_SURFACE) + { + err() << "Failed to create EGL Surface" << std::endl; + } +} + + +//////////////////////////////////////////////////////////// +void DRMContext::destroySurface() +{ + eglCheck(eglDestroySurface(m_display, m_surface)); + m_surface = EGL_NO_SURFACE; + + gbm_surface_destroy(m_gbmSurface); + m_gbmSurface = NULL; + + // Ensure that this context is no longer active since our surface is now destroyed + setActive(false); +} + + +//////////////////////////////////////////////////////////// +EGLConfig DRMContext::getBestConfig(EGLDisplay display, unsigned int bitsPerPixel, const ContextSettings& settings) +{ + // Set our video settings constraint + const EGLint attributes[] = + { + EGL_BUFFER_SIZE, static_cast(bitsPerPixel), + EGL_DEPTH_SIZE, static_cast(settings.depthBits), + EGL_STENCIL_SIZE, static_cast(settings.stencilBits), + EGL_SAMPLE_BUFFERS, static_cast(settings.antialiasingLevel), + EGL_BLUE_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_RED_SIZE, 8, + EGL_ALPHA_SIZE, 8, + + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, +#if defined(SFML_OPENGL_ES) + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES_BIT, +#else + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, +#endif + EGL_NONE + }; + + EGLint configCount; + EGLConfig configs[1]; + + // Ask EGL for the best config matching our video settings + eglCheck(eglChooseConfig(display, attributes, configs, 1, &configCount)); + + return configs[0]; +} + + +//////////////////////////////////////////////////////////// +void DRMContext::updateSettings() +{ + EGLint tmp; + + // Update the internal context settings with the current config + eglCheck(eglGetConfigAttrib(m_display, m_config, EGL_DEPTH_SIZE, &tmp)); + m_settings.depthBits = static_cast(tmp); + + eglCheck(eglGetConfigAttrib(m_display, m_config, EGL_STENCIL_SIZE, &tmp)); + m_settings.stencilBits = static_cast(tmp); + + eglCheck(eglGetConfigAttrib(m_display, m_config, EGL_SAMPLES, &tmp)); + m_settings.antialiasingLevel = static_cast(tmp); + + m_settings.majorVersion = 1; + m_settings.minorVersion = 1; + m_settings.attributeFlags = ContextSettings::Default; +} + + +//////////////////////////////////////////////////////////// +GlFunctionPointer DRMContext::getFunction(const char* name) +{ + return reinterpret_cast(eglGetProcAddress(name)); +} + + +//////////////////////////////////////////////////////////// +drm* DRMContext::getDRM() +{ + checkInit(); + return &drmNode; +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/DRM/DRMContext.hpp b/src/SFML/Window/DRM/DRMContext.hpp new file mode 100644 index 000000000..8a5bf2244 --- /dev/null +++ b/src/SFML/Window/DRM/DRMContext.hpp @@ -0,0 +1,214 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2020 Andrew Mickelson +// 2013 Jonathan De Wachter (dewachter.jonathan@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_DRMCONTEXT_HPP +#define SFML_DRMCONTEXT_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#define EGL_NO_X11 +#define MESA_EGL_NO_X11_HEADERS +#include +#include +#include + + +namespace sf +{ +namespace priv +{ +class WindowImplDRM; + +class DRMContext : public GlContext +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Create a new context, not associated to a window + /// + /// \param shared Context to share the new one with (can be NULL) + /// + //////////////////////////////////////////////////////////// + DRMContext(DRMContext* shared); + + //////////////////////////////////////////////////////////// + /// \brief Create a new context attached to a window + /// + /// \param shared Context to share the new one with + /// \param settings Creation parameters + /// \param owner Pointer to the owner window + /// \param bitsPerPixel Pixel depth, in bits per pixel + /// + //////////////////////////////////////////////////////////// + DRMContext(DRMContext* shared, const ContextSettings& settings, const WindowImpl& owner, unsigned int bitsPerPixel); + + //////////////////////////////////////////////////////////// + /// \brief Create a new context that embeds its own rendering target + /// + /// \param shared Context to share the new one with + /// \param settings Creation parameters + /// \param width Back buffer width, in pixels + /// \param height Back buffer height, in pixels + /// + //////////////////////////////////////////////////////////// + DRMContext(DRMContext* shared, const ContextSettings& settings, unsigned int width, unsigned int height); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~DRMContext(); + + //////////////////////////////////////////////////////////// + /// \brief Activate the context as the current target + /// for rendering + /// + /// \param current Whether to make the context current or no longer current + /// + /// \return True on success, false if any error happened + /// + //////////////////////////////////////////////////////////// + virtual bool makeCurrent(bool current) override; + + //////////////////////////////////////////////////////////// + /// \brief Display what has been rendered to the context so far + /// + //////////////////////////////////////////////////////////// + virtual void display() override; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable vertical synchronization + /// + /// Activating vertical synchronization will limit the number + /// of frames displayed to the refresh rate of the monitor. + /// This can avoid some visual artifacts, and limit the framerate + /// to a good value (but not constant across different computers). + /// + /// \param enabled: True to enable v-sync, false to deactivate + /// + //////////////////////////////////////////////////////////// + virtual void setVerticalSyncEnabled(bool enabled) override; + + //////////////////////////////////////////////////////////// + /// \brief Create the EGL context + /// + /// \param shared Context to share the new one with (can be NULL) + /// \param bitsPerPixel Pixel depth, in bits per pixel + /// \param settings Creation parameters + /// + //////////////////////////////////////////////////////////// + void createContext(DRMContext* shared); + + //////////////////////////////////////////////////////////// + /// \brief Create the EGL surface + /// + /// \param width Back buffer width, in pixels + /// \param height Back buffer height, in pixels + /// \param bpp Pixel depth, in bits per pixel + /// \param scanout True to present the surface to the screen + /// + //////////////////////////////////////////////////////////// + void createSurface(unsigned int width, unsigned int height, unsigned int bpp, bool scanout); + + //////////////////////////////////////////////////////////// + /// \brief Destroy the EGL surface + /// + /// This function must be called when the activity is stopped, or + /// when the orientation change. + /// + //////////////////////////////////////////////////////////// + void destroySurface(); + + //////////////////////////////////////////////////////////// + /// \brief Get the best EGL visual for a given set of video settings + /// + /// \param display EGL display + /// \param bitsPerPixel Pixel depth, in bits per pixel + /// \param settings Requested context settings + /// + /// \return The best EGL config + /// + //////////////////////////////////////////////////////////// + static EGLConfig getBestConfig(EGLDisplay display, unsigned int bitsPerPixel, const ContextSettings& settings); + + //////////////////////////////////////////////////////////// + /// \brief Get the address of an OpenGL function + /// + /// \param name Name of the function to get the address of + /// + /// \return Address of the OpenGL function, 0 on failure + /// + //////////////////////////////////////////////////////////// + static GlFunctionPointer getFunction(const char* name); + +protected: + + friend class VideoModeImpl; + friend class WindowImplDRM; + + //////////////////////////////////////////////////////////// + /// \brief Get Direct Rendering Manager pointer + /// + //////////////////////////////////////////////////////////// + static drm* getDRM(); + +private: + + //////////////////////////////////////////////////////////// + /// \brief Helper to copy the picked EGL configuration + /// + //////////////////////////////////////////////////////////// + void updateSettings(); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + EGLDisplay m_display; ///< The internal EGL display + EGLContext m_context; ///< The internal EGL context + EGLSurface m_surface; ///< The internal EGL surface + EGLConfig m_config; ///< The internal EGL config + + gbm_bo* m_currentBO; + gbm_bo* m_nextBO; + gbm_surface* m_gbmSurface; + unsigned int m_width; + unsigned int m_height; + bool m_shown; + bool m_scanOut; +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_DRMCONTEXT_HPP diff --git a/src/SFML/Window/DRM/InputImplUDev.cpp b/src/SFML/Window/DRM/InputImplUDev.cpp new file mode 100644 index 000000000..64ca6a600 --- /dev/null +++ b/src/SFML/Window/DRM/InputImplUDev.cpp @@ -0,0 +1,715 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2016 Andrew Mickelson +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace +{ + struct TouchSlot + { + int oldId; + int id; + sf::Vector2i pos; + + TouchSlot() : + oldId(-1), + id(-1), + pos(0, 0) + { + } + }; + + std::recursive_mutex inputMutex; // threadsafe? maybe... + sf::Vector2i mousePos; // current mouse position + + std::vector fileDescriptors; // list of open file descriptors for /dev/input + std::vector mouseMap(sf::Mouse::ButtonCount, false); // track whether keys are down + std::vector keyMap(sf::Keyboard::KeyCount, false); // track whether mouse buttons are down + + int touchFd = -1; // file descriptor we have seen MT events on; assumes only 1 + std::vector touchSlots; // track the state of each touch "slot" + int currentSlot = 0; // which slot are we currently updating? + + std::queue eventQueue; // events received and waiting to be consumed + const int MAX_QUEUE = 64; // The maximum size we let eventQueue grow to + + termios newTerminalConfig, oldTerminalConfig; // Terminal configurations + + bool altDown() { return (keyMap[sf::Keyboard::LAlt] || keyMap[sf::Keyboard::RAlt]); } + bool controlDown() { return (keyMap[sf::Keyboard::LControl] || keyMap[sf::Keyboard::RControl]); } + bool shiftDown() { return (keyMap[sf::Keyboard::LShift] || keyMap[sf::Keyboard::RShift]); } + bool systemDown() { return (keyMap[sf::Keyboard::LSystem] || keyMap[sf::Keyboard::RSystem]); } + + void uninitFileDescriptors(void) + { + for (std::vector::iterator itr = fileDescriptors.begin(); itr != fileDescriptors.end(); ++itr) + close(*itr); + } + +#define BITS_PER_LONG (sizeof(unsigned long) * 8) +#define NBITS(x) (((x - 1) / BITS_PER_LONG) + 1) +#define OFF(x) (x % BITS_PER_LONG) +#define LONG(x) (x / BITS_PER_LONG) +#define TEST_BIT(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + + // Only keep fileDescriptors that we think are a keyboard, mouse or touchpad/touchscreen + // Joysticks are handled in /src/SFML/Window/Unix/JoystickImpl.cpp + bool keepFileDescriptor(int fileDesc) + { + unsigned long bitmask_ev[NBITS(EV_MAX)]; + unsigned long bitmask_key[NBITS(KEY_MAX)]; + unsigned long bitmask_abs[NBITS(ABS_MAX)]; + unsigned long bitmask_rel[NBITS(REL_MAX)]; + + ioctl(fileDesc, EVIOCGBIT(0, sizeof(bitmask_ev)), &bitmask_ev); + ioctl(fileDesc, EVIOCGBIT(EV_KEY, sizeof(bitmask_key)), &bitmask_key); + ioctl(fileDesc, EVIOCGBIT(EV_ABS, sizeof(bitmask_abs)), &bitmask_abs); + ioctl(fileDesc, EVIOCGBIT(EV_REL, sizeof(bitmask_rel)), &bitmask_rel); + + // This is the keyboard test used by SDL. + // The first 32 bits are ESC, numbers and Q to D; If we have any of those, + // consider it a keyboard device; do not test for KEY_RESERVED, though + bool is_keyboard = (bitmask_key[0] & 0xFFFFFFFE); + + bool is_abs = TEST_BIT(EV_ABS, bitmask_ev) + && TEST_BIT(ABS_X, bitmask_abs) && TEST_BIT(ABS_Y, bitmask_abs); + + bool is_rel = TEST_BIT(EV_REL, bitmask_ev) + && TEST_BIT(REL_X, bitmask_rel) && TEST_BIT(REL_Y, bitmask_rel); + + bool is_mouse = (is_abs || is_rel) && TEST_BIT(BTN_MOUSE, bitmask_key); + + bool is_touch = is_abs && (TEST_BIT(BTN_TOOL_FINGER, bitmask_key) || TEST_BIT(BTN_TOUCH, bitmask_key)); + + return is_keyboard || is_mouse || is_touch; + } + + void initFileDescriptors() + { + static bool initialized = false; + if (initialized) + return; + + initialized = true; + + for (int i = 0; i < 32; i++) + { + std::string name("/dev/input/event"); + std::ostringstream stream; + stream << i; + name += stream.str(); + + int tempFD = open(name.c_str(), O_RDONLY | O_NONBLOCK); + + if (tempFD < 0) + { + if (errno != ENOENT) + sf::err() << "Error opening " << name << ": " << std::strerror(errno) << std::endl; + + continue; + } + + if (keepFileDescriptor(tempFD)) + fileDescriptors.push_back(tempFD); + else + close(tempFD); + } + + std::atexit(uninitFileDescriptors); + } + + sf::Mouse::Button toMouseButton(int code) + { + switch (code) + { + case BTN_LEFT: return sf::Mouse::Left; + case BTN_RIGHT: return sf::Mouse::Right; + case BTN_MIDDLE: return sf::Mouse::Middle; + case BTN_SIDE: return sf::Mouse::XButton1; + case BTN_EXTRA: return sf::Mouse::XButton2; + + default: + return sf::Mouse::ButtonCount; + } + } + + sf::Keyboard::Key toKey(int code) + { + switch (code) + { + case KEY_ESC: return sf::Keyboard::Escape; + case KEY_1: return sf::Keyboard::Num1; + case KEY_2: return sf::Keyboard::Num2; + case KEY_3: return sf::Keyboard::Num3; + case KEY_4: return sf::Keyboard::Num4; + case KEY_5: return sf::Keyboard::Num5; + case KEY_6: return sf::Keyboard::Num6; + case KEY_7: return sf::Keyboard::Num7; + case KEY_8: return sf::Keyboard::Num8; + case KEY_9: return sf::Keyboard::Num9; + case KEY_0: return sf::Keyboard::Num0; + case KEY_MINUS: return sf::Keyboard::Hyphen; + case KEY_EQUAL: return sf::Keyboard::Equal; + case KEY_BACKSPACE: return sf::Keyboard::Backspace; + case KEY_TAB: return sf::Keyboard::Tab; + case KEY_Q: return sf::Keyboard::Q; + case KEY_W: return sf::Keyboard::W; + case KEY_E: return sf::Keyboard::E; + case KEY_R: return sf::Keyboard::R; + case KEY_T: return sf::Keyboard::T; + case KEY_Y: return sf::Keyboard::Y; + case KEY_U: return sf::Keyboard::U; + case KEY_I: return sf::Keyboard::I; + case KEY_O: return sf::Keyboard::O; + case KEY_P: return sf::Keyboard::P; + case KEY_LEFTBRACE: return sf::Keyboard::LBracket; + case KEY_RIGHTBRACE: return sf::Keyboard::RBracket; + case KEY_KPENTER: + case KEY_ENTER: return sf::Keyboard::Enter; + case KEY_LEFTCTRL: return sf::Keyboard::LControl; + case KEY_A: return sf::Keyboard::A; + case KEY_S: return sf::Keyboard::S; + case KEY_D: return sf::Keyboard::D; + case KEY_F: return sf::Keyboard::F; + case KEY_G: return sf::Keyboard::G; + case KEY_H: return sf::Keyboard::H; + case KEY_J: return sf::Keyboard::J; + case KEY_K: return sf::Keyboard::K; + case KEY_L: return sf::Keyboard::L; + case KEY_SEMICOLON: return sf::Keyboard::Semicolon; + case KEY_APOSTROPHE: return sf::Keyboard::Quote; + case KEY_GRAVE: return sf::Keyboard::Tilde; + case KEY_LEFTSHIFT: return sf::Keyboard::LShift; + case KEY_BACKSLASH: return sf::Keyboard::Backslash; + case KEY_Z: return sf::Keyboard::Z; + case KEY_X: return sf::Keyboard::X; + case KEY_C: return sf::Keyboard::C; + case KEY_V: return sf::Keyboard::V; + case KEY_B: return sf::Keyboard::B; + case KEY_N: return sf::Keyboard::N; + case KEY_M: return sf::Keyboard::M; + case KEY_COMMA: return sf::Keyboard::Comma; + case KEY_DOT: return sf::Keyboard::Period; + case KEY_SLASH: return sf::Keyboard::Slash; + case KEY_RIGHTSHIFT: return sf::Keyboard::RShift; + case KEY_KPASTERISK: return sf::Keyboard::Multiply; + case KEY_LEFTALT: return sf::Keyboard::LAlt; + case KEY_SPACE: return sf::Keyboard::Space; + case KEY_F1: return sf::Keyboard::F1; + case KEY_F2: return sf::Keyboard::F2; + case KEY_F3: return sf::Keyboard::F3; + case KEY_F4: return sf::Keyboard::F4; + case KEY_F5: return sf::Keyboard::F5; + case KEY_F6: return sf::Keyboard::F6; + case KEY_F7: return sf::Keyboard::F7; + case KEY_F8: return sf::Keyboard::F8; + case KEY_F9: return sf::Keyboard::F9; + case KEY_F10: return sf::Keyboard::F10; + case KEY_F11: return sf::Keyboard::F11; + case KEY_F12: return sf::Keyboard::F12; + case KEY_F13: return sf::Keyboard::F13; + case KEY_F14: return sf::Keyboard::F14; + case KEY_F15: return sf::Keyboard::F15; + case KEY_KP7: return sf::Keyboard::Numpad7; + case KEY_KP8: return sf::Keyboard::Numpad8; + case KEY_KP9: return sf::Keyboard::Numpad9; + case KEY_KPMINUS: return sf::Keyboard::Subtract; + case KEY_KP4: return sf::Keyboard::Numpad4; + case KEY_KP5: return sf::Keyboard::Numpad5; + case KEY_KP6: return sf::Keyboard::Numpad6; + case KEY_KPPLUS: return sf::Keyboard::Add; + case KEY_KP1: return sf::Keyboard::Numpad1; + case KEY_KP2: return sf::Keyboard::Numpad2; + case KEY_KP3: return sf::Keyboard::Numpad3; + case KEY_KP0: return sf::Keyboard::Numpad0; + case KEY_KPDOT: return sf::Keyboard::Delete; + case KEY_RIGHTCTRL: return sf::Keyboard::RControl; + case KEY_KPSLASH: return sf::Keyboard::Divide; + case KEY_RIGHTALT: return sf::Keyboard::RAlt; + case KEY_HOME: return sf::Keyboard::Home; + case KEY_UP: return sf::Keyboard::Up; + case KEY_PAGEUP: return sf::Keyboard::PageUp; + case KEY_LEFT: return sf::Keyboard::Left; + case KEY_RIGHT: return sf::Keyboard::Right; + case KEY_END: return sf::Keyboard::End; + case KEY_DOWN: return sf::Keyboard::Down; + case KEY_PAGEDOWN: return sf::Keyboard::PageDown; + case KEY_INSERT: return sf::Keyboard::Insert; + case KEY_DELETE: return sf::Keyboard::Delete; + case KEY_PAUSE: return sf::Keyboard::Pause; + case KEY_LEFTMETA: return sf::Keyboard::LSystem; + case KEY_RIGHTMETA: return sf::Keyboard::RSystem; + + case KEY_RESERVED: + case KEY_SYSRQ: + case KEY_CAPSLOCK: + case KEY_NUMLOCK: + case KEY_SCROLLLOCK: + default: + return sf::Keyboard::Unknown; + } + } + + void pushEvent(sf::Event& event) + { + if (eventQueue.size() >= MAX_QUEUE) + eventQueue.pop(); + + eventQueue.push(event); + } + + TouchSlot& atSlot(int idx) + { + if (idx >= static_cast(touchSlots.size())) + touchSlots.resize(static_cast(idx + 1)); + return touchSlots.at(static_cast(idx)); + } + + void processSlots() + { + for (std::vector::iterator slot = touchSlots.begin(); slot != touchSlots.end(); ++slot) + { + sf::Event event; + + event.touch.x = slot->pos.x; + event.touch.y = slot->pos.y; + + if (slot->oldId == slot->id) + { + event.type = sf::Event::TouchMoved; + event.touch.finger = static_cast(slot->id); + pushEvent(event); + } + else + { + if (slot->oldId != -1) + { + event.type = sf::Event::TouchEnded; + event.touch.finger = static_cast(slot->oldId); + pushEvent(event); + } + if (slot->id != -1) + { + event.type = sf::Event::TouchBegan; + event.touch.finger = static_cast(slot->id); + pushEvent(event); + } + + slot->oldId = slot->id; + } + } + } + + bool eventProcess(sf::Event& event) + { + std::scoped_lock lock(inputMutex); + + // Ensure that we are initialized + initFileDescriptors(); + + // This is for handling the Backspace and DEL text events, which we + // generate based on keystrokes (and not stdin) + static unsigned int doDeferredText = 0; + if (doDeferredText) + { + event.type = sf::Event::TextEntered; + event.text.unicode = doDeferredText; + doDeferredText = 0; + return true; + } + + ssize_t bytesRead; + + // Check all the open file descriptors for the next event + for (std::vector::iterator itr = fileDescriptors.begin(); itr != fileDescriptors.end(); ++itr) + { + input_event inputEvent; + bytesRead = read(*itr, &inputEvent, sizeof(inputEvent)); + + while (bytesRead > 0) + { + if (inputEvent.type == EV_KEY) + { + sf::Mouse::Button mb = toMouseButton(inputEvent.code); + if (mb != sf::Mouse::ButtonCount) + { + event.type = inputEvent.value ? sf::Event::MouseButtonPressed : sf::Event::MouseButtonReleased; + event.mouseButton.button = mb; + event.mouseButton.x = mousePos.x; + event.mouseButton.y = mousePos.y; + + mouseMap[mb] = inputEvent.value; + return true; + } + else + { + sf::Keyboard::Key kb = toKey(inputEvent.code); + + unsigned int special = 0; + if ((kb == sf::Keyboard::Delete) + || (kb == sf::Keyboard::Backspace)) + special = (kb == sf::Keyboard::Delete) ? 127 : 8; + + if (inputEvent.value == 2) + { + // key repeat events + // + if (special) + { + event.type = sf::Event::TextEntered; + event.text.unicode = special; + return true; + } + } + else if (kb != sf::Keyboard::Unknown) + { + // key down and key up events + // + event.type = inputEvent.value ? sf::Event::KeyPressed : sf::Event::KeyReleased; + event.key.code = kb; + event.key.alt = altDown(); + event.key.control = controlDown(); + event.key.shift = shiftDown(); + event.key.system = systemDown(); + + keyMap[kb] = inputEvent.value; + + if (special && inputEvent.value) + doDeferredText = special; + + return true; + } + } + } + else if (inputEvent.type == EV_REL) + { + bool posChange = false; + switch (inputEvent.code) + { + case REL_X: + mousePos.x += inputEvent.value; + posChange = true; + break; + + case REL_Y: + mousePos.y += inputEvent.value; + posChange = true; + break; + + case REL_WHEEL: + event.type = sf::Event::MouseWheelScrolled; + event.mouseWheelScroll.delta = static_cast(inputEvent.value); + event.mouseWheelScroll.x = mousePos.x; + event.mouseWheelScroll.y = mousePos.y; + return true; + } + + if (posChange) + { + event.type = sf::Event::MouseMoved; + event.mouseMove.x = mousePos.x; + event.mouseMove.y = mousePos.y; + return true; + } + } + else if (inputEvent.type == EV_ABS) + { + switch (inputEvent.code) + { + case ABS_MT_SLOT: + currentSlot = inputEvent.value; + touchFd = *itr; + break; + case ABS_MT_TRACKING_ID: + atSlot(currentSlot).id = inputEvent.value; + touchFd = *itr; + break; + case ABS_MT_POSITION_X: + atSlot(currentSlot).pos.x = inputEvent.value; + touchFd = *itr; + break; + case ABS_MT_POSITION_Y: + atSlot(currentSlot).pos.y = inputEvent.value; + touchFd = *itr; + break; + } + } + else if (inputEvent.type == EV_SYN && inputEvent.code == SYN_REPORT && + *itr == touchFd) + { + // This pushes events directly to the queue, because it + // can generate more than one event. + processSlots(); + } + + bytesRead = read(*itr, &inputEvent, sizeof(inputEvent)); + } + + if ((bytesRead < 0) && (errno != EAGAIN)) + sf::err() << " Error: " << std::strerror(errno) << std::endl; + } + + // Finally check if there is a Text event on stdin + // + // We only clear the ICANON flag for the time of reading + + newTerminalConfig.c_lflag &= ~(tcflag_t)ICANON; + tcsetattr(STDIN_FILENO, TCSANOW, &newTerminalConfig); + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + unsigned char code = 0; + + fd_set readFDSet; + FD_ZERO(&readFDSet); + FD_SET(STDIN_FILENO, &readFDSet); + int ready = select(STDIN_FILENO + 1, &readFDSet, NULL, NULL, &timeout); + + if (ready > 0 && FD_ISSET(STDIN_FILENO, &readFDSet)) + bytesRead = read(STDIN_FILENO, &code, 1); + + if ((code == 127) || (code == 8)) // Suppress 127 (DEL) to 8 (BACKSPACE) + code = 0; + else if (code == 27) // ESC + { + // Suppress ANSI escape sequences + FD_ZERO(&readFDSet); + FD_SET(STDIN_FILENO, &readFDSet); + ready = select(STDIN_FILENO + 1, &readFDSet, NULL, NULL, &timeout); + if (ready > 0 && FD_ISSET(STDIN_FILENO, &readFDSet)) + { + unsigned char tempBuffer[16]; + bytesRead = read(STDIN_FILENO, tempBuffer, 16); + code = 0; + } + } + + newTerminalConfig.c_lflag |= ICANON; + tcsetattr(STDIN_FILENO, TCSANOW, &newTerminalConfig); + + if (code > 0) + { + // TODO: Proper unicode handling + event.type = sf::Event::TextEntered; + event.text.unicode = code; + return true; + } + + // No events available + return false; + } + + // assumes inputMutex is locked + void update() + { + sf::Event event; + while (eventProcess(event)) + { + pushEvent(event); + } + } +} + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +bool InputImpl::isKeyPressed(Keyboard::Key key) +{ + std::scoped_lock lock(inputMutex); + if ((key < 0) || (key >= static_cast(keyMap.size()))) + return false; + + update(); + return keyMap[key]; +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setVirtualKeyboardVisible(bool /*visible*/) +{ + // Not applicable +} + + +//////////////////////////////////////////////////////////// +bool InputImpl::isMouseButtonPressed(Mouse::Button button) +{ + std::scoped_lock lock(inputMutex); + if ((button < 0) || (button >= static_cast(mouseMap.size()))) + return false; + + update(); + return mouseMap[button]; +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getMousePosition() +{ + std::scoped_lock lock(inputMutex); + return mousePos; +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getMousePosition(const WindowBase& /*relativeTo*/) +{ + return getMousePosition(); +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setMousePosition(const Vector2i& position) +{ + std::scoped_lock lock(inputMutex); + mousePos = position; +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setMousePosition(const Vector2i& position, const WindowBase& /*relativeTo*/) +{ + setMousePosition(position); +} + + +//////////////////////////////////////////////////////////// +bool InputImpl::isTouchDown(unsigned int finger) +{ + for (std::vector::iterator slot = touchSlots.begin(); slot != touchSlots.end(); ++slot) + { + if (slot->id == static_cast(finger)) + return true; + } + + return false; +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getTouchPosition(unsigned int finger) +{ + for (std::vector::iterator slot = touchSlots.begin(); slot != touchSlots.end(); ++slot) + { + if (slot->id == static_cast(finger)) + return slot->pos; + } + + return Vector2i(); +} + + +//////////////////////////////////////////////////////////// +Vector2i InputImpl::getTouchPosition(unsigned int finger, const WindowBase& /*relativeTo*/) +{ + return getTouchPosition(finger); +} + + +//////////////////////////////////////////////////////////// +bool InputImpl::checkEvent(sf::Event& event) +{ + std::scoped_lock lock(inputMutex); + if (!eventQueue.empty()) + { + event = eventQueue.front(); + eventQueue.pop(); + + return true; + } + + if (eventProcess(event)) + { + return true; + } + else + { + // In the case of multitouch, eventProcess() could have returned false + // but added events directly to the queue. (This is ugly, but I'm not + // sure of a good way to handle generating multiple events at once.) + if (!eventQueue.empty()) + { + event = eventQueue.front(); + eventQueue.pop(); + + return true; + } + } + + return false; +} + + +//////////////////////////////////////////////////////////// +void InputImpl::setTerminalConfig() +{ + std::scoped_lock lock(inputMutex); + initFileDescriptors(); + + tcgetattr(STDIN_FILENO, &newTerminalConfig); // get current terminal config + oldTerminalConfig = newTerminalConfig; // create a backup + newTerminalConfig.c_lflag &= ~(tcflag_t)ECHO; // disable console feedback + newTerminalConfig.c_lflag &= ~(tcflag_t)ISIG; // disable signals + newTerminalConfig.c_lflag |= ICANON; // disable noncanonical mode + newTerminalConfig.c_iflag |= IGNCR; // ignore carriage return + tcsetattr(STDIN_FILENO, TCSANOW, &newTerminalConfig); // set our new config + tcflush(STDIN_FILENO, TCIFLUSH); // flush the buffer +} + + +//////////////////////////////////////////////////////////// +void InputImpl::restoreTerminalConfig() +{ + std::scoped_lock lock(inputMutex); + initFileDescriptors(); + + tcsetattr(STDIN_FILENO, TCSANOW, &oldTerminalConfig); // restore terminal config + tcflush(STDIN_FILENO, TCIFLUSH); // flush the buffer +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/DRM/InputImplUDev.hpp b/src/SFML/Window/DRM/InputImplUDev.hpp new file mode 100644 index 000000000..4838dbf10 --- /dev/null +++ b/src/SFML/Window/DRM/InputImplUDev.hpp @@ -0,0 +1,193 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2016 Andrew Mickelson +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_INPUTIMPLUDEV_HPP +#define SFML_INPUTIMPLUDEV_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief Linux (DRM) implementation of inputs (keyboard + mouse) +/// +//////////////////////////////////////////////////////////// +class InputImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Check if a key is pressed + /// + /// \param key Key to check + /// + /// \return True if the key is pressed, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isKeyPressed(Keyboard::Key key); + + //////////////////////////////////////////////////////////// + /// \brief Show or hide the virtual keyboard + /// + /// \param visible True to show, false to hide + /// + //////////////////////////////////////////////////////////// + static void setVirtualKeyboardVisible(bool visible); + + //////////////////////////////////////////////////////////// + /// \brief Check if a mouse button is pressed + /// + /// \param button Button to check + /// + /// \return True if the button is pressed, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isMouseButtonPressed(Mouse::Button button); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of the mouse in desktop coordinates + /// + /// This function returns the current position of the mouse + /// cursor, in global (desktop) coordinates. + /// + /// \return Current position of the mouse + /// + //////////////////////////////////////////////////////////// + static Vector2i getMousePosition(); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of the mouse in window coordinates + /// + /// This function returns the current position of the mouse + /// cursor, relative to the given window. + /// If no window is used, it returns desktop coordinates. + /// + /// \param relativeTo Reference window + /// + /// \return Current position of the mouse + /// + //////////////////////////////////////////////////////////// + static Vector2i getMousePosition(const WindowBase& relativeTo); + + //////////////////////////////////////////////////////////// + /// \brief Set the current position of the mouse in desktop coordinates + /// + /// This function sets the current position of the mouse + /// cursor in global (desktop) coordinates. + /// If no window is used, it sets the position in desktop coordinates. + /// + /// \param position New position of the mouse + /// + //////////////////////////////////////////////////////////// + static void setMousePosition(const Vector2i& position); + + //////////////////////////////////////////////////////////// + /// \brief Set the current position of the mouse in window coordinates + /// + /// This function sets the current position of the mouse + /// cursor, relative to the given window. + /// If no window is used, it sets the position in desktop coordinates. + /// + /// \param position New position of the mouse + /// \param relativeTo Reference window + /// + //////////////////////////////////////////////////////////// + static void setMousePosition(const Vector2i& position, const WindowBase& relativeTo); + + //////////////////////////////////////////////////////////// + /// \brief Check if a touch event is currently down + /// + /// \param finger Finger index + /// + /// \return True if \a finger is currently touching the screen, false otherwise + /// + //////////////////////////////////////////////////////////// + static bool isTouchDown(unsigned int finger); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of a touch in desktop coordinates + /// + /// This function returns the current touch position + /// in global (desktop) coordinates. + /// + /// \param finger Finger index + /// + /// \return Current position of \a finger, or undefined if it's not down + /// + //////////////////////////////////////////////////////////// + static Vector2i getTouchPosition(unsigned int finger); + + //////////////////////////////////////////////////////////// + /// \brief Get the current position of a touch in window coordinates + /// + /// This function returns the current touch position + /// in global (desktop) coordinates. + /// + /// \param finger Finger index + /// \param relativeTo Reference window + /// + /// \return Current position of \a finger, or undefined if it's not down + /// + //////////////////////////////////////////////////////////// + static Vector2i getTouchPosition(unsigned int finger, const WindowBase& relativeTo); + +private: + + friend class WindowImplDRM; + + //////////////////////////////////////////////////////////// + /// \brief Fetch input event from event queue + /// + /// \return False if event queue is empty + /// + //////////////////////////////////////////////////////////// + static bool checkEvent(sf::Event& event); + + //////////////////////////////////////////////////////////// + /// \brief Backup terminal configuration and disable console feedback + /// + //////////////////////////////////////////////////////////// + static void setTerminalConfig(); + + //////////////////////////////////////////////////////////// + /// \brief Restore terminal configuration from backup + /// + //////////////////////////////////////////////////////////// + static void restoreTerminalConfig(); +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_INPUTIMPLUDEV_HPP diff --git a/src/SFML/Window/DRM/VideoModeImpl.cpp b/src/SFML/Window/DRM/VideoModeImpl.cpp new file mode 100644 index 000000000..13471a474 --- /dev/null +++ b/src/SFML/Window/DRM/VideoModeImpl.cpp @@ -0,0 +1,73 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2020 Andrew Mickelson +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +std::vector VideoModeImpl::getFullscreenModes() +{ + std::vector modes; + + drm* drm = sf::priv::DRMContext::getDRM(); + drmModeConnectorPtr conn = drm->saved_connector; + + if (conn) + { + for (int i = 0; i < conn->count_modes; i++) + modes.push_back( + VideoMode(conn->modes[i].hdisplay, + conn->modes[i].vdisplay)); + } + else + modes.push_back(getDesktopMode()); + + return modes; +} + + +//////////////////////////////////////////////////////////// +VideoMode VideoModeImpl::getDesktopMode() +{ + drm* drm = sf::priv::DRMContext::getDRM(); + drmModeModeInfoPtr ptr = drm->mode; + if (ptr) + return VideoMode(ptr->hdisplay, ptr->vdisplay); + else + return VideoMode(0, 0); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/DRM/WindowImplDRM.cpp b/src/SFML/Window/DRM/WindowImplDRM.cpp new file mode 100644 index 000000000..761245c0e --- /dev/null +++ b/src/SFML/Window/DRM/WindowImplDRM.cpp @@ -0,0 +1,161 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2020 Andrew Mickelson +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include + + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +WindowImplDRM::WindowImplDRM(WindowHandle /*handle*/) : +m_size(0, 0) +{ + sf::priv::InputImpl::setTerminalConfig(); +} + + +//////////////////////////////////////////////////////////// +WindowImplDRM::WindowImplDRM(VideoMode mode, const String& /*title*/, unsigned long /*style*/, const ContextSettings& /*settings*/) : +m_size(mode.width, mode.height) +{ + sf::priv::InputImpl::setTerminalConfig(); +} + + +//////////////////////////////////////////////////////////// +WindowImplDRM::~WindowImplDRM() +{ + sf::priv::InputImpl::restoreTerminalConfig(); +} + + +//////////////////////////////////////////////////////////// +WindowHandle WindowImplDRM::getSystemHandle() const +{ + drm* drm = sf::priv::DRMContext::getDRM(); + return static_cast(drm->fd); +} + +//////////////////////////////////////////////////////////// +Vector2i WindowImplDRM::getPosition() const +{ + return Vector2i(0, 0); +} + + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setPosition(const Vector2i& /*position*/) +{ +} + + +//////////////////////////////////////////////////////////// +Vector2u WindowImplDRM::getSize() const +{ + return m_size; +} + + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setSize(const Vector2u& /*size*/) +{ +} + + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setTitle(const String& /*title*/) +{ +} + + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setIcon(unsigned int /*width*/, unsigned int /*height*/, const Uint8* /*pixels*/) +{ +} + + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setVisible(bool /*visible*/) +{ +} + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setMouseCursorVisible(bool /*visible*/) +{ + // TODO: not implemented +} + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setMouseCursorGrabbed(bool /*grabbed*/) +{ + //TODO: not implemented +} + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setMouseCursor(const CursorImpl& /*cursor*/) +{ + // TODO: not implemented +} + +//////////////////////////////////////////////////////////// +void WindowImplDRM::setKeyRepeatEnabled(bool /*enabled*/) +{ + // TODO: not implemented +} + + +//////////////////////////////////////////////////////////// +void WindowImplDRM::requestFocus() +{ + // Not applicable +} + + +//////////////////////////////////////////////////////////// +bool WindowImplDRM::hasFocus() const +{ + return true; +} + +void WindowImplDRM::processEvents() +{ + sf::Event ev; + while (sf::priv::InputImpl::checkEvent(ev)) + pushEvent(ev); +} + +} // namespace priv + +} // namespace sf diff --git a/src/SFML/Window/DRM/WindowImplDRM.hpp b/src/SFML/Window/DRM/WindowImplDRM.hpp new file mode 100644 index 000000000..f3f756970 --- /dev/null +++ b/src/SFML/Window/DRM/WindowImplDRM.hpp @@ -0,0 +1,205 @@ +//////////////////////////////////////////////////////////// +// +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2020 Andrew Mickelson +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. +// +//////////////////////////////////////////////////////////// + +#ifndef SFML_WINDOWIMPLDRM_HPP +#define SFML_WINDOWIMPLDRM_HPP + +//////////////////////////////////////////////////////////// +// Headers +//////////////////////////////////////////////////////////// +#include +#include + +namespace sf +{ +namespace priv +{ +//////////////////////////////////////////////////////////// +/// \brief DRM implementation of WindowImpl +/// +//////////////////////////////////////////////////////////// +class WindowImplDRM : public WindowImpl +{ +public: + + //////////////////////////////////////////////////////////// + /// \brief Construct the window implementation from an existing control + /// + /// \param handle Platform-specific handle of the control + /// + //////////////////////////////////////////////////////////// + WindowImplDRM(WindowHandle handle); + + //////////////////////////////////////////////////////////// + /// \brief Create the window implementation + /// + /// \param mode Video mode to use + /// \param title Title of the window + /// \param style Window style (resizable, fixed, or fullscren) + /// \param settings Additional settings for the underlying OpenGL context + /// + //////////////////////////////////////////////////////////// + WindowImplDRM(VideoMode mode, const String& title, unsigned long style, const ContextSettings& settings); + + //////////////////////////////////////////////////////////// + /// \brief Destructor + /// + //////////////////////////////////////////////////////////// + ~WindowImplDRM(); + + //////////////////////////////////////////////////////////// + /// \brief Get the OS-specific handle of the window + /// + /// \return Handle of the window + /// + //////////////////////////////////////////////////////////// + virtual WindowHandle getSystemHandle() const override; + + //////////////////////////////////////////////////////////// + /// \brief Get the position of the window + /// + /// \return Position of the window, in pixels + /// + //////////////////////////////////////////////////////////// + virtual Vector2i getPosition() const override; + + //////////////////////////////////////////////////////////// + /// \brief Change the position of the window on screen + /// + /// \param position New position of the window, in pixels + /// + //////////////////////////////////////////////////////////// + virtual void setPosition(const Vector2i& position) override; + + //////////////////////////////////////////////////////////// + /// \brief Get the client size of the window + /// + /// \return Size of the window, in pixels + /// + //////////////////////////////////////////////////////////// + virtual Vector2u getSize() const override; + + //////////////////////////////////////////////////////////// + /// \brief Change the size of the rendering region of the window + /// + /// \param size New size, in pixels + /// + //////////////////////////////////////////////////////////// + virtual void setSize(const Vector2u& size) override; + + //////////////////////////////////////////////////////////// + /// \brief Change the title of the window + /// + /// \param title New title + /// + //////////////////////////////////////////////////////////// + virtual void setTitle(const String& title) override; + + //////////////////////////////////////////////////////////// + /// \brief Change the window's icon + /// + /// \param width Icon's width, in pixels + /// \param height Icon's height, in pixels + /// \param pixels Pointer to the pixels in memory, format must be RGBA 32 bits + /// + //////////////////////////////////////////////////////////// + virtual void setIcon(unsigned int width, unsigned int height, const Uint8* pixels) override; + + //////////////////////////////////////////////////////////// + /// \brief Show or hide the window + /// + /// \param visible True to show, false to hide + /// + //////////////////////////////////////////////////////////// + virtual void setVisible(bool visible) override; + + //////////////////////////////////////////////////////////// + /// \brief Show or hide the mouse cursor + /// + /// \param visible True to show, false to hide + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorVisible(bool visible) override; + + //////////////////////////////////////////////////////////// + /// \brief Grab or release the mouse cursor + /// + /// \param grabbed True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursorGrabbed(bool grabbed) override; + + //////////////////////////////////////////////////////////// + /// \brief Set the displayed cursor to a native system cursor + /// + /// \param cursor Native system cursor type to display + /// + //////////////////////////////////////////////////////////// + virtual void setMouseCursor(const CursorImpl& cursor) override; + + //////////////////////////////////////////////////////////// + /// \brief Enable or disable automatic key-repeat + /// + /// \param enabled True to enable, false to disable + /// + //////////////////////////////////////////////////////////// + virtual void setKeyRepeatEnabled(bool enabled) override; + + //////////////////////////////////////////////////////////// + /// \brief Request the current window to be made the active + /// foreground window + /// + //////////////////////////////////////////////////////////// + virtual void requestFocus() override; + + //////////////////////////////////////////////////////////// + /// \brief Check whether the window has the input focus + /// + /// \return True if window has focus, false otherwise + /// + //////////////////////////////////////////////////////////// + virtual bool hasFocus() const override; + +protected: + + //////////////////////////////////////////////////////////// + /// \brief Process incoming events from the operating system + /// + //////////////////////////////////////////////////////////// + virtual void processEvents() override; + +private: + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + Vector2u m_size; ///< Window size +}; + +} // namespace priv + +} // namespace sf + + +#endif // SFML_WINDOWIMPLDRM_HPP diff --git a/src/SFML/Window/EglContext.cpp b/src/SFML/Window/EglContext.cpp index 75b0512e2..98eb10492 100644 --- a/src/SFML/Window/EglContext.cpp +++ b/src/SFML/Window/EglContext.cpp @@ -35,7 +35,7 @@ #ifdef SFML_SYSTEM_ANDROID #include #endif -#ifdef SFML_SYSTEM_LINUX +#if defined(SFML_SYSTEM_LINUX) && !defined(SFML_USE_DRM) #include #endif @@ -361,7 +361,7 @@ void EglContext::updateSettings() } -#ifdef SFML_SYSTEM_LINUX +#if defined(SFML_SYSTEM_LINUX) && !defined(SFML_USE_DRM) //////////////////////////////////////////////////////////// XVisualInfo EglContext::selectBestVisual(::Display* XDisplay, unsigned int bitsPerPixel, const ContextSettings& settings) { diff --git a/src/SFML/Window/EglContext.hpp b/src/SFML/Window/EglContext.hpp index 7858b4ce7..eae711c5e 100644 --- a/src/SFML/Window/EglContext.hpp +++ b/src/SFML/Window/EglContext.hpp @@ -34,7 +34,7 @@ #include #include // Prevent conflict with macro None from Xlib #include -#ifdef SFML_SYSTEM_LINUX +#if defined(SFML_SYSTEM_LINUX) && !defined(SFML_USE_DRM) #include #endif @@ -165,7 +165,7 @@ public: //////////////////////////////////////////////////////////// static EGLConfig getBestConfig(EGLDisplay display, unsigned int bitsPerPixel, const ContextSettings& settings); -#ifdef SFML_SYSTEM_LINUX +#if defined(SFML_SYSTEM_LINUX) && !defined(SFML_USE_DRM) //////////////////////////////////////////////////////////// /// \brief Select the best EGL visual for a given set of settings /// diff --git a/src/SFML/Window/GlContext.cpp b/src/SFML/Window/GlContext.cpp index 2072042ab..605dc727b 100644 --- a/src/SFML/Window/GlContext.cpp +++ b/src/SFML/Window/GlContext.cpp @@ -65,6 +65,11 @@ using ContextType = sf::priv::EglContext; + #elif defined(SFML_USE_DRM) + + #include + using ContextType = sf::priv::DRMContext; + #else #include diff --git a/src/SFML/Window/InputImpl.hpp b/src/SFML/Window/InputImpl.hpp index ffc661803..9655f7078 100644 --- a/src/SFML/Window/InputImpl.hpp +++ b/src/SFML/Window/InputImpl.hpp @@ -33,7 +33,11 @@ #if defined(SFML_SYSTEM_WINDOWS) #include #elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD) || defined(SFML_SYSTEM_OPENBSD) || defined(SFML_SYSTEM_NETBSD) - #include + #if defined(SFML_USE_DRM) + #include + #else + #include + #endif #elif defined(SFML_SYSTEM_MACOS) #include #elif defined(SFML_SYSTEM_IOS) @@ -42,5 +46,4 @@ #include #endif - #endif // SFML_INPUTIMPL_HPP diff --git a/src/SFML/Window/Vulkan.cpp b/src/SFML/Window/Vulkan.cpp index a5cc86161..9dce81636 100644 --- a/src/SFML/Window/Vulkan.cpp +++ b/src/SFML/Window/Vulkan.cpp @@ -34,8 +34,16 @@ using VulkanImplType = sf::priv::VulkanImplWin32; #elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD) || defined(SFML_SYSTEM_OPENBSD) || defined(SFML_SYSTEM_NETBSD) -#include -using VulkanImplType = sf::priv::VulkanImplX11; + #if defined(SFML_USE_DRM) + + #define SFML_VULKAN_IMPLEMENTATION_NOT_AVAILABLE + + #else + + #include + using VulkanImplType = sf::priv::VulkanImplX11; + + #endif #else diff --git a/src/SFML/Window/WindowImpl.cpp b/src/SFML/Window/WindowImpl.cpp index a98e88acc..3e45efe11 100644 --- a/src/SFML/Window/WindowImpl.cpp +++ b/src/SFML/Window/WindowImpl.cpp @@ -46,11 +46,22 @@ #elif defined(SFML_SYSTEM_LINUX) || defined(SFML_SYSTEM_FREEBSD) || defined(SFML_SYSTEM_OPENBSD) || defined(SFML_SYSTEM_NETBSD) - #include - using WindowImplType = sf::priv::WindowImplX11; + #if defined(SFML_USE_DRM) - #include - using VulkanImplType = sf::priv::VulkanImplX11; + #include + using WindowImplType = sf::priv::WindowImplDRM; + + #define SFML_VULKAN_IMPLEMENTATION_NOT_AVAILABLE + + #else + + #include + using WindowImplType = sf::priv::WindowImplX11; + + #include + using VulkanImplType = sf::priv::VulkanImplX11; + + #endif #elif defined(SFML_SYSTEM_MACOS) @@ -80,6 +91,7 @@ namespace sf { namespace priv { + //////////////////////////////////////////////////////////// struct WindowImpl::JoystickStatesImpl {