diff --git a/CMakeLists.txt b/CMakeLists.txt index d44561ff7..3e45c8989 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,9 +69,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 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/include/SFML/Window/Mouse.hpp b/include/SFML/Window/Mouse.hpp index 2e6fa2a0a..9ee9f3f45 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/CMakeLists.txt b/src/SFML/Window/CMakeLists.txt index b57950cb9..79c300194 100644 --- a/src/SFML/Window/CMakeLists.txt +++ b/src/SFML/Window/CMakeLists.txt @@ -274,13 +274,10 @@ sfml_add_library(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 0fb2bd29e..320610c51 100644 --- a/src/SFML/Window/DRM/DRMContext.cpp +++ b/src/SFML/Window/DRM/DRMContext.cpp @@ -34,8 +34,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. @@ -47,8 +49,14 @@ namespace { +struct DrmFb +{ + gbm_bo* bo; + std::uint32_t fbId; +}; + bool initialized = false; -drm drmNode; +sf::priv::Drm drmNode; drmEventContext drmEventCtx; pollfd pollFD; gbm_device* gbmDevice = nullptr; @@ -76,7 +84,7 @@ static bool waitForFlip(int timeout) if (pollFD.revents & POLLIN) { - drmHandleEvent(drmNode.fd, &drmEventCtx); + drmHandleEvent(drmNode.fileDescriptor, &drmEventCtx); } else { @@ -91,22 +99,18 @@ void cleanup() if (!initialized) 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, - 1, - &drmNode.original_crtc->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.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; @@ -114,10 +118,10 @@ void cleanup() gbm_device_destroy(gbmDevice); gbmDevice = nullptr; - close(drmNode.fd); + close(drmNode.fileDescriptor); - drmNode.fd = -1; - drmNode.mode = 0; + drmNode.fileDescriptor = -1; + drmNode.mode = 0; pollFD = {}; drmEventCtx = {}; @@ -127,6 +131,306 @@ void cleanup() 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 std::uint32_t width = gbm_bo_get_width(&bo); + const std::uint32_t height = gbm_bo_get_height(&bo); + const std::uint32_t format = gbm_bo_get_format(&bo); + + std::uint32_t strides[4] = {0}; + std::uint32_t handles[4] = {0}; + std::uint32_t offsets[4] = {0}; + std::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]; + } + + std::uint32_t 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 nullptr; + } + + gbm_bo_set_user_data(&bo, fb, drmFbDestroyCallback); + + return fb; +} + +std::uint32_t 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 std::uint32_t crtcMask = 1U << i; + const std::uint32_t crtcId = resources.crtcs[i]; + if (encoder.possible_crtcs & crtcMask) + { + return crtcId; + } + } + + // No match found + return 0; +} + +std::uint32_t findCrtcForConnector(const sf::priv::Drm& drm, const drmModeRes& resources, const drmModeConnector& connector) +{ + for (int i = 0; i < connector.count_encoders; ++i) + { + const std::uint32_t encoderId = connector.encoders[i]; + const drmModeEncoderPtr encoder = drmModeGetEncoder(drm.fileDescriptor, encoderId); + + if (encoder) + { + const std::uint32_t 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 == nullptr) + 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 = nullptr; + return 1; + } + drmModeFreeConnector(connector); + connector = nullptr; + } + return 0; +} + +int findDrmDevice(drmModeResPtr& resources) +{ + static const int maxDrmDevices = 64; + + drmDevicePtr devices[maxDrmDevices] = {nullptr}; + + 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 = nullptr; + 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 = nullptr; + } + + 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 = nullptr; + 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 = nullptr; + } + + if (encoder) + { + drm.crtcId = encoder->crtc_id; + } + else + { + const std::uint32_t 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) @@ -149,27 +453,26 @@ void checkInit() 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(); @@ -360,7 +663,7 @@ void DRMContext::display() } // Handle display of buffer to the screen - drm_fb* fb = nullptr; + DrmFb* fb = nullptr; if (!waitForFlip(-1)) return; @@ -381,7 +684,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; @@ -391,7 +694,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(); @@ -400,7 +703,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; } @@ -549,10 +852,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 ef5896420..dd208e57a 100644 --- a/src/SFML/Window/DRM/DRMContext.hpp +++ b/src/SFML/Window/DRM/DRMContext.hpp @@ -36,7 +36,6 @@ #include -#include #include #include @@ -45,6 +44,20 @@ namespace sf { namespace priv { +struct Drm +{ + int fileDescriptor; + + drmModeModeInfoPtr mode; + std::uint32_t crtcId; + std::uint32_t connectorId; + + drmModeCrtcPtr originalCrtc; + + drmModeConnectorPtr savedConnector; + drmModeEncoderPtr savedEncoder; +}; + class WindowImplDRM; class DRMContext : public GlContext @@ -174,7 +187,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 3409e2cb8..c889da1f3 100644 --- a/src/SFML/Window/DRM/VideoModeImpl.cpp +++ b/src/SFML/Window/DRM/VideoModeImpl.cpp @@ -29,8 +29,6 @@ #include #include -#include - namespace sf { @@ -41,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) { @@ -59,8 +57,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 e26cb3b7c..3246976df 100644 --- a/src/SFML/Window/DRM/WindowImplDRM.cpp +++ b/src/SFML/Window/DRM/WindowImplDRM.cpp @@ -32,8 +32,6 @@ #include #include -#include - namespace sf { @@ -64,8 +62,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); } //////////////////////////////////////////////////////////// diff --git a/src/SFML/Window/Unix/InputImpl.cpp b/src/SFML/Window/Unix/InputImpl.cpp index deeebe54f..085c8de4b 100644 --- a/src/SFML/Window/Unix/InputImpl.cpp +++ b/src/SFML/Window/Unix/InputImpl.cpp @@ -25,7 +25,6 @@ //////////////////////////////////////////////////////////// // Headers //////////////////////////////////////////////////////////// - #include #include #include @@ -207,6 +206,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. // clang-format off switch (button) { diff --git a/src/SFML/Window/Unix/WindowImplX11.cpp b/src/SFML/Window/Unix/WindowImplX11.cpp index 7784ba44c..6efe6b6bc 100644 --- a/src/SFML/Window/Unix/WindowImplX11.cpp +++ b/src/SFML/Window/Unix/WindowImplX11.cpp @@ -2030,8 +2030,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) || (button == Button3) || (button == 8) || (button == 9)) { diff --git a/src/SFML/Window/Win32/WindowImplWin32.cpp b/src/SFML/Window/Win32/WindowImplWin32.cpp index cbfbca152..48ceec0d9 100644 --- a/src/SFML/Window/Win32/WindowImplWin32.cpp +++ b/src/SFML/Window/Win32/WindowImplWin32.cpp @@ -85,8 +85,12 @@ void setProcessDpiAware() { // 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; }