From 0310b03aefc9243394610f71e174a122902774a1 Mon Sep 17 00:00:00 2001 From: kimci86 Date: Sun, 13 Nov 2022 15:08:00 +0100 Subject: [PATCH 1/7] Document that checking XButton1 and XButton2 state on X11 is not supported --- include/SFML/Window/Mouse.hpp | 3 +++ src/SFML/Window/Unix/InputImpl.cpp | 3 +++ src/SFML/Window/Unix/WindowImplX11.cpp | 3 +-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/include/SFML/Window/Mouse.hpp b/include/SFML/Window/Mouse.hpp index 10cfa5ff7..13d280cbf 100644 --- a/include/SFML/Window/Mouse.hpp +++ b/include/SFML/Window/Mouse.hpp @@ -72,6 +72,9 @@ public: //////////////////////////////////////////////////////////// /// \brief Check if a mouse button is pressed /// + /// \warning Checking the state of buttons Mouse::XButton1 and + /// Mouse::XButton2 is not supported on Linux with X11. + /// /// \param button Button to check /// /// \return True if the button is pressed, false otherwise diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index aaf450413..dc0f38203 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -202,6 +202,9 @@ bool InputImpl::isMouseButtonPressed(Mouse::Button button) // Close the connection with the X server CloseDisplay(display); + // Buttons 4 and 5 are the vertical wheel and 6 and 7 the horizontal wheel. + // There is no mask for buttons 8 and 9, so checking the state of buttons + // Mouse::XButton1 and Mouse::XButton2 is not supported. switch (button) { case Mouse::Left: return buttons & Button1Mask; diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index fb2a6ff77..ef8e93e70 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -2020,8 +2020,7 @@ bool WindowImplX11::processEvent(XEvent& windowEvent) // Mouse button pressed case ButtonPress: { - // XXX: Why button 8 and 9? - // Because 4 and 5 are the vertical wheel and 6 and 7 are horizontal wheel ;) + // Buttons 4 and 5 are the vertical wheel and 6 and 7 the horizontal wheel. unsigned int button = windowEvent.xbutton.button; if ((button == Button1) || (button == Button2) || From c744369552ff61f482c3615dc4c2495a77b7b125 Mon Sep 17 00:00:00 2001 From: Bruno Van de Velde Date: Sat, 5 Nov 2022 10:51:36 +0100 Subject: [PATCH 2/7] Use Per Monitor Aware instead of System Aware for DPI scaling on Windows --- src/SFML/Window/Win32/WindowImplWin32.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/SFML/Window/Win32/WindowImplWin32.cpp b/src/SFML/Window/Win32/WindowImplWin32.cpp index 68df74f23..8b6ba3b74 100755 --- a/src/SFML/Window/Win32/WindowImplWin32.cpp +++ b/src/SFML/Window/Win32/WindowImplWin32.cpp @@ -92,8 +92,12 @@ namespace { // We only check for E_INVALIDARG because we would get // E_ACCESSDENIED if the DPI was already set previously - // and S_OK means the call was successful - if (SetProcessDpiAwarenessFunc(ProcessSystemDpiAware) == E_INVALIDARG) + // and S_OK means the call was successful. + // We intentionally don't use Per Monitor V2 which can be + // enabled with SetProcessDpiAwarenessContext, because that + // would scale the title bar and thus change window size + // by default when moving the window between monitors. + if (SetProcessDpiAwarenessFunc(ProcessPerMonitorDpiAware) == E_INVALIDARG) { sf::err() << "Failed to set process DPI awareness" << std::endl; } From fa2e61b9d5864f6a909c5ad7130c49903948a785 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Sun, 11 Dec 2022 14:53:33 -0700 Subject: [PATCH 3/7] Lock CI jobs to Ubuntu 20 Recently GitHub updated the ubuntu-latest images to point at Ubuntu 22 which broke CI for ths 2.6.x branch so let's stick to Ubuntu 20 until we can sort out what was failing. --- .github/workflows/ci.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 52896d8d4..a78fd977c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,8 +14,8 @@ jobs: - { name: Windows VS2019, os: windows-2019 } - { name: Windows VS2022, os: windows-2022 } - { name: Windows VS2022 Clang, os: windows-2022, flags: -T ClangCL } - - { name: Linux GCC, os: ubuntu-latest } - - { name: Linux Clang, os: ubuntu-latest, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } + - { name: Linux GCC, os: ubuntu-20.04 } + - { name: Linux Clang, os: ubuntu-20.04, flags: -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ } - { name: MacOS XCode, os: macos-11 } config: - { name: Shared, flags: -DBUILD_SHARED_LIBS=TRUE } @@ -28,17 +28,17 @@ jobs: config: { name: Frameworks, flags: -DSFML_BUILD_FRAMEWORKS=TRUE } - platform: { name: MacOS XCode, os: macos-11 } config: { name: iOS, flags: -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/cmake/toolchains/iOS.toolchain.cmake -DIOS_PLATFORM=SIMULATOR } - - platform: { name: Android, os: ubuntu-latest } + - platform: { name: Android, os: ubuntu-20.04 } 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-r18b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 } - - platform: { name: Android, os: ubuntu-latest } + - platform: { name: Android, os: ubuntu-20.04 } 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-r18b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 } - - platform: { name: Android, os: ubuntu-latest } + - platform: { name: Android, os: ubuntu-20.04 } config: { name: arm64-v8a, flags: -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a -DCMAKE_SYSTEM_NAME=Android -DSFML_BUILD_TEST_SUITE=FALSE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/android-ndk-r18b/build/cmake/android.toolchain.cmake -DCMAKE_ANDROID_NDK=$GITHUB_WORKSPACE/android-ndk-r18b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 } - - platform: { name: Android, os: ubuntu-latest } + - platform: { name: Android, os: ubuntu-20.04 } config: { name: x86_64, flags: -DCMAKE_ANDROID_ARCH_ABI=x86_64 -DCMAKE_SYSTEM_NAME=Android -DSFML_BUILD_TEST_SUITE=FALSE -DCMAKE_TOOLCHAIN_FILE=$GITHUB_WORKSPACE/android-ndk-r18b/build/cmake/android.toolchain.cmake -DCMAKE_ANDROID_NDK=$GITHUB_WORKSPACE/android-ndk-r18b -DCMAKE_ANDROID_NDK_TOOLCHAIN_VERSION=clang -DCMAKE_ANDROID_STL_TYPE=c++_shared -DCMAKE_ANDROID_API=26 } - - platform: { name: Linux GCC, os: ubuntu-latest } + - platform: { name: Linux GCC, os: ubuntu-20.04 } config: { name: Static DRM, flags: -DBUILD_SHARED_LIBS=FALSE -DSFML_USE_DRM=TRUE } - - platform: { name: Linux GCC, os: ubuntu-latest } + - platform: { name: Linux GCC, os: ubuntu-20.04 } config: { name: Shared DRM, flags: -DBUILD_SHARED_LIBS=TRUE -DSFML_USE_DRM=TRUE } steps: - name: Checkout Code From 75b0c3a9a1bb5aacc7b19bc896795f5e5aec7219 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Sun, 11 Dec 2022 14:37:06 -0700 Subject: [PATCH 4/7] Disable warnings as errors for Clang on Windows --- cmake/CompilerWarnings.cmake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake index b4e5b3a1a..a26cc8f46 100644 --- a/cmake/CompilerWarnings.cmake +++ b/cmake/CompilerWarnings.cmake @@ -69,9 +69,8 @@ function(set_file_warnings) ${NON_ANDROID_CLANG_AND_GCC_WARNINGS} ) - # For now if we're using MSVC-like clang interface on Windows - # we'll disable warnings as errors - if(SFML_OS_WINDOWS AND SFML_COMPILER_CLANG_CL) + # Disable warnings as errors when using Clang on Windows to work around deprecation warnings in Windows APIs + if(SFML_OS_WINDOWS AND (SFML_COMPILER_CLANG OR SFML_COMPILER_CLANG_CL)) set(WARNINGS_AS_ERRORS FALSE) endif() From 07bf6f8c12f0cebbe654b1de77039ec0ec39c008 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Sun, 23 Oct 2022 18:45:40 -0600 Subject: [PATCH 5/7] Add option for enabling DRM --- CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 963cba01e..6f43e6e96 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,9 +77,14 @@ sfml_set_option(SFML_BUILD_NETWORK TRUE BOOL "TRUE to build SFML's Network modul # add an option for building the API documentation sfml_set_option(SFML_BUILD_DOC FALSE BOOL "TRUE to generate the API documentation, FALSE to ignore it") -# add an option for choosing the OpenGL implementation if(SFML_BUILD_WINDOW) + # add an option for choosing the OpenGL implementation sfml_set_option(SFML_OPENGL_ES ${OPENGL_ES} BOOL "TRUE to use an OpenGL ES implementation, FALSE to use a desktop OpenGL implementation") + + # add an option for choosing whether to use the DRM windowing backend + if(SFML_OS_LINUX) + sfml_set_option(SFML_USE_DRM FALSE BOOL "TRUE to use DRM windowing backend") + endif() endif() # add an option for building the test suite From 866dbee8cbec411103bd9aff6a8fe869c78d5cbf Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Sun, 23 Oct 2022 18:48:08 -0600 Subject: [PATCH 6/7] Add DRM utilities to sfml-window --- extlibs/headers/drm/drm-common.c | 364 ------------------------- extlibs/headers/drm/drm-common.h | 86 ------ src/SFML/Window/CMakeLists.txt | 5 +- src/SFML/Window/DRM/DRMContext.cpp | 365 +++++++++++++++++++++++--- src/SFML/Window/DRM/DRMContext.hpp | 17 +- src/SFML/Window/DRM/VideoModeImpl.cpp | 9 +- src/SFML/Window/DRM/WindowImplDRM.cpp | 5 +- 7 files changed, 357 insertions(+), 494 deletions(-) delete mode 100644 extlibs/headers/drm/drm-common.c delete mode 100644 extlibs/headers/drm/drm-common.h diff --git a/extlibs/headers/drm/drm-common.c b/extlibs/headers/drm/drm-common.c deleted file mode 100644 index 9a870054a..000000000 --- a/extlibs/headers/drm/drm-common.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * 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; -} - -static int has_monitor_connected(int fd, drmModeRes* resources) -{ - int i; - drmModeConnector *connector; - for (i = 0; i < resources->count_connectors; i++) - { - connector = drmModeGetConnector(fd, resources->connectors[i]); - if (connector->connection == DRM_MODE_CONNECTED) - { - /* There is a monitor connected */ - drmModeFreeConnector(connector); - connector = NULL; - return 1; - } - drmModeFreeConnector(connector); - connector = NULL; - } - 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(getenv("SFML_DRM_DEBUG")) - { - printf("DRM device used: %d\n", i); - } - if(!ret && has_monitor_connected(fd, *resources) != 0) - 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 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); - - /* Let's use the current mode rather than the preferred one if the user didn't - * specify a mode with env vars - */ - if (!drm->mode) - { - if(getenv("SFML_DRM_DEBUG")) - printf("DRM using the current mode\n"); - drm->mode = &(drm->original_crtc->mode); - } - - 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 deleted file mode 100644 index d388e6d0c..000000000 --- a/extlibs/headers/drm/drm-common.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 b6f117e3f..133c89eaf 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -280,13 +280,10 @@ sfml_add_library(sfml-window # 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) + target_include_directories(sfml-window PRIVATE ${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) diff --git a/src/SFML/Window/DRM/DRMContext.cpp b/src/SFML/Window/DRM/DRMContext.cpp index d92a10a4e..e36875ba4 100644 --- a/src/SFML/Window/DRM/DRMContext.cpp +++ b/src/SFML/Window/DRM/DRMContext.cpp @@ -33,8 +33,10 @@ #include #include #include +#include #include #include +#include // We check for this definition in order to avoid multiple definitions of GLAD // entities during unity builds of SFML. @@ -46,8 +48,14 @@ namespace { + struct DrmFb + { + gbm_bo* bo; + sf::Uint32 fbId; + }; + bool initialized = false; - drm drmNode; + sf::priv::Drm drmNode; drmEventContext drmEventCtx; pollfd pollFD; gbm_device* gbmDevice = NULL; @@ -79,7 +87,7 @@ namespace if (pollFD.revents & POLLIN) { - drmHandleEvent(drmNode.fd, &drmEventCtx); + drmHandleEvent(drmNode.fileDescriptor, &drmEventCtx); } else { @@ -95,21 +103,21 @@ namespace return; /* Avoid a modeswitch if possible */ - if (drmNode.mode != &drmNode.original_crtc->mode) - drmModeSetCrtc(drmNode.fd, - drmNode.original_crtc->crtc_id, - drmNode.original_crtc->buffer_id, - drmNode.original_crtc->x, - drmNode.original_crtc->y, - &drmNode.connector_id, + if (drmNode.mode != &drmNode.originalCrtc->mode) + drmModeSetCrtc(drmNode.fileDescriptor, + drmNode.originalCrtc->crtc_id, + drmNode.originalCrtc->buffer_id, + drmNode.originalCrtc->x, + drmNode.originalCrtc->y, + &drmNode.connectorId, 1, - &drmNode.original_crtc->mode); + &drmNode.originalCrtc->mode); else if (getenv("SFML_DRM_DEBUG")) printf("DRM keeping the same mode since using the original one\n"); - drmModeFreeConnector(drmNode.saved_connector); - drmModeFreeEncoder(drmNode.saved_encoder); - drmModeFreeCrtc(drmNode.original_crtc); + drmModeFreeConnector(drmNode.savedConnector); + drmModeFreeEncoder(drmNode.savedEncoder); + drmModeFreeCrtc(drmNode.originalCrtc); eglTerminate(display); display = EGL_NO_DISPLAY; @@ -117,9 +125,9 @@ namespace gbm_device_destroy(gbmDevice); gbmDevice = NULL; - close(drmNode.fd); + close(drmNode.fileDescriptor); - drmNode.fd = -1; + drmNode.fileDescriptor = -1; drmNode.mode = 0; std::memset(&pollFD, 0, sizeof(pollfd)); @@ -130,6 +138,306 @@ namespace initialized = false; } + void drmFbDestroyCallback(gbm_bo* bo, void* data) + { + int drmFd = gbm_device_get_fd(gbm_bo_get_device(bo)); + DrmFb* fb = static_cast(data); + + if (fb->fbId) + drmModeRmFB(drmFd, fb->fbId); + + delete fb; + } + + DrmFb* drmFbGetFromBo(gbm_bo& bo) + { + int drmFd = gbm_device_get_fd(gbm_bo_get_device(&bo)); + DrmFb* fb = static_cast(gbm_bo_get_user_data(&bo)); + if (fb) + return fb; + + fb = new DrmFb(); + fb->bo = &bo; + + const sf::Uint32 width = gbm_bo_get_width(&bo); + const sf::Uint32 height = gbm_bo_get_height(&bo); + const sf::Uint32 format = gbm_bo_get_format(&bo); + + sf::Uint32 strides[4] = {0}; + sf::Uint32 handles[4] = {0}; + sf::Uint32 offsets[4] = {0}; + 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]; + } + + sf::Uint32 flags = 0; + if (modifiers[0]) + { + flags = DRM_MODE_FB_MODIFIERS; + } + + int result = drmModeAddFB2WithModifiers(drmFd, width, height, format, handles, strides, offsets, modifiers, &fb->fbId, flags); + + if (result) + { + std::memset(handles, 0, 16); + handles[0] = gbm_bo_get_handle(&bo).u32; + std::memset(strides, 0, 16); + strides[0] = gbm_bo_get_stride(&bo); + std::memset(offsets, 0, 16); + result = drmModeAddFB2(drmFd, width, height, format, handles, strides, offsets, &fb->fbId, 0); + } + + if (result) + { + sf::err() << "Failed to create fb: " << std::strerror(errno) << std::endl; + delete fb; + return NULL; + } + + gbm_bo_set_user_data(&bo, fb, drmFbDestroyCallback); + + return fb; + } + + sf::Uint32 findCrtcForEncoder(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 sf::Uint32 crtcMask = 1U << i; + const sf::Uint32 crtcId = resources.crtcs[i]; + if (encoder.possible_crtcs & crtcMask) + { + return crtcId; + } + } + + // No match found + return 0; + } + + sf::Uint32 findCrtcForConnector(const sf::priv::Drm& drm, const drmModeRes& resources, const drmModeConnector& connector) + { + for (int i = 0; i < connector.count_encoders; ++i) + { + const sf::Uint32 encoderId = connector.encoders[i]; + const drmModeEncoderPtr encoder = drmModeGetEncoder(drm.fileDescriptor, encoderId); + + if (encoder) + { + const sf::Uint32 crtcId = findCrtcForEncoder(resources, *encoder); + + drmModeFreeEncoder(encoder); + if (crtcId != 0) + { + return crtcId; + } + } + } + + // No match found + return 0; + } + + int getResources(int fd, drmModeResPtr& resources) + { + resources = drmModeGetResources(fd); + if (resources == NULL) + return -1; + return 0; + } + + int hasMonitorConnected(int fd, drmModeRes& resources) + { + drmModeConnectorPtr connector; + for (int i = 0; i < resources.count_connectors; ++i) + { + connector = drmModeGetConnector(fd, resources.connectors[i]); + if (connector->connection == DRM_MODE_CONNECTED) + { + // There is a monitor connected + drmModeFreeConnector(connector); + connector = NULL; + return 1; + } + drmModeFreeConnector(connector); + connector = NULL; + } + return 0; + } + + int findDrmDevice(drmModeResPtr& resources) + { + static const int maxDrmDevices = 64; + + drmDevicePtr devices[maxDrmDevices] = { NULL }; + + const int numDevices = drmGetDevices2(0, devices, maxDrmDevices); + if (numDevices < 0) + { + sf::err() << "drmGetDevices2 failed: " << std::strerror(-numDevices) << std::endl; + return -1; + } + + int fileDescriptor = -1; + for (int i = 0; i < numDevices; ++i) + { + drmDevicePtr device = devices[i]; + int result = 0; + + 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. + fileDescriptor = open(device->nodes[DRM_NODE_PRIMARY], O_RDWR); + if (fileDescriptor < 0) + continue; + result = getResources(fileDescriptor, resources); +#ifdef SFML_DEBUG + sf::err() << "DRM device used: " << i << std::endl; +#endif + if(!result && hasMonitorConnected(fileDescriptor, *resources) != 0) + break; + close(fileDescriptor); + fileDescriptor = -1; + } + drmFreeDevices(devices, numDevices); + + if (fileDescriptor < 0) + sf::err() << "No drm device found!" << std::endl; + return fileDescriptor; + } + + int initDrm(sf::priv::Drm& drm, const char* device, const char* modeStr, unsigned int vrefresh) + { + drmModeResPtr resources; + + if (device) + { + drm.fileDescriptor = open(device, O_RDWR); + const int ret = getResources(drm.fileDescriptor, resources); + if (ret < 0 && errno == EOPNOTSUPP) + sf::err() << device << " does not look like a modeset device" << std::endl; + } + else + { + drm.fileDescriptor = findDrmDevice(resources); + } + + if (drm.fileDescriptor < 0) + { + sf::err() << "Could not open drm device" << std::endl; + return -1; + } + + if (!resources) + { + sf::err() << "drmModeGetResources failed: " << std::strerror(errno) << std::endl; + return -1; + } + + // Find a connected connector: + drmModeConnectorPtr connector = NULL; + for (int i = 0; i < resources->count_connectors; ++i) + { + connector = drmModeGetConnector(drm.fileDescriptor, 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.. + sf::err() << "No connected connector!" << std::endl; + return -1; + } + + // Find user requested mode: + if (modeStr && *modeStr) + { + for (int i = 0; i < connector->count_modes; ++i) + { + drmModeModeInfoPtr currentMode = &connector->modes[i]; + + if (std::strcmp(currentMode->name, modeStr) == 0) + { + if (vrefresh == 0 || currentMode->vrefresh == vrefresh) + { + drm.mode = currentMode; + break; + } + } + } + if (!drm.mode) + sf::err() << "Requested mode not found, using default mode!" << std::endl; + } + + // Find encoder: + drmModeEncoderPtr encoder = NULL; + for (int i = 0; i < resources->count_encoders; ++i) + { + encoder = drmModeGetEncoder(drm.fileDescriptor, resources->encoders[i]); + if (encoder->encoder_id == connector->encoder_id) + break; + drmModeFreeEncoder(encoder); + encoder = NULL; + } + + if (encoder) + { + drm.crtcId = encoder->crtc_id; + } + else + { + const sf::Uint32 crtcId = findCrtcForConnector(drm, *resources, *connector); + if (crtcId == 0) + { + sf::err() << "No crtc found!" << std::endl; + return -1; + } + + drm.crtcId = crtcId; + } + + drmModeFreeResources(resources); + + drm.connectorId = connector->connector_id; + + drm.savedConnector = connector; + drm.savedEncoder = encoder; + + // Get original display mode so we can restore display mode after program exits + drm.originalCrtc = drmModeGetCrtc(drm.fileDescriptor, drm.crtcId); + + // Let's use the current mode rather than the preferred one if the user didn't specify a mode with env vars + if (!drm.mode) + { +#ifdef SFML_DEBUG + sf::err() << "DRM using the current mode" << std::endl; +#endif + drm.mode = &(drm.originalCrtc->mode); + } + +#ifdef SFML_DEBUG + sf::err() << "DRM Mode used: " << drm.mode->name << "@" << drm.mode->vrefresh << std::endl; +#endif + + return 0; + } + void checkInit() { if (initialized) @@ -152,27 +460,26 @@ namespace if (refreshString) refreshRate = static_cast(atoi(refreshString)); - if (init_drm(&drmNode, - deviceString, // device - modeString, // requested mode - refreshRate) < 0) // screen refresh rate + if (initDrm(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); + gbmDevice = gbm_create_device(drmNode.fileDescriptor); std::atexit(cleanup); initialized = true; - pollFD.fd = drmNode.fd; + pollFD.fd = drmNode.fileDescriptor; pollFD.events = POLLIN; drmEventCtx.version = 2; drmEventCtx.page_flip_handler = pageFlipHandler; } - EGLDisplay getInitializedDisplay() { checkInit(); @@ -369,7 +676,7 @@ void DRMContext::display() } // Handle display of buffer to the screen - drm_fb* fb = NULL; + DrmFb* fb = NULL; if (!waitForFlip(-1)) return; @@ -390,7 +697,7 @@ void DRMContext::display() if (!m_nextBO) return; - fb = drm_fb_get_from_bo(m_nextBO); + fb = drmFbGetFromBo(*m_nextBO); if (!fb) { err() << "Failed to get FB from buffer object" << std::endl; @@ -400,8 +707,7 @@ void DRMContext::display() // 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)) + if (drmModeSetCrtc(drmNode.fileDescriptor, drmNode.crtcId, fb->fbId, 0, 0, &drmNode.connectorId, 1, drmNode.mode)) { err() << "Failed to set mode: " << std::strerror(errno) << std::endl; std::abort(); @@ -410,8 +716,7 @@ void DRMContext::display() } // Do page flip - if (!drmModePageFlip(drmNode.fd, drmNode.crtc_id, fb->fb_id, - DRM_MODE_PAGE_FLIP_EVENT, &waitingForFlip)) + if (!drmModePageFlip(drmNode.fileDescriptor, drmNode.crtcId, fb->fbId, DRM_MODE_PAGE_FLIP_EVENT, &waitingForFlip)) waitingForFlip = 1; } @@ -560,10 +865,10 @@ GlFunctionPointer DRMContext::getFunction(const char* name) //////////////////////////////////////////////////////////// -drm* DRMContext::getDRM() +Drm& DRMContext::getDRM() { checkInit(); - return &drmNode; + return drmNode; } } // namespace priv diff --git a/src/SFML/Window/DRM/DRMContext.hpp b/src/SFML/Window/DRM/DRMContext.hpp index f302a0a71..cde542309 100644 --- a/src/SFML/Window/DRM/DRMContext.hpp +++ b/src/SFML/Window/DRM/DRMContext.hpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +42,20 @@ namespace sf { namespace priv { +struct Drm +{ + int fileDescriptor; + + drmModeModeInfoPtr mode; + Uint32 crtcId; + Uint32 connectorId; + + drmModeCrtcPtr originalCrtc; + + drmModeConnectorPtr savedConnector; + drmModeEncoderPtr savedEncoder; +}; + class WindowImplDRM; class DRMContext : public GlContext @@ -176,7 +189,7 @@ protected: /// \brief Get Direct Rendering Manager pointer /// //////////////////////////////////////////////////////////// - static drm* getDRM(); + static Drm& getDRM(); private: diff --git a/src/SFML/Window/DRM/VideoModeImpl.cpp b/src/SFML/Window/DRM/VideoModeImpl.cpp index 13471a474..4f85cad1f 100644 --- a/src/SFML/Window/DRM/VideoModeImpl.cpp +++ b/src/SFML/Window/DRM/VideoModeImpl.cpp @@ -28,7 +28,6 @@ #include #include #include -#include namespace sf @@ -40,8 +39,8 @@ std::vector VideoModeImpl::getFullscreenModes() { std::vector modes; - drm* drm = sf::priv::DRMContext::getDRM(); - drmModeConnectorPtr conn = drm->saved_connector; + Drm& drm = sf::priv::DRMContext::getDRM(); + drmModeConnectorPtr conn = drm.savedConnector; if (conn) { @@ -60,8 +59,8 @@ std::vector VideoModeImpl::getFullscreenModes() //////////////////////////////////////////////////////////// VideoMode VideoModeImpl::getDesktopMode() { - drm* drm = sf::priv::DRMContext::getDRM(); - drmModeModeInfoPtr ptr = drm->mode; + Drm& drm = sf::priv::DRMContext::getDRM(); + drmModeModeInfoPtr ptr = drm.mode; if (ptr) return VideoMode(ptr->hdisplay, ptr->vdisplay); else diff --git a/src/SFML/Window/DRM/WindowImplDRM.cpp b/src/SFML/Window/DRM/WindowImplDRM.cpp index 761245c0e..e372ca01c 100644 --- a/src/SFML/Window/DRM/WindowImplDRM.cpp +++ b/src/SFML/Window/DRM/WindowImplDRM.cpp @@ -31,7 +31,6 @@ #include #include #include -#include namespace sf @@ -64,8 +63,8 @@ WindowImplDRM::~WindowImplDRM() //////////////////////////////////////////////////////////// WindowHandle WindowImplDRM::getSystemHandle() const { - drm* drm = sf::priv::DRMContext::getDRM(); - return static_cast(drm->fd); + Drm& drm = sf::priv::DRMContext::getDRM(); + return static_cast(drm.fileDescriptor); } //////////////////////////////////////////////////////////// From ed6d94481035fb23416690b4c6cf3f1fef3f3cf3 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Wed, 26 Oct 2022 12:54:01 -0600 Subject: [PATCH 7/7] Always trigger drmModeSetCrtc --- src/SFML/Window/DRM/DRMContext.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/src/SFML/Window/DRM/DRMContext.cpp b/src/SFML/Window/DRM/DRMContext.cpp index e36875ba4..c449b8f8c 100644 --- a/src/SFML/Window/DRM/DRMContext.cpp +++ b/src/SFML/Window/DRM/DRMContext.cpp @@ -102,18 +102,14 @@ namespace if (!initialized) return; - /* Avoid a modeswitch if possible */ - if (drmNode.mode != &drmNode.originalCrtc->mode) - drmModeSetCrtc(drmNode.fileDescriptor, - drmNode.originalCrtc->crtc_id, - drmNode.originalCrtc->buffer_id, - drmNode.originalCrtc->x, - drmNode.originalCrtc->y, - &drmNode.connectorId, - 1, - &drmNode.originalCrtc->mode); - else if (getenv("SFML_DRM_DEBUG")) - printf("DRM keeping the same mode since using the original one\n"); + drmModeSetCrtc(drmNode.fileDescriptor, + drmNode.originalCrtc->crtc_id, + drmNode.originalCrtc->buffer_id, + drmNode.originalCrtc->x, + drmNode.originalCrtc->y, + &drmNode.connectorId, + 1, + &drmNode.originalCrtc->mode); drmModeFreeConnector(drmNode.savedConnector); drmModeFreeEncoder(drmNode.savedEncoder);