|
11 | 11 | #include "ofIcon.h"
|
12 | 12 | #include "ofImage.h"
|
13 | 13 | #define GLFW_EXPOSE_NATIVE_X11
|
| 14 | + #define GLFW_EXPOSE_NATIVE_WAYLAND |
14 | 15 | #ifndef TARGET_OPENGLES
|
15 | 16 | #define GLFW_EXPOSE_NATIVE_GLX
|
16 | 17 | #else
|
17 | 18 | #define GLFW_EXPOSE_NATIVE_EGL
|
18 | 19 | #endif
|
19 | 20 | #include <GLFW/glfw3native.h>
|
20 |
| - #include <X11/XKBlib.h> |
21 |
| - #include <X11/Xatom.h> |
22 |
| - #include <X11/extensions/Xrandr.h> |
| 21 | + #ifdef GLFW_EXPOSE_NATIVE_X11 |
| 22 | + #include <X11/XKBlib.h> |
| 23 | + #include <X11/Xatom.h> |
| 24 | + #include <X11/extensions/Xrandr.h> |
| 25 | + #endif |
23 | 26 | #include <xcb/xcb.h>
|
24 | 27 | #include <xcb/xcbext.h>
|
25 | 28 | #elif defined(TARGET_OSX)
|
@@ -59,6 +62,10 @@ ofAppGLFWWindow::ofAppGLFWWindow()
|
59 | 62 | currentW = 0;
|
60 | 63 | currentH = 0;
|
61 | 64 |
|
| 65 | + #if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY) |
| 66 | + usingWayland = false; |
| 67 | + #endif |
| 68 | + |
62 | 69 | glfwSetErrorCallback(error_cb);
|
63 | 70 | }
|
64 | 71 |
|
@@ -160,6 +167,11 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
|
160 | 167 | ofLogError("ofAppGLFWWindow") << "couldn't init GLFW";
|
161 | 168 | return;
|
162 | 169 | }
|
| 170 | + |
| 171 | + #if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY) |
| 172 | + usingWayland = (glfwGetPlatform() == GLFW_PLATFORM_WAYLAND); |
| 173 | + ofLogVerbose("ofAppGLFWWindow") << "Running under " << (usingWayland ? "Wayland" : "X11"); |
| 174 | + #endif |
163 | 175 |
|
164 | 176 | // ofLogNotice("ofAppGLFWWindow") << "WINDOW MODE IS " << screenMode;
|
165 | 177 |
|
@@ -399,31 +411,45 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
|
399 | 411 | glfwSetWindowRefreshCallback(windowP, refresh_cb);
|
400 | 412 |
|
401 | 413 | #ifdef TARGET_LINUX
|
402 |
| - XSetLocaleModifiers(""); |
403 |
| - xim = XOpenIM(getX11Display(), 0, 0, 0); |
404 |
| - if (!xim) { |
405 |
| - // fallback to internal input method |
406 |
| - XSetLocaleModifiers("@im=none"); |
407 |
| - xim = XOpenIM(getX11Display(), 0, 0, 0); |
408 |
| - } |
409 |
| - xic = XCreateIC(xim, |
410 |
| - XNInputStyle, XIMPreeditNothing | XIMStatusNothing, |
411 |
| - XNClientWindow, getX11Window(), |
412 |
| - XNFocusWindow, getX11Window(), |
413 |
| - NULL); |
414 |
| -#endif |
| 414 | + if (!usingWayland) { |
| 415 | + XSetLocaleModifiers(""); |
| 416 | + xim = XOpenIM(getX11Display(), 0, 0, 0); |
| 417 | + if (!xim) { |
| 418 | + // fallback to internal input method |
| 419 | + XSetLocaleModifiers("@im=none"); |
| 420 | + xim = XOpenIM(getX11Display(), 0, 0, 0); |
| 421 | + } |
| 422 | + xic = XCreateIC(xim, |
| 423 | + XNInputStyle, XIMPreeditNothing | XIMStatusNothing, |
| 424 | + XNClientWindow, getX11Window(), |
| 425 | + XNFocusWindow, getX11Window(), |
| 426 | + NULL); |
| 427 | + } else { |
| 428 | + // Wayland input is handled by GLFW directly |
| 429 | + ofLogVerbose("ofAppGLFWWindow") << "Using Wayland native input handling"; |
| 430 | + } |
| 431 | + |
| 432 | + #endif |
415 | 433 | }
|
416 | 434 |
|
417 | 435 | #ifdef TARGET_LINUX
|
418 | 436 | //------------------------------------------------------------
|
419 | 437 | void ofAppGLFWWindow::setWindowIcon(const of::filesystem::path & path) {
|
| 438 | + if (usingWayland) { |
| 439 | + ofLogWarning("ofAppGLFWWindow") << "Setting window icon is not supported on Wayland"; |
| 440 | + return; |
| 441 | + } |
420 | 442 | ofPixels iconPixels;
|
421 | 443 | ofLoadImage(iconPixels, path);
|
422 | 444 | setWindowIcon(iconPixels);
|
423 | 445 | }
|
424 | 446 |
|
425 | 447 | //------------------------------------------------------------
|
426 | 448 | void ofAppGLFWWindow::setWindowIcon(const ofPixels & iconPixels) {
|
| 449 | + if (usingWayland) { |
| 450 | + ofLogWarning("ofAppGLFWWindow") << "Setting window icon is not supported on Wayland"; |
| 451 | + return; |
| 452 | + } |
427 | 453 | iconSet = true;
|
428 | 454 | int length = 2 + iconPixels.getWidth() * iconPixels.getHeight();
|
429 | 455 | vector<unsigned long> buffer(length);
|
@@ -744,127 +770,135 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
|
744 | 770 | }
|
745 | 771 |
|
746 | 772 | #ifdef TARGET_LINUX
|
747 |
| -#include <X11/Xatom.h> |
748 |
| - |
749 |
| - Window nativeWin = glfwGetX11Window(windowP); |
750 |
| - Display * display = glfwGetX11Display(); |
751 |
| - if (targetWindowMode == OF_FULLSCREEN) { |
752 |
| - |
753 |
| -#ifdef TARGET_RASPBERRY_PI |
754 |
| - // save window shape before going fullscreen |
755 |
| - if (windowP) { |
756 |
| - int tmpW, tmpH; |
757 |
| - glfwGetWindowSize(windowP, &tmpW, &tmpH); |
758 |
| - windowRect.setSize(tmpW, tmpH); |
759 |
| - } |
760 |
| -#endif |
761 |
| - |
762 |
| - int monitorCount; |
763 |
| - GLFWmonitor ** monitors = glfwGetMonitors(&monitorCount); |
764 |
| - if (settings.multiMonitorFullScreen && monitorCount > 1) { |
765 |
| - // find the monitors at the edges of the virtual desktop |
766 |
| - int minx = numeric_limits<int>::max(); |
767 |
| - int miny = numeric_limits<int>::max(); |
768 |
| - int maxx = numeric_limits<int>::min(); |
769 |
| - int maxy = numeric_limits<int>::min(); |
770 |
| - int x, y, w, h; |
771 |
| - int monitorLeft = 0, monitorRight = 0, monitorTop = 0, monitorBottom = 0; |
772 |
| - for (int i = 0; i < monitorCount; i++) { |
773 |
| - glfwGetMonitorPos(monitors[i], &x, &y); |
774 |
| - auto videoMode = glfwGetVideoMode(monitors[i]); |
775 |
| - w = videoMode->width; |
776 |
| - h = videoMode->height; |
777 |
| - if (x < minx) { |
778 |
| - monitorLeft = i; |
779 |
| - minx = x; |
780 |
| - } |
781 |
| - if (y < miny) { |
782 |
| - monitorTop = i; |
783 |
| - miny = y; |
784 |
| - } |
785 |
| - if (x + w > maxx) { |
786 |
| - monitorRight = i; |
787 |
| - maxx = x + w; |
788 |
| - } |
789 |
| - if (y + h > maxy) { |
790 |
| - monitorBottom = i; |
791 |
| - maxy = y + h; |
792 |
| - } |
793 |
| - } |
794 |
| - |
795 |
| - // send fullscreen_monitors event with the edges monitors |
796 |
| - Atom m_net_fullscreen_monitors = XInternAtom(display, "_NET_WM_FULLSCREEN_MONITORS", false); |
797 |
| - |
798 |
| - XEvent xev; |
799 |
| - |
800 |
| - xev.xclient.type = ClientMessage; |
801 |
| - xev.xclient.serial = 0; |
802 |
| - xev.xclient.send_event = True; |
803 |
| - xev.xclient.window = nativeWin; |
804 |
| - xev.xclient.message_type = m_net_fullscreen_monitors; |
805 |
| - xev.xclient.format = 32; |
806 |
| - |
807 |
| - xev.xclient.data.l[0] = monitorTop; |
808 |
| - xev.xclient.data.l[1] = monitorBottom; |
809 |
| - xev.xclient.data.l[2] = monitorLeft; |
810 |
| - xev.xclient.data.l[3] = monitorRight; |
811 |
| - xev.xclient.data.l[4] = 1; |
812 |
| - XSendEvent(display, RootWindow(display, DefaultScreen(display)), |
813 |
| - False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); |
814 |
| - currentW = maxx - minx; |
815 |
| - currentH = maxy - minx; |
816 |
| - } else { |
817 |
| - auto monitor = glfwGetWindowMonitor(windowP); |
818 |
| - if (monitor) { |
819 |
| - auto videoMode = glfwGetVideoMode(monitor); |
820 |
| - if (videoMode) { |
821 |
| - currentW = videoMode->width; |
822 |
| - currentH = videoMode->height; |
823 |
| - } |
824 |
| - } |
825 |
| - } |
826 |
| - } |
827 |
| - |
828 |
| - // send fullscreen event |
829 |
| - Atom m_net_state = XInternAtom(display, "_NET_WM_STATE", false); |
830 |
| - Atom m_net_fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", false); |
831 |
| - |
832 |
| - XEvent xev; |
833 |
| - |
834 |
| - xev.xclient.type = ClientMessage; |
835 |
| - xev.xclient.serial = 0; |
836 |
| - xev.xclient.send_event = True; |
837 |
| - xev.xclient.window = nativeWin; |
838 |
| - xev.xclient.message_type = m_net_state; |
839 |
| - xev.xclient.format = 32; |
840 |
| - |
841 |
| - if (fullscreen) |
842 |
| - xev.xclient.data.l[0] = 1; |
843 |
| - else |
844 |
| - xev.xclient.data.l[0] = 0; |
845 |
| - |
846 |
| - xev.xclient.data.l[1] = m_net_fullscreen; |
847 |
| - xev.xclient.data.l[2] = 0; |
848 |
| - xev.xclient.data.l[3] = 0; |
849 |
| - xev.xclient.data.l[4] = 0; |
850 |
| - XSendEvent(display, RootWindow(display, DefaultScreen(display)), |
851 |
| - False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); |
852 |
| - |
853 |
| - // tell the window manager to bypass composition for this window in fullscreen for speed |
854 |
| - // it'll probably help solving vsync issues |
855 |
| - Atom m_bypass_compositor = XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); |
856 |
| - unsigned long value = fullscreen ? 1 : 0; |
857 |
| - XChangeProperty(display, nativeWin, m_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); |
858 |
| - |
859 |
| - XFlush(display); |
860 |
| - |
861 |
| -#ifdef TARGET_RASPBERRY_PI |
862 |
| - if (!fullscreen) { |
863 |
| - needsResizeCheck = true; |
864 |
| - } |
865 |
| -#endif |
866 |
| - |
867 |
| - // setWindowShape(windowW, windowH); |
| 773 | + if (!usingWayland) { |
| 774 | + Window nativeWin = glfwGetX11Window(windowP); |
| 775 | + Display * display = glfwGetX11Display(); |
| 776 | + if (targetWindowMode == OF_FULLSCREEN) { |
| 777 | + |
| 778 | + #ifdef TARGET_RASPBERRY_PI |
| 779 | + // save window shape before going fullscreen |
| 780 | + if (windowP) { |
| 781 | + int tmpW, tmpH; |
| 782 | + glfwGetWindowSize(windowP, &tmpW, &tmpH); |
| 783 | + windowRect.setSize(tmpW, tmpH); |
| 784 | + } |
| 785 | + #endif |
| 786 | + |
| 787 | + int monitorCount; |
| 788 | + GLFWmonitor ** monitors = glfwGetMonitors(&monitorCount); |
| 789 | + if (settings.multiMonitorFullScreen && monitorCount > 1) { |
| 790 | + // find the monitors at the edges of the virtual desktop |
| 791 | + int minx = numeric_limits<int>::max(); |
| 792 | + int miny = numeric_limits<int>::max(); |
| 793 | + int maxx = numeric_limits<int>::min(); |
| 794 | + int maxy = numeric_limits<int>::min(); |
| 795 | + int x, y, w, h; |
| 796 | + int monitorLeft = 0, monitorRight = 0, monitorTop = 0, monitorBottom = 0; |
| 797 | + for (int i = 0; i < monitorCount; i++) { |
| 798 | + glfwGetMonitorPos(monitors[i], &x, &y); |
| 799 | + auto videoMode = glfwGetVideoMode(monitors[i]); |
| 800 | + w = videoMode->width; |
| 801 | + h = videoMode->height; |
| 802 | + if (x < minx) { |
| 803 | + monitorLeft = i; |
| 804 | + minx = x; |
| 805 | + } |
| 806 | + if (y < miny) { |
| 807 | + monitorTop = i; |
| 808 | + miny = y; |
| 809 | + } |
| 810 | + if (x + w > maxx) { |
| 811 | + monitorRight = i; |
| 812 | + maxx = x + w; |
| 813 | + } |
| 814 | + if (y + h > maxy) { |
| 815 | + monitorBottom = i; |
| 816 | + maxy = y + h; |
| 817 | + } |
| 818 | + } |
| 819 | + |
| 820 | + // send fullscreen_monitors event with the edges monitors |
| 821 | + Atom m_net_fullscreen_monitors = XInternAtom(display, "_NET_WM_FULLSCREEN_MONITORS", false); |
| 822 | + |
| 823 | + XEvent xev; |
| 824 | + |
| 825 | + xev.xclient.type = ClientMessage; |
| 826 | + xev.xclient.serial = 0; |
| 827 | + xev.xclient.send_event = True; |
| 828 | + xev.xclient.window = nativeWin; |
| 829 | + xev.xclient.message_type = m_net_fullscreen_monitors; |
| 830 | + xev.xclient.format = 32; |
| 831 | + |
| 832 | + xev.xclient.data.l[0] = monitorTop; |
| 833 | + xev.xclient.data.l[1] = monitorBottom; |
| 834 | + xev.xclient.data.l[2] = monitorLeft; |
| 835 | + xev.xclient.data.l[3] = monitorRight; |
| 836 | + xev.xclient.data.l[4] = 1; |
| 837 | + XSendEvent(display, RootWindow(display, DefaultScreen(display)), |
| 838 | + False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); |
| 839 | + currentW = maxx - minx; |
| 840 | + currentH = maxy - minx; |
| 841 | + } else { |
| 842 | + auto monitor = glfwGetWindowMonitor(windowP); |
| 843 | + if (monitor) { |
| 844 | + auto videoMode = glfwGetVideoMode(monitor); |
| 845 | + if (videoMode) { |
| 846 | + currentW = videoMode->width; |
| 847 | + currentH = videoMode->height; |
| 848 | + } |
| 849 | + } |
| 850 | + } |
| 851 | + } |
| 852 | + |
| 853 | + // send fullscreen event |
| 854 | + Atom m_net_state = XInternAtom(display, "_NET_WM_STATE", false); |
| 855 | + Atom m_net_fullscreen = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", false); |
| 856 | + |
| 857 | + XEvent xev; |
| 858 | + |
| 859 | + xev.xclient.type = ClientMessage; |
| 860 | + xev.xclient.serial = 0; |
| 861 | + xev.xclient.send_event = True; |
| 862 | + xev.xclient.window = nativeWin; |
| 863 | + xev.xclient.message_type = m_net_state; |
| 864 | + xev.xclient.format = 32; |
| 865 | + |
| 866 | + if (fullscreen) |
| 867 | + xev.xclient.data.l[0] = 1; |
| 868 | + else |
| 869 | + xev.xclient.data.l[0] = 0; |
| 870 | + |
| 871 | + xev.xclient.data.l[1] = m_net_fullscreen; |
| 872 | + xev.xclient.data.l[2] = 0; |
| 873 | + xev.xclient.data.l[3] = 0; |
| 874 | + xev.xclient.data.l[4] = 0; |
| 875 | + XSendEvent(display, RootWindow(display, DefaultScreen(display)), |
| 876 | + False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); |
| 877 | + |
| 878 | + // tell the window manager to bypass composition for this window in fullscreen for speed |
| 879 | + // it'll probably help solving vsync issues |
| 880 | + Atom m_bypass_compositor = XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False); |
| 881 | + unsigned long value = fullscreen ? 1 : 0; |
| 882 | + XChangeProperty(display, nativeWin, m_bypass_compositor, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); |
| 883 | + |
| 884 | + XFlush(display); |
| 885 | + |
| 886 | + #ifdef TARGET_RASPBERRY_PI |
| 887 | + if (!fullscreen) { |
| 888 | + needsResizeCheck = true; |
| 889 | + } |
| 890 | + #endif |
| 891 | + } else { |
| 892 | + int monitorCount; |
| 893 | + GLFWmonitor ** monitors = glfwGetMonitors(&monitorCount); |
| 894 | + int monitorIdx = getCurrentMonitor(); |
| 895 | + if (fullscreen) { |
| 896 | + auto mode = glfwGetVideoMode(monitors[monitorIdx]); |
| 897 | + glfwSetWindowMonitor(windowP, monitors[monitorIdx], 0, 0, mode->width, mode->height, mode->refreshRate); |
| 898 | + } else { |
| 899 | + glfwSetWindowMonitor(windowP, nullptr, windowRect.x, windowRect.y, windowW, windowH, 0); |
| 900 | + } |
| 901 | + } |
868 | 902 |
|
869 | 903 | #elif defined(TARGET_OSX)
|
870 | 904 |
|
@@ -1738,17 +1772,29 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
|
1738 | 1772 |
|
1739 | 1773 | #if defined(TARGET_LINUX)
|
1740 | 1774 | Display * ofAppGLFWWindow::getX11Display() {
|
1741 |
| - return glfwGetX11Display(); |
| 1775 | + return usingWayland ? nullptr : glfwGetX11Display(); |
1742 | 1776 | }
|
1743 | 1777 |
|
1744 | 1778 | Window ofAppGLFWWindow::getX11Window() {
|
1745 |
| - return glfwGetX11Window(windowP); |
| 1779 | + return usingWayland ? 0 : glfwGetX11Window(windowP); |
1746 | 1780 | }
|
1747 | 1781 |
|
1748 | 1782 | XIC ofAppGLFWWindow::getX11XIC() {
|
1749 |
| - return xic; |
| 1783 | + return usingWayland ? nullptr : xic; |
1750 | 1784 | }
|
1751 |
| -#endif |
| 1785 | + |
| 1786 | + wl_display * ofAppGLFWWindow::getWaylandDisplay() { |
| 1787 | + return usingWayland ? glfwGetWaylandDisplay() : nullptr; |
| 1788 | + } |
| 1789 | + |
| 1790 | + wl_surface * ofAppGLFWWindow::getWaylandSurface() { |
| 1791 | + return usingWayland ? glfwGetWaylandWindow(windowP) : nullptr; |
| 1792 | + } |
| 1793 | + |
| 1794 | + bool ofAppGLFWWindow::isUsingWayland() const { |
| 1795 | + return usingWayland; |
| 1796 | + } |
| 1797 | + #endif |
1752 | 1798 |
|
1753 | 1799 | #if defined(TARGET_LINUX) && !defined(TARGET_OPENGLES)
|
1754 | 1800 | GLXContext ofAppGLFWWindow::getGLXContext() {
|
|
0 commit comments