diff --git a/libs/openFrameworks/app/ofAppGLFWWindow.cpp b/libs/openFrameworks/app/ofAppGLFWWindow.cpp index a44aacb0de2..b4f3c7b5e97 100644 --- a/libs/openFrameworks/app/ofAppGLFWWindow.cpp +++ b/libs/openFrameworks/app/ofAppGLFWWindow.cpp @@ -11,15 +11,18 @@ #include "ofIcon.h" #include "ofImage.h" #define GLFW_EXPOSE_NATIVE_X11 + #define GLFW_EXPOSE_NATIVE_WAYLAND #ifndef TARGET_OPENGLES #define GLFW_EXPOSE_NATIVE_GLX #else #define GLFW_EXPOSE_NATIVE_EGL #endif #include - #include - #include - #include + #ifdef GLFW_EXPOSE_NATIVE_X11 + #include + #include + #include + #endif #include #include #elif defined(TARGET_OSX) @@ -59,6 +62,10 @@ ofAppGLFWWindow::ofAppGLFWWindow() currentW = 0; currentH = 0; + #if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY) + usingWayland = false; + #endif + glfwSetErrorCallback(error_cb); } @@ -160,6 +167,11 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { ofLogError("ofAppGLFWWindow") << "couldn't init GLFW"; return; } + + #if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY) + usingWayland = (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND); + ofLogVerbose("ofAppGLFWWindow") << "Running under " << (usingWayland ? "Wayland" : "X11"); + #endif // ofLogNotice("ofAppGLFWWindow") << "WINDOW MODE IS " << screenMode; @@ -399,24 +411,34 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { glfwSetWindowRefreshCallback(windowP, refresh_cb); #ifdef TARGET_LINUX - XSetLocaleModifiers(""); - xim = XOpenIM(getX11Display(), 0, 0, 0); - if (!xim) { - // fallback to internal input method - XSetLocaleModifiers("@im=none"); - xim = XOpenIM(getX11Display(), 0, 0, 0); - } - xic = XCreateIC(xim, - XNInputStyle, XIMPreeditNothing | XIMStatusNothing, - XNClientWindow, getX11Window(), - XNFocusWindow, getX11Window(), - NULL); -#endif + if (!usingWayland) { + XSetLocaleModifiers(""); + xim = XOpenIM(getX11Display(), 0, 0, 0); + if (!xim) { + // fallback to internal input method + XSetLocaleModifiers("@im=none"); + xim = XOpenIM(getX11Display(), 0, 0, 0); + } + xic = XCreateIC(xim, + XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, getX11Window(), + XNFocusWindow, getX11Window(), + NULL); + } else { + // Wayland input is handled by GLFW directly + ofLogVerbose("ofAppGLFWWindow") << "Using Wayland native input handling"; + } + + #endif } #ifdef TARGET_LINUX //------------------------------------------------------------ void ofAppGLFWWindow::setWindowIcon(const of::filesystem::path & path) { + if (usingWayland) { + ofLogWarning("ofAppGLFWWindow") << "Setting window icon is not supported on Wayland"; + return; + } ofPixels iconPixels; ofLoadImage(iconPixels, path); setWindowIcon(iconPixels); @@ -424,6 +446,10 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { //------------------------------------------------------------ void ofAppGLFWWindow::setWindowIcon(const ofPixels & iconPixels) { + if (usingWayland) { + ofLogWarning("ofAppGLFWWindow") << "Setting window icon is not supported on Wayland"; + return; + } iconSet = true; int length = 2 + iconPixels.getWidth() * iconPixels.getHeight(); vector buffer(length); @@ -744,127 +770,135 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { } #ifdef TARGET_LINUX -#include - - Window nativeWin = glfwGetX11Window(windowP); - Display * display = glfwGetX11Display(); - if (targetWindowMode == OF_FULLSCREEN) { - -#ifdef TARGET_RASPBERRY_PI - // save window shape before going fullscreen - if (windowP) { - int tmpW, tmpH; - glfwGetWindowSize(windowP, &tmpW, &tmpH); - windowRect.setSize(tmpW, tmpH); - } -#endif - - int monitorCount; - GLFWmonitor ** monitors = glfwGetMonitors(&monitorCount); - if (settings.multiMonitorFullScreen && monitorCount > 1) { - // find the monitors at the edges of the virtual desktop - int minx = numeric_limits::max(); - int miny = numeric_limits::max(); - int maxx = numeric_limits::min(); - int maxy = numeric_limits::min(); - int x, y, w, h; - int monitorLeft = 0, monitorRight = 0, monitorTop = 0, monitorBottom = 0; - for (int i = 0; i < monitorCount; i++) { - glfwGetMonitorPos(monitors[i], &x, &y); - auto videoMode = glfwGetVideoMode(monitors[i]); - w = videoMode->width; - h = videoMode->height; - if (x < minx) { - monitorLeft = i; - minx = x; - } - if (y < miny) { - monitorTop = i; - miny = y; - } - if (x + w > maxx) { - monitorRight = i; - maxx = x + w; - } - if (y + h > maxy) { - monitorBottom = i; - maxy = y + h; - } - } - - // send fullscreen_monitors event with the edges monitors - Atom m_net_fullscreen_monitors = XInternAtom(display, "_NET_WM_FULLSCREEN_MONITORS", false); - - XEvent xev; - - xev.xclient.type = ClientMessage; - xev.xclient.serial = 0; - xev.xclient.send_event = True; - xev.xclient.window = nativeWin; - xev.xclient.message_type = m_net_fullscreen_monitors; - xev.xclient.format = 32; - - xev.xclient.data.l[0] = monitorTop; - xev.xclient.data.l[1] = monitorBottom; - xev.xclient.data.l[2] = monitorLeft; - xev.xclient.data.l[3] = monitorRight; - xev.xclient.data.l[4] = 1; - XSendEvent(display, RootWindow(display, DefaultScreen(display)), - False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - currentW = maxx - minx; - currentH = maxy - minx; - } else { - auto monitor = glfwGetWindowMonitor(windowP); - if (monitor) { - auto videoMode = glfwGetVideoMode(monitor); - if (videoMode) { - currentW = videoMode->width; - currentH = videoMode->height; - } - } - } - } - - // send fullscreen event - Atom m_net_state = XInternAtom(display, "_NET_WM_STATE", false); - Atom m_net_fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", false); - - XEvent xev; - - xev.xclient.type = ClientMessage; - xev.xclient.serial = 0; - xev.xclient.send_event = True; - xev.xclient.window = nativeWin; - xev.xclient.message_type = m_net_state; - xev.xclient.format = 32; - - if (fullscreen) - xev.xclient.data.l[0] = 1; - else - xev.xclient.data.l[0] = 0; - - xev.xclient.data.l[1] = m_net_fullscreen; - xev.xclient.data.l[2] = 0; - xev.xclient.data.l[3] = 0; - xev.xclient.data.l[4] = 0; - XSendEvent(display, RootWindow(display, DefaultScreen(display)), - False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); - - // tell the window manager to bypass composition for this window in fullscreen for speed - // it'll probably help solving vsync issues - Atom m_bypass_compositor = XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); - unsigned long value = fullscreen ? 1 : 0; - XChangeProperty(display, nativeWin, m_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); - - XFlush(display); - -#ifdef TARGET_RASPBERRY_PI - if (!fullscreen) { - needsResizeCheck = true; - } -#endif - - // setWindowShape(windowW, windowH); + if (!usingWayland) { + Window nativeWin = glfwGetX11Window(windowP); + Display * display = glfwGetX11Display(); + if (targetWindowMode == OF_FULLSCREEN) { + + #ifdef TARGET_RASPBERRY_PI + // save window shape before going fullscreen + if (windowP) { + int tmpW, tmpH; + glfwGetWindowSize(windowP, &tmpW, &tmpH); + windowRect.setSize(tmpW, tmpH); + } + #endif + + int monitorCount; + GLFWmonitor ** monitors = glfwGetMonitors(&monitorCount); + if (settings.multiMonitorFullScreen && monitorCount > 1) { + // find the monitors at the edges of the virtual desktop + int minx = numeric_limits::max(); + int miny = numeric_limits::max(); + int maxx = numeric_limits::min(); + int maxy = numeric_limits::min(); + int x, y, w, h; + int monitorLeft = 0, monitorRight = 0, monitorTop = 0, monitorBottom = 0; + for (int i = 0; i < monitorCount; i++) { + glfwGetMonitorPos(monitors[i], &x, &y); + auto videoMode = glfwGetVideoMode(monitors[i]); + w = videoMode->width; + h = videoMode->height; + if (x < minx) { + monitorLeft = i; + minx = x; + } + if (y < miny) { + monitorTop = i; + miny = y; + } + if (x + w > maxx) { + monitorRight = i; + maxx = x + w; + } + if (y + h > maxy) { + monitorBottom = i; + maxy = y + h; + } + } + + // send fullscreen_monitors event with the edges monitors + Atom m_net_fullscreen_monitors = XInternAtom(display, "_NET_WM_FULLSCREEN_MONITORS", false); + + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = nativeWin; + xev.xclient.message_type = m_net_fullscreen_monitors; + xev.xclient.format = 32; + + xev.xclient.data.l[0] = monitorTop; + xev.xclient.data.l[1] = monitorBottom; + xev.xclient.data.l[2] = monitorLeft; + xev.xclient.data.l[3] = monitorRight; + xev.xclient.data.l[4] = 1; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + currentW = maxx - minx; + currentH = maxy - minx; + } else { + auto monitor = glfwGetWindowMonitor(windowP); + if (monitor) { + auto videoMode = glfwGetVideoMode(monitor); + if (videoMode) { + currentW = videoMode->width; + currentH = videoMode->height; + } + } + } + } + + // send fullscreen event + Atom m_net_state = XInternAtom(display, "_NET_WM_STATE", false); + Atom m_net_fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", false); + + XEvent xev; + + xev.xclient.type = ClientMessage; + xev.xclient.serial = 0; + xev.xclient.send_event = True; + xev.xclient.window = nativeWin; + xev.xclient.message_type = m_net_state; + xev.xclient.format = 32; + + if (fullscreen) + xev.xclient.data.l[0] = 1; + else + xev.xclient.data.l[0] = 0; + + xev.xclient.data.l[1] = m_net_fullscreen; + xev.xclient.data.l[2] = 0; + xev.xclient.data.l[3] = 0; + xev.xclient.data.l[4] = 0; + XSendEvent(display, RootWindow(display, DefaultScreen(display)), + False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); + + // tell the window manager to bypass composition for this window in fullscreen for speed + // it'll probably help solving vsync issues + Atom m_bypass_compositor = XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); + unsigned long value = fullscreen ? 1 : 0; + XChangeProperty(display, nativeWin, m_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); + + XFlush(display); + + #ifdef TARGET_RASPBERRY_PI + if (!fullscreen) { + needsResizeCheck = true; + } + #endif + } else { + int monitorCount; + GLFWmonitor ** monitors = glfwGetMonitors(&monitorCount); + int monitorIdx = getCurrentMonitor(); + if (fullscreen) { + auto mode = glfwGetVideoMode(monitors[monitorIdx]); + glfwSetWindowMonitor(windowP, monitors[monitorIdx], 0, 0, mode->width, mode->height, mode->refreshRate); + } else { + glfwSetWindowMonitor(windowP, nullptr, windowRect.x, windowRect.y, windowW, windowH, 0); + } + } #elif defined(TARGET_OSX) @@ -1738,17 +1772,29 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) { #if defined(TARGET_LINUX) Display * ofAppGLFWWindow::getX11Display() { - return glfwGetX11Display(); + return usingWayland ? nullptr : glfwGetX11Display(); } Window ofAppGLFWWindow::getX11Window() { - return glfwGetX11Window(windowP); + return usingWayland ? 0 : glfwGetX11Window(windowP); } XIC ofAppGLFWWindow::getX11XIC() { - return xic; + return usingWayland ? nullptr : xic; } -#endif + + wl_display * ofAppGLFWWindow::getWaylandDisplay() { + return usingWayland ? glfwGetWaylandDisplay() : nullptr; + } + + wl_surface * ofAppGLFWWindow::getWaylandSurface() { + return usingWayland ? glfwGetWaylandWindow(windowP) : nullptr; + } + + bool ofAppGLFWWindow::isUsingWayland() const { + return usingWayland; + } + #endif #if defined(TARGET_LINUX) && !defined(TARGET_OPENGLES) GLXContext ofAppGLFWWindow::getGLXContext() { diff --git a/libs/openFrameworks/app/ofAppGLFWWindow.h b/libs/openFrameworks/app/ofAppGLFWWindow.h index 1a58743cf84..1a832081720 100644 --- a/libs/openFrameworks/app/ofAppGLFWWindow.h +++ b/libs/openFrameworks/app/ofAppGLFWWindow.h @@ -10,6 +10,8 @@ #if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY) typedef struct _XIM * XIM; typedef struct _XIC * XIC; +typedef struct wl_display wl_display; +typedef struct wl_surface wl_surface; #endif class ofBaseApp; @@ -171,6 +173,12 @@ class ofAppGLFWWindow : public ofAppBaseGLWindow { void setWindowIcon(const of::filesystem::path & path); void setWindowIcon(const ofPixels & iconPixels); + + wl_display * getWaylandDisplay(); + wl_surface * getWaylandSurface(); + + // Helper to detect the current backend + bool isUsingWayland() const; #endif #if defined(TARGET_LINUX) && !defined(TARGET_OPENGLES) @@ -214,6 +222,7 @@ class ofAppGLFWWindow : public ofAppBaseGLWindow { #if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY) XIM xim; XIC xic; + bool usingWayland; #endif std::unique_ptr coreEvents; diff --git a/scripts/linux/ubuntu/install_dependencies.sh b/scripts/linux/ubuntu/install_dependencies.sh index bc837655ae4..aeecf58083f 100755 --- a/scripts/linux/ubuntu/install_dependencies.sh +++ b/scripts/linux/ubuntu/install_dependencies.sh @@ -160,39 +160,39 @@ then fi #check if glfw3 exists -apt-cache show libglfw3-dev -exit_code=$? -if [ $exit_code = 0 ]; then - GLFW_PKG=libglfw3-dev -else - echo installing glfw from source - GLFW_VER=32f38b97d544eb2fd9a568e94e37830106417b51 +# apt-cache show libglfw3-dev +# exit_code=$? +# if [ $exit_code = 0 ]; then +# GLFW_PKG=libglfw3-dev +# else +# echo installing glfw from source +# GLFW_VER=32f38b97d544eb2fd9a568e94e37830106417b51 - # tools for git use - GLFW_GIT_TAG=$GLFW_VER - apt-get install -y -qq libxrandr-dev libxinerama-dev libxcursor-dev cmake - wget https://github.com/glfw/glfw/archive/$GLFW_GIT_TAG.tar.gz -O glfw-$GLFW_GIT_TAG.tar.gz - tar -xf glfw-$GLFW_GIT_TAG.tar.gz - mv glfw-$GLFW_GIT_TAG glfw - rm glfw-$GLFW_GIT_TAG.tar.gz - cd glfw - mkdir -p build - cd build - cmake .. -DGLFW_BUILD_DOCS=OFF \ - -DGLFW_BUILD_TESTS=OFF \ - -DGLFW_BUILD_EXAMPLES=OFF \ - -DBUILD_SHARED_LIBS=OFF \ - -DCMAKE_BUILD_TYPE=Release - make clean - make - make install - cd ../.. - rm -rf glfw - GLFW_PKG= -fi +# # tools for git use +# GLFW_GIT_TAG=$GLFW_VER +# apt-get install -y -qq libxrandr-dev libxinerama-dev libxcursor-dev cmake +# wget https://github.com/glfw/glfw/archive/$GLFW_GIT_TAG.tar.gz -O glfw-$GLFW_GIT_TAG.tar.gz +# tar -xf glfw-$GLFW_GIT_TAG.tar.gz +# mv glfw-$GLFW_GIT_TAG glfw +# rm glfw-$GLFW_GIT_TAG.tar.gz +# cd glfw +# mkdir -p build +# cd build +# cmake .. -DGLFW_BUILD_DOCS=OFF \ +# -DGLFW_BUILD_TESTS=OFF \ +# -DGLFW_BUILD_EXAMPLES=OFF \ +# -DBUILD_SHARED_LIBS=OFF \ +# -DCMAKE_BUILD_TYPE=Release +# make clean +# make +# make install +# cd ../.. +# rm -rf glfw +# GLFW_PKG= +# fi -PACKAGES="make nlohmann-json3-dev libssl3 libcurl4 brotli libcurl4-openssl-dev libjack-jackd2-0 libjack-jackd2-dev freeglut3-dev libasound2-dev libxmu-dev libxxf86vm-dev g++${CXX_VER} libgl1-mesa-dev${XTAG} libglu1-mesa-dev libraw1394-dev libudev-dev libdrm-dev libglew-dev libopenal-dev libsndfile1-dev libfreeimage-dev libcairo2-dev libfreetype6-dev libssl-dev libpulse-dev libusb-1.0-0-dev ${LIB_GTK_DEV} libopencv-dev libassimp-dev librtaudio-dev libgstreamer${GSTREAMER_VERSION}-dev libgstreamer-plugins-base${GSTREAMER_VERSION}-dev ${GSTREAMER_FFMPEG} gstreamer${GSTREAMER_VERSION}-pulseaudio gstreamer${GSTREAMER_VERSION}-x gstreamer${GSTREAMER_VERSION}-plugins-bad gstreamer${GSTREAMER_VERSION}-alsa gstreamer${GSTREAMER_VERSION}-plugins-base gstreamer${GSTREAMER_VERSION}-plugins-good gdb ${GLFW_PKG} liburiparser-dev libpugixml-dev libgtk2.0-0 libpoco-dev libxcursor-dev libxi-dev libxinerama-dev libxml2-dev" +PACKAGES="make nlohmann-json3-dev libssl3 libcurl4 brotli libcurl4-openssl-dev libjack-jackd2-0 libjack-jackd2-dev freeglut3-dev libasound2-dev libxmu-dev libxxf86vm-dev g++${CXX_VER} libgl1-mesa-dev${XTAG} libglu1-mesa-dev libraw1394-dev libudev-dev libdrm-dev libglew-dev libopenal-dev libsndfile1-dev libfreeimage-dev libcairo2-dev libfreetype6-dev libssl-dev libpulse-dev libusb-1.0-0-dev ${LIB_GTK_DEV} libopencv-dev libassimp-dev librtaudio-dev libgstreamer${GSTREAMER_VERSION}-dev libgstreamer-plugins-base${GSTREAMER_VERSION}-dev ${GSTREAMER_FFMPEG} gstreamer${GSTREAMER_VERSION}-pulseaudio gstreamer${GSTREAMER_VERSION}-x gstreamer${GSTREAMER_VERSION}-plugins-bad gstreamer${GSTREAMER_VERSION}-alsa gstreamer${GSTREAMER_VERSION}-plugins-base gstreamer${GSTREAMER_VERSION}-plugins-good gdb liburiparser-dev libpugixml-dev libgtk2.0-0 libpoco-dev libxcursor-dev libxi-dev libxinerama-dev libxml2-dev" # libgconf-2-4 libboost-filesystem${BOOST_VER}-dev echo "installing OF dependencies"