HumbleUI / JWM

Cross-platform window management and OS integration library for Java
Apache License 2.0
547 stars 44 forks source link

Wayland support #278

Open TheDrawingCoder-Gamer opened 7 months ago

TheDrawingCoder-Gamer commented 7 months ago

Untested (couldn't get javac to work) Highly expect something here to be extremely broken. Would not be surprised if there was an instant segfault. bc it's literally a new platform this touches a LOT of files. Will work on merging

dzaima commented 7 months ago

could it be that like the surface that pointerHandleEnter gets is the libdecor-internal-window surface, while the one set up in WindowWayland::show is the outer one that includes decoration, or something like that? or vice versa? (I have no clue)

TheDrawingCoder-Gamer commented 7 months ago

If so I would assume vice versa, as I'm pretty sure that libdecor instances an outer surface to draw its frame.

TheDrawingCoder-Gamer commented 7 months ago

The issue is that, if it IS a different surface, then changing back to the _nativeWindowToMy won't work bc it's a different pointer. However that would mean we would just do nothing if it isn't a window

TheDrawingCoder-Gamer commented 7 months ago

New commit fixes that. However I still have incorrect rendering (and no rendering at all under sway)

TheDrawingCoder-Gamer commented 7 months ago

!!! image AND IT WORKS WITH HIDPI!

TheDrawingCoder-Gamer commented 7 months ago

what needs to be done for tomorrow: Repeating on keyboard input Multiple windows (it technically functions but something is very obviously wrong) Make raster layer work

TheDrawingCoder-Gamer commented 7 months ago

@dzaima, i'm curious on what debugging tools you are using. I am not really using debugging tools, which is why I'm struggling with making this stuff work.

dzaima commented 7 months ago

@dzaima, i'm curious on what debugging tools you are using. I am not really using debugging tools, which is why I'm struggling with making this stuff work.

I've been using https://rr-project.org/ with a currently-unpublished GUI I've been working on for it (uses JWM & Skija!), but I don't think it's really been too helpful, especially with rr having some bugs and crashing every now and then. Spamming printfs goes a long way, main thing rr's been useful for is tracking memory issues but even then it's very time-consuming to jump around & decipher things.

TheDrawingCoder-Gamer commented 7 months ago

todo tommorow:

tbh i have no idea how i'm going to figure out number 2, bc it's kind of odd, and idk how to really inspect the app. number 1 is also going to be a pain - switching layers isn't working most of the time, and raster only redraws whenever the compositor is like OH CRAP THIS IS SO MANY BUFFERS or an event occurs.

dzaima commented 7 months ago

for whatever reason, raster seems to work a lot better if libdecor_dispatch is called twice after libdecor_frame_map, not once (and works better more frequently with more libdecor_dispatch calls). Feels like something that'd happen if some events are sent in the wrong order otherwise?

dzaima commented 7 months ago

oh, and btw, changes I need to have it build/work better (some that have been lingering around, some new:)

diff ```diff diff --git a/script/package.py b/script/package.py index 5bd1fa1..adde315 100755 --- a/script/package.py +++ b/script/package.py @@ -22,7 +22,8 @@ def main() -> Tuple[str, str, str]: jar = build_utils.jar(f"target/jwm-{common.version}.jar", ("target/classes", "."), ("target/maven", "META-INF")) build_utils.makedirs("target/src/io/github/humbleui/jwm") - shutil.copytree("linux/java", "target/src/io/github/humbleui/jwm", dirs_exist_ok=True) + shutil.copytree("x11/java", "target/src/io/github/humbleui/jwm", dirs_exist_ok=True) + shutil.copytree("wayland/java", "target/src/io/github/humbleui/jwm", dirs_exist_ok=True) shutil.copytree("macos/java", "target/src/io/github/humbleui/jwm", dirs_exist_ok=True) shutil.copytree("shared/java", "target/src/io/github/humbleui/jwm", dirs_exist_ok=True) shutil.copytree("windows/java", "target/src/io/github/humbleui/jwm", dirs_exist_ok=True) diff --git a/wayland/cc/WindowManagerWayland.cc b/wayland/cc/WindowManagerWayland.cc index f3ef205..193fadb 100644 --- a/wayland/cc/WindowManagerWayland.cc +++ b/wayland/cc/WindowManagerWayland.cc @@ -403,7 +403,7 @@ void WindowManagerWayland::keyboardKey(void* data, wl_keyboard* keyboard, uint32 // TODO: while unlikely, it could be possible that you can be entering text even if the // pointer hasn't entered if (self->keyboardFocus && jwmKey != jwm::Key::UNDEFINED) { - jwm::KeyLocation location; + jwm::KeyLocation location = jwm::KeyLocation::DEFAULT; JNILocal keyEvent( app.getJniEnv(), classes::EventKey::make( diff --git a/wayland/cc/WindowWayland.cc b/wayland/cc/WindowWayland.cc index 083e0bc..5f643f2 100644 --- a/wayland/cc/WindowWayland.cc +++ b/wayland/cc/WindowWayland.cc @@ -14,8 +14,10 @@ using namespace jwm; wl_surface_listener WindowWayland::_surfaceListener = { .enter = WindowWayland::surfaceEnter, .leave = WindowWayland::surfaceLeave, + #ifdef HAVE_WAYLAND_1_22 .preferred_buffer_scale = WindowWayland::surfacePreferredBufferScale, .preferred_buffer_transform = WindowWayland::surfacePreferredBufferTransform + #endif }; @@ -462,7 +464,7 @@ extern "C" JNIEXPORT void JNICALL Java_io_github_humbleui_jwm_WindowWayland__1nS const jchar* bytes = env->GetStringChars(title, nullptr); jsize length = env->GetStringLength(title); - std::u16string thingie = {reinterpret_cast(bytes), length}; + std::u16string thingie = {reinterpret_cast(bytes), static_cast(length)}; std::string titleS = std::wstring_convert< std::codecvt_utf8_utf16, char16_t>{}.to_bytes(thingie); ```
dzaima commented 7 months ago

glfw after libdecor_frame_map has wl_display_roundtrip, which seems to be exactly the thing for my hypothesis of wrong event order: https://github.com/glfw/glfw/blob/46cebb5081820418f2a20f3e90b07f9b1bd44b42/src/wl_window.c#L803-L804; things seem to be running reasonably smoothly with libdecor_decorate; libdecor_frame_map; wl_display_roundtrip(_windowManager.display); libdecor_dispatch;

dzaima commented 7 months ago

Adding a wl_display_roundtrip(self->_windowManager.display); after decorFrameCommit's wl_surface_commit also affects things.

TheDrawingCoder-Gamer commented 7 months ago

For me adding a dispatch after roundtrip, it stalls. Bc it blocks waiting for events :/

TheDrawingCoder-Gamer commented 7 months ago

Frames aren't really drawn while paused, unlike on X11, which is odd. Not sure if this is an incorrectness with X11 or Wayland ( I assume wayland, as it only doesn't draw on Raster.) Again, raster is god awful slow ("swapBuffers" creates a new ShmPool and Buffer, which involves memory mapping a shared object. This is called every frame.) I'm capping at about 11 fps on my laptop, compared to max framerate of EGL.

I considered adding a frame callback, but that just causes the window to never show. I seriously think some form of frame throttling needs to take place bc of how god awful slow this stuff is. Of course down stream in HumbleUI it will never end up being an issue, but it's important for it to at least function.

Switching layers still doesn't work - I even tried doing a hack by closing and reopening, but it didn't work.

TheDrawingCoder-Gamer commented 7 months ago

just realized that events are never actually sent - how interesting

TheDrawingCoder-Gamer commented 6 months ago

can't for the life of me figure out why mouse cursor setting isn't working - only the first three (arrow, crosshair, help) load? and text sometimes works???? but I can tell that the hotspot is working??? I hate this

this is like one of the only issues preventing me from marking this as ready for review.

TheDrawingCoder-Gamer commented 6 months ago

Rebasing is causing issues on sway. God knows why.

git merge result ``` Auto-merging script/build_utils.py Auto-merging x11/cc/KeyX11.cc Auto-merging x11/cc/ScreenInfo.cc Auto-merging x11/cc/WindowX11.cc Merge made by the 'ort' strategy. .github/workflows/build-deploy.yml | 1 + .github/workflows/rebuild-docker-image.yaml | 2 +- CHANGELOG.md | 13 +++++++++++ script/build_utils.py | 3 ++- shared/cc/impl/JNILocal.hh | 2 +- x11/cc/KeyX11.cc | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- x11/cc/KeyX11.hh | 3 ++- x11/cc/WindowManagerX11.cc | 14 ++++++++---- x11/cc/WindowX11.cc | 11 +++++++--- 9 files changed, 113 insertions(+), 35 deletions(-) ```

Something is obviously wrong bc none of the files touched are related to wayland in any way. How awful

TheDrawingCoder-Gamer commented 6 months ago

nvm it works. There SHOULD:tm: be no more conflict (even though github is being wrong :( ) A flat copy will work (once ready)

TheDrawingCoder-Gamer commented 6 months ago

note; you'll need the patch version of skija (pretty sure master branch has the patch, but you have to pull down master and build it). For me it mostly JUST WORKS(TM) on wayland, even with hidpi.

Some jank: Positioning is completely impossible. Too bad. Resizing works only while invisible. Wayland doesn't let you resize yourself non-interactively while mapped. Resizing ALSO is tied to scale. The dimensions must be divisible by the scale, otherwise it returns early (it's implemented as a bool method but this is never exposed in java). Vsync is disabled in general. This SHOULD(TM) be ok for both GL and Raster, as the compositor is what actually handles drawing, and it's syncronized properly. the reason vsync is disabled client-side is because under Sway it blocks the whole application if even 1 window is invisible.

There are a lot of weird function changes you can revert, but iirc I didn't touch X11 at all.

It should JUST WORK(tm) on other platforms bc nothing new changed.

Wayland and X11 work side by side - Wayland is loaded if the envvar WAYLAND_DISPLAY is set, and x11 otherwise. There isn't really any error checking here (nor with a lot of this)

Yes, I know code organization is GOD AWFUL. I hate it too. Not used to C++ so bear with this god awful code organization.

I think (?) I've stripped all the debug stuff but I'll do another pass-over soon.

TheDrawingCoder-Gamer commented 6 months ago

edge cases that I totally expect to break things multiple seats (I've never actually seen/used this in the wild, but it's technically part of the spec so I GUESS we should support it) Window being halfway between monitors (I don't expect a full on crash here but I expect scaling to not be correct, esp. when moving from a HiDPI screen to a LoDPI screen)

Otherwise I haven't rigorously tested things, only doing stuff that I saw was outright broken. The test itself works fine.

Rn I need to do animated cursors. Well, idk if I need to, but it would be nice to have. My "wait" mouse being still is very jarring to me.

but rn it's time for eepy : ) have fun reviewing this god awful code in the mean time

TheDrawingCoder-Gamer commented 6 months ago

resizing interactively is also a bit jank, likely because the buffer is getting updated way too fast.

Keyboard support is also jank, there are some weird things not supported (notably the compose key, and key repeating).

Going to work on key repeating and maybe compose key? Resizing is also probably more important

TheDrawingCoder-Gamer commented 6 months ago

resizing is now responsive on sway and weston, but on weston it sometimes puts up a black screen. Fully functions under wlroots though (tested on sway & wayfire)

TheDrawingCoder-Gamer commented 6 months ago

now works without having to use --skija-dir. Repeating keys also half functions (technically functions but lags in an odd way). Compose key fully (?) functions, but not at parity with X11.

For some file conflicts it only happened bc I open the file and use :wq instead of :q!. :wq adds a newline at the end of the file.

Resizing still jank on weston, but works well enough on wlroots. Need to test on other major compositors (mutter and kwin). It functions by forcing a draw by dispatching a frame event then blocking on the processEvents thing.

It only blocks if the new screen scale is different from old screen scale, because wayland can crash if you set preferred buffer size to e.g. 2 while the dimensions of the buffer aren't multiples of the scale. Previously this was solved by making it a frame callback but that caused jank.

TheDrawingCoder-Gamer commented 6 months ago

...? it doesn't accept any events while window isn't focused. Bad for clipboard bc of how wayland has clipboard; event loop HAS to be running for clipboard copying to work. No idea what/if anything is blocking. This will cause wl-paste to freeze if i try to paste from it, but it pastes if I focus back. Firefox flat-out doesn't paste at all (likely an attempt to prevent stalling bc of pasting). Painful... No idea what's blocking

TheDrawingCoder-Gamer commented 6 months ago

it was GL. apparently eglMakeCurrent resets swap interval. how upsetting.

only real thing is animated cursors but that is going to be so so painful.

TheDrawingCoder-Gamer commented 6 months ago

please try testing it now - it works enough for me. weston flickers while resizing on GL, but who cares about weston. Test Mutter (gnome) and KWin (KDE).

animated cursor support is basically the last thing I need? again I don't really think it's needed but it will make me endlessly upset if I don't add it.

dzaima commented 6 months ago

App.getPrimaryScreen() appears to still error, which breaks my JWM usage; replacing that with a hard-coded rectangle gets it running.

Scroll wheel is inverted & way too slow for me on Weston, compared to X11 (speed could be a difference between weston config & my native system, but direction shouldn't be).

FPS seems to be uncapped if every frame requests another frame (e.g. in the ./script/run.py window clicking ctrl+p; also seems that that ctrl+p is sent twice?).

Interacting with the decorations is somewhat buggy, though I don't know how much of that is the fault of whatever outdated versions of libdecor/weston I have from apt.

There are some segfaults/exceptions around closing windows, which I might look into.

TheDrawingCoder-Gamer commented 6 months ago

perhaps I am just not mentally processing everything I have to for C++. I have never used RAII before outside of rust and rust makes it blatantly obvious. I would wager a guess that a vast majority of these are related to RAII in some way.

TheDrawingCoder-Gamer commented 6 months ago

In wayland, you are expected to assume that all keys are released, but it's unspecified what you do on enter, when it gives you the keys in an array. I currently process it like they were just pressed, and I assume that's why there is jank on new windows? Very frustrating : / really wish I was writing in either a GC'd language or rust.

Yeah holding down Ctrl-N under sway spawns a ton of windows...

dzaima commented 6 months ago

RAII only applies to the C++ JWM-specific parts, which I think work fine? Segfaults usually come from within the wayland library, which has its own memory management rules (and cannot be RAII as the interface is all C pointers)

TheDrawingCoder-Gamer commented 6 months ago

hmmmm.... does this mean I'm doing something wrong? wonder where.

i've wrapped keyboard and pointer in classes with destructors but idk... I will try to add some extra things to make the compiler not do implicit copies?

TheDrawingCoder-Gamer commented 6 months ago

yeah I kind of feel like I'm out of my depth with debugging C++. I don't write systems languages outside of rust, and rust practically holds my hand compared to C++. I'm going to take a break for the holidays and rotate what I can do now.

Reverting to draft as well.

TheDrawingCoder-Gamer commented 6 months ago

as expected nvidia EGL doesn't work. How shocking.

it's just a black window with proprietary drivers. Raster works. Will try to fix this, but sway itself requires me to pass --unsupported-gpu bc of how god awful the support is.

TheDrawingCoder-Gamer commented 6 months ago

have a suspicion that there is memory corruption at play somewhere in pointerLeave and mouseUpdateUnscaled. jank

TheDrawingCoder-Gamer commented 6 months ago

somehow managed to break it JUST on non-nvidia sway. wayfire works?

TheDrawingCoder-Gamer commented 6 months ago

Have to memory leak frame to prevent crash on close. Jank. This is a bug with the free code of libdecor.

Weston is so buggy on NVIDIA that I don't really think it's worth trying to test it. Sway and Wayfire work so I assume most wlroots work.

I've observed that hooking this up to my local UI toolkit causes the window to be black until an update that forces a redraw is sent.

HumbleUI itself seems to be working with the obvious tweaks to prevent breakage with matching on Platform.

there are some nvidia and mesa exclusive bugs too so it's good I have a laptop and desktop. Getting segfaults on resizing with nvidia that aren't observable in mesa. I think (?) mesa is mostly working but idk. try testing it again to see how busted it is.

TheDrawingCoder-Gamer commented 6 months ago

tbh i'm okay with nvidia bugs just cause like... if you are on nvidia and using wayland you are doing something wrong. I'm sure that for older cards the open source drivers work, but propreitary drivers are almost universally unsupported. I'll try to fix it but it's not as high priority as it would be on mesa.

TheDrawingCoder-Gamer commented 6 months ago

re: lock cursor. Should I be receiving mouse motion events on mouse move? if so then locking the cursor won't work, doesn't actually send any events. X11 has no impl so my local machine has no reference.

I would see either way, esp. considering there is a "movementX" and "movementY" argument.

note: compilation now requires wayland-scanner and wayland-protocols (and KDE's extra-cmake-modules, but if this isn't ok I can copy files directly.)

TheDrawingCoder-Gamer commented 6 months ago

want to say that winit has a bunch of options in certain places, including primary screens. I think it's ok to just add Nullable to getPrimaryScreen and let the IDE handle it. This will likely break downstream projects (including yours) but I'm ok with that. It's better to say there is no data that makes sense then give data that doesn't make sense.

TheDrawingCoder-Gamer commented 6 months ago

would like to add touch support but have no touch screen, and afaik my tablet is just a mouse in non supported apps. I can add touch support for my trackpad though.

edit: no I can't. wayland doesn't expose any interface to get finger coordinates.

TheDrawingCoder-Gamer commented 6 months ago

weston support is blocked by https://gitlab.freedesktop.org/libdecor/libdecor/-/issues/59. any WM that has decorations will cause errors as GTK doesn't work, and I can't figure out how to force Cairo instead of GTK. would be fixed when the related PR up there is merged, or if I figure out how to force Cairo, as my desktop doesn't use GTK and it's functioning better

TheDrawingCoder-Gamer commented 6 months ago

@tonsky it's unhelpful to have this running every time and wasting runtime. idk if you can disable it but please do. I've made local edits to Dockerfile but idk how to really test them.

TheDrawingCoder-Gamer commented 6 months ago

not getting anymore crashes related to window closing. Going to remark as ready for review - note for the title bar it no longer uses libdecor and there is no more text - text would require pulling in an extra drawing library on the native level, like cairo, which I'm unsure if wanted. If this is ok point me at a library to use and i'll make it look somewhat nicer. Right now it uses subsurfaces to build a window border for resizing and movement, and subsurface buttons for minimize close and maximize/restore.

probably still has issues under nvidia, too bad! will test later but as said previously i'm fine with crashing under jank circumstances on wayland for nvidia, bc they are propreitary drivers that can't be debugged.

Notes on vsync: can't really do it (?) Tried delaying the redraw with surface. Causes window not to show, and with extra tinkering to add a force redraw flag, it shows but under sway it doesn't update. Raster will never screen tear as wayland compositors are the ones that actually render out frames. GL may cause screen tearing because the underlying method hooks into the tearing control protocol. This means KWin and Weston may have screen tearing (other compositors don't implement this protocol). All in all I think it's working well enough on Mesa that I would use it downstream. Of course this requires changes downstream in HumbleUI (I already have a local working build).

Other things I would suggest are adding ways to tell the end client that a key is now pressed but that it shouldn't react to this in UI (for surface enter).

dzaima commented 5 months ago

Wanted to try this out on the just-released Linux Mint 21.3 experimental wayland, but with on latest commit with sudo apt-get install wayland-protocols wayland-scanner++ extra-cmake-modules I get this:

-- Java home found automatically at /usr/lib/jvm/java-1.11.0-openjdk-amd64. Set JAVA_HOME environment variable to override.
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/linux2/git/jwm-wayland/x11/build
[7/7] Linking CXX shared library libjwm_x64.so
-- Could NOT find WaylandProtocols: Found unsuitable version "1.25", but required is at least "1.32" (found //usr/share/wayland-protocols)
CMake Error at CMakeLists.txt:15 (message):
  No protocols installed

-- Configuring incomplete, errors occurred!

Changing that 1.32 to 1.25 then gives this:

-- Java home found automatically at /usr/lib/jvm/java-1.11.0-openjdk-amd64. Set JAVA_HOME environment variable to override.
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/linux2/git/jwm-wayland/x11/build
ninja: no work to do.
-- Found WaylandProtocols: //usr/share/wayland-protocols (found suitable version "1.25", minimum required is "1.25")
-- Java home found automatically at /usr/lib/jvm/java-1.11.0-openjdk-amd64. Set JAVA_HOME environment variable to override.
-- Configuring done
CMake Error at CMakeLists.txt:38 (add_library):
  No SOURCES given to target: protocols

CMake Generate step failed.  Build files cannot be regenerated correctly.

switching to a random old commit of 1d7fb1af4fed7bbd0f40b23569a7b1d86bd59a9c and building there compiled, but running then crashed Cinnamon, killing all apps, including the browser, making me rewrite this comment :/

TheDrawingCoder-Gamer commented 5 months ago

try latest commit - reduces version requirement + uses a var for protocols instead of directly appending.

dzaima commented 5 months ago

Fails to build with

FAILED: CMakeFiles/jwm.dir/cc/Decoration.cc.o
/usr/bin/c++ -Djwm_EXPORTS -I/mnt/linux2/git/jwm-wayland/wayland/../shared/cc -I/mnt/linux2/git/jwm-wayland/wayland/../linux/cc -I/usr/lib/jvm/java-1.11.0-openjdk-amd64/include -I/usr/lib/jvm/java-1.11.0-openjdk-amd64/include/linux -I/mnt/linux2/git/jwm-wayland/wayland/build -O3 -DNDEBUG -fPIC -std=gnu++14 -MD -MT CMakeFiles/jwm.dir/cc/Decoration.cc.o -MF CMakeFiles/jwm.dir/cc/Decoration.cc.o.d -o CMakeFiles/jwm.dir/cc/Decoration.cc.o -c /mnt/linux2/git/jwm-wayland/wayland/cc/Decoration.cc
/mnt/linux2/git/jwm-wayland/wayland/cc/Decoration.cc:196:6: error: field designator 'wm_capabilities' does not refer to any field in type 'xdg_toplevel_listener'
  196 |     .wm_capabilities = _xdgToplevelWmCapabilities
      |     ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

commenting out that line results in it running though! Doesn't look like that Mint's wayland cinnamon is anywhere near complete enough to test on though, weston manages to be less janky.

TheDrawingCoder-Gamer commented 5 months ago

bc of how I bind xdg_shell it's ok to omit these versions. Compositors don't send events to clients that bind to lower versions.

TheDrawingCoder-Gamer commented 2 months ago

update: i'm probably not going to work on this anymore. anyone else is free to continue this. It works well enough for me but I don't really know what else to do here. I think (?) it's good enough but needs more testing