Skip to content

Commit

Permalink
Support fullscreen on X11 HumbleUI#259
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky authored Dec 24, 2022
2 parents 382e762 + d5a10cf commit 2e032f2
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 8 deletions.
4 changes: 2 additions & 2 deletions examples/dashboard/java/PanelLegend.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public PanelLegend(Window window) {
shortcuts.put("N", "New Window");
shortcuts.put("T", "Toggle Titlebar");
shortcuts.put("W", "Close Window");
shortcuts.put("F", "Fullscreen");
shortcuts.put("F", "Toggle Fullscreen");
shortcuts.put("X", "Clipboard formats");
shortcuts.put("Y", "Hide mouse cursor");
shortcuts.put("U", "Lock mouse cursor");
Expand Down Expand Up @@ -63,4 +63,4 @@ public void paintImpl(Canvas canvas, int width, int height, float scale) {
}
}
}
}
}
3 changes: 2 additions & 1 deletion linux/cc/WindowManagerX11.hh
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ namespace jwm {
DEFINE_ATOM(_NET_WM_STATE_MAXIMIZED_HORZ);
DEFINE_ATOM(_NET_FRAME_EXTENTS);
DEFINE_ATOM(_NET_WM_SYNC_REQUEST_COUNTER);
DEFINE_ATOM(_NET_WM_STATE_FULLSCREEN);
DEFINE_ATOM(WM_PROTOCOLS);
DEFINE_ATOM(UTF8_STRING);
DEFINE_ATOM(CLIPBOARD);
Expand All @@ -136,4 +137,4 @@ namespace jwm {
Atoms& getAtoms() { return _atoms; }
void setClipboardContents(std::map<std::string, ByteBuf>&& c);
};
}
}
134 changes: 133 additions & 1 deletion linux/cc/WindowX11.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "WindowX11.hh"
#include <jni.h>
#include <memory>
#include <cstring>
#include "AppX11.hh"
#include "impl/Library.hh"
#include "impl/JNILocal.hh"
Expand Down Expand Up @@ -165,6 +166,125 @@ void WindowX11::restore() {
}
}

void WindowX11::setFullScreen(bool isFullScreen) {
// NOTE: Largely borrowed from https://github.com/godotengine/godot/blob/f7cf9fb148140b86ee5795110373a0d55ff32860/platform/linuxbsd/x11/display_server_x11.cpp
Display* display = _windowManager.display;

// Should the window be exclusively full screen (i.e. block out other popups).
// There isn't a HumbleUI setting for this, and my WM defaults to exclusive full-screen,
// (as does Windows, as I recall) so let's assume that we want the window to be exclusively fullscreen.
bool isExclusiveFullScreen = true;

if (isFullScreen) { // and the window is not borderless:
// Remove window decorations to simulate full screen
MotifHints hints;
Atom property;
hints.flags = 2;
hints.decorations = 0;
property = XInternAtom(display, "_MOTIF_WM_HINTS", True);
if (property != None) {
XChangeProperty(display, _x11Window, property, property, 32, PropModeReplace, (unsigned char *)&hints, 5);
}
}

XEvent xev;

memset(&xev, 0, sizeof(xev));
xev.type = ClientMessage;
xev.xclient.window = _x11Window;
xev.xclient.message_type = _windowManager._atoms._NET_WM_STATE;
xev.xclient.format = 32;
xev.xclient.data.l[0] = isFullScreen ? _WM_ADD : _WM_REMOVE;
xev.xclient.data.l[1] = _windowManager._atoms._NET_WM_STATE_FULLSCREEN;
xev.xclient.data.l[2] = 0;

XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev);

// set bypass compositor hint
Atom bypass_compositor = XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", True);
unsigned long compositing_disable_on = 0; // By default, don't allow window compositing

if (isFullScreen) {
// NOTE: Compositor flickers. May be an issue.
if (isExclusiveFullScreen) {
compositing_disable_on = 1; // Force compositing to disable for efficiency
} else {
compositing_disable_on = 2; // Force composition on to allow pop-up windows
}
}

if (bypass_compositor != None) {
XChangeProperty(display,
_x11Window,
bypass_compositor,
XA_CARDINAL,
32,
PropModeReplace,
(unsigned char *)&compositing_disable_on,
1);
}

XFlush(display);

if (!isFullScreen) {
// Reset window decorations to their previous states
MotifHints hints;
Atom property;
hints.flags = 2;
hints.decorations = 1; // Add window borders back
property = XInternAtom(display, "_MOTIF_WM_HINTS", True);
if (property != None) {
XChangeProperty(display,
_x11Window,
property,
property,
32,
PropModeReplace,
(unsigned char *)&hints,
5);
}
}
}

bool WindowX11::isFullScreen() {
// NOTE: Largely borrowed from https://github.com/godotengine/godot/blob/f7cf9fb148140b86ee5795110373a0d55ff32860/platform/linuxbsd/x11/display_server_x11.cpp
Display* display = _windowManager.display;

Atom type;
int format;
unsigned long len;
unsigned long remaining;
unsigned char *data = nullptr;
bool retval = false;

int result = XGetWindowProperty(
display,
_x11Window,
_windowManager._atoms._NET_WM_STATE,
0,
1024,
False,
XA_ATOM,
&type,
&format,
&len,
&remaining,
&data);

if (result == Success) {
Atom *atoms = (Atom *)data;
for (uint64_t i = 0; i < len; i++) {
if (atoms[i] == _windowManager._atoms._NET_WM_STATE_FULLSCREEN) {
retval = true;
break;
}
}
XFree(data);
}

return retval;
}

void WindowX11::getDecorations(int& left, int& top, int& right, int& bottom) {
unsigned long* data = nullptr;
_xGetWindowProperty(_windowManager.getAtoms()._NET_FRAME_EXTENTS, XA_CARDINAL, reinterpret_cast<unsigned char**>(&data));
Expand Down Expand Up @@ -452,4 +572,16 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetMo
jwm::WindowX11* instance = reinterpret_cast<jwm::WindowX11*>(jwm::classes::Native::fromJava(env, obj));

instance->setCursor(static_cast<jwm::MouseCursor>(idx));
}
}

extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowX11__1nSetFullScreen
(JNIEnv* env, jobject obj, jboolean isFullScreen) {
jwm::WindowX11* instance = reinterpret_cast<jwm::WindowX11*>(jwm::classes::Native::fromJava(env, obj));
instance->setFullScreen(isFullScreen);
}

extern "C" JNIEXPORT jboolean JNICALL Java_io_github_humbleui_jwm_WindowX11__1nIsFullScreen
(JNIEnv* env, jobject obj) {
jwm::WindowX11* instance = reinterpret_cast<jwm::WindowX11*>(jwm::classes::Native::fromJava(env, obj));
return instance->isFullScreen();
}
8 changes: 6 additions & 2 deletions linux/cc/WindowX11.hh
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ namespace jwm {
void minimize();
void restore();

void setFullScreen(bool isFullScreen);
bool isFullScreen();

XIC getIC() const {
return _ic;
}
Expand All @@ -69,6 +72,8 @@ namespace jwm {
int _posY = -1;
int _width = -1;
int _height = -1;
int _WM_ADD = 1L;
int _WM_REMOVE = 0L;
bool _visible = false;

bool _isRedrawRequested = false;
Expand All @@ -77,6 +82,5 @@ namespace jwm {
ILayer* _layer = nullptr;
::Window _x11Window = 0;
XIC _ic;

};
}
}
9 changes: 7 additions & 2 deletions linux/java/WindowX11.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,15 @@ public Window restore() {

@Override
public Window setFullScreen(boolean value) {
throw new UnsupportedOperationException("impl me!");
assert _onUIThread();
_nSetFullScreen(value);
return this;
}

@Override
public boolean isFullScreen() {
throw new UnsupportedOperationException("impl me!");
assert _onUIThread();
return _nIsFullScreen();
}

@ApiStatus.Internal public static native long _nMake();
Expand All @@ -210,4 +213,6 @@ public boolean isFullScreen() {
@ApiStatus.Internal public native void _nRestore();
@ApiStatus.Internal public native Screen _nSetTitle(byte[] title);
@ApiStatus.Internal public native void _nSetTitlebarVisible(boolean isVisible);
@ApiStatus.Internal public native void _nSetFullScreen(boolean isFullScreen);
@ApiStatus.Internal public native boolean _nIsFullScreen();
}

0 comments on commit 2e032f2

Please sign in to comment.