diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index f268342..1cd2dac 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -34,3 +34,27 @@ jobs: - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + build-wayland: + strategy: + matrix: + os: [ubuntu-22.04] + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get -y install libxrandr-dev libfreeimage-dev libxinerama-dev libxcursor-dev libxi-dev libgl-dev libglew-dev freeglut3-dev libsdl2-dev liblz4-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libxxf86vm-dev libglm-dev libglfw3-dev libmpv-dev mpv libmpv1 libpulse-dev libpulse0 wayland-scanner++ wayland-protocols libwayland-dev libwayland-egl-backend-dev + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_WAYLAND=True + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} diff --git a/.gitignore b/.gitignore index a2705b0..cbaea3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ cmake-build-debug* -.idea \ No newline at end of file +.idea +build/ +.vscode/ + +*-protocol.o +*-protocol.c +*-protocol.h \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 221e7f8..20c58c2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,55 @@ include_directories( ${FREEIMAGE_INCLUDE_DIR} ${PULSEAUDIO_INCLUDE_DIR} src + ${CMAKE_SOURCE_DIR} include) +if (ENABLE_WAYLAND) + message(STATUS "Wayland support is enabled!") + + pkg_check_modules(wayland-cursor wayland-protocols egl wayland-egl) + + find_program(WaylandScanner NAMES wayland-scanner) + message(STATUS "Found WaylandScanner at ${WaylandScanner}") + execute_process( + COMMAND pkg-config --variable=pkgdatadir wayland-protocols + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE WAYLAND_PROTOCOLS_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + message(STATUS "Found wayland-protocols at ${WAYLAND_PROTOCOLS_DIR}") + + message(STATUS "Building protocols...") + execute_process( + COMMAND ${WaylandScanner} client-header ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml wlr-layer-shell-unstable-v1-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${CMAKE_SOURCE_DIR}/protocols/wlr-layer-shell-unstable-v1.xml wlr-layer-shell-unstable-v1-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} client-header ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + execute_process( + COMMAND ${WaylandScanner} private-code ${WAYLAND_PROTOCOLS_DIR}/stable/xdg-shell/xdg-shell.xml xdg-shell-protocol.c + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) + + add_compile_definitions(ENABLE_WAYLAND) + set(WAYLAND_SOURCES + "src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h" + "src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.cpp" + "src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp" + "src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h" + "src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.cpp" + "src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h" + "src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.cpp" + "src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h" + "src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp" + "src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h" + "xdg-shell-protocol.c" + "wlr-layer-shell-unstable-v1-protocol.c") +else() + set(WAYLAND_SOURCES "") +endif() + add_executable( linux-wallpaperengine main.cpp @@ -105,6 +152,8 @@ add_executable( src/WallpaperEngine/Input/CInputContext.h src/WallpaperEngine/Input/CMouseInput.cpp src/WallpaperEngine/Input/CMouseInput.h + src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.cpp + src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.h src/WallpaperEngine/Render/Shaders/Variables/CShaderVariable.h src/WallpaperEngine/Render/Shaders/Variables/CShaderVariable.cpp @@ -136,6 +185,10 @@ add_executable( src/WallpaperEngine/Render/Drivers/Output/CX11Output.h src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.cpp src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h + src/WallpaperEngine/Render/Drivers/Output/COutputViewport.cpp + src/WallpaperEngine/Render/Drivers/Output/COutputViewport.h + src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.cpp + src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.h src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.cpp src/WallpaperEngine/Render/Drivers/CVideoDriver.h @@ -268,6 +321,8 @@ add_executable( src/WallpaperEngine/Core/Objects/Images/Materials/CPass.cpp src/WallpaperEngine/Core/Objects/Images/Materials/CPass.h + + ${WAYLAND_SOURCES} ) target_link_libraries(linux-wallpaperengine @@ -286,6 +341,14 @@ target_link_libraries(linux-wallpaperengine ${PULSEAUDIO_LIBRARY} glfw) +if (ENABLE_WAYLAND) + target_link_libraries(linux-wallpaperengine pthread + wayland-cursor + wayland-client + wayland-egl + ${OPENGL_egl_LIBRARY}) +endif() + file(CREATE_LINK linux-wallpaperengine wallengine SYMBOLIC) set(CMAKE_REQUIRED_LIBRARIES ${X11_LIBRARIES}) diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml new file mode 100644 index 0000000..f29eb87 --- /dev/null +++ b/protocols/wlr-layer-shell-unstable-v1.xml @@ -0,0 +1,285 @@ + + + + Copyright © 2017 Drew DeVault + + Permission to use, copy, modify, distribute, and sell this + software and its documentation for any purpose is hereby granted + without fee, provided that the above copyright notice appear in + all copies and that both that copyright notice and this permission + notice appear in supporting documentation, and that the name of + the copyright holders not be used in advertising or publicity + pertaining to distribution of the software without specific, + written prior permission. The copyright holders make no + representations about the suitability of this software for any + purpose. It is provided "as is" without express or implied + warranty. + + THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS + SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF + THIS SOFTWARE. + + + + + Clients can use this interface to assign the surface_layer role to + wl_surfaces. Such surfaces are assigned to a "layer" of the output and + rendered with a defined z-depth respective to each other. They may also be + anchored to the edges and corners of a screen and specify input handling + semantics. This interface should be suitable for the implementation of + many desktop shell components, and a broad number of other applications + that interact with the desktop. + + + + + Create a layer surface for an existing surface. This assigns the role of + layer_surface, or raises a protocol error if another role is already + assigned. + + Creating a layer surface from a wl_surface which has a buffer attached + or committed is a client error, and any attempts by a client to attach + or manipulate a buffer prior to the first layer_surface.configure call + must also be treated as errors. + + You may pass NULL for output to allow the compositor to decide which + output to use. Generally this will be the one that the user most + recently interacted with. + + Clients can specify a namespace that defines the purpose of the layer + surface. + + + + + + + + + + + + + + + + + These values indicate which layers a surface can be rendered in. They + are ordered by z depth, bottom-most first. Traditional shell surfaces + will typically be rendered between the bottom and top layers. + Fullscreen shell surfaces are typically rendered at the top layer. + Multiple surfaces can share a single layer, and ordering within a + single layer is undefined. + + + + + + + + + + + + An interface that may be implemented by a wl_surface, for surfaces that + are designed to be rendered as a layer of a stacked desktop-like + environment. + + Layer surface state (size, anchor, exclusive zone, margin, interactivity) + is double-buffered, and will be applied at the time wl_surface.commit of + the corresponding wl_surface is called. + + + + + Sets the size of the surface in surface-local coordinates. The + compositor will display the surface centered with respect to its + anchors. + + If you pass 0 for either value, the compositor will assign it and + inform you of the assignment in the configure event. You must set your + anchor to opposite edges in the dimensions you omit; not doing so is a + protocol error. Both values are 0 by default. + + Size is double-buffered, see wl_surface.commit. + + + + + + + + Requests that the compositor anchor the surface to the specified edges + and corners. If two orthoginal edges are specified (e.g. 'top' and + 'left'), then the anchor point will be the intersection of the edges + (e.g. the top left corner of the output); otherwise the anchor point + will be centered on that edge, or in the center if none is specified. + + Anchor is double-buffered, see wl_surface.commit. + + + + + + + Requests that the compositor avoids occluding an area of the surface + with other surfaces. The compositor's use of this information is + implementation-dependent - do not assume that this region will not + actually be occluded. + + A positive value is only meaningful if the surface is anchored to an + edge, rather than a corner. The zone is the number of surface-local + coordinates from the edge that are considered exclusive. + + Surfaces that do not wish to have an exclusive zone may instead specify + how they should interact with surfaces that do. If set to zero, the + surface indicates that it would like to be moved to avoid occluding + surfaces with a positive excluzive zone. If set to -1, the surface + indicates that it would not like to be moved to accommodate for other + surfaces, and the compositor should extend it all the way to the edges + it is anchored to. + + For example, a panel might set its exclusive zone to 10, so that + maximized shell surfaces are not shown on top of it. A notification + might set its exclusive zone to 0, so that it is moved to avoid + occluding the panel, but shell surfaces are shown underneath it. A + wallpaper or lock screen might set their exclusive zone to -1, so that + they stretch below or over the panel. + + The default value is 0. + + Exclusive zone is double-buffered, see wl_surface.commit. + + + + + + + Requests that the surface be placed some distance away from the anchor + point on the output, in surface-local coordinates. Setting this value + for edges you are not anchored to has no effect. + + The exclusive zone includes the margin. + + Margin is double-buffered, see wl_surface.commit. + + + + + + + + + + Set to 1 to request that the seat send keyboard events to this layer + surface. For layers below the shell surface layer, the seat will use + normal focus semantics. For layers above the shell surface layers, the + seat will always give exclusive keyboard focus to the top-most layer + which has keyboard interactivity set to true. + + Layer surfaces receive pointer, touch, and tablet events normally. If + you do not want to receive them, set the input region on your surface + to an empty region. + + Events is double-buffered, see wl_surface.commit. + + + + + + + This assigns an xdg_popup's parent to this layer_surface. This popup + should have been created via xdg_surface::get_popup with the parent set + to NULL, and this request must be invoked before committing the popup's + initial state. + + See the documentation of xdg_popup for more details about what an + xdg_popup is and how it is used. + + + + + + + When a configure event is received, if a client commits the + surface in response to the configure event, then the client + must make an ack_configure request sometime before the commit + request, passing along the serial of the configure event. + + If the client receives multiple configure events before it + can respond to one, it only has to ack the last configure event. + + A client is not required to commit immediately after sending + an ack_configure request - it may even ack_configure several times + before its next surface commit. + + A client may send multiple ack_configure requests before committing, but + only the last request sent before a commit indicates which configure + event the client really is responding to. + + + + + + + This request destroys the layer surface. + + + + + + The configure event asks the client to resize its surface. + + Clients should arrange their surface for the new states, and then send + an ack_configure request with the serial sent in this configure event at + some point before committing the new surface. + + The client is free to dismiss all but the last configure event it + received. + + The width and height arguments specify the size of the window in + surface-local coordinates. + + The size is a hint, in the sense that the client is free to ignore it if + it doesn't resize, pick a smaller size (to satisfy aspect ratio or + resize in steps of NxM pixels). If the client picks a smaller size and + is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the + surface will be centered on this axis. + + If the width or height arguments are zero, it means the client should + decide its own window dimension. + + + + + + + + + The closed event is sent by the compositor when the surface will no + longer be shown. The output may have been destroyed or the user may + have asked for it to be removed. Further changes to the surface will be + ignored. The client should destroy the resource after receiving this + event, and create a new surface if they so choose. + + + + + + + + + + + + + + + + + diff --git a/src/External/Android/fft.cpp b/src/External/Android/fft.cpp index 93122ef..6406889 100644 --- a/src/External/Android/fft.cpp +++ b/src/External/Android/fft.cpp @@ -26,8 +26,8 @@ namespace External::Android * half of the twiddle factors are stored. Although there are still ways to make * it even faster or smaller, it costs too much on one of the aspects. */ -#include -#include +#include +#include #ifdef __arm__ #include #endif diff --git a/src/WallpaperEngine/Application/CApplicationContext.cpp b/src/WallpaperEngine/Application/CApplicationContext.cpp index e22b497..857a995 100644 --- a/src/WallpaperEngine/Application/CApplicationContext.cpp +++ b/src/WallpaperEngine/Application/CApplicationContext.cpp @@ -12,22 +12,22 @@ using namespace WallpaperEngine::Application; struct option long_options[] = { - { "screen-root", required_argument, nullptr, 'r' }, - { "bg", required_argument, nullptr, 'b' }, - { "window", required_argument, nullptr, 'w' }, - { "pkg", required_argument, nullptr, 'p' }, - { "dir", required_argument, nullptr, 'd' }, - { "silent", no_argument, nullptr, 's' }, - { "volume", required_argument, nullptr, 'v' }, - { "help", no_argument, nullptr, 'h' }, - { "fps", required_argument, nullptr, 'f' }, - { "assets-dir", required_argument, nullptr, 'a' }, - { "screenshot", required_argument, nullptr, 'c' }, - { "list-properties", no_argument, nullptr, 'l' }, - { "set-property", required_argument, nullptr, 'o' }, - { "noautomute", no_argument, nullptr, 'm' }, - { "no-fullscreen-pause", no_argument,nullptr, 'n' }, - { nullptr, 0, nullptr, 0 } + { "screen-root", required_argument, nullptr, 'r' }, + { "bg", required_argument, nullptr, 'b' }, + { "window", required_argument, nullptr, 'w' }, + { "pkg", required_argument, nullptr, 'p' }, + { "dir", required_argument, nullptr, 'd' }, + { "silent", no_argument, nullptr, 's' }, + { "volume", required_argument, nullptr, 'v' }, + { "help", no_argument, nullptr, 'h' }, + { "fps", required_argument, nullptr, 'f' }, + { "assets-dir", required_argument, nullptr, 'a' }, + { "screenshot", required_argument, nullptr, 'c' }, + { "list-properties", no_argument, nullptr, 'l' }, + { "set-property", required_argument, nullptr, 'o' }, + { "noautomute", no_argument, nullptr, 'm' }, + { "no-fullscreen-pause", no_argument, nullptr, 'n' }, + { nullptr, 0, nullptr, 0 } }; std::string stringPathFixes (const std::string& s) @@ -124,13 +124,13 @@ CApplicationContext::CApplicationContext (int argc, char* argv[]) if (this->settings.render.mode == EXPLICIT_WINDOW) sLog.exception ("Cannot run in both background and window mode"); - this->settings.render.mode = X11_BACKGROUND; + this->settings.render.mode = DESKTOP_BACKGROUND; lastScreen = optarg; this->settings.general.screenBackgrounds[lastScreen] = ""; break; case 'w': - if (this->settings.render.mode == X11_BACKGROUND) + if (this->settings.render.mode == DESKTOP_BACKGROUND) sLog.exception ("Cannot run in both background and window mode"); if (optarg != nullptr) diff --git a/src/WallpaperEngine/Application/CApplicationContext.h b/src/WallpaperEngine/Application/CApplicationContext.h index 700a0db..5d37fbf 100644 --- a/src/WallpaperEngine/Application/CApplicationContext.h +++ b/src/WallpaperEngine/Application/CApplicationContext.h @@ -25,8 +25,8 @@ namespace WallpaperEngine::Application { /** Default window mode */ NORMAL_WINDOW = 0, - /** Draw to X11 background */ - X11_BACKGROUND = 1, + /** Draw to the window server desktop */ + DESKTOP_BACKGROUND = 1, /** Explicit window mode with specified geometry */ EXPLICIT_WINDOW = 2, }; diff --git a/src/WallpaperEngine/Application/CWallpaperApplication.cpp b/src/WallpaperEngine/Application/CWallpaperApplication.cpp index 5d52a3a..2096677 100644 --- a/src/WallpaperEngine/Application/CWallpaperApplication.cpp +++ b/src/WallpaperEngine/Application/CWallpaperApplication.cpp @@ -3,17 +3,15 @@ #include "Steam/FileSystem/FileSystem.h" #include "WallpaperEngine/Assets/CDirectory.h" #include "WallpaperEngine/Assets/CVirtualContainer.h" -#include "WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h" #include "WallpaperEngine/Core/CVideo.h" #include "WallpaperEngine/Logging/CLog.h" #include "WallpaperEngine/Render/CRenderContext.h" -#include "WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h" -#include "WallpaperEngine/Render/Drivers/Output/CX11Output.h" #include "WallpaperEngine/Application/CApplicationState.h" -#include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h" #include "WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h" +#include "WallpaperEngine/Input/Drivers/CGLFWMouseInput.h" -#include +#include "WallpaperEngine/Input/Drivers/CWaylandMouseInput.h" +#include "WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h" float g_Time; float g_TimeLast; @@ -29,6 +27,15 @@ namespace WallpaperEngine::Application this->setupProperties (); } + CWallpaperApplication::~CWallpaperApplication () + { + delete context; + delete videoDriver; + delete audioContext; + delete audioDriver; + delete inputContext; + } + void CWallpaperApplication::setupContainer (CCombinedContainer& container, const std::string& bg) const { std::filesystem::path basepath = bg; @@ -227,126 +234,128 @@ namespace WallpaperEngine::Application ) { // this should be getting called at the end of the frame, so the right thing should be bound already - - int width = context.getOutput ()->getFullWidth (); - int height = context.getOutput ()->getFullHeight (); - - // make room for storing the pixel data - uint8_t* buffer = new uint8_t[width * height * sizeof (uint8_t) * 3]; - uint8_t* pixel = buffer; - - // read the image into the buffer - glReadPixels (0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer); + int width = context.getOutput ().getFullWidth (); + int height = context.getOutput ().getFullHeight (); // build the output file with FreeImage - FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24); + static FIBITMAP* bitmap = FreeImage_Allocate (width, height, 24); RGBQUAD color; + int xoffset = 0; - // now get access to the pixels - for (int y = height; y > 0; y--) + for (const auto& viewport : context.getOutput ().getViewports ()) { - for (int x = 0; x < width; x++) - { - color.rgbRed = *pixel++; - color.rgbGreen = *pixel++; - color.rgbBlue = *pixel++; + // activate opengl context so we can read from the framebuffer + viewport.second->makeCurrent (); + // make room for storing the pixel of this viewport + uint8_t* buffer = new uint8_t[viewport.second->viewport.z * viewport.second->viewport.w * sizeof (uint8_t) * 3]; + uint8_t* pixel = buffer; - // set the pixel in the destination - FreeImage_SetPixelColor (bitmap, x, y, &color); + // read the viewport data into the pixel buffer + glReadPixels ( + viewport.second->viewport.x, viewport.second->viewport.y, + viewport.second->viewport.z, viewport.second->viewport.w, + GL_RGB, GL_UNSIGNED_BYTE, buffer + ); + + // now get access to the pixels + for (int y = viewport.second->viewport.w; y > 0; y--) + { + for (int x = 0; x < viewport.second->viewport.z; x++) + { + color.rgbRed = *pixel++; + color.rgbGreen = *pixel++; + color.rgbBlue = *pixel++; + + // set the pixel in the destination + FreeImage_SetPixelColor (bitmap, x + xoffset, context.getOutput ().renderVFlip() ? (viewport.second->viewport.w - y) : y, &color); + } } + + if (viewport.second->single) + xoffset += viewport.second->viewport.z; + + // free the buffer allocated for the viewport + delete[] buffer; } // finally save the file FreeImage_Save (format, bitmap, filename.c_str (), 0); - // free all the used memory - delete[] buffer; - FreeImage_Unload (bitmap); } void CWallpaperApplication::show () { - // initialize OpenGL driver - WallpaperEngine::Render::Drivers::CX11OpenGLDriver videoDriver ("wallpaperengine", this->m_context); - // initialize the input subsystem - WallpaperEngine::Input::CInputContext inputContext (videoDriver); - // output requested - WallpaperEngine::Render::Drivers::Output::COutput* output; - // fullscreen detector is common for the different render modes - WallpaperEngine::Render::Drivers::Detectors::CX11FullScreenDetector fullscreenDetector (this->m_context, videoDriver); +#ifdef ENABLE_WAYLAND + const bool WAYLAND_DISPLAY = getenv ("WAYLAND_DISPLAY"); + + // setup the right video driver based on the environment and the startup mode requested + if (WAYLAND_DISPLAY && this->m_context.settings.render.mode == CApplicationContext::DESKTOP_BACKGROUND) + { + auto waylandDriver = new WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver (this->m_context, *this); + inputContext = new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CWaylandMouseInput (waylandDriver)); + + videoDriver = waylandDriver; + } + else +#endif + { + auto x11Driver = new WallpaperEngine::Render::Drivers::CX11OpenGLDriver ("wallpaperengine", this->m_context, *this); + // no wayland detected, try the old X11 method + inputContext = new WallpaperEngine::Input::CInputContext (new WallpaperEngine::Input::Drivers::CGLFWMouseInput (x11Driver)); + + videoDriver = x11Driver; + } + // stereo mix recorder for audio processing WallpaperEngine::Audio::Drivers::Recorders::CPulseAudioPlaybackRecorder audioRecorder; // audio playing detector - WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context, fullscreenDetector); + WallpaperEngine::Audio::Drivers::Detectors::CPulseAudioPlayingDetector audioDetector (this->m_context, videoDriver->getFullscreenDetector ()); // initialize sdl audio driver - WallpaperEngine::Audio::Drivers::CSDLAudioDriver audioDriver (this->m_context, audioDetector, audioRecorder); + audioDriver = new WallpaperEngine::Audio::Drivers::CSDLAudioDriver (this->m_context, audioDetector, audioRecorder); // initialize audio context - WallpaperEngine::Audio::CAudioContext audioContext (audioDriver); - - // initialize the requested output - switch (this->m_context.settings.render.mode) - { - case CApplicationContext::EXPLICIT_WINDOW: - case CApplicationContext::NORMAL_WINDOW: - output = new WallpaperEngine::Render::Drivers::Output::CGLFWWindowOutput (this->m_context, videoDriver, fullscreenDetector); - break; - - case CApplicationContext::X11_BACKGROUND: - output = new WallpaperEngine::Render::Drivers::Output::CX11Output (this->m_context, videoDriver, fullscreenDetector); - break; - } - + audioContext = new WallpaperEngine::Audio::CAudioContext (*audioDriver); // initialize render context - WallpaperEngine::Render::CRenderContext context (output, videoDriver, inputContext, *this); + context = new WallpaperEngine::Render::CRenderContext (*videoDriver, *inputContext, *this); // set all the specific wallpapers required for (const auto& it : this->m_backgrounds) - context.setWallpaper ( + context->setWallpaper ( it.first, - WallpaperEngine::Render::CWallpaper::fromWallpaper (it.second->getWallpaper (), context, audioContext) + WallpaperEngine::Render::CWallpaper::fromWallpaper (it.second->getWallpaper (), *context, *audioContext) ); // set the default rendering wallpaper if available if (this->m_defaultBackground != nullptr) - context.setDefaultWallpaper (WallpaperEngine::Render::CWallpaper::fromWallpaper ( - this->m_defaultBackground->getWallpaper (), context, audioContext + context->setDefaultWallpaper (WallpaperEngine::Render::CWallpaper::fromWallpaper ( + this->m_defaultBackground->getWallpaper (), *context, *audioContext )); - float startTime, endTime, minimumTime = 1.0f / this->m_context.settings.render.maximumFPS; - time_t seconds; - struct tm* timeinfo; + static time_t seconds; + static struct tm* timeinfo; - while (!videoDriver.closeRequested () && this->m_context.state.general.keepRunning) + while (this->m_context.state.general.keepRunning && !videoDriver->closeRequested ()) { // update g_Daytime time (&seconds); timeinfo = localtime(&seconds); g_Daytime = ((timeinfo->tm_hour * 60) + timeinfo->tm_min) / (24.0 * 60.0); - // update audio recorder - audioDriver.update (); - // update input information - inputContext.update (); + // keep track of the previous frame's time g_TimeLast = g_Time; // calculate the current time value - g_Time = videoDriver.getRenderTime (); - // get the start time of the frame - startTime = g_Time; - // render the scene - context.render (); - // get the end time of the frame - endTime = videoDriver.getRenderTime (); + g_Time = videoDriver->getRenderTime (); + // update audio recorder + audioDriver->update (); + // update input information + inputContext->update (); + // process driver events + videoDriver->dispatchEventQueue (); - // ensure the frame time is correct to not overrun FPS - if ((endTime - startTime) < minimumTime) - usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC); - - if (!this->m_context.settings.screenshot.take || videoDriver.getFrameCounter () != 5) + if (!this->m_context.settings.screenshot.take || videoDriver->getFrameCounter () < 5) continue; - this->takeScreenshot (context, this->m_context.settings.screenshot.path, this->m_context.settings.screenshot.format); - // disable screenshot just in case the counter overflows + this->takeScreenshot (*context, this->m_context.settings.screenshot.path, this->m_context.settings.screenshot.format); this->m_context.settings.screenshot.take = false; } @@ -358,6 +367,12 @@ namespace WallpaperEngine::Application SDL_Quit (); } + void CWallpaperApplication::update(Render::Drivers::Output::COutputViewport* viewport) + { + // render the scene + context->render (viewport); + } + void CWallpaperApplication::signal (int signal) { this->m_context.state.general.keepRunning = false; diff --git a/src/WallpaperEngine/Application/CWallpaperApplication.h b/src/WallpaperEngine/Application/CWallpaperApplication.h index 959f595..70ffacf 100644 --- a/src/WallpaperEngine/Application/CWallpaperApplication.h +++ b/src/WallpaperEngine/Application/CWallpaperApplication.h @@ -9,6 +9,24 @@ #include "WallpaperEngine/Render/CWallpaper.h" #include "WallpaperEngine/Render/CRenderContext.h" #include "WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h" +#ifdef ENABLE_WAYLAND +#include "WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h" +#endif + +#include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h" +#ifdef ENABLE_WAYLAND +#include "WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h" +#endif + +#include "WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h" +#include "WallpaperEngine/Render/Drivers/Output/CX11Output.h" +#ifdef ENABLE_WAYLAND +#include "WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h" +#endif + +#include "WallpaperEngine/Audio/Drivers/CSDLAudioDriver.h" + +#include "WallpaperEngine/Input/CInputContext.h" namespace WallpaperEngine::Application { @@ -21,6 +39,7 @@ namespace WallpaperEngine::Application { public: explicit CWallpaperApplication (CApplicationContext& context); + ~CWallpaperApplication (); /** * Shows the application until it's closed @@ -44,6 +63,14 @@ namespace WallpaperEngine::Application * @return The current application context */ [[nodiscard]] CApplicationContext& getContext () const; + /** + * Renders a frame + */ + void update(Render::Drivers::Output::COutputViewport* viewport); + /** + * Gets the output + */ + WallpaperEngine::Render::Drivers::Output::COutput* getOutput() const; private: /** @@ -89,5 +116,11 @@ namespace WallpaperEngine::Application CApplicationContext& m_context; /** Maps screens to backgrounds */ std::map m_backgrounds; + + WallpaperEngine::Render::Drivers::CVideoDriver* videoDriver; + WallpaperEngine::Input::CInputContext* inputContext; + WallpaperEngine::Audio::Drivers::CSDLAudioDriver* audioDriver; + WallpaperEngine::Render::CRenderContext* context; + WallpaperEngine::Audio::CAudioContext* audioContext; }; } diff --git a/src/WallpaperEngine/Assets/CFileEntry.h b/src/WallpaperEngine/Assets/CFileEntry.h index bf2ab9e..e73112a 100644 --- a/src/WallpaperEngine/Assets/CFileEntry.h +++ b/src/WallpaperEngine/Assets/CFileEntry.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace WallpaperEngine::Assets { /** diff --git a/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.cpp b/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.cpp index b02c740..ba0962a 100644 --- a/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.cpp +++ b/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.cpp @@ -4,7 +4,7 @@ namespace WallpaperEngine::Audio::Drivers::Detectors { CAudioPlayingDetector::CAudioPlayingDetector ( Application::CApplicationContext& appContext, - Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector) : + const Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector) : m_applicationContext (appContext), m_fullscreenDetector (fullscreenDetector), m_isPlaying (false) @@ -21,7 +21,7 @@ namespace WallpaperEngine::Audio::Drivers::Detectors return this->m_applicationContext; } - Render::Drivers::Detectors::CFullScreenDetector& CAudioPlayingDetector::getFullscreenDetector () + const Render::Drivers::Detectors::CFullScreenDetector& CAudioPlayingDetector::getFullscreenDetector () const { return this->m_fullscreenDetector; } diff --git a/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.h b/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.h index 28bfde2..cec3341 100644 --- a/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.h +++ b/src/WallpaperEngine/Audio/Drivers/Detectors/CAudioPlayingDetector.h @@ -23,7 +23,7 @@ namespace WallpaperEngine class CAudioPlayingDetector { public: - CAudioPlayingDetector (Application::CApplicationContext& appContext, Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector); + CAudioPlayingDetector (Application::CApplicationContext& appContext, const Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector); /** * @return If any kind of sound is currently playing on the default audio device @@ -48,13 +48,13 @@ namespace WallpaperEngine /** * @return The fullscreen detector used */ - [[nodiscard]] Render::Drivers::Detectors::CFullScreenDetector& getFullscreenDetector (); + [[nodiscard]] const Render::Drivers::Detectors::CFullScreenDetector& getFullscreenDetector () const; private: bool m_isPlaying; Application::CApplicationContext& m_applicationContext; - Render::Drivers::Detectors::CFullScreenDetector& m_fullscreenDetector; + const Render::Drivers::Detectors::CFullScreenDetector& m_fullscreenDetector; }; } } diff --git a/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.cpp b/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.cpp index 2e16cfe..3019e01 100644 --- a/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.cpp +++ b/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.cpp @@ -18,7 +18,7 @@ namespace WallpaperEngine::Audio::Drivers::Detectors // get processid const char* value = pa_proplist_gets (info->proplist, PA_PROP_APPLICATION_PROCESS_ID); - if (atoi (value) != getpid () && pa_cvolume_avg (&info->volume) != PA_VOLUME_MUTED) + if (value && atoi (value) != getpid () && pa_cvolume_avg (&info->volume) != PA_VOLUME_MUTED) detector->setIsPlaying (true); } @@ -34,7 +34,7 @@ namespace WallpaperEngine::Audio::Drivers::Detectors CPulseAudioPlayingDetector::CPulseAudioPlayingDetector ( Application::CApplicationContext& appContext, - Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector) : + const Render::Drivers::Detectors::CFullScreenDetector& fullscreenDetector) : CAudioPlayingDetector (appContext, fullscreenDetector), m_mainloop (nullptr), m_mainloopApi (nullptr), diff --git a/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h b/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h index f48a0b7..b4628c2 100644 --- a/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h +++ b/src/WallpaperEngine/Audio/Drivers/Detectors/CPulseAudioPlayingDetector.h @@ -10,7 +10,7 @@ namespace WallpaperEngine::Audio::Drivers::Detectors class CPulseAudioPlayingDetector : public CAudioPlayingDetector { public: - explicit CPulseAudioPlayingDetector (Application::CApplicationContext& appContext, Render::Drivers::Detectors::CFullScreenDetector&); + explicit CPulseAudioPlayingDetector (Application::CApplicationContext& appContext, const Render::Drivers::Detectors::CFullScreenDetector&); ~CPulseAudioPlayingDetector (); void update () override; diff --git a/src/WallpaperEngine/Input/CInputContext.cpp b/src/WallpaperEngine/Input/CInputContext.cpp index 8ae463f..7c3970b 100644 --- a/src/WallpaperEngine/Input/CInputContext.cpp +++ b/src/WallpaperEngine/Input/CInputContext.cpp @@ -1,20 +1,19 @@ #include "CInputContext.h" -#include "WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h" using namespace WallpaperEngine::Input; using namespace WallpaperEngine::Render::Drivers; -CInputContext::CInputContext (CX11OpenGLDriver& videoDriver) : - m_mouse (videoDriver.getWindow ()) +CInputContext::CInputContext (CMouseInput* mouseInput) : + m_mouse (mouseInput) { } void CInputContext::update () { - this->m_mouse.update (); + this->m_mouse->update (); } const CMouseInput& CInputContext::getMouseInput () const { - return this->m_mouse; + return *this->m_mouse; } \ No newline at end of file diff --git a/src/WallpaperEngine/Input/CInputContext.h b/src/WallpaperEngine/Input/CInputContext.h index bffc9c3..40e0394 100644 --- a/src/WallpaperEngine/Input/CInputContext.h +++ b/src/WallpaperEngine/Input/CInputContext.h @@ -1,11 +1,10 @@ #pragma once -#include "WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h" #include "CMouseInput.h" namespace WallpaperEngine::Render::Drivers { - class CX11OpenGLDriver; + class CVideoDriver; } namespace WallpaperEngine::Input @@ -13,12 +12,16 @@ namespace WallpaperEngine::Input class CInputContext { public: - explicit CInputContext (Render::Drivers::CX11OpenGLDriver& videoDriver); + explicit CInputContext (CMouseInput* mouseInput); + + /** + * Updates input information + */ void update (); [[nodiscard]] const CMouseInput& getMouseInput () const; private: - CMouseInput m_mouse; + CMouseInput* m_mouse; }; } diff --git a/src/WallpaperEngine/Input/CMouseInput.cpp b/src/WallpaperEngine/Input/CMouseInput.cpp index 458bb4d..e69de29 100644 --- a/src/WallpaperEngine/Input/CMouseInput.cpp +++ b/src/WallpaperEngine/Input/CMouseInput.cpp @@ -1,14 +0,0 @@ -#include -#include "CMouseInput.h" - -using namespace WallpaperEngine::Input; - -CMouseInput::CMouseInput (GLFWwindow* window) : position (), m_mousePosition (), m_window (window) {} - -void CMouseInput::update () -{ - // update current mouse position - glfwGetCursorPos (this->m_window, &this->m_mousePosition.x, &this->m_mousePosition.y); - // interpolate to the new position - this->position = glm::mix (this->position, this->m_mousePosition, 1.0); -} \ No newline at end of file diff --git a/src/WallpaperEngine/Input/CMouseInput.h b/src/WallpaperEngine/Input/CMouseInput.h index 7909081..06021f6 100644 --- a/src/WallpaperEngine/Input/CMouseInput.h +++ b/src/WallpaperEngine/Input/CMouseInput.h @@ -1,7 +1,6 @@ #pragma once #include -#include "GLFW/glfw3.h" namespace WallpaperEngine::Input { @@ -11,28 +10,15 @@ namespace WallpaperEngine::Input class CMouseInput { public: - explicit CMouseInput(GLFWwindow* window); - /** * Takes current mouse position and updates it */ - void update (); + virtual void update () = 0; /** * The virtual pointer's position */ - glm::dvec2 position; - - private: - /** - * The GLFW window to get mouse position from - */ - GLFWwindow* m_window; - - /** - * The current mouse position - */ - glm::dvec2 m_mousePosition; + virtual glm::dvec2 position () const = 0; }; } diff --git a/src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.cpp b/src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.cpp new file mode 100644 index 0000000..ffc8836 --- /dev/null +++ b/src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.cpp @@ -0,0 +1,24 @@ +#include +#include "CGLFWMouseInput.h" + +#include "WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h" + +using namespace WallpaperEngine::Input::Drivers; + +CGLFWMouseInput::CGLFWMouseInput (Render::Drivers::CX11OpenGLDriver* driver) : + m_reportedPosition (), + m_mousePosition (), + m_driver (driver) {} + +void CGLFWMouseInput::update () +{ + // update current mouse position + glfwGetCursorPos (this->m_driver->getWindow (), &this->m_mousePosition.x, &this->m_mousePosition.y); + // interpolate to the new position + this->m_reportedPosition = glm::mix (this->m_reportedPosition, this->m_mousePosition, 1.0); +} + +glm::dvec2 CGLFWMouseInput::position() const +{ + return this->m_reportedPosition; +} \ No newline at end of file diff --git a/src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.h b/src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.h new file mode 100644 index 0000000..73bf9d1 --- /dev/null +++ b/src/WallpaperEngine/Input/Drivers/CGLFWMouseInput.h @@ -0,0 +1,42 @@ +#pragma once + +#include "WallpaperEngine/Input/CMouseInput.h" + +#include + +namespace WallpaperEngine::Render::Drivers +{ + class CX11OpenGLDriver; +} + +namespace WallpaperEngine::Input::Drivers +{ + /** + * Handles mouse input for the background + */ + class CGLFWMouseInput : public CMouseInput + { + public: + explicit CGLFWMouseInput(Render::Drivers::CX11OpenGLDriver* driver); + + /** + * Takes current mouse position and updates it + */ + void update () override; + + /** + * The virtual pointer's position + */ + glm::dvec2 position () const override; + + private: + Render::Drivers::CX11OpenGLDriver* m_driver; + + /** + * The current mouse position + */ + glm::dvec2 m_mousePosition; + glm::dvec2 m_reportedPosition; + }; +} + diff --git a/src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp b/src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp new file mode 100644 index 0000000..2261996 --- /dev/null +++ b/src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.cpp @@ -0,0 +1,21 @@ +#include +#include "CWaylandMouseInput.h" + +using namespace WallpaperEngine::Input::Drivers; + +CWaylandMouseInput::CWaylandMouseInput(WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver* driver) : + waylandDriver (driver) +{ +} + +void CWaylandMouseInput::update () +{ +} + +glm::dvec2 CWaylandMouseInput::position() const +{ + if (waylandDriver->viewportInFocus && waylandDriver->viewportInFocus->rendering) + return waylandDriver->viewportInFocus->mousePos; + + return {0, 0}; +} \ No newline at end of file diff --git a/src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h b/src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h new file mode 100644 index 0000000..dd83a61 --- /dev/null +++ b/src/WallpaperEngine/Input/Drivers/CWaylandMouseInput.h @@ -0,0 +1,41 @@ +#ifdef ENABLE_WAYLAND +#pragma once + +#include "WallpaperEngine/Input/CMouseInput.h" +#include "WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h" + +#include +#include "GLFW/glfw3.h" + + +namespace WallpaperEngine::Input::Drivers +{ + /** + * Handles mouse input for the background + */ + class CWaylandMouseInput : public CMouseInput + { + public: + explicit CWaylandMouseInput(WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver* driver); + + /** + * Takes current mouse position and updates it + */ + void update () override; + + /** + * The virtual pointer's position + */ + glm::dvec2 position () const override; + + private: + /** + * Wayland: Driver + */ + WallpaperEngine::Render::Drivers::CWaylandOpenGLDriver* waylandDriver = nullptr; + + glm::dvec2 pos; + }; +} + +#endif /* ENABLE_WAYLAND */ \ No newline at end of file diff --git a/src/WallpaperEngine/Render/CRenderContext.cpp b/src/WallpaperEngine/Render/CRenderContext.cpp index 22803f9..08ad438 100644 --- a/src/WallpaperEngine/Render/CRenderContext.cpp +++ b/src/WallpaperEngine/Render/CRenderContext.cpp @@ -10,11 +10,10 @@ namespace WallpaperEngine::Render { CRenderContext::CRenderContext ( - const Drivers::Output::COutput* output, Drivers::CVideoDriver& driver, Input::CInputContext& input, + Drivers::CVideoDriver& driver, Input::CInputContext& input, CWallpaperApplication& app ) : m_defaultWallpaper (nullptr), - m_output (output), m_driver (driver), m_app (app), m_input (input), @@ -22,48 +21,30 @@ namespace WallpaperEngine::Render { } - void CRenderContext::render () + void CRenderContext::render (Drivers::Output::COutputViewport* viewport) { - bool firstFrame = true; - bool renderFrame = true; + viewport->makeCurrent (); - for (const auto& cur : this->m_output->getViewports ()) - { #if !NDEBUG - std::string str = "Rendering to output " + cur.first; + std::string str = "Rendering to output " + viewport->name; - glPushDebugGroup (GL_DEBUG_SOURCE_APPLICATION, 0, -1, str.c_str ()); + glPushDebugGroup (GL_DEBUG_SOURCE_APPLICATION, 0, -1, str.c_str ()); #endif /* DEBUG */ - // search the background in the viewport selection - auto ref = this->m_wallpapers.find (cur.first); + // search the background in the viewport selection + auto ref = this->m_wallpapers.find (viewport->name); + + // render the background + if (ref != this->m_wallpapers.end ()) + ref->second->render (viewport->viewport, this->getOutput ().renderVFlip ()); + else + this->m_defaultWallpaper->render (viewport->viewport, this->getOutput ().renderVFlip ()); - // render the background - if (ref != this->m_wallpapers.end ()) - ref->second->render (cur.second.viewport, this->m_output->renderVFlip (), renderFrame, firstFrame); - else - this->m_defaultWallpaper->render ( - cur.second.viewport, this->m_output->renderVFlip (), renderFrame, firstFrame - ); - // scenes need to render a new frame for each viewport as they produce different results - // but videos should only be rendered once per group of viewports - firstFrame = false; #if !NDEBUG - glPopDebugGroup (); + glPopDebugGroup (); #endif /* DEBUG */ - } - // read the full texture into the image - if (this->m_output->haveImageBuffer ()) - glReadPixels ( - 0, 0, this->m_output->getFullWidth (), this->m_output->getFullHeight (), GL_BGRA, GL_UNSIGNED_BYTE, - this->m_output->getImageBuffer () - ); - - // update the output with the given image - this->m_output->updateRender (); - // finally swap buffers - this->m_driver.swapBuffers (); + viewport->swapOutput (); } void CRenderContext::setDefaultWallpaper (CWallpaper* wallpaper) @@ -91,9 +72,9 @@ namespace WallpaperEngine::Render return this->m_driver; } - const Drivers::Output::COutput* CRenderContext::getOutput () const + const Drivers::Output::COutput& CRenderContext::getOutput () const { - return this->m_output; + return this->m_driver.getOutput (); } const ITexture* CRenderContext::resolveTexture (const std::string& name) diff --git a/src/WallpaperEngine/Render/CRenderContext.h b/src/WallpaperEngine/Render/CRenderContext.h index 87d4b31..2ce79e1 100644 --- a/src/WallpaperEngine/Render/CRenderContext.h +++ b/src/WallpaperEngine/Render/CRenderContext.h @@ -9,6 +9,7 @@ #include "WallpaperEngine/Input/CMouseInput.h" #include "WallpaperEngine/Render/Drivers/CVideoDriver.h" #include "WallpaperEngine/Render/Drivers/Output/COutput.h" +#include "WallpaperEngine/Render/Drivers/Output/COutputViewport.h" namespace WallpaperEngine { @@ -26,6 +27,7 @@ namespace WallpaperEngine namespace Output { class COutput; + class COutputViewport; } } @@ -35,15 +37,15 @@ namespace WallpaperEngine class CRenderContext { public: - CRenderContext (const Drivers::Output::COutput* output, Drivers::CVideoDriver& driver, Input::CInputContext& input, CWallpaperApplication& app); + CRenderContext (Drivers::CVideoDriver& driver, Input::CInputContext& input, CWallpaperApplication& app); - void render (); + void render (Drivers::Output::COutputViewport* viewport); void setDefaultWallpaper (CWallpaper* wallpaper); void setWallpaper (const std::string& display, CWallpaper* wallpaper); [[nodiscard]] Input::CInputContext& getInputContext () const; [[nodiscard]] const CWallpaperApplication& getApp () const; [[nodiscard]] const Drivers::CVideoDriver& getDriver () const; - [[nodiscard]] const Drivers::Output::COutput* getOutput () const; + [[nodiscard]] const Drivers::Output::COutput& getOutput () const; const ITexture* resolveTexture (const std::string& name); private: @@ -59,8 +61,6 @@ namespace WallpaperEngine CWallpaperApplication& m_app; /** Texture cache for the render */ CTextureCache* m_textureCache; - /** Output driver that describes how the wallpapers are rendered */ - const Drivers::Output::COutput* m_output; }; } } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/CScene.cpp b/src/WallpaperEngine/Render/CScene.cpp index c345ede..3f8f80c 100644 --- a/src/WallpaperEngine/Render/CScene.cpp +++ b/src/WallpaperEngine/Render/CScene.cpp @@ -248,7 +248,7 @@ void CScene::renderFrame (glm::ivec4 viewport) void CScene::updateMouse (glm::ivec4 viewport) { // update virtual mouse position first - glm::dvec2 position = this->getContext ().getInputContext ().getMouseInput ().position; + glm::dvec2 position = this->getContext ().getInputContext ().getMouseInput ().position(); // TODO: PROPERLY TRANSLATE THESE TO WHAT'S VISIBLE ON SCREEN (FOR BACKGROUNDS THAT DO NOT EXACTLY FIT ON SCREEN) // rollover the position to the last diff --git a/src/WallpaperEngine/Render/CVideo.cpp b/src/WallpaperEngine/Render/CVideo.cpp index d3fe4a6..8606e19 100644 --- a/src/WallpaperEngine/Render/CVideo.cpp +++ b/src/WallpaperEngine/Render/CVideo.cpp @@ -8,7 +8,7 @@ using namespace WallpaperEngine::Render; void* get_proc_address (void* ctx, const char* name) { - return reinterpret_cast (glfwGetProcAddress (name)); + return static_cast (ctx)->getContext ().getDriver ().getProcAddress (name); } CVideo::CVideo (Core::CVideo* video, CRenderContext& context, CAudioContext& audioContext) : @@ -40,7 +40,7 @@ CVideo::CVideo (Core::CVideo* video, CRenderContext& context, CAudioContext& aud mpv_set_option (this->m_mpv, "volume", MPV_FORMAT_DOUBLE, &volume); // initialize gl context for mpv - mpv_opengl_init_params gl_init_params {get_proc_address, nullptr}; + mpv_opengl_init_params gl_init_params {get_proc_address, this}; mpv_render_param params[] { {MPV_RENDER_PARAM_API_TYPE, const_cast (MPV_RENDER_API_TYPE_OPENGL)}, {MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params}, diff --git a/src/WallpaperEngine/Render/CWallpaper.cpp b/src/WallpaperEngine/Render/CWallpaper.cpp index 235484f..f925020 100644 --- a/src/WallpaperEngine/Render/CWallpaper.cpp +++ b/src/WallpaperEngine/Render/CWallpaper.cpp @@ -211,10 +211,9 @@ void CWallpaper::setDestinationFramebuffer (GLuint framebuffer) this->m_destFramebuffer = framebuffer; } -void CWallpaper::render (glm::ivec4 viewport, bool vflip, bool renderFrame, bool newFrame) +void CWallpaper::render (glm::ivec4 viewport, bool vflip) { - if (renderFrame) - this->renderFrame (viewport); + this->renderFrame (viewport); uint32_t projectionWidth = this->getWidth (); uint32_t projectionHeight = this->getHeight (); @@ -293,9 +292,6 @@ void CWallpaper::render (glm::ivec4 viewport, bool vflip, bool renderFrame, bool glBindVertexArray (this->m_vaoBuffer); - if (newFrame) - glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glDisable (GL_BLEND); glDisable (GL_DEPTH_TEST); // do not use any shader diff --git a/src/WallpaperEngine/Render/CWallpaper.h b/src/WallpaperEngine/Render/CWallpaper.h index 1ba10c9..ed71611 100644 --- a/src/WallpaperEngine/Render/CWallpaper.h +++ b/src/WallpaperEngine/Render/CWallpaper.h @@ -37,7 +37,7 @@ namespace WallpaperEngine::Render /** * Performs a render pass of the wallpaper */ - void render (glm::ivec4 viewport, bool vflip, bool renderFrame = true, bool newFrame = true); + void render (glm::ivec4 viewport, bool vflip); /** * @return The container to resolve files for this wallpaper diff --git a/src/WallpaperEngine/Render/Drivers/CVideoDriver.cpp b/src/WallpaperEngine/Render/Drivers/CVideoDriver.cpp index 36d8178..cfb3b77 100644 --- a/src/WallpaperEngine/Render/Drivers/CVideoDriver.cpp +++ b/src/WallpaperEngine/Render/Drivers/CVideoDriver.cpp @@ -1,3 +1,17 @@ #include "CVideoDriver.h" using namespace WallpaperEngine::Render::Drivers; + +CVideoDriver::CVideoDriver (CWallpaperApplication& app) : + m_app (app) +{ +} + +CVideoDriver::~CVideoDriver () +{ +} + +CWallpaperApplication& CVideoDriver::getApp () const +{ + return this->m_app; +} \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/CVideoDriver.h b/src/WallpaperEngine/Render/Drivers/CVideoDriver.h index c164b2e..f42eb02 100644 --- a/src/WallpaperEngine/Render/Drivers/CVideoDriver.h +++ b/src/WallpaperEngine/Render/Drivers/CVideoDriver.h @@ -2,20 +2,39 @@ #include #include +#include +#include "WallpaperEngine/Render/Drivers/Output/COutput.h" + +namespace WallpaperEngine::Application +{ + class CWallpaperApplication; +} namespace WallpaperEngine::Render::Drivers { + namespace Detectors + { + class CFullScreenDetector; + } + class CVideoDriver { public: + CVideoDriver (CWallpaperApplication& app); + virtual ~CVideoDriver (); + /** - * @return The window handle used by this video driver + * @return The fullscreen detector this video driver uses */ - virtual void* getWindowHandle () const = 0; + [[nodiscard]] virtual Detectors::CFullScreenDetector& getFullscreenDetector () = 0; + /** + * @return The current output in use + */ + [[nodiscard]] virtual Output::COutput& getOutput () = 0; /** * @return The time that has passed since the driver started */ - virtual float getRenderTime () const = 0; + [[nodiscard]] virtual float getRenderTime () const = 0; /** * @return If a close was requested by the OS */ @@ -39,14 +58,27 @@ namespace WallpaperEngine::Render::Drivers /** * @return The size of the framebuffer available for the driver */ - virtual glm::ivec2 getFramebufferSize () const = 0; - /** - * Performs buffer swapping - */ - virtual void swapBuffers () = 0; + [[nodiscard]] virtual glm::ivec2 getFramebufferSize () const = 0; /** * @return The number of rendered frames since the start of the driver */ - virtual uint32_t getFrameCounter () const = 0; + [[nodiscard]] virtual uint32_t getFrameCounter () const = 0; + /** + * @param name + * @return GetProcAddress for this video driver + */ + [[nodiscard]] virtual void* getProcAddress (const char* name) const = 0; + /** + * Process events on the driver and renders a frame + */ + virtual void dispatchEventQueue() = 0; + /** + * @return The app that owns this driver + */ + [[nodiscard]] CWallpaperApplication& getApp () const; + + private: + /** App that owns this driver */ + CWallpaperApplication& m_app; }; } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.cpp b/src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.cpp new file mode 100644 index 0000000..e72aa9f --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.cpp @@ -0,0 +1,401 @@ +#include "CWaylandOpenGLDriver.h" +#include "WallpaperEngine/Application/CWallpaperApplication.h" + +#include + +#include "common.h" + +#define class _class +#define namespace _namespace +#define static +extern "C" { +#include "xdg-shell-protocol.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" +} +#undef class +#undef namespace +#undef static + +#include +#include + +using namespace WallpaperEngine::Render::Drivers; + +static void handlePointerEnter ( + void* data, struct wl_pointer* wl_pointer, uint32_t serial, struct wl_surface* surface, + wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + const auto driver = static_cast (data); + const auto viewport = driver->surfaceToViewport (surface); + driver->viewportInFocus = viewport; + wl_surface_set_buffer_scale(viewport->cursorSurface, viewport->scale); + wl_surface_attach(viewport->cursorSurface, wl_cursor_image_get_buffer(viewport->pointer->images[0]), 0, 0); + wl_pointer_set_cursor(wl_pointer, serial, viewport->cursorSurface, viewport->pointer->images[0]->hotspot_x, viewport->pointer->images[0]->hotspot_y); + wl_surface_commit(viewport->cursorSurface); +} + +static void handlePointerLeave(void* data, struct wl_pointer* wl_pointer, uint32_t serial, struct wl_surface* surface) +{ +} + +static void handlePointerAxis(void* data, wl_pointer* wl_pointer, uint32_t time, uint32_t axis, wl_fixed_t value) +{ +} + +static void handlePointerMotion( + void* data, struct wl_pointer* wl_pointer, uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) +{ + const auto driver = static_cast (data); + + auto x = wl_fixed_to_double(surface_x); + auto y = wl_fixed_to_double(surface_y); + + if (!driver->viewportInFocus) + return; + + driver->viewportInFocus->mousePos = {x * driver->viewportInFocus->scale, y * driver->viewportInFocus->scale}; +} + +static void handlePointerButton( + void* data, struct wl_pointer* wl_pointer, uint32_t serial, uint32_t time, uint32_t button, uint32_t button_state) +{ +} + +const struct wl_pointer_listener pointerListener = +{ + .enter = handlePointerEnter, + .leave = handlePointerLeave, + .motion = handlePointerMotion, + .button = handlePointerButton, + .axis = handlePointerAxis +}; + +static void handleCapabilities(void* data, wl_seat* wl_seat, uint32_t capabilities) +{ + if (capabilities & WL_SEAT_CAPABILITY_POINTER) + wl_pointer_add_listener(wl_seat_get_pointer(wl_seat), &pointerListener, data); +} + +const struct wl_seat_listener seatListener = +{ + .capabilities = handleCapabilities +}; + +static void handleGlobal(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) +{ + const auto driver = static_cast (data); + + if (strcmp(interface, wl_compositor_interface.name) == 0) { + driver->getWaylandContext()->compositor = (wl_compositor*)wl_registry_bind(registry, name, &wl_compositor_interface, 4); + } else if (strcmp(interface, wl_shm_interface.name) == 0) { + driver->getWaylandContext()->shm = (wl_shm*)wl_registry_bind(registry, name, &wl_shm_interface, 1); + } else if (strcmp(interface, wl_output_interface.name) == 0) { + driver->m_screens.emplace_back (new WallpaperEngine::Render::Drivers::Output::CWaylandOutputViewport (driver, name, registry)); + } else if (strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0) { + driver->getWaylandContext()->layerShell = (zwlr_layer_shell_v1*)wl_registry_bind(registry, name, &zwlr_layer_shell_v1_interface, 1); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + driver->getWaylandContext()->seat = (wl_seat*)wl_registry_bind(registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(driver->getWaylandContext()->seat, &seatListener, driver); + } +} + +static void handleGlobalRemoved(void *data, struct wl_registry *registry, uint32_t id) +{ + // todo: outputs +} + +const struct wl_registry_listener registryListener = +{ + .global = handleGlobal, + .global_remove = handleGlobalRemoved, +}; + +void CWaylandOpenGLDriver::initEGL() +{ + const char* CLIENT_EXTENSIONS = eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS); + if (!CLIENT_EXTENSIONS) + sLog.exception("Failed to query EGL Extensions"); + + const std::string CLIENTEXTENSIONS = std::string(CLIENT_EXTENSIONS); + + if (CLIENTEXTENSIONS.find("EGL_EXT_platform_base") == std::string::npos) + sLog.exception("EGL_EXT_platform_base not supported by EGL!"); + + if (CLIENTEXTENSIONS.find("EGL_EXT_platform_wayland") == std::string::npos) + sLog.exception("EGL_EXT_platform_wayland not supported by EGL!"); + + PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = (PFNEGLGETPLATFORMDISPLAYEXTPROC)eglGetProcAddress("eglGetPlatformDisplayEXT"); + m_eglContext.eglCreatePlatformWindowSurfaceEXT = (PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC)eglGetProcAddress("eglCreatePlatformWindowSurfaceEXT"); + + if (!eglGetPlatformDisplayEXT || !m_eglContext.eglCreatePlatformWindowSurfaceEXT) + sLog.exception("EGL did not return EXT proc pointers!"); + + auto deinitEGL = [&] () -> void { + eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (m_eglContext.display) + eglTerminate(m_eglContext.display); + eglReleaseThread(); + }; + + m_eglContext.display = eglGetPlatformDisplayEXT(EGL_PLATFORM_WAYLAND_EXT, m_waylandContext.display, nullptr); + + if (m_eglContext.display == EGL_NO_DISPLAY) + { + deinitEGL (); + sLog.exception ("eglGetPlatformDisplayEXT failed!"); + } + + if (!eglInitialize(m_eglContext.display, nullptr, nullptr)) + { + deinitEGL (); + sLog.exception ("eglInitialize failed!"); + } + + const std::string CLIENTEXTENSIONSPOSTINIT = std::string(eglQueryString(m_eglContext.display, EGL_EXTENSIONS)); + + if (CLIENTEXTENSIONSPOSTINIT.find("EGL_KHR_create_context") == std::string::npos) + { + deinitEGL (); + sLog.exception ("EGL_KHR_create_context not supported!"); + } + + EGLint matchedConfigs = 0; + const EGLint CONFIG_ATTRIBUTES[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 1, + EGL_GREEN_SIZE, 1, + EGL_BLUE_SIZE, 1, + EGL_SAMPLES, 4, + EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT, + EGL_NONE, + }; + + if (!eglChooseConfig(m_eglContext.display, CONFIG_ATTRIBUTES, &m_eglContext.config, 1, &matchedConfigs)) + { + deinitEGL (); + sLog.exception ("eglChooseConfig failed!"); + } + + if (matchedConfigs == 0) + { + deinitEGL (); + sLog.exception ("eglChooseConfig failed! (matched 0 configs)"); + } + + if (!eglBindAPI(EGL_OPENGL_API)) + { + deinitEGL (); + sLog.exception ("eglBindAPI failed!"); + } + + const EGLint CONTEXT_ATTRIBUTES[] = { + EGL_CONTEXT_MAJOR_VERSION_KHR, 3, + EGL_CONTEXT_MINOR_VERSION_KHR, 3, + EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR, + EGL_NONE, + }; + + m_eglContext.context = eglCreateContext(m_eglContext.display, m_eglContext.config, EGL_NO_CONTEXT, CONTEXT_ATTRIBUTES); + + if (m_eglContext.context == EGL_NO_CONTEXT) + { + deinitEGL (); + sLog.error("eglCreateContext error " + std::to_string(eglGetError())); + sLog.exception("eglCreateContext failed!"); + } +} + +void CWaylandOpenGLDriver::onLayerClose(Output::CWaylandOutputViewport* viewport) +{ + sLog.error ("Compositor closed our LS, freeing data..."); + + if (viewport->eglSurface) + eglDestroySurface (m_eglContext.display, viewport->eglSurface); + + if (viewport->eglWindow) + wl_egl_window_destroy(viewport->eglWindow); + + if (viewport->layerSurface) + zwlr_layer_surface_v1_destroy(viewport->layerSurface); + + if (viewport->surface) + wl_surface_destroy(viewport->surface); + + // remove the output from the list + std::remove (this->m_screens.begin (), this->m_screens.end (), viewport); + + // reset the viewports + this->getOutput ().reset (); + + // finally free memory used by the viewport + delete viewport; +} + +CWaylandOpenGLDriver::CWaylandOpenGLDriver(CApplicationContext& context, CWallpaperApplication& app) : + m_frameCounter(0), + m_fullscreenDetector (context, *this), + m_output (context, *this), + m_requestedExit (false), + m_context (context), + CVideoDriver (app) +{ + m_waylandContext.display = wl_display_connect (nullptr); + + if (!m_waylandContext.display) + sLog.exception ("Failed to query wayland display"); + + m_waylandContext.registry = wl_display_get_registry(m_waylandContext.display); + wl_registry_add_listener(m_waylandContext.registry, ®istryListener, this); + + wl_display_dispatch(m_waylandContext.display); + wl_display_roundtrip(m_waylandContext.display); + + if (!m_waylandContext.compositor || !m_waylandContext.shm || !m_waylandContext.layerShell || this->m_screens.empty()) + sLog.exception ("Failed to bind to required interfaces"); + + initEGL(); + + bool any = false; + + for (auto& o : this->m_screens) + { + const auto cur = context.settings.general.screenBackgrounds.find (o->name); + + if (cur == context.settings.general.screenBackgrounds.end ()) + continue; + + o->setupLS (); + any = true; + } + + if (!any) + sLog.exception("No outputs could be initialized, please check the parameters and try again"); + + GLenum result = glewInit (); + + if (result != GLEW_OK) + sLog.error("Failed to initialize GLEW: ", glewGetErrorString (result)); + + FreeImage_Initialise (TRUE); +} + +CWaylandOpenGLDriver::~CWaylandOpenGLDriver () +{ + // stop EGL + eglMakeCurrent (EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (m_eglContext.context != EGL_NO_CONTEXT) + eglDestroyContext (m_eglContext.display, m_eglContext.context); + + eglTerminate (m_eglContext.display); + eglReleaseThread (); + + // disconnect from wayland display + if (this->m_waylandContext.display) + wl_display_disconnect (this->m_waylandContext.display); +} + +void CWaylandOpenGLDriver::dispatchEventQueue() +{ + static bool initialized = false; + + if (!initialized) + { + initialized = true; + + for (const auto& viewport : this->getOutput ().getViewports ()) + this->getApp ().update (viewport.second); + } + + // TODO: FRAMETIME CONTROL SHOULD GO BACK TO THE CWALLPAPAERAPPLICATION ONCE ACTUAL PARTICLES ARE IMPLEMENTED + // TODO: AS THOSE, MORE THAN LIKELY, WILL REQUIRE OF A DIFFERENT PROCESSING RATE + + // TODO: WRITE A NON-BLOCKING VERSION OF THIS ONCE PARTICLE SIMULATION STARTS WORKING + // TODO: OTHERWISE wl_display_dispatch WILL BLOCK IF NO SURFACES ARE BEING DRAWN + static float startTime, endTime, minimumTime = 1.0f / this->m_context.settings.render.maximumFPS; + // get the start time of the frame + startTime = this->getRenderTime (); + + if (wl_display_dispatch(m_waylandContext.display) == -1) + m_requestedExit = true; + + m_frameCounter ++; + + endTime = this->getRenderTime (); + + // ensure the frame time is correct to not overrun FPS + if ((endTime - startTime) < minimumTime) + usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC); +} + +Detectors::CFullScreenDetector& CWaylandOpenGLDriver::getFullscreenDetector () +{ + return this->m_fullscreenDetector; +} + +Output::COutput& CWaylandOpenGLDriver::getOutput () +{ + return this->m_output; +} + +float CWaylandOpenGLDriver::getRenderTime () const +{ + return (float)std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - renderStart).count() / 1000000.0; +} + +bool CWaylandOpenGLDriver::closeRequested () +{ + return this->m_requestedExit; +} + +void CWaylandOpenGLDriver::resizeWindow (glm::ivec2 size) +{ +} + +void CWaylandOpenGLDriver::resizeWindow (glm::ivec4 sizeandpos) +{ +} + +void CWaylandOpenGLDriver::showWindow () +{ +} + +void CWaylandOpenGLDriver::hideWindow () +{ +} + +glm::ivec2 CWaylandOpenGLDriver::getFramebufferSize () const +{ + return glm::ivec2{0, 0}; +} + +uint32_t CWaylandOpenGLDriver::getFrameCounter () const +{ + return m_frameCounter; +} + +CWaylandOpenGLDriver::SEGLContext* CWaylandOpenGLDriver::getEGLContext () +{ + return &this->m_eglContext; +} + +void* CWaylandOpenGLDriver::getProcAddress (const char* name) const +{ + return reinterpret_cast (eglGetProcAddress (name)); +} + +CWaylandOpenGLDriver::SWaylandContext* CWaylandOpenGLDriver::getWaylandContext () +{ + return &this->m_waylandContext; +} + +Output::CWaylandOutputViewport* CWaylandOpenGLDriver::surfaceToViewport(wl_surface* surface) +{ + for (auto& o : m_screens) + { + if (o->surface == surface) + return o; + } + + return nullptr; +} diff --git a/src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h b/src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h new file mode 100644 index 0000000..4c7e3d0 --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/CWaylandOpenGLDriver.h @@ -0,0 +1,114 @@ +#ifdef ENABLE_WAYLAND +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "WallpaperEngine/Render/Drivers/CVideoDriver.h" +#include "WallpaperEngine/Application/CApplicationContext.h" +#include "WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h" +#include "WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h" +#include "WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h" + +namespace WallpaperEngine::Application +{ + class CApplicationContext; + class CWallpaperApplication; +} + +namespace WallpaperEngine::Input::Drivers +{ + class CWaylandMouseInput; +} + +struct zwlr_layer_shell_v1; +struct zwlr_layer_surface_v1; + +namespace WallpaperEngine::Render::Drivers +{ + using namespace WallpaperEngine::Application; + using namespace WallpaperEngine::Input::Drivers; + class CWaylandOpenGLDriver; + + namespace Output + { + class CWaylandOutputViewport; + class CWaylandOutput; + } + + class CWaylandOpenGLDriver : public CVideoDriver + { + friend class Output::CWaylandOutput; + friend class CWaylandMouseInput; + public: + struct SEGLContext + { + EGLDisplay display = nullptr; + EGLConfig config = nullptr; + EGLContext context = nullptr; + PFNEGLCREATEPLATFORMWINDOWSURFACEEXTPROC eglCreatePlatformWindowSurfaceEXT = nullptr; + }; + + struct SWaylandContext + { + wl_display* display = nullptr; + wl_registry* registry = nullptr; + wl_compositor* compositor = nullptr; + wl_shm* shm = nullptr; + zwlr_layer_shell_v1* layerShell = nullptr; + wl_seat* seat = nullptr; + }; + + explicit CWaylandOpenGLDriver (CApplicationContext& context, CWallpaperApplication& app); + ~CWaylandOpenGLDriver(); + + [[nodiscard]] Detectors::CFullScreenDetector& getFullscreenDetector () override; + [[nodiscard]] Output::COutput& getOutput () override; + float getRenderTime () const override; + bool closeRequested () override; + void resizeWindow (glm::ivec2 size) override; + void resizeWindow (glm::ivec4 sizeandpos) override; + void showWindow () override; + void hideWindow () override; + glm::ivec2 getFramebufferSize () const override; + uint32_t getFrameCounter () const override; + void dispatchEventQueue() override; + [[nodiscard]] void* getProcAddress (const char* name) const override; + + void onLayerClose(Output::CWaylandOutputViewport*); + Output::CWaylandOutputViewport* surfaceToViewport(wl_surface*); + + Output::CWaylandOutputViewport* viewportInFocus = nullptr; + + [[nodiscard]] SEGLContext* getEGLContext (); + [[nodiscard]] SWaylandContext* getWaylandContext (); + + + /** List of available screens */ + std::vector m_screens; + + private: + /** Fullscreen detection used by this driver */ + Detectors::CWaylandFullScreenDetector m_fullscreenDetector; + /** The output used by the driver */ + Output::CWaylandOutput m_output; + /** The EGL context in use */ + SEGLContext m_eglContext; + /** The Wayland context in use */ + SWaylandContext m_waylandContext; + mutable bool m_requestedExit; + + void initEGL(); + void finishEGL(); + + uint32_t m_frameCounter; + CApplicationContext& m_context; + + std::chrono::high_resolution_clock::time_point renderStart = std::chrono::high_resolution_clock::now(); + }; +} +#endif /* ENABLE_WAYLAND */ \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.cpp b/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.cpp index 28038a6..ccb979d 100644 --- a/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.cpp +++ b/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.cpp @@ -1,10 +1,13 @@ #include "CX11OpenGLDriver.h" #include "common.h" +#include "WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h" #include #define GLFW_EXPOSE_NATIVE_X11 #include +#include + using namespace WallpaperEngine::Render::Drivers; void CustomGLFWErrorHandler (int errorCode, const char* reason) @@ -12,8 +15,11 @@ void CustomGLFWErrorHandler (int errorCode, const char* reason) sLog.error ("GLFW error ", errorCode, ": ", reason); } -CX11OpenGLDriver::CX11OpenGLDriver (const char* windowTitle, CApplicationContext& context) : - m_frameCounter (0) +CX11OpenGLDriver::CX11OpenGLDriver (const char* windowTitle, CApplicationContext& context, CWallpaperApplication& app) : + m_frameCounter (0), + m_fullscreenDetector (context, *this), + m_context (context), + CVideoDriver (app) { glfwSetErrorCallback (CustomGLFWErrorHandler); @@ -60,6 +66,17 @@ CX11OpenGLDriver::CX11OpenGLDriver (const char* windowTitle, CApplicationContext // initialize free image FreeImage_Initialise (TRUE); + + // setup output + if (context.settings.render.mode == CApplicationContext::EXPLICIT_WINDOW || + context.settings.render.mode == CApplicationContext::NORMAL_WINDOW) + { + m_output = new WallpaperEngine::Render::Drivers::Output::CGLFWWindowOutput (context, *this); + } + else + { + m_output = new WallpaperEngine::Render::Drivers::Output::CX11Output (context, *this); + } } CX11OpenGLDriver::~CX11OpenGLDriver () @@ -68,9 +85,14 @@ CX11OpenGLDriver::~CX11OpenGLDriver () FreeImage_DeInitialise(); } -void* CX11OpenGLDriver::getWindowHandle () const +Detectors::CFullScreenDetector& CX11OpenGLDriver::getFullscreenDetector () { - return reinterpret_cast (glfwGetX11Window (this->m_window)); + return this->m_fullscreenDetector; +} + +Output::COutput& CX11OpenGLDriver::getOutput () +{ + return *this->m_output; } float CX11OpenGLDriver::getRenderTime () const @@ -113,19 +135,50 @@ glm::ivec2 CX11OpenGLDriver::getFramebufferSize () const return size; } -void CX11OpenGLDriver::swapBuffers () +uint32_t CX11OpenGLDriver::getFrameCounter () const { + return this->m_frameCounter; +} + +void CX11OpenGLDriver::dispatchEventQueue() +{ + static float startTime, endTime, minimumTime = 1.0f / this->m_context.settings.render.maximumFPS; + // get the start time of the frame + startTime = this->getRenderTime (); + // clear the screen + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + for (const auto& viewport : this->m_output->getViewports ()) + this->getApp ().update (viewport.second); + + // read the full texture into the image + if (this->m_output->haveImageBuffer ()) + glReadPixels ( + 0, 0, this->m_output->getFullWidth (), this->m_output->getFullHeight (), GL_BGRA, GL_UNSIGNED_BYTE, + this->m_output->getImageBuffer () + ); + + // TODO: FRAMETIME CONTROL SHOULD GO BACK TO THE CWALLPAPAERAPPLICATION ONCE ACTUAL PARTICLES ARE IMPLEMENTED + // TODO: AS THOSE, MORE THAN LIKELY, WILL REQUIRE OF A DIFFERENT PROCESSING RATE + // update the output with the given image + this->m_output->updateRender (); // do buffer swapping first glfwSwapBuffers (this->m_window); // poll for events glfwPollEvents (); // increase frame counter this->m_frameCounter ++; + // get the end time of the frame + endTime = this->getRenderTime (); + + // ensure the frame time is correct to not overrun FPS + if ((endTime - startTime) < minimumTime) + usleep ((minimumTime - (endTime - startTime)) * CLOCKS_PER_SEC); } -uint32_t CX11OpenGLDriver::getFrameCounter () const +void* CX11OpenGLDriver::getProcAddress (const char* name) const { - return this->m_frameCounter; + return reinterpret_cast (glfwGetProcAddress (name)); } GLFWwindow* CX11OpenGLDriver::getWindow () diff --git a/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h b/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h index 99ca118..2249959 100644 --- a/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h +++ b/src/WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h @@ -4,10 +4,15 @@ #include #include "WallpaperEngine/Render/Drivers/CVideoDriver.h" #include "WallpaperEngine/Application/CApplicationContext.h" +#include "WallpaperEngine/Render/Drivers/Detectors/CFullScreenDetector.h" +#include "WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h" +#include "WallpaperEngine/Render/Drivers/Output/CX11Output.h" +#include "WallpaperEngine/Application/CWallpaperApplication.h" namespace WallpaperEngine::Application { class CApplicationContext; + class CWallpaperApplication; } namespace WallpaperEngine::Render::Drivers @@ -17,23 +22,28 @@ namespace WallpaperEngine::Render::Drivers class CX11OpenGLDriver : public CVideoDriver { public: - explicit CX11OpenGLDriver (const char* windowTitle, CApplicationContext& context); + explicit CX11OpenGLDriver (const char* windowTitle, CApplicationContext& context, CWallpaperApplication& app); ~CX11OpenGLDriver(); - void* getWindowHandle () const; - float getRenderTime () const override; + [[nodiscard]] Detectors::CFullScreenDetector& getFullscreenDetector () override; + [[nodiscard]] Output::COutput& getOutput () override; + [[nodiscard]] float getRenderTime () const override; bool closeRequested () override; void resizeWindow (glm::ivec2 size) override; void resizeWindow (glm::ivec4 sizeandpos) override; void showWindow () override; void hideWindow () override; - glm::ivec2 getFramebufferSize () const override; - void swapBuffers () override; - uint32_t getFrameCounter () const override; + [[nodiscard]] glm::ivec2 getFramebufferSize () const override; + [[nodiscard]] uint32_t getFrameCounter () const override; + void dispatchEventQueue() override; + [[nodiscard]] void* getProcAddress (const char* name) const override; GLFWwindow* getWindow (); private: + Detectors::CX11FullScreenDetector m_fullscreenDetector; + CApplicationContext& m_context; + Output::COutput* m_output; GLFWwindow* m_window; uint32_t m_frameCounter; }; diff --git a/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp new file mode 100644 index 0000000..2625e91 --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.cpp @@ -0,0 +1,20 @@ +#include "CWaylandFullScreenDetector.h" + +using namespace WallpaperEngine::Render::Drivers::Detectors; + +CWaylandFullScreenDetector::CWaylandFullScreenDetector (Application::CApplicationContext& appContext, CWaylandOpenGLDriver& driver) : + CFullScreenDetector (appContext) +{ +} + +CWaylandFullScreenDetector::~CWaylandFullScreenDetector () { + ; +} + +bool CWaylandFullScreenDetector::anythingFullscreen () const { + return false; // todo +} + +void CWaylandFullScreenDetector::reset () { + ; +} \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h new file mode 100644 index 0000000..00650b0 --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Detectors/CWaylandFullScreenDetector.h @@ -0,0 +1,28 @@ +#ifdef ENABLE_WAYLAND +#pragma once + +#include +#include +#include + +#include "CFullScreenDetector.h" +#include "WallpaperEngine/Render/Drivers/CVideoDriver.h" + +namespace WallpaperEngine::Render::Drivers +{ + class CWaylandOpenGLDriver; + + namespace Detectors + { + class CWaylandFullScreenDetector : public CFullScreenDetector + { + public: + CWaylandFullScreenDetector (Application::CApplicationContext& appContext, CWaylandOpenGLDriver& driver); + ~CWaylandFullScreenDetector (); + + [[nodiscard]] bool anythingFullscreen () const override; + void reset () override; + }; + } +} +#endif /* ENABLE_WAYLAND */ \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.cpp b/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.cpp index 849e368..2ce526f 100644 --- a/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.cpp +++ b/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.cpp @@ -4,6 +4,7 @@ #include #include +#include "WallpaperEngine/Render/Drivers/CX11OpenGLDriver.h" namespace WallpaperEngine::Render::Drivers::Detectors { @@ -31,7 +32,7 @@ namespace WallpaperEngine::Render::Drivers::Detectors return 0; } - CX11FullScreenDetector::CX11FullScreenDetector (Application::CApplicationContext& appContext, CVideoDriver& driver) : + CX11FullScreenDetector::CX11FullScreenDetector (Application::CApplicationContext& appContext, CX11OpenGLDriver& driver) : CFullScreenDetector (appContext), m_driver (driver) { @@ -67,7 +68,7 @@ namespace WallpaperEngine::Render::Drivers::Detectors if (!XQueryTree (this->m_display, this->m_root, &_, &_, &children, &nchildren)) return false; - auto ourWindow = reinterpret_cast (this->m_driver.getWindowHandle ()); + auto ourWindow = reinterpret_cast (this->m_driver.getWindow ()); Window parentWindow; { diff --git a/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h b/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h index 272e680..dd5b73a 100644 --- a/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h +++ b/src/WallpaperEngine/Render/Drivers/Detectors/CX11FullScreenDetector.h @@ -5,34 +5,37 @@ #include #include "CFullScreenDetector.h" -#include "WallpaperEngine/Render/Drivers/CVideoDriver.h" - #include -namespace WallpaperEngine::Render::Drivers::Detectors +namespace WallpaperEngine::Render::Drivers { - class CX11FullScreenDetector : public CFullScreenDetector + class CX11OpenGLDriver; + + namespace Detectors { - public: - CX11FullScreenDetector (Application::CApplicationContext& appContext, CVideoDriver& driver); - ~CX11FullScreenDetector (); - - [[nodiscard]] bool anythingFullscreen () const override; - void reset () override; - - private: - void initialize (); - void stop (); - - struct ScreenInfo + class CX11FullScreenDetector : public CFullScreenDetector { - glm::ivec4 viewport; - std::string name; - }; + public: + CX11FullScreenDetector (Application::CApplicationContext& appContext, CX11OpenGLDriver& driver); + ~CX11FullScreenDetector (); - Display* m_display; - Window m_root; - std::vector m_screens; - CVideoDriver& m_driver; - }; + [[nodiscard]] bool anythingFullscreen () const override; + void reset () override; + + private: + void initialize (); + void stop (); + + struct ScreenInfo + { + glm::ivec4 viewport; + std::string name; + }; + + Display* m_display; + Window m_root; + std::vector m_screens; + CX11OpenGLDriver& m_driver; + }; + } } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.cpp b/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.cpp index 4b10aa4..7994f4f 100644 --- a/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.cpp +++ b/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.cpp @@ -1,6 +1,7 @@ #include #include "CGLFWWindowOutput.h" #include "WallpaperEngine/Logging/CLog.h" +#include "CX11OutputViewport.h" #include @@ -8,9 +9,8 @@ using namespace WallpaperEngine::Render::Drivers::Output; -CGLFWWindowOutput::CGLFWWindowOutput (CApplicationContext& context, CVideoDriver& driver, Detectors::CFullScreenDetector& detector) : - COutput (context, detector), - m_driver (driver) +CGLFWWindowOutput::CGLFWWindowOutput (CApplicationContext& context, CVideoDriver& driver) : + COutput (context, driver) { if ( this->m_context.settings.render.mode != Application::CApplicationContext::NORMAL_WINDOW && @@ -34,7 +34,7 @@ CGLFWWindowOutput::CGLFWWindowOutput (CApplicationContext& context, CVideoDriver } // register the default viewport - this->m_viewports ["default"] = {{0, 0, this->m_fullWidth, this->m_fullHeight}, "default"}; + this->m_viewports ["default"] = new CX11OutputViewport {{0, 0, this->m_fullWidth, this->m_fullHeight}, "default"}; } void CGLFWWindowOutput::repositionWindow () @@ -78,9 +78,9 @@ void CGLFWWindowOutput::updateRender () const this->m_fullHeight = this->m_driver.getFramebufferSize ().y; // update the default viewport - this->m_viewports ["default"] = {{0, 0, this->m_fullWidth, this->m_fullHeight}, "default"}; + this->m_viewports ["default"]->viewport = {0, 0, this->m_fullWidth, this->m_fullHeight}; // check for fullscreen windows and wait until there's none fullscreen - while (this->m_detector.anythingFullscreen () && this->m_context.state.general.keepRunning) + while (this->m_driver.getFullscreenDetector ().anythingFullscreen () && this->m_context.state.general.keepRunning) usleep (FULLSCREEN_CHECK_WAIT_TIME); } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h b/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h index 7486cc6..099903a 100644 --- a/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h +++ b/src/WallpaperEngine/Render/Drivers/Output/CGLFWWindowOutput.h @@ -8,7 +8,7 @@ namespace WallpaperEngine::Render::Drivers::Output class CGLFWWindowOutput : public COutput { public: - CGLFWWindowOutput (CApplicationContext& context, CVideoDriver& driver, Detectors::CFullScreenDetector& detector); + CGLFWWindowOutput (CApplicationContext& context, CVideoDriver& driver); void reset () override; bool renderVFlip () const override; @@ -19,7 +19,5 @@ namespace WallpaperEngine::Render::Drivers::Output private: void repositionWindow (); - - CVideoDriver& m_driver; }; } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/COutput.cpp b/src/WallpaperEngine/Render/Drivers/Output/COutput.cpp index ab3a4e7..6b514dc 100644 --- a/src/WallpaperEngine/Render/Drivers/Output/COutput.cpp +++ b/src/WallpaperEngine/Render/Drivers/Output/COutput.cpp @@ -2,13 +2,13 @@ using namespace WallpaperEngine::Render::Drivers::Output; -COutput::COutput (CApplicationContext& context, Detectors::CFullScreenDetector& detector) : +COutput::COutput (CApplicationContext& context, CVideoDriver& driver) : m_context (context), - m_detector (detector) + m_driver (driver) { } -const std::map & COutput::getViewports () const +const std::map & COutput::getViewports () const { return this->m_viewports; } diff --git a/src/WallpaperEngine/Render/Drivers/Output/COutput.h b/src/WallpaperEngine/Render/Drivers/Output/COutput.h index 79330fd..cabf9d2 100644 --- a/src/WallpaperEngine/Render/Drivers/Output/COutput.h +++ b/src/WallpaperEngine/Render/Drivers/Output/COutput.h @@ -14,41 +14,42 @@ namespace WallpaperEngine::Application class CApplicationContext; } -namespace WallpaperEngine::Render::Drivers::Detectors +namespace WallpaperEngine::Render::Drivers { - class CFullScreenDetector; -} + class CVideoDriver; -namespace WallpaperEngine::Render::Drivers::Output -{ - class COutput + namespace Detectors { - public: - COutput (CApplicationContext& context, Detectors::CFullScreenDetector& detector); + class CFullScreenDetector; + } - virtual void reset () = 0; + namespace Output + { + class COutputViewport; - int getFullWidth () const; - int getFullHeight () const; - - struct ScreenInfo + class COutput { - glm::ivec4 viewport; - std::string name; + public: + COutput (CApplicationContext& context, CVideoDriver& driver); + + virtual void reset () = 0; + + int getFullWidth () const; + int getFullHeight () const; + + virtual bool renderVFlip () const = 0; + virtual bool renderMultiple () const = 0; + virtual bool haveImageBuffer () const = 0; + const std::map & getViewports () const; + virtual void* getImageBuffer () const = 0; + virtual void updateRender () const = 0; + + protected: + mutable int m_fullWidth; + mutable int m_fullHeight; + mutable std::map m_viewports; + CApplicationContext& m_context; + CVideoDriver& m_driver; }; - - virtual bool renderVFlip () const = 0; - virtual bool renderMultiple () const = 0; - virtual bool haveImageBuffer () const = 0; - const std::map & getViewports () const; - virtual void* getImageBuffer () const = 0; - virtual void updateRender () const = 0; - - protected: - mutable int m_fullWidth; - mutable int m_fullHeight; - mutable std::map m_viewports; - CApplicationContext& m_context; - Detectors::CFullScreenDetector& m_detector; - }; + } } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/COutputViewport.cpp b/src/WallpaperEngine/Render/Drivers/Output/COutputViewport.cpp new file mode 100644 index 0000000..11abc8b --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/COutputViewport.cpp @@ -0,0 +1,16 @@ +#include "COutputViewport.h" + +#include + +using namespace WallpaperEngine::Render::Drivers::Output; + +COutputViewport::COutputViewport (glm::ivec4 viewport, std::string name, bool single) : + viewport (viewport), + name (std::move(name)), + single (single) +{ +} + +COutputViewport::~COutputViewport () +{ +} \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/COutputViewport.h b/src/WallpaperEngine/Render/Drivers/Output/COutputViewport.h new file mode 100644 index 0000000..3e5ce4a --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/COutputViewport.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +namespace WallpaperEngine::Render::Drivers::Output +{ + class COutputViewport + { + public: + COutputViewport (glm::ivec4 viewport, std::string name, bool single = false); + virtual ~COutputViewport (); + + glm::ivec4 viewport; + std::string name; + + /** Whether this viewport is single in the framebuffer or shares space with more viewports */ + bool single; + + /** + * Activates output's context for drawing + */ + virtual void makeCurrent () = 0; + + /** + * Swaps buffers to present data on the viewport + */ + virtual void swapOutput () = 0; + }; +} diff --git a/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.cpp b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.cpp new file mode 100644 index 0000000..2a60a64 --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.cpp @@ -0,0 +1,66 @@ +#include "common.h" +#include "CWaylandOutput.h" +#include "../CWaylandOpenGLDriver.h" +#include "WallpaperEngine/Application/CWallpaperApplication.h" + +using namespace WallpaperEngine::Render::Drivers::Output; + +CWaylandOutput::CWaylandOutput (CApplicationContext& context, CWaylandOpenGLDriver& driver) : + COutput (context, driver) +{ + updateViewports(); +} + +CWaylandOutput::~CWaylandOutput () +{ +} + +void CWaylandOutput::updateViewports() +{ + m_viewports.clear(); + const auto PDRIVER = (CWaylandOpenGLDriver*)&m_driver; + glm::ivec2 fullw = {0,0}; + for (auto& o : PDRIVER->m_screens) + { + if (!o->layerSurface) + continue; + + m_viewports[o->name] = o; + + fullw = fullw + glm::ivec2{o->size.x * o->scale, 0}; + if (o->size.y > fullw.y) + fullw.y = o->size.y; + } + + m_fullWidth = fullw.x; + m_fullHeight = fullw.y; +} + +void CWaylandOutput::reset () +{ + updateViewports(); +} + +bool CWaylandOutput::renderVFlip () const +{ + return true; +} + +bool CWaylandOutput::renderMultiple () const +{ + return false; // todo +} + +bool CWaylandOutput::haveImageBuffer () const +{ + return false; +} + +void* CWaylandOutput::getImageBuffer () const +{ + return nullptr; +} + +void CWaylandOutput::updateRender () const +{ +} \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h new file mode 100644 index 0000000..eca4367 --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutput.h @@ -0,0 +1,36 @@ +#ifdef ENABLE_WAYLAND +#pragma once + +#include +#include +#include + +#include "WallpaperEngine/Render/Drivers/CVideoDriver.h" +#include "COutput.h" + +namespace WallpaperEngine::Render::Drivers +{ + class CWaylandOpenGLDriver; + + namespace Output + { + class CWaylandOutput : public COutput + { + public: + CWaylandOutput (CApplicationContext& context, CWaylandOpenGLDriver& driver); + ~CWaylandOutput (); + + void reset () override; + + bool renderVFlip () const override; + bool renderMultiple () const override; + bool haveImageBuffer () const override; + void* getImageBuffer () const override; + void updateRender () const override; + + private: + void updateViewports(); + }; + } +} +#endif /* ENABLE_WAYLAND */ \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.cpp b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.cpp new file mode 100644 index 0000000..c47fa2b --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.cpp @@ -0,0 +1,224 @@ +#include "WallpaperEngine/Application/CWallpaperApplication.h" +#include "CWaylandOutputViewport.h" +#include "WallpaperEngine/Logging/CLog.h" + +#define class _class +#define namespace _namespace +#define static +extern "C" { +#include "xdg-shell-protocol.h" +#include "wlr-layer-shell-unstable-v1-protocol.h" +} +#undef class +#undef namespace +#undef static + +#include + +using namespace WallpaperEngine::Render::Drivers; +using namespace WallpaperEngine::Render::Drivers::Output; + +static void handleLSConfigure(void *data, zwlr_layer_surface_v1 *surface, uint32_t serial, uint32_t w, uint32_t h) +{ + const auto viewport = static_cast (data); + viewport->size = {w, h}; + viewport->viewport = {0, 0, viewport->size.x * viewport->scale, viewport->size.y * viewport->scale}; + viewport->resize (); + + zwlr_layer_surface_v1_ack_configure(surface, serial); +} + +static void handleLSClosed(void *data, zwlr_layer_surface_v1 *surface) +{ + const auto viewport = static_cast (data); + + viewport->getDriver ()->onLayerClose(viewport); +} + +static void geometry( + void* data, wl_output* output, int32_t x, int32_t y, int32_t width_mm, int32_t height_mm, int32_t subpixel, + const char* make, const char* model, int32_t transform +) +{ + // ignored +} + +static void mode(void* data, wl_output* output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) +{ + const auto viewport = static_cast (data); + + // update viewport size too + viewport->size = {width, height}; + viewport->viewport = {0, 0, viewport->size.x * viewport->scale, viewport->size.y * viewport->scale}; + + if (viewport->layerSurface) + viewport->resize (); + + if (viewport->initialized) + viewport->getDriver ()->getOutput ().reset (); +} + +static void done(void* data, wl_output* wl_output) +{ + static_cast (data)->initialized = true; +} + +static void scale(void* data, wl_output* wl_output, int32_t scale) +{ + const auto viewport = static_cast (data); + + viewport->scale = scale; + + if (viewport->layerSurface) + viewport->resize (); + + if (viewport->initialized) + viewport->getDriver ()->getOutput ().reset (); +} + +static void name(void* data, wl_output* wl_output, const char* name) +{ + const auto viewport = static_cast (data); + + if (name) + viewport->name = name; + + // ensure the output is updated with the new name too + viewport->getDriver ()->getOutput ().reset (); +} + +static void description(void* data, wl_output* wl_output, const char* description) +{ + // ignored +} + +static void surfaceFrameCallback(void *data, struct wl_callback *cb, uint32_t time) +{ + const auto viewport = static_cast (data); + + wl_callback_destroy(cb); + + viewport->frameCallback = nullptr; + viewport->rendering = true; + viewport->getDriver ()->getApp ().update (viewport); + viewport->rendering = false; + + float renderTime = viewport->getDriver ()->getRenderTime(); +} + +const struct wl_callback_listener frameListener = +{ + .done = surfaceFrameCallback +}; + +const wl_output_listener outputListener = +{ + .geometry = geometry, + .mode = mode, + .done = done, + .scale = scale, + .name = name, + .description = description +}; + +const struct zwlr_layer_surface_v1_listener layerSurfaceListener = { + .configure = handleLSConfigure, + .closed = handleLSClosed, +}; + +CWaylandOutputViewport::CWaylandOutputViewport (CWaylandOpenGLDriver* driver, uint32_t waylandName, struct wl_registry *registry) : + m_driver (driver), + waylandName (waylandName), + COutputViewport ({0,0,0,0}, "", true) +{ + // setup output listener + this->output = (wl_output*) wl_registry_bind(registry, waylandName, &wl_output_interface, 4); + this->name = ""; + this->size = {0, 0}; + wl_output_add_listener(output, &outputListener, this); +} + +void CWaylandOutputViewport::setupLS () +{ + surface = wl_compositor_create_surface(m_driver->getWaylandContext()->compositor); + layerSurface = zwlr_layer_shell_v1_get_layer_surface(m_driver->getWaylandContext()->layerShell, surface, output, ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND, "linux-wallpaperengine"); + + if (!layerSurface) + sLog.exception("Failed to get a layer surface"); + + wl_region* region = wl_compositor_create_region(m_driver->getWaylandContext()->compositor); + wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX); + + zwlr_layer_surface_v1_set_size(layerSurface, 0, 0); + zwlr_layer_surface_v1_set_anchor(layerSurface, ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM); + zwlr_layer_surface_v1_set_keyboard_interactivity(layerSurface, false); + zwlr_layer_surface_v1_add_listener(layerSurface, &layerSurfaceListener, this); + zwlr_layer_surface_v1_set_exclusive_zone(layerSurface, -1); + wl_surface_set_input_region(surface, region); + wl_surface_commit(surface); + wl_display_roundtrip(m_driver->getWaylandContext()->display); + + eglWindow = wl_egl_window_create(surface, size.x * scale, size.y * scale); + eglSurface = m_driver->getEGLContext ()->eglCreatePlatformWindowSurfaceEXT(m_driver->getEGLContext ()->display, m_driver->getEGLContext ()->config, eglWindow, nullptr); + wl_surface_commit(surface); + wl_display_roundtrip(m_driver->getWaylandContext()->display); + wl_display_flush(m_driver->getWaylandContext()->display); + + static const auto XCURSORSIZE = getenv("XCURSOR_SIZE") ? std::stoi(getenv("XCURSOR_SIZE")) : 24; + const auto PRCURSORTHEME = wl_cursor_theme_load(getenv("XCURSOR_THEME"), XCURSORSIZE * scale, m_driver->getWaylandContext()->shm); + + if (!PRCURSORTHEME) + sLog.exception("Failed to get a cursor theme"); + + pointer = wl_cursor_theme_get_cursor(PRCURSORTHEME, "left_ptr"); + cursorSurface = wl_compositor_create_surface(m_driver->getWaylandContext()->compositor); + + if (!cursorSurface) + sLog.exception("Failed to get a cursor surface"); + + if (eglMakeCurrent(m_driver->getEGLContext ()->display, eglSurface, eglSurface, m_driver->getEGLContext ()->context) == EGL_FALSE) + sLog.exception("Failed to make egl current"); + + this->m_driver->getOutput ().reset (); +} + +CWaylandOpenGLDriver* CWaylandOutputViewport::getDriver () +{ + return this->m_driver; +} + +void CWaylandOutputViewport::makeCurrent () +{ + EGLBoolean result = eglMakeCurrent ( + m_driver->getEGLContext ()->display, + eglSurface, + eglSurface, + m_driver->getEGLContext ()->context + ); + + if (result == EGL_FALSE) + sLog.error ("Couldn't make egl current"); +} + +void CWaylandOutputViewport::swapOutput () +{ + this->callbackInitialized = true; + + this->makeCurrent (); + frameCallback = wl_surface_frame (surface); + wl_callback_add_listener (frameCallback, &frameListener, this); + eglSwapBuffers (m_driver->getEGLContext ()->display, this->eglSurface); + wl_surface_set_buffer_scale(surface, scale); + wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); + wl_surface_commit(surface); +} + +void CWaylandOutputViewport::resize () +{ + if (!this->eglWindow) + return; + + wl_egl_window_resize(this->eglWindow, this->size.x * this->scale, this->size.y * this->scale, 0, 0); + + this->getDriver ()->getOutput().reset(); +} \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h new file mode 100644 index 0000000..671dfca --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/CWaylandOutputViewport.h @@ -0,0 +1,75 @@ +#ifdef ENABLE_WAYLAND +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include "COutputViewport.h" +#include "../CWaylandOpenGLDriver.h" + +struct zwlr_layer_shell_v1; +struct zwlr_layer_surface_v1; + +namespace WallpaperEngine::Render::Drivers +{ + class CWaylandOpenGLDriver; + + namespace Output + { + class COutputViewport; + + class CWaylandOutputViewport : public COutputViewport + { + public: + CWaylandOutputViewport (CWaylandOpenGLDriver* driver, uint32_t waylandName, struct wl_registry *registry); + + /** + * @return The wayland driver + */ + CWaylandOpenGLDriver* getDriver (); + + wl_output* output; + glm::ivec2 size; + uint32_t waylandName; + int scale = 1; + bool initialized = false; + bool rendering = false; + + wl_egl_window* eglWindow = nullptr; + EGLSurface eglSurface = nullptr; + wl_surface* surface = nullptr; + zwlr_layer_surface_v1* layerSurface = nullptr; + wl_callback* frameCallback = nullptr; + glm::dvec2 mousePos = {0, 0}; + wl_cursor* pointer = nullptr; + wl_surface* cursorSurface = nullptr; + bool callbackInitialized = false; + + void setupLS (); + + /** + * Activates output's context for drawing + */ + void makeCurrent () override; + + /** + * Swaps buffers to present data on the viewport + */ + void swapOutput () override; + + /** + * Updates the viewport size + */ + void resize (); + + private: + CWaylandOpenGLDriver* m_driver; + }; + } +} +#endif /* ENABLE_WAYLAND */ \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CX11Output.cpp b/src/WallpaperEngine/Render/Drivers/Output/CX11Output.cpp index aadfa0d..79be24b 100644 --- a/src/WallpaperEngine/Render/Drivers/Output/CX11Output.cpp +++ b/src/WallpaperEngine/Render/Drivers/Output/CX11Output.cpp @@ -1,5 +1,6 @@ #include "common.h" #include "CX11Output.h" +#include "CX11OutputViewport.h" #include #include @@ -34,9 +35,8 @@ int CustomXIOErrorHandler (Display* dsp) return 0; } -CX11Output::CX11Output (CApplicationContext& context, CVideoDriver& driver, Detectors::CFullScreenDetector& detector) : - COutput (context, detector), - m_driver (driver) +CX11Output::CX11Output (CApplicationContext& context, CVideoDriver& driver) : + COutput (context, driver) { // do not use previous handler, it might stop the app under weird circumstances XSetErrorHandler (CustomXErrorHandler); @@ -57,11 +57,17 @@ void CX11Output::reset () // re-load screen info this->loadScreenInfo (); // do the same for the detector - this->m_detector.reset (); + this->m_driver.getFullscreenDetector ().reset (); } void CX11Output::free () { + // go through all the viewports and free them + for(const auto& cur : this->m_viewports) + delete cur.second; + + this->m_viewports.clear (); + // free all the resources we've got XDestroyImage (this->m_image); XFreeGC (this->m_display, this->m_gc); @@ -132,6 +138,7 @@ void CX11Output::loadScreenInfo () // add the screen to the list of screens this->m_screens.push_back ( + new CX11OutputViewport { {crtc->x, crtc->y, crtc->width, crtc->height}, info->name @@ -144,6 +151,7 @@ void CX11Output::loadScreenInfo () sLog.out ("Found requested screen: ", info->name, " -> ", crtc->x, "x", crtc->y, ":", crtc->width, "x", crtc->height); this->m_viewports[info->name] = + new CX11OutputViewport { {crtc->x, crtc->y, crtc->width, crtc->height}, info->name @@ -187,6 +195,6 @@ void CX11Output::updateRender () const XFlush(this->m_display); // check for fullscreen windows and wait until there's none fullscreen - while (this->m_detector.anythingFullscreen () && this->m_context.state.general.keepRunning) + while (this->m_driver.getFullscreenDetector ().anythingFullscreen () && this->m_context.state.general.keepRunning) usleep (FULLSCREEN_CHECK_WAIT_TIME); } diff --git a/src/WallpaperEngine/Render/Drivers/Output/CX11Output.h b/src/WallpaperEngine/Render/Drivers/Output/CX11Output.h index 7bbd88e..e6be9c8 100644 --- a/src/WallpaperEngine/Render/Drivers/Output/CX11Output.h +++ b/src/WallpaperEngine/Render/Drivers/Output/CX11Output.h @@ -14,7 +14,7 @@ namespace WallpaperEngine::Render::Drivers::Output class CX11Output : public COutput { public: - CX11Output (CApplicationContext& context, CVideoDriver& driver, Detectors::CFullScreenDetector& detector); + CX11Output (CApplicationContext& context, CVideoDriver& driver); ~CX11Output (); void reset () override; @@ -35,7 +35,6 @@ namespace WallpaperEngine::Render::Drivers::Output GC m_gc; char* m_imageData; XImage* m_image; - CVideoDriver& m_driver; - std::vector m_screens; + std::vector m_screens; }; } \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.cpp b/src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.cpp new file mode 100644 index 0000000..cb436bc --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.cpp @@ -0,0 +1,18 @@ +#include "CX11OutputViewport.h" + +#include + +using namespace WallpaperEngine::Render::Drivers::Output; + +CX11OutputViewport::CX11OutputViewport (glm::ivec4 viewport, std::string name) : + COutputViewport (viewport, std::move(name)) +{ +} + +void CX11OutputViewport::makeCurrent () +{ +} + +void CX11OutputViewport::swapOutput () +{ +} \ No newline at end of file diff --git a/src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.h b/src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.h new file mode 100644 index 0000000..42b6280 --- /dev/null +++ b/src/WallpaperEngine/Render/Drivers/Output/CX11OutputViewport.h @@ -0,0 +1,15 @@ +#pragma once + +#include "COutputViewport.h" + +namespace WallpaperEngine::Render::Drivers::Output +{ + class CX11OutputViewport : public COutputViewport + { + public: + CX11OutputViewport (glm::ivec4 viewport, std::string name); + + void makeCurrent () override; + void swapOutput () override; + }; +}