Skip to content

Commit d40b787

Browse files
committed
Linux Wayland support
1 parent af36cb4 commit d40b787

File tree

2 files changed

+196
-141
lines changed

2 files changed

+196
-141
lines changed

libs/openFrameworks/app/ofAppGLFWWindow.cpp

Lines changed: 187 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,18 @@
1111
#include "ofIcon.h"
1212
#include "ofImage.h"
1313
#define GLFW_EXPOSE_NATIVE_X11
14+
#define GLFW_EXPOSE_NATIVE_WAYLAND
1415
#ifndef TARGET_OPENGLES
1516
#define GLFW_EXPOSE_NATIVE_GLX
1617
#else
1718
#define GLFW_EXPOSE_NATIVE_EGL
1819
#endif
1920
#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
2326
#include <xcb/xcb.h>
2427
#include <xcb/xcbext.h>
2528
#elif defined(TARGET_OSX)
@@ -59,6 +62,10 @@ ofAppGLFWWindow::ofAppGLFWWindow()
5962
currentW = 0;
6063
currentH = 0;
6164

65+
#if defined(TARGET_LINUX) && !defined(TARGET_RASPBERRY_PI_LEGACY)
66+
usingWayland = false;
67+
#endif
68+
6269
glfwSetErrorCallback(error_cb);
6370
}
6471

@@ -160,6 +167,11 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
160167
ofLogError("ofAppGLFWWindow") << "couldn't init GLFW";
161168
return;
162169
}
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
163175

164176
// ofLogNotice("ofAppGLFWWindow") << "WINDOW MODE IS " << screenMode;
165177

@@ -399,31 +411,45 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
399411
glfwSetWindowRefreshCallback(windowP, refresh_cb);
400412

401413
#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
415433
}
416434

417435
#ifdef TARGET_LINUX
418436
//------------------------------------------------------------
419437
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+
}
420442
ofPixels iconPixels;
421443
ofLoadImage(iconPixels, path);
422444
setWindowIcon(iconPixels);
423445
}
424446

425447
//------------------------------------------------------------
426448
void ofAppGLFWWindow::setWindowIcon(const ofPixels & iconPixels) {
449+
if (usingWayland) {
450+
ofLogWarning("ofAppGLFWWindow") << "Setting window icon is not supported on Wayland";
451+
return;
452+
}
427453
iconSet = true;
428454
int length = 2 + iconPixels.getWidth() * iconPixels.getHeight();
429455
vector<unsigned long> buffer(length);
@@ -744,127 +770,135 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
744770
}
745771

746772
#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+
}
868902

869903
#elif defined(TARGET_OSX)
870904

@@ -1738,17 +1772,29 @@ void ofAppGLFWWindow::setup(const ofGLESWindowSettings & settings) {
17381772

17391773
#if defined(TARGET_LINUX)
17401774
Display * ofAppGLFWWindow::getX11Display() {
1741-
return glfwGetX11Display();
1775+
return usingWayland ? nullptr : glfwGetX11Display();
17421776
}
17431777

17441778
Window ofAppGLFWWindow::getX11Window() {
1745-
return glfwGetX11Window(windowP);
1779+
return usingWayland ? 0 : glfwGetX11Window(windowP);
17461780
}
17471781

17481782
XIC ofAppGLFWWindow::getX11XIC() {
1749-
return xic;
1783+
return usingWayland ? nullptr : xic;
17501784
}
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
17521798

17531799
#if defined(TARGET_LINUX) && !defined(TARGET_OPENGLES)
17541800
GLXContext ofAppGLFWWindow::getGLXContext() {

0 commit comments

Comments
 (0)